aboutsummaryrefslogtreecommitdiff
path: root/kamon-core-tests/src/test/scala/kamon/metric
diff options
context:
space:
mode:
Diffstat (limited to 'kamon-core-tests/src/test/scala/kamon/metric')
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/FilterSpec.scala72
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/GlobPathFilterSpec.scala72
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/HistogramSpec.scala94
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/LongAdderCounterSpec.scala62
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala62
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala90
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala58
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/RegexPathFilterSpec.scala61
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/TimerSpec.scala72
-rw-r--r--kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala114
10 files changed, 757 insertions, 0 deletions
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/FilterSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/FilterSpec.scala
new file mode 100644
index 00000000..cda76dc2
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/FilterSpec.scala
@@ -0,0 +1,72 @@
+package kamon
+package metric
+
+import com.typesafe.config.ConfigFactory
+import org.scalatest.{Matchers, WordSpec}
+
+
+class FilterSpec extends WordSpec with Matchers {
+ val testConfig = ConfigFactory.parseString(
+ """
+ |kamon.util.filters {
+ |
+ | some-filter {
+ | includes = ["**"]
+ | excludes = ["not-me"]
+ | }
+ |
+ | only-includes {
+ | includes = ["only-me"]
+ | }
+ |
+ | only-excludes {
+ | excludes = ["not-me"]
+ | }
+ |
+ | specific-rules {
+ | includes = ["glob:/user/**", "regex:test-[0-5]"]
+ | }
+ |
+ | "filter.with.quotes" {
+ | includes = ["**"]
+ | excludes = ["not-me"]
+ | }
+ |}
+ """.stripMargin
+ )
+
+ Kamon.reconfigure(testConfig.withFallback(Kamon.config()))
+
+ "the entity filters" should {
+ "reject anything that doesn't match any configured filter" in {
+ Kamon.filter("not-a-filter", "hello") shouldBe false
+ }
+
+ "evaluate patterns for filters with includes and excludes" in {
+ Kamon.filter("some-filter", "anything") shouldBe true
+ Kamon.filter("some-filter", "some-other") shouldBe true
+ Kamon.filter("some-filter", "not-me") shouldBe false
+ }
+
+ "allow configuring includes only or excludes only for any filter" in {
+ Kamon.filter("only-includes", "only-me") shouldBe true
+ Kamon.filter("only-includes", "anything") shouldBe false
+ Kamon.filter("only-excludes", "any-other") shouldBe false
+ Kamon.filter("only-excludes", "not-me") shouldBe false
+ }
+
+ "allow to explicitly decide whether patterns are treated as Glob or Regex" in {
+ Kamon.filter("specific-rules", "/user/accepted") shouldBe true
+ Kamon.filter("specific-rules", "/other/rejected/") shouldBe false
+ Kamon.filter("specific-rules", "test-5") shouldBe true
+ Kamon.filter("specific-rules", "test-6") shouldBe false
+ }
+
+ "allow filters with quoted names" in {
+ Kamon.filter("filter.with.quotes", "anything") shouldBe true
+ Kamon.filter("filter.with.quotes", "some-other") shouldBe true
+ Kamon.filter("filter.with.quotes", "not-me") shouldBe false
+ }
+
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/GlobPathFilterSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/GlobPathFilterSpec.scala
new file mode 100644
index 00000000..c21b1256
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/GlobPathFilterSpec.scala
@@ -0,0 +1,72 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2017 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon
+package metric
+
+import kamon.util.GlobPathFilter
+import org.scalatest.{Matchers, WordSpecLike}
+
+class GlobPathFilterSpec extends WordSpecLike with Matchers {
+ "The GlobPathFilter" should {
+
+ "match a single expression" in {
+ val filter = new GlobPathFilter("/user/actor")
+
+ filter.accept("/user/actor") shouldBe true
+ filter.accept("/user/actor/something") shouldBe false
+ filter.accept("/user/actor/somethingElse") shouldBe false
+ }
+
+ "match all expressions in the same level" in {
+ val filter = new GlobPathFilter("/user/*")
+
+ filter.accept("/user/actor") shouldBe true
+ filter.accept("/user/otherActor") shouldBe true
+ filter.accept("/user/something/actor") shouldBe false
+ filter.accept("/user/something/otherActor") shouldBe false
+ }
+
+ "match any expressions when using double star alone (**)" in {
+ val filter = new GlobPathFilter("**")
+
+ filter.accept("GET: /ping") shouldBe true
+ filter.accept("GET: /ping/pong") shouldBe true
+ filter.accept("this-doesn't_look good but-passes") shouldBe true
+ }
+
+ "match all expressions and cross the path boundaries when using double star suffix (**)" in {
+ val filter = new GlobPathFilter("/user/actor-**")
+
+ filter.accept("/user/actor-") shouldBe true
+ filter.accept("/user/actor-one") shouldBe true
+ filter.accept("/user/actor-one/other") shouldBe true
+ filter.accept("/user/something/actor") shouldBe false
+ filter.accept("/user/something/otherActor") shouldBe false
+ }
+
+ "match exactly one character when using question mark (?)" in {
+ val filter = new GlobPathFilter("/user/actor-?")
+
+ filter.accept("/user/actor-1") shouldBe true
+ filter.accept("/user/actor-2") shouldBe true
+ filter.accept("/user/actor-3") shouldBe true
+ filter.accept("/user/actor-one") shouldBe false
+ filter.accept("/user/actor-two") shouldBe false
+ filter.accept("/user/actor-tree") shouldBe false
+ }
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/HistogramSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/HistogramSpec.scala
new file mode 100644
index 00000000..f0ea1292
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/HistogramSpec.scala
@@ -0,0 +1,94 @@
+package kamon.metric
+
+import kamon.Kamon
+import org.scalatest.{Matchers, WordSpec}
+import MeasurementUnit._
+
+
+class HistogramSpec extends WordSpec with Matchers {
+ import HistogramTestHelper.HistogramMetricSyntax
+
+ "a Histogram" should {
+ "record values and reset internal state when a snapshot is taken" in {
+ val histogram = Kamon.histogram("test", unit = time.nanoseconds)
+ histogram.record(100)
+ histogram.record(150, 998)
+ histogram.record(200)
+
+ val distribution = histogram.distribution()
+ distribution.min shouldBe(100)
+ distribution.max shouldBe(200)
+ distribution.count shouldBe(1000)
+ distribution.buckets.length shouldBe 3
+ distribution.buckets.map(b => (b.value, b.frequency)) should contain.allOf(
+ (100 -> 1),
+ (150 -> 998),
+ (200 -> 1)
+ )
+
+ val emptyDistribution = histogram.distribution()
+ emptyDistribution.min shouldBe(0)
+ emptyDistribution.max shouldBe(0)
+ emptyDistribution.count shouldBe(0)
+ emptyDistribution.buckets.length shouldBe 0
+ }
+
+ "accept a smallest discernible value configuration" in {
+ // The lowestDiscernibleValue gets rounded down to the closest power of 2, so, here it will be 64.
+ val histogram = Kamon.histogram("test-lowest-discernible-value", unit = time.nanoseconds, dynamicRange = DynamicRange.Fine.withLowestDiscernibleValue(100))
+ histogram.record(100)
+ histogram.record(200)
+ histogram.record(300)
+ histogram.record(1000)
+ histogram.record(2000)
+ histogram.record(3000)
+
+ val distribution = histogram.distribution()
+ distribution.min shouldBe(64)
+ distribution.max shouldBe(2944)
+ distribution.count shouldBe(6)
+ distribution.buckets.length shouldBe 6
+ distribution.buckets.map(b => (b.value, b.frequency)) should contain.allOf(
+ (64 -> 1),
+ (192 -> 1),
+ (256 -> 1),
+ (960 -> 1),
+ (1984 -> 1),
+ (2944 -> 1)
+ )
+ }
+
+ "[private api] record values and optionally keep the internal state when a snapshot is taken" in {
+ val histogram = Kamon.histogram("test", unit = time.nanoseconds)
+ histogram.record(100)
+ histogram.record(150, 998)
+ histogram.record(200)
+
+ val distribution = {
+ histogram.distribution(resetState = false) // first one gets discarded
+ histogram.distribution(resetState = false)
+ }
+
+ distribution.min shouldBe(100)
+ distribution.max shouldBe(200)
+ distribution.count shouldBe(1000)
+ distribution.buckets.length shouldBe 3
+ distribution.buckets.map(b => (b.value, b.frequency)) should contain.allOf(
+ (100 -> 1),
+ (150 -> 998),
+ (200 -> 1)
+ )
+ }
+ }
+}
+
+object HistogramTestHelper {
+
+ implicit class HistogramMetricSyntax(metric: HistogramMetric) {
+ def distribution(resetState: Boolean = true): Distribution =
+ metric.refine(Map.empty[String, String]) match {
+ case h: AtomicHdrHistogram => h.snapshot(resetState).distribution
+ case h: HdrHistogram => h.snapshot(resetState).distribution
+ }
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/LongAdderCounterSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/LongAdderCounterSpec.scala
new file mode 100644
index 00000000..4014d6df
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/LongAdderCounterSpec.scala
@@ -0,0 +1,62 @@
+/* =========================================================================================
+ * Copyright © 2013-2017 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.metric
+
+import org.scalatest.{Matchers, WordSpec}
+
+class LongAdderCounterSpec extends WordSpec with Matchers {
+
+ "a LongAdderCounter" should {
+ "allow unit and bundled increments" in {
+ val counter = buildCounter("unit-increments")
+ counter.increment()
+ counter.increment()
+ counter.increment(40)
+
+ counter.snapshot().value shouldBe 42
+ }
+
+ "warn the user and ignore attempts to decrement the counter" in {
+ val counter = buildCounter("attempt-to-decrement")
+ counter.increment(100)
+ counter.increment(100)
+ counter.increment(100)
+
+ counter.snapshot().value shouldBe 300
+ }
+
+ "reset the internal state to zero after taking snapshots as a default behavior" in {
+ val counter = buildCounter("reset-after-snapshot")
+ counter.increment()
+ counter.increment(10)
+
+ counter.snapshot().value shouldBe 11
+ counter.snapshot().value shouldBe 0
+ }
+
+ "optionally leave the internal state unchanged" in {
+ val counter = buildCounter("reset-after-snapshot")
+ counter.increment()
+ counter.increment(10)
+
+ counter.snapshot(resetState = false).value shouldBe 11
+ counter.snapshot(resetState = false).value shouldBe 11
+ }
+ }
+
+ def buildCounter(name: String, tags: Map[String, String] = Map.empty, unit: MeasurementUnit = MeasurementUnit.none): LongAdderCounter =
+ new LongAdderCounter(name, tags, unit)
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala
new file mode 100644
index 00000000..1d60a28f
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/MetricLookupSpec.scala
@@ -0,0 +1,62 @@
+package kamon.metric
+
+import kamon.Kamon
+import org.scalatest.{Matchers, WordSpec}
+
+class MetricLookupSpec extends WordSpec with Matchers {
+
+ "the Kamon companion object" can {
+ "lookup a metric and" should {
+ "always return the same histogram metric" in {
+ val histogramOne = Kamon.histogram("histogram-lookup")
+ val histogramTwo = Kamon.histogram("histogram-lookup")
+ histogramOne shouldBe theSameInstanceAs(histogramTwo)
+ }
+
+ "always return the same counter metric" in {
+ val counterOne = Kamon.counter("counter-lookup")
+ val counterTwo = Kamon.counter("counter-lookup")
+ counterOne shouldBe theSameInstanceAs(counterTwo)
+ }
+
+ "always return the same gauge metric" in {
+ val gaugeOne = Kamon.gauge("gauge-lookup")
+ val gaugeTwo = Kamon.gauge("gauge-lookup")
+ gaugeOne shouldBe theSameInstanceAs(gaugeTwo)
+ }
+
+ "always return the same min-max-counter metric" in {
+ val minMaxCounterOne = Kamon.minMaxCounter("min-max-counter-lookup")
+ val minMaxCounterTwo = Kamon.minMaxCounter("min-max-counter-lookup")
+ minMaxCounterOne shouldBe theSameInstanceAs(minMaxCounterTwo)
+ }
+ }
+
+ "refine a metric with tags and" should {
+ "always return the same histogram for a set of tags" in {
+ val histogramOne = Kamon.histogram("histogram-lookup").refine("tag" -> "value")
+ val histogramTwo = Kamon.histogram("histogram-lookup").refine("tag" -> "value")
+ histogramOne shouldBe theSameInstanceAs(histogramTwo)
+ }
+
+ "always return the same counter for a set of tags" in {
+ val counterOne = Kamon.counter("counter-lookup").refine("tag" -> "value")
+ val counterTwo = Kamon.counter("counter-lookup").refine("tag" -> "value")
+ counterOne shouldBe theSameInstanceAs(counterTwo)
+ }
+
+ "always return the same gauge for a set of tags" in {
+ val gaugeOne = Kamon.gauge("gauge-lookup").refine("tag" -> "value")
+ val gaugeTwo = Kamon.gauge("gauge-lookup").refine("tag" -> "value")
+ gaugeOne shouldBe theSameInstanceAs(gaugeTwo)
+ }
+
+ "always return the same min-max-counter for a set of tags" in {
+ val minMaxCounterOne = Kamon.minMaxCounter("min-max-counter-lookup").refine("tag" -> "value")
+ val minMaxCounterTwo = Kamon.minMaxCounter("min-max-counter-lookup").refine("tag" -> "value")
+ minMaxCounterOne shouldBe theSameInstanceAs(minMaxCounterTwo)
+ }
+ }
+ }
+
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala
new file mode 100644
index 00000000..0ad3c45c
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/MinMaxCounterSpec.scala
@@ -0,0 +1,90 @@
+/* =========================================================================================
+ * Copyright © 2013-2017 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+
+package kamon.metric
+
+import java.time.Duration
+
+import org.scalatest.{Matchers, WordSpec}
+
+case class TemporalBucket(value: Long, frequency: Long) extends Bucket
+
+class MinMaxCounterSpec extends WordSpec with Matchers {
+
+ "a MinMaxCounter" should {
+ "track ascending tendencies" in {
+ val mmCounter = buildMinMaxCounter("track-ascending")
+ mmCounter.increment()
+ mmCounter.increment(3)
+ mmCounter.increment()
+
+ mmCounter.sample()
+
+ val snapshot = mmCounter.snapshot()
+
+ snapshot.distribution.min should be(0)
+ snapshot.distribution.max should be(5)
+ }
+
+ "track descending tendencies" in {
+ val mmCounter = buildMinMaxCounter("track-descending")
+ mmCounter.increment(5)
+ mmCounter.decrement()
+ mmCounter.decrement(3)
+ mmCounter.decrement()
+
+ mmCounter.sample()
+
+ val snapshot = mmCounter.snapshot()
+ snapshot.distribution.min should be(0)
+ snapshot.distribution.max should be(5)
+ }
+
+ "reset the min and max to the current value after taking a snapshot" in {
+ val mmCounter = buildMinMaxCounter("reset-min-max-to-current")
+
+ mmCounter.increment(5)
+ mmCounter.decrement(3)
+ mmCounter.sample()
+
+ val firstSnapshot = mmCounter.snapshot()
+ firstSnapshot.distribution.min should be(0)
+ firstSnapshot.distribution.max should be(5)
+
+ mmCounter.sample()
+
+ val secondSnapshot = mmCounter.snapshot()
+ secondSnapshot.distribution.min should be(2)
+ secondSnapshot.distribution.max should be(2)
+ }
+
+ "report zero as the min and current values if the current value fell bellow zero" in {
+ val mmCounter = buildMinMaxCounter("report-zero")
+
+ mmCounter.decrement(3)
+
+ mmCounter.sample()
+
+ val snapshot = mmCounter.snapshot()
+
+ snapshot.distribution.min should be(0)
+ snapshot.distribution.max should be(0)
+ }
+ }
+
+ def buildMinMaxCounter(name: String, tags: Map[String, String] = Map.empty, unit: MeasurementUnit = MeasurementUnit.none): SimpleMinMaxCounter =
+ new SimpleMinMaxCounter(name, tags, new AtomicHdrHistogram(name, tags, unit, dynamicRange = DynamicRange.Default), Duration.ofMillis(100))
+} \ No newline at end of file
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala
new file mode 100644
index 00000000..1053aa5f
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/RecorderRegistrySpec.scala
@@ -0,0 +1,58 @@
+/* =========================================================================================
+ * Copyright © 2013-2017 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon.metric
+
+import com.typesafe.config.ConfigFactory
+import org.scalatest.{Matchers, WordSpec}
+
+//class RecorderRegistrySpec extends WordSpec with Matchers {
+// private val testConfig = ConfigFactory.parseString(
+// """
+// |kamon.metric.filters {
+// | accept-unmatched = false
+// |
+// | my-category {
+// | includes = ["**"]
+// | excludes = ["excluded"]
+// | }
+// |}
+// """.stripMargin
+// )
+// private val recorderRegistry = new RecorderRegistryImpl(testConfig.withFallback(ConfigFactory.load()))
+//
+//
+// "the RecorderRegistry" should {
+// "create entity recorders as requested and always return the same instance for a given entity" in {
+// val myFirstEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty))
+// val mySecondEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty))
+// mySecondEntityRecorder shouldBe theSameInstanceAs(myFirstEntityRecorder)
+// }
+//
+// "properly advice regarding entity filtering read from configuration" in {
+// recorderRegistry.shouldTrack(Entity("my-entity", "my-category", Map.empty)) shouldBe true
+// recorderRegistry.shouldTrack(Entity("other-eny", "my-category", Map.empty)) shouldBe true
+// recorderRegistry.shouldTrack(Entity("excluded", "my-category", Map.empty)) shouldBe false
+// }
+//
+// "allow removing entities" in {
+// val myFirstEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty))
+// recorderRegistry.removeRecorder(Entity("my-entity", "my-category", Map.empty))
+//
+// val mySecondEntityRecorder = recorderRegistry.getRecorder(Entity("my-entity", "my-category", Map.empty))
+// mySecondEntityRecorder shouldNot be theSameInstanceAs(myFirstEntityRecorder)
+// }
+// }
+//}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/RegexPathFilterSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/RegexPathFilterSpec.scala
new file mode 100644
index 00000000..f742df1d
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/RegexPathFilterSpec.scala
@@ -0,0 +1,61 @@
+/*
+ * =========================================================================================
+ * Copyright © 2013-2017 the kamon project <http://kamon.io/>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the
+ * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific language governing permissions
+ * and limitations under the License.
+ * =========================================================================================
+ */
+
+package kamon
+package metric
+
+import kamon.util.RegexMatcher
+import org.scalatest.{Matchers, WordSpecLike}
+
+class RegexPathFilterSpec extends WordSpecLike with Matchers {
+ "The RegexPathFilter" should {
+
+ "match a single expression" in {
+ val filter = new RegexMatcher("/user/actor")
+
+ filter.accept("/user/actor") shouldBe true
+
+ filter.accept("/user/actor/something") shouldBe false
+ filter.accept("/user/actor/somethingElse") shouldBe false
+ }
+
+ "match arbitray expressions ending with wildcard" in {
+ val filter = new RegexMatcher("/user/.*")
+
+ filter.accept("/user/actor") shouldBe true
+ filter.accept("/user/otherActor") shouldBe true
+ filter.accept("/user/something/actor") shouldBe true
+ filter.accept("/user/something/otherActor") shouldBe true
+
+ filter.accept("/otheruser/actor") shouldBe false
+ filter.accept("/otheruser/otherActor") shouldBe false
+ filter.accept("/otheruser/something/actor") shouldBe false
+ filter.accept("/otheruser/something/otherActor") shouldBe false
+ }
+
+ "match numbers" in {
+ val filter = new RegexMatcher("/user/actor-\\d")
+
+ filter.accept("/user/actor-1") shouldBe true
+ filter.accept("/user/actor-2") shouldBe true
+ filter.accept("/user/actor-3") shouldBe true
+
+ filter.accept("/user/actor-one") shouldBe false
+ filter.accept("/user/actor-two") shouldBe false
+ filter.accept("/user/actor-tree") shouldBe false
+ }
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/TimerSpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/TimerSpec.scala
new file mode 100644
index 00000000..3fc1e169
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/TimerSpec.scala
@@ -0,0 +1,72 @@
+package kamon.metric
+
+import kamon.Kamon
+import org.scalatest.{Matchers, WordSpec}
+
+
+class TimerSpec extends WordSpec with Matchers {
+ import TimerTestHelper._
+
+ "a Timer" should {
+ "record the duration between calls to .start() and .stop() in the StartedTimer" in {
+ val timer = Kamon.timer("timer-spec")
+ timer.start().stop()
+ timer.start().stop()
+ timer.start().stop()
+
+ timer.distribution().count shouldBe(3)
+ }
+
+ "ensure that a started timer can only be stopped once" in {
+ val timer = Kamon.timer("timer-spec")
+ val startedTimer = timer.start()
+ startedTimer.stop()
+ startedTimer.stop()
+ startedTimer.stop()
+
+ timer.distribution().count shouldBe(1)
+ }
+
+
+ "allow to record values and produce distributions as Histograms do" in {
+ val timer = Kamon.timer("test-timer")
+ timer.record(100)
+ timer.record(150, 998)
+ timer.record(200)
+
+ val distribution = timer.distribution()
+ distribution.min shouldBe(100)
+ distribution.max shouldBe(200)
+ distribution.count shouldBe(1000)
+ distribution.buckets.length shouldBe 3
+ distribution.buckets.map(b => (b.value, b.frequency)) should contain.allOf(
+ (100 -> 1),
+ (150 -> 998),
+ (200 -> 1)
+ )
+
+ val emptyDistribution = timer.distribution()
+ emptyDistribution.min shouldBe(0)
+ emptyDistribution.max shouldBe(0)
+ emptyDistribution.count shouldBe(0)
+ emptyDistribution.buckets.length shouldBe 0
+ }
+ }
+}
+
+object TimerTestHelper {
+
+ implicit class HistogramMetricSyntax(histogram: Histogram) {
+ def distribution(resetState: Boolean = true): Distribution = histogram match {
+ case h: AtomicHdrHistogram => h.snapshot(resetState).distribution
+ case h: HdrHistogram => h.snapshot(resetState).distribution
+ }
+ }
+
+ implicit class TimerMetricSyntax(metric: TimerMetric) {
+ def distribution(resetState: Boolean = true): Distribution =
+ metric.refine(Map.empty[String, String]) match {
+ case t: TimerImpl => t.histogram.distribution(resetState)
+ }
+ }
+}
diff --git a/kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala b/kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala
new file mode 100644
index 00000000..21fe2b4d
--- /dev/null
+++ b/kamon-core-tests/src/test/scala/kamon/metric/instrument/InstrumentFactorySpec.scala
@@ -0,0 +1,114 @@
+package kamon.metric.instrument
+
+//import java.time.Duration
+//
+//import com.typesafe.config.ConfigFactory
+//import kamon.metric.Entity
+//import org.scalatest.{Matchers, WordSpec}
+//
+//class InstrumentFactorySpec extends WordSpec with Matchers{
+// val testEntity = Entity("test", "test-category", Map.empty)
+// val customEntity = Entity("test", "custom-category", Map.empty)
+// val baseConfiguration = ConfigFactory.parseString(
+// """
+// |kamon.metric.instrument-factory {
+// | default-settings {
+// | histogram {
+// | lowest-discernible-value = 100
+// | highest-trackable-value = 5000
+// | significant-value-digits = 2
+// | }
+// |
+// | min-max-counter {
+// | lowest-discernible-value = 200
+// | highest-trackable-value = 6000
+// | significant-value-digits = 3
+// | sample-interval = 647 millis
+// | }
+// | }
+// |
+// | custom-settings {
+// |
+// | }
+// |}
+// """.stripMargin
+// )
+//
+//
+// "the metrics InstrumentFactory" should {
+// "create instruments using the default configuration settings" in {
+// val factory = InstrumentFactory.fromConfig(baseConfiguration)
+// val histogram = factory.buildHistogram(testEntity, "my-histogram")
+// val mmCounter = factory.buildMinMaxCounter(testEntity, "my-mm-counter")
+//
+// histogram.dynamicRange.lowestDiscernibleValue shouldBe(100)
+// histogram.dynamicRange.highestTrackableValue shouldBe(5000)
+// histogram.dynamicRange.significantValueDigits shouldBe(2)
+//
+// mmCounter.dynamicRange.lowestDiscernibleValue shouldBe(200)
+// mmCounter.dynamicRange.highestTrackableValue shouldBe(6000)
+// mmCounter.dynamicRange.significantValueDigits shouldBe(3)
+// mmCounter.sampleInterval shouldBe(Duration.ofMillis(647))
+// }
+//
+// "accept custom settings when building instruments" in {
+// val factory = InstrumentFactory.fromConfig(baseConfiguration)
+// val histogram = factory.buildHistogram(testEntity, "my-histogram", DynamicRange.Loose)
+// val mmCounter = factory.buildMinMaxCounter(testEntity, "my-mm-counter", DynamicRange.Fine, Duration.ofMillis(500))
+//
+// histogram.dynamicRange shouldBe(DynamicRange.Loose)
+//
+// mmCounter.dynamicRange shouldBe(DynamicRange.Fine)
+// mmCounter.sampleInterval shouldBe(Duration.ofMillis(500))
+// }
+//
+// "allow overriding any default and provided settings via the custom-settings configuration key" in {
+// val customConfig = ConfigFactory.parseString(
+// """
+// |kamon.metric.instrument-factory.custom-settings {
+// | custom-category {
+// | modified-histogram {
+// | lowest-discernible-value = 99
+// | highest-trackable-value = 999
+// | significant-value-digits = 4
+// | }
+// |
+// | modified-mm-counter {
+// | lowest-discernible-value = 784
+// | highest-trackable-value = 14785
+// | significant-value-digits = 1
+// | sample-interval = 3 seconds
+// | }
+// | }
+// |}
+// """.stripMargin
+// ).withFallback(baseConfiguration)
+//
+// val factory = InstrumentFactory.fromConfig(customConfig)
+// val defaultHistogram = factory.buildHistogram(customEntity, "default-histogram")
+// val modifiedHistogram = factory.buildHistogram(customEntity, "modified-histogram", DynamicRange.Loose)
+//
+// defaultHistogram.dynamicRange.lowestDiscernibleValue shouldBe(100)
+// defaultHistogram.dynamicRange.highestTrackableValue shouldBe(5000)
+// defaultHistogram.dynamicRange.significantValueDigits shouldBe(2)
+//
+// modifiedHistogram.dynamicRange.lowestDiscernibleValue shouldBe(99)
+// modifiedHistogram.dynamicRange.highestTrackableValue shouldBe(999)
+// modifiedHistogram.dynamicRange.significantValueDigits shouldBe(4)
+//
+//
+// val defaultMMCounter = factory.buildMinMaxCounter(customEntity, "default-mm-counter")
+// val modifiedMMCounter = factory.buildMinMaxCounter(customEntity, "modified-mm-counter", DynamicRange.Loose)
+//
+// defaultMMCounter.dynamicRange.lowestDiscernibleValue shouldBe(200)
+// defaultMMCounter.dynamicRange.highestTrackableValue shouldBe(6000)
+// defaultMMCounter.dynamicRange.significantValueDigits shouldBe(3)
+// defaultMMCounter.sampleInterval shouldBe(Duration.ofMillis(647))
+//
+// modifiedMMCounter.dynamicRange.lowestDiscernibleValue shouldBe(784)
+// modifiedMMCounter.dynamicRange.highestTrackableValue shouldBe(14785)
+// modifiedMMCounter.dynamicRange.significantValueDigits shouldBe(1)
+// modifiedMMCounter.sampleInterval shouldBe(Duration.ofSeconds(3))
+// }
+// }
+//}