From be114a1c9bbadc456a79f9022cda96e50fda9ab0 Mon Sep 17 00:00:00 2001 From: Douglas Q Hawkins Date: Thu, 10 Sep 2026 22:42:11 -0400 Subject: [PATCH] Fix metrics-api JaCoCo coverage gate The module never opted into the excludedClassesCoverage convention used by sibling modules, so the repo-wide 0.9 minimum instruction/ branch coverage rule failed for every NoOp* class as soon as CI ran this module's full check task. Exclude the true no-op classes and add real unit tests for the classes with actual logic (Histograms registration/dispatch, StatsDClientManager's default overload, Recording.close() delegating to stop()). Co-Authored-By: Claude Sonnet 5 --- products/metrics/metrics-api/build.gradle.kts | 13 ++++ .../datadog/metrics/api/HistogramsTest.java | 63 +++++++++++++++++++ .../datadog/metrics/api/RecordingTest.java | 37 +++++++++++ .../api/statsd/StatsDClientManagerTest.java | 40 ++++++++++++ .../metrics/api/statsd/StatsDClientTest.java | 61 ++++++++++++++++++ 5 files changed, 214 insertions(+) create mode 100644 products/metrics/metrics-api/src/test/java/datadog/metrics/api/HistogramsTest.java create mode 100644 products/metrics/metrics-api/src/test/java/datadog/metrics/api/RecordingTest.java create mode 100644 products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientManagerTest.java create mode 100644 products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientTest.java diff --git a/products/metrics/metrics-api/build.gradle.kts b/products/metrics/metrics-api/build.gradle.kts index bc995a5c87d..679b3aba56c 100644 --- a/products/metrics/metrics-api/build.gradle.kts +++ b/products/metrics/metrics-api/build.gradle.kts @@ -7,4 +7,17 @@ description = "Metrics API" dependencies { implementation(libs.slf4j) + + testImplementation(libs.bundles.junit5) + testImplementation(libs.bundles.mockito) } + +extra["excludedClassesCoverage"] = listOf( + "datadog.metrics.api.Monitoring", + "datadog.metrics.api.NoOpCounter", + "datadog.metrics.api.NoOpHistogram", + "datadog.metrics.api.NoOpHistogramsFactory", + "datadog.metrics.api.NoOpMonitoring", + "datadog.metrics.api.NoOpRecording", + "datadog.metrics.api.statsd.NoOpStatsDClient", +) diff --git a/products/metrics/metrics-api/src/test/java/datadog/metrics/api/HistogramsTest.java b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/HistogramsTest.java new file mode 100644 index 00000000000..ae5b4c783bb --- /dev/null +++ b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/HistogramsTest.java @@ -0,0 +1,63 @@ +package datadog.metrics.api; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Arrays; +import java.util.List; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Test; + +class HistogramsTest { + + @AfterEach + void resetFactory() { + Histograms.factory = Histograms.NO_OP; + } + + @Test + void constructorIsUsable() { + assertNotNull(new Histograms()); + } + + @Test + void registerIgnoresNull() { + Histograms.Factory before = Histograms.factory; + Histograms.register(null); + assertSame(before, Histograms.factory); + } + + @Test + void registerReplacesFactory() { + Histograms.Factory factory = mock(Histograms.Factory.class); + Histograms.register(factory); + assertSame(factory, Histograms.factory); + } + + @Test + void staticFactoryMethodsDelegateToRegisteredFactory() { + Histograms.Factory factory = mock(Histograms.Factory.class); + Histogram histogram = mock(Histogram.class); + Histogram logHistogram = mock(Histogram.class); + Histogram histogramWithBins = mock(Histogram.class); + HistogramWithSum histogramWithSum = mock(HistogramWithSum.class); + HistogramWithSum histogramWithSumFromBoundaries = mock(HistogramWithSum.class); + + when(factory.newHistogram()).thenReturn(histogram); + when(factory.newLogHistogram()).thenReturn(logHistogram); + when(factory.newHistogram(0.01, 10)).thenReturn(histogramWithBins); + when(factory.newHistogramWithSum(0.01, 10)).thenReturn(histogramWithSum); + List boundaries = Arrays.asList(1.0, 2.0); + when(factory.newHistogramWithSum(boundaries)).thenReturn(histogramWithSumFromBoundaries); + + Histograms.register(factory); + + assertSame(histogram, Histogram.newHistogram()); + assertSame(logHistogram, Histogram.newLogHistogram()); + assertSame(histogramWithBins, Histogram.newHistogram(0.01, 10)); + assertSame(histogramWithSum, Histogram.newHistogramWithSum(0.01, 10)); + assertSame(histogramWithSumFromBoundaries, Histogram.newHistogramWithSum(boundaries)); + } +} diff --git a/products/metrics/metrics-api/src/test/java/datadog/metrics/api/RecordingTest.java b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/RecordingTest.java new file mode 100644 index 00000000000..a4c4906cd2d --- /dev/null +++ b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/RecordingTest.java @@ -0,0 +1,37 @@ +package datadog.metrics.api; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.junit.jupiter.api.Test; + +class RecordingTest { + + @Test + void closeDelegatesToStop() { + StoppableRecording recording = new StoppableRecording(); + + recording.close(); + + assertTrue(recording.stopped); + } + + private static final class StoppableRecording extends Recording { + boolean stopped; + + @Override + public Recording start() { + return this; + } + + @Override + public void reset() {} + + @Override + public void stop() { + stopped = true; + } + + @Override + public void flush() {} + } +} diff --git a/products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientManagerTest.java b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientManagerTest.java new file mode 100644 index 00000000000..e37497e8293 --- /dev/null +++ b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientManagerTest.java @@ -0,0 +1,40 @@ +package datadog.metrics.api.statsd; + +import static org.junit.jupiter.api.Assertions.assertSame; + +import org.junit.jupiter.api.Test; + +class StatsDClientManagerTest { + + @Test + void defaultFiveArgOverloadDelegatesWithAggregationEnabled() { + StatsDClient client = StatsDClient.NO_OP; + RecordingStatsDClientManager manager = new RecordingStatsDClientManager(client); + + StatsDClient result = manager.statsDClient("host", 8125, null, "ns", new String[] {"tag"}); + + assertSame(client, result); + assertSame(Boolean.TRUE, manager.lastUseAggregation); + } + + private static final class RecordingStatsDClientManager implements StatsDClientManager { + private final StatsDClient client; + private Boolean lastUseAggregation; + + RecordingStatsDClientManager(StatsDClient client) { + this.client = client; + } + + @Override + public StatsDClient statsDClient( + String host, + Integer port, + String namedPipe, + String namespace, + String[] constantTags, + boolean useAggregation) { + this.lastUseAggregation = useAggregation; + return client; + } + } +} diff --git a/products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientTest.java b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientTest.java new file mode 100644 index 00000000000..9624e7b41bf --- /dev/null +++ b/products/metrics/metrics-api/src/test/java/datadog/metrics/api/statsd/StatsDClientTest.java @@ -0,0 +1,61 @@ +package datadog.metrics.api.statsd; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +import org.junit.jupiter.api.Test; + +class StatsDClientTest { + + @Test + void noOpConstantIsUsable() { + assertNotNull(StatsDClient.NO_OP); + } + + @Test + void defaultRecordEventIsNoOp() { + StatsDClient client = new MinimalStatsDClient(); + + client.recordEvent("type", "source", "eventName", "message", "tag"); + } + + private static final class MinimalStatsDClient implements StatsDClient { + @Override + public void incrementCounter(String metricName, String... tags) {} + + @Override + public void count(String metricName, long delta, String... tags) {} + + @Override + public void gauge(String metricName, long value, String... tags) {} + + @Override + public void gauge(String metricName, double value, String... tags) {} + + @Override + public void histogram(String metricName, long value, String... tags) {} + + @Override + public void histogram(String metricName, double value, String... tags) {} + + @Override + public void distribution(String metricName, long value, String... tags) {} + + @Override + public void distribution(String metricName, double value, String... tags) {} + + @Override + public void serviceCheck( + String serviceCheckName, String status, String message, String... tags) {} + + @Override + public void error(Exception error) {} + + @Override + public int getErrorCount() { + return 0; + } + + @Override + public void close() {} + } +}