diff --git a/products/metrics/metrics-lib/build.gradle.kts b/products/metrics/metrics-lib/build.gradle.kts index ae38432669a..4f58777ff02 100644 --- a/products/metrics/metrics-lib/build.gradle.kts +++ b/products/metrics/metrics-lib/build.gradle.kts @@ -36,15 +36,8 @@ tasks.named("shadowJar") { extra["minimumBranchCoverage"] = 0.5 extra["minimumInstructionCoverage"] = 0.8 extra["excludedClassesCoverage"] = listOf( - "datadog.communication.monitor.DDAgentStatsDConnection", - "datadog.communication.monitor.DDAgentStatsDConnection.*", - "datadog.communication.monitor.LoggingStatsDClient", + "datadog.metrics.impl.statsd.DDAgentStatsDClientManager", + "datadog.metrics.impl.statsd.DDAgentStatsDConnection", + "datadog.metrics.impl.statsd.DDAgentStatsDConnection.*", + "datadog.metrics.impl.statsd.LoggingStatsDClient", ) -// val excludedClassesBranchCoverage by extra( -// listOf( -// ) -// ) -// val excludedClassesInstructionCoverage by extra( -// listOf( -// ) -// ) diff --git a/products/metrics/metrics-lib/src/test/java/datadog/metrics/impl/DDSketchHistogramsTest.java b/products/metrics/metrics-lib/src/test/java/datadog/metrics/impl/DDSketchHistogramsTest.java new file mode 100644 index 00000000000..d5e87846495 --- /dev/null +++ b/products/metrics/metrics-lib/src/test/java/datadog/metrics/impl/DDSketchHistogramsTest.java @@ -0,0 +1,91 @@ +package datadog.metrics.impl; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import datadog.metrics.impl.DDSketchHistograms.ExplicitBoundaries; +import java.util.List; +import org.junit.jupiter.api.Test; + +class DDSketchHistogramsTest { + + // boundaries define 4 bins: + // bin 0: (-inf, 10] + // bin 1: (10, 20] + // bin 2: (20, 30] + // bin 3: (30, +inf) + private static final List BOUNDARIES = asList(10.0, 20.0, 30.0); + + @Test + void indexBelowFirstBoundary() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(0, eb.index(5.0)); + } + + @Test + void indexOnBoundary() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(0, eb.index(10.0)); + assertEquals(1, eb.index(20.0)); + assertEquals(2, eb.index(30.0)); + } + + @Test + void indexBetweenBoundaries() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(1, eb.index(15.0)); + assertEquals(2, eb.index(25.0)); + } + + @Test + void indexAboveLastBoundary() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(3, eb.index(35.0)); + } + + @Test + void lowerBoundOfFirstBinIsNegativeInfinity() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(Double.NEGATIVE_INFINITY, eb.lowerBound(0)); + } + + @Test + void lowerBoundMapsToPreceeedingBoundary() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(10.0, eb.lowerBound(1)); + assertEquals(20.0, eb.lowerBound(2)); + assertEquals(30.0, eb.lowerBound(3)); + } + + @Test + void upperBoundMapsToCorrespondingBoundary() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(10.0, eb.upperBound(0)); + assertEquals(20.0, eb.upperBound(1)); + assertEquals(30.0, eb.upperBound(2)); + } + + @Test + void upperBoundOfLastBinIsPositiveInfinity() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(Double.POSITIVE_INFINITY, eb.upperBound(3)); + } + + @Test + void extremeIndexableValues() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertEquals(Double.NEGATIVE_INFINITY, eb.minIndexableValue()); + assertEquals(Double.POSITIVE_INFINITY, eb.maxIndexableValue()); + } + + @Test + void unsupportedOperationsThrow() { + ExplicitBoundaries eb = new ExplicitBoundaries(BOUNDARIES); + assertThrows(UnsupportedOperationException.class, () -> eb.value(0)); + assertThrows(UnsupportedOperationException.class, eb::relativeAccuracy); + assertThrows(UnsupportedOperationException.class, () -> eb.encode(null)); + assertThrows(UnsupportedOperationException.class, eb::serializedSize); + assertThrows(UnsupportedOperationException.class, () -> eb.serialize(null)); + } +} diff --git a/products/metrics/metrics-lib/src/test/java/datadog/metrics/impl/ThreadLocalRecordingTest.java b/products/metrics/metrics-lib/src/test/java/datadog/metrics/impl/ThreadLocalRecordingTest.java new file mode 100644 index 00000000000..f533355eb89 --- /dev/null +++ b/products/metrics/metrics-lib/src/test/java/datadog/metrics/impl/ThreadLocalRecordingTest.java @@ -0,0 +1,86 @@ +package datadog.metrics.impl; + +import static java.util.Arrays.asList; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import datadog.metrics.api.Recording; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import org.junit.jupiter.api.Test; + +class ThreadLocalRecordingTest { + + @Test + void delegatesPerThread() throws Exception { + Map> callsByThread = new ConcurrentHashMap<>(); + + ThreadLocal sink = + ThreadLocal.withInitial( + () -> { + List calls = new ArrayList<>(); + callsByThread.put(Thread.currentThread(), calls); + return recordCalls(calls); + }); + + Recording recording = new ThreadLocalRecording(sink); + + int threadCount = 4; + CountDownLatch ready = new CountDownLatch(threadCount); + CountDownLatch done = new CountDownLatch(threadCount); + Thread[] threads = new Thread[threadCount]; + + for (int i = 0; i < threadCount; i++) { + threads[i] = + new Thread( + () -> { + ready.countDown(); + try { + ready.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } + recording.start(); + recording.reset(); + recording.stop(); + recording.flush(); + done.countDown(); + }); + threads[i].start(); + } + done.await(); + + assertEquals(threadCount, callsByThread.size(), "each thread should have its own Recording"); + for (List calls : callsByThread.values()) { + assertEquals(asList("start", "reset", "stop", "flush"), calls); + } + } + + private static Recording recordCalls(List calls) { + return new Recording() { + @Override + public Recording start() { + calls.add("start"); + return this; + } + + @Override + public void reset() { + calls.add("reset"); + } + + @Override + public void stop() { + calls.add("stop"); + } + + @Override + public void flush() { + calls.add("flush"); + } + }; + } +}