From 28b40484bcdc8b2bf32b1c1c37e8852f775f31b8 Mon Sep 17 00:00:00 2001 From: Gregor Zeitlinger Date: Fri, 18 Oct 2024 07:48:58 +0200 Subject: [PATCH] add coverage check (#1162) Signed-off-by: Gregor Zeitlinger --- CONTRIBUTING.md | 10 +- benchmarks/pom.xml | 2 +- examples/pom.xml | 2 +- integration-tests/pom.xml | 2 +- pom.xml | 7 +- .../pom.xml | 7 +- .../otelmodel/PrometheusInfo.java | 1 + .../otelmodel/PrometheusMetricData.java | 2 +- .../otelmodel/PrometheusStateSet.java | 1 + .../exporter/opentelemetry/ExportTest.java | 312 ++++++++++++++++++ .../otelmodel/PrometheusMetricDataTest.java | 47 +++ .../model/snapshots/CounterSnapshot.java | 1 + .../model/snapshots/DataPointSnapshot.java | 1 + .../model/snapshots/MetricSnapshot.java | 5 +- 14 files changed, 387 insertions(+), 13 deletions(-) create mode 100644 prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java create mode 100644 prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricDataTest.java diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 79cd4b6ca..3d8c766ec 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -17,14 +17,18 @@ This repository uses [Google Java Format](https://github.com/google/google-java- Run `./mvnw spotless:apply` to format the code (only changed files) before committing. -Use `-Dspotless.check.skip=true` to skip the formatting check during development. - ## Running Tests If you're getting errors when running tests: - Make sure that the IDE uses only the "Maven Shade" dependency of "prometheus-metrics-exposition-formats" and the "prometheus-metrics-tracer*" dependencies. - + +### Avoid failures while running tests + +- Use `-Dspotless.check.skip=true` to skip the formatting check during development. +- Use `-Dcoverage.skip=true` to skip the coverage check during development. +- Use `-Dwarnings=-nowarn` to skip the warnings during development. + ## Updating the Protobuf Java Classes Use `PROTO_GENERATION=true mvn clean install` to generate protobuf classes. diff --git a/benchmarks/pom.xml b/benchmarks/pom.xml index 11e24c3bd..64a929953 100644 --- a/benchmarks/pom.xml +++ b/benchmarks/pom.xml @@ -20,7 +20,7 @@ 1.37 0.16.0 3.0.2 - 0.0 + true diff --git a/examples/pom.xml b/examples/pom.xml index e1fd7ceb4..079ceb982 100644 --- a/examples/pom.xml +++ b/examples/pom.xml @@ -17,7 +17,7 @@ - 0.0 + true diff --git a/integration-tests/pom.xml b/integration-tests/pom.xml index ad98ba538..ce13424df 100644 --- a/integration-tests/pom.xml +++ b/integration-tests/pom.xml @@ -19,7 +19,7 @@ - 0.0 + true diff --git a/pom.xml b/pom.xml index 79130011c..f7f9dd3fe 100644 --- a/pom.xml +++ b/pom.xml @@ -21,6 +21,8 @@ 2.8.0-alpha 8 0.70 + false + -Werror @@ -227,6 +229,7 @@ jacoco-maven-plugin 0.8.12 + ${coverage.skip} **/generated/** @@ -309,8 +312,8 @@ ${java.version} true - -Xlint:all,-serial,-processing - -Werror + -Xlint:all,-serial,-processing,-options + ${warnings} -XDcompilePolicy=simple -Xplugin:ErrorProne diff --git a/prometheus-metrics-exporter-opentelemetry/pom.xml b/prometheus-metrics-exporter-opentelemetry/pom.xml index 04790be25..a603302c7 100644 --- a/prometheus-metrics-exporter-opentelemetry/pom.xml +++ b/prometheus-metrics-exporter-opentelemetry/pom.xml @@ -19,8 +19,6 @@ io.prometheus.metrics.exporter.opentelemetry - - 0.0 @@ -96,6 +94,11 @@ 1.7.1-alpha test + + io.opentelemetry + opentelemetry-sdk-testing + test + diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusInfo.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusInfo.java index 33e4c158d..2a7b8110f 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusInfo.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusInfo.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.stream.Collectors; +@SuppressWarnings("this-escape") public class PrometheusInfo extends PrometheusData implements SumData { diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricData.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricData.java index 4995b98d6..baca70774 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricData.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricData.java @@ -48,7 +48,7 @@ private String getNameWithoutUnit(MetricMetadata metricMetadata) { // See // https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/6cf4dec6cb42d87d8840e9f67d4acf66d4eb8fda/pkg/translator/prometheus/normalize_name.go#L19 - private String convertUnit(Unit unit) { + static String convertUnit(Unit unit) { if (unit == null) { return null; } diff --git a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusStateSet.java b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusStateSet.java index b0bb39841..10417afc7 100644 --- a/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusStateSet.java +++ b/prometheus-metrics-exporter-opentelemetry/src/main/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusStateSet.java @@ -16,6 +16,7 @@ public class PrometheusStateSet extends PrometheusData private final List points; + @SuppressWarnings("this-escape") public PrometheusStateSet(StateSetSnapshot snapshot, long currentTimeMillis) { super(MetricDataType.DOUBLE_SUM); this.points = new ArrayList<>(); diff --git a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java new file mode 100644 index 000000000..55589ce84 --- /dev/null +++ b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/ExportTest.java @@ -0,0 +1,312 @@ +package io.prometheus.metrics.exporter.opentelemetry; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.common.InstrumentationScopeInfo; +import io.opentelemetry.sdk.metrics.data.MetricData; +import io.opentelemetry.sdk.metrics.export.MetricReader; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.sdk.testing.assertj.MetricAssert; +import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions; +import io.opentelemetry.sdk.testing.junit5.OpenTelemetryExtension; +import io.prometheus.metrics.core.metrics.Counter; +import io.prometheus.metrics.core.metrics.Gauge; +import io.prometheus.metrics.core.metrics.Histogram; +import io.prometheus.metrics.core.metrics.Info; +import io.prometheus.metrics.core.metrics.StateSet; +import io.prometheus.metrics.core.metrics.Summary; +import io.prometheus.metrics.model.registry.Collector; +import io.prometheus.metrics.model.registry.PrometheusRegistry; +import io.prometheus.metrics.model.snapshots.Labels; +import io.prometheus.metrics.model.snapshots.Unit; +import io.prometheus.metrics.model.snapshots.UnknownSnapshot; +import java.lang.reflect.Field; +import java.util.Collections; +import java.util.List; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +public class ExportTest { + + private static final Attributes ATTRIBUTES = + Attributes.of(AttributeKey.stringKey("label"), "val", AttributeKey.stringKey("key"), "value"); + @RegisterExtension OpenTelemetryExtension testing = OpenTelemetryExtension.create(); + + private final PrometheusRegistry registry = new PrometheusRegistry(); + + @BeforeEach + void setUp() throws IllegalAccessException, NoSuchFieldException { + Field field = testing.getClass().getDeclaredField("metricReader"); + field.setAccessible(true); + MetricReader reader = (MetricReader) field.get(testing); + + PrometheusMetricProducer prometheusMetricProducer = + new PrometheusMetricProducer( + registry, + InstrumentationScopeInfo.create("test"), + Resource.create(Attributes.builder().put("staticRes", "value").build())); + + reader.register(prometheusMetricProducer); + } + + @Test + void targetInfo() { + Info.builder().name("target").constLabels(Labels.of("res", "value")).register(registry); + Labels scope = Labels.of("otel_scope_name", "scope", "otel_scope_version", "1"); + Info.builder() + .name("otel_scope") + .constLabels(scope.add("scopeKey", "value")) + .register(registry); + Counter counter = Counter.builder().name("name").constLabels(scope).register(registry); + counter.inc(); + metricAssert() + .hasResource( + Resource.create( + Attributes.builder().put("res", "value").put("staticRes", "value").build())) + .hasInstrumentationScope( + InstrumentationScopeInfo.builder("scope") + .setAttributes(Attributes.of(AttributeKey.stringKey("scopeKey"), "value")) + .setVersion("1") + .build()); + } + + @Test + void counter() { + Counter counter = + Counter.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .unit(Unit.BYTES) + .withExemplars() + .register(registry); + counter.labelValues("val").inc(); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasUnit("By") + .hasDoubleSumSatisfying( + sum -> + sum.isMonotonic() + .isCumulative() + .hasPointsSatisfying(points -> points.hasValue(1.0).hasAttributes(ATTRIBUTES))); + } + + @Test + void histogram() { + Histogram histogram = + Histogram.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .unit(Unit.BYTES) + .classicOnly() + .classicUpperBounds(1, 2, 3) + .register(registry); + histogram.labelValues("val").observe(1); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasUnit("By") + .hasHistogramSatisfying( + hist -> + hist.hasPointsSatisfying( + points -> + points + .hasAttributes(ATTRIBUTES) + .hasCount(1) + .hasSum(1.0) + .satisfies( + p -> { + assertThat(p.getMin()).isNaN(); + assertThat(p.getMax()).isNaN(); + assertThat(p.getStartEpochNanos()).isPositive(); + assertThat(p.getEpochNanos()).isPositive(); + }) + .hasExemplars() + .hasBucketBoundaries(1, 2, 3, Double.POSITIVE_INFINITY) + .hasBucketCounts(1, 0, 0, 0))); + } + + @Test + void exponentialHistogram() { + Histogram histogram = + Histogram.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .unit(Unit.BYTES) + .nativeOnly() + .register(registry); + histogram.labelValues("val").observe(1); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasUnit("By") + .hasExponentialHistogramSatisfying( + hist -> + hist.isCumulative() + .hasPointsSatisfying( + points -> + points + .hasAttributes(ATTRIBUTES) + .hasCount(1) + .hasScale(5) + .hasZeroCount(0) + .hasSum(1.0) + .satisfies( + p -> { + assertThat(p.getMin()).isNaN(); + assertThat(p.getMax()).isNaN(); + assertThat(p.getStartEpochNanos()).isPositive(); + assertThat(p.getEpochNanos()).isPositive(); + }) + .hasExemplars() + .hasPositiveBucketsSatisfying( + buckets -> + buckets + .hasOffset(-1) + .hasTotalCount(1) + .hasCounts(Collections.singletonList(1L))))); + } + + @Test + void summary() { + Summary summary = + Summary.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .unit(Unit.BYTES) + .quantile(0.5, 0.1) + .register(registry); + summary.labelValues("val").observe(1); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasUnit("By") + .hasSummarySatisfying( + sum -> + sum.hasPointsSatisfying( + points -> + points + .hasAttributes(ATTRIBUTES) + .hasCount(1) + .hasSum(1.0) + .satisfies( + p -> { + assertThat(p.getStartEpochNanos()).isPositive(); + assertThat(p.getEpochNanos()).isPositive(); + }) + .hasValuesSatisfying(values -> values.hasQuantile(0.5).hasValue(1.0)))); + } + + @Test + void gauge() { + Gauge gauge = + Gauge.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .unit(Unit.BYTES) + .register(registry); + gauge.labelValues("val").set(1); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasUnit("By") + .hasDoubleGaugeSatisfying( + gaugeData -> + gaugeData.hasPointsSatisfying( + points -> points.hasValue(1.0).hasExemplars().hasAttributes(ATTRIBUTES))); + } + + @Test + void stateSet() { + StateSet stateSet = + StateSet.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .states("state") + .register(registry); + stateSet.labelValues("val").setTrue("state"); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasDoubleSumSatisfying( + sum -> + sum.isNotMonotonic() + .isCumulative() + .hasPointsSatisfying( + points -> + points + .hasValue(1.0) + .hasAttributes( + ATTRIBUTES.toBuilder().put("name", "state").build()))); + } + + @Test + void info() { + Info info = + Info.builder() + .name("name") + .help("help") + .constLabels(Labels.of("key", "value")) + .labelNames("label") + .register(registry); + info.addLabelValues("val"); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasDoubleSumSatisfying( + sum -> + sum.isCumulative() + .isNotMonotonic() + .hasPointsSatisfying(points -> points.hasAttributes(ATTRIBUTES).hasValue(1.0))); + } + + @Test + void unknown() { + Collector collector = + () -> + UnknownSnapshot.builder() + .name("name_bytes") + .help("help") + .unit(Unit.BYTES) + .dataPoint( + UnknownSnapshot.UnknownDataPointSnapshot.builder() + .value(1.0) + .labels(Labels.of("label", "val")) + .build()) + .build(); + registry.register(collector); + metricAssert() + .hasName("name") + .hasDescription("help") + .hasUnit("By") + .hasDoubleGaugeSatisfying( + gaugeData -> + gaugeData.hasPointsSatisfying( + points -> + points + .hasValue(1.0) + .hasExemplars() + .hasAttributes(Attributes.of(AttributeKey.stringKey("label"), "val")))); + } + + private MetricAssert metricAssert() { + List metrics = testing.getMetrics(); + assertThat(metrics).hasSize(1); + return OpenTelemetryAssertions.assertThat(metrics.get(0)); + } +} diff --git a/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricDataTest.java b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricDataTest.java new file mode 100644 index 000000000..2c84b1e37 --- /dev/null +++ b/prometheus-metrics-exporter-opentelemetry/src/test/java/io/prometheus/metrics/exporter/opentelemetry/otelmodel/PrometheusMetricDataTest.java @@ -0,0 +1,47 @@ +package io.prometheus.metrics.exporter.opentelemetry.otelmodel; + +import static org.junit.jupiter.api.Assertions.*; + +import com.google.common.collect.ImmutableMap; +import io.prometheus.metrics.model.snapshots.Unit; +import org.junit.jupiter.api.Test; + +class PrometheusMetricDataTest { + + ImmutableMap translations = + ImmutableMap.builder() + .put("days", "d") + .put("hours", "h") + .put("minutes", "min") + .put("seconds", "s") + .put("milliseconds", "ms") + .put("microseconds", "us") + .put("nanoseconds", "ns") + .put("bytes", "By") + .put("kibibytes", "KiBy") + .put("mebibytes", "MiBy") + .put("gibibytes", "GiBy") + .put("tibibytes", "TiBy") + .put("kilobytes", "KBy") + .put("megabytes", "MBy") + .put("gigabytes", "GBy") + .put("terabytes", "TBy") + .put("meters", "m") + .put("volts", "V") + .put("amperes", "A") + .put("joules", "J") + .put("watts", "W") + .put("grams", "g") + .put("celsius", "Cel") + .put("hertz", "Hz") + .put("percent", "%") + .build(); + + @Test + void convertUnit() { + translations.forEach( + (unit, expected) -> { + assertEquals(expected, PrometheusMetricData.convertUnit(new Unit(unit.toString()))); + }); + } +} diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/CounterSnapshot.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/CounterSnapshot.java index 45be6f0f1..c74a68e39 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/CounterSnapshot.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/CounterSnapshot.java @@ -50,6 +50,7 @@ public CounterDataPointSnapshot( * scrape timestamp is usually set by the Prometheus server during scraping. Exceptions include * mirroring metrics with given timestamps from other metric sources. */ + @SuppressWarnings("this-escape") public CounterDataPointSnapshot( double value, Labels labels, diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/DataPointSnapshot.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/DataPointSnapshot.java index 47ad4486b..4c2d57e8e 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/DataPointSnapshot.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/DataPointSnapshot.java @@ -1,5 +1,6 @@ package io.prometheus.metrics.model.snapshots; +@SuppressWarnings("this-escape") public abstract class DataPointSnapshot { private final Labels labels; private final long createdTimestampMillis; diff --git a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java index 420feeced..5d4c15461 100644 --- a/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java +++ b/prometheus-metrics-model/src/main/java/io/prometheus/metrics/model/snapshots/MetricSnapshot.java @@ -23,7 +23,7 @@ protected MetricSnapshot(MetricMetadata metadata, Collection dataPoints) { List dataCopy = new ArrayList<>(dataPoints); dataCopy.sort(Comparator.comparing(DataPointSnapshot::getLabels)); this.dataPoints = Collections.unmodifiableList(dataCopy); - validateLabels(); + validateLabels(this.dataPoints, metadata); } public MetricMetadata getMetadata() { @@ -32,7 +32,8 @@ public MetricMetadata getMetadata() { public abstract List getDataPoints(); - protected void validateLabels() { + private static void validateLabels( + List dataPoints, MetricMetadata metadata) { // Verify that labels are unique (the same set of names/values must not be used multiple times // for the same metric). for (int i = 0; i < dataPoints.size() - 1; i++) {