diff --git a/CHANGELOG.md b/CHANGELOG.md index 43226212e15..87276b4f384 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ #### Testing * Add `hasValueSatisfying` to `LongPointAssert` and `DoublePointAssert` for fuzzy value matching +* Add `containsPointsSatisfying` to metric data asserts for "each given assertion must be + satisfied by at least one point, extras allowed" checks on sum, gauge, histogram, exponential + histogram, and summary data ## Version 1.61.0 (2026-04-10) diff --git a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt index 5db9a297e59..e6213f43ccf 100644 --- a/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt +++ b/docs/apidiffs/current_vs_latest/opentelemetry-sdk-testing.txt @@ -1,7 +1,42 @@ Comparing source compatibility of opentelemetry-sdk-testing-1.62.0-SNAPSHOT.jar against opentelemetry-sdk-testing-1.61.0.jar +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.DoubleGaugeAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.DoubleGaugeAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.DoubleGaugeAssert containsPointsSatisfying(java.lang.Iterable>) *** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.DoublePointAssert (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.DoublePointAssert hasValueSatisfying(java.util.function.Consumer>) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.DoubleSumAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.DoubleSumAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.DoubleSumAssert containsPointsSatisfying(java.lang.Iterable>) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.ExponentialHistogramAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.ExponentialHistogramAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.ExponentialHistogramAssert containsPointsSatisfying(java.lang.Iterable>) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.HistogramAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.HistogramAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.HistogramAssert containsPointsSatisfying(java.lang.Iterable>) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.LongGaugeAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.LongGaugeAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.LongGaugeAssert containsPointsSatisfying(java.lang.Iterable>) *** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.LongPointAssert (not serializable) === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.LongPointAssert hasValueSatisfying(java.util.function.Consumer>) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.LongSumAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.LongSumAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.LongSumAssert containsPointsSatisfying(java.lang.Iterable>) +*** MODIFIED CLASS: PUBLIC FINAL io.opentelemetry.sdk.testing.assertj.SummaryAssert (not serializable) + === CLASS FILE FORMAT VERSION: 52.0 <- 52.0 + +++ NEW METHOD: PUBLIC(+) FINAL(+) io.opentelemetry.sdk.testing.assertj.SummaryAssert containsPointsSatisfying(java.util.function.Consumer[]) + +++ NEW ANNOTATION: java.lang.SafeVarargs + +++ NEW METHOD: PUBLIC(+) io.opentelemetry.sdk.testing.assertj.SummaryAssert containsPointsSatisfying(java.lang.Iterable>) diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleGaugeAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleGaugeAssert.java index 04b80ede27d..3144fd4232b 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleGaugeAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleGaugeAssert.java @@ -45,4 +45,31 @@ public DoubleGaugeAssert hasPointsSatisfying( .satisfiesExactlyInAnyOrder(AssertUtil.toConsumers(assertions, DoublePointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the gauge satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final DoubleGaugeAssert containsPointsSatisfying( + Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the gauge satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + public DoubleGaugeAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new DoublePointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleSumAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleSumAssert.java index 8b06f609816..2007831b062 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleSumAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/DoubleSumAssert.java @@ -88,4 +88,30 @@ public DoubleSumAssert hasPointsSatisfying( .satisfiesExactlyInAnyOrder(AssertUtil.toConsumers(assertions, DoublePointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the sum satisfies it. Extra points + * that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final DoubleSumAssert containsPointsSatisfying(Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the sum satisfies it. Extra points + * that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + public DoubleSumAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new DoublePointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/ExponentialHistogramAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/ExponentialHistogramAssert.java index 451868345a9..4dd06fc22c9 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/ExponentialHistogramAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/ExponentialHistogramAssert.java @@ -74,4 +74,31 @@ public ExponentialHistogramAssert hasPointsSatisfying( AssertUtil.toConsumers(assertions, ExponentialHistogramPointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the exponential histogram + * satisfies it. Extra points that match none of the assertions are allowed, and a single point + * may satisfy multiple assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final ExponentialHistogramAssert containsPointsSatisfying( + Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the exponential histogram + * satisfies it. Extra points that match none of the assertions are allowed, and a single point + * may satisfy multiple assertions. + */ + public ExponentialHistogramAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new ExponentialHistogramPointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/HistogramAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/HistogramAssert.java index ba6ffb6dcab..3bd20083770 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/HistogramAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/HistogramAssert.java @@ -72,4 +72,31 @@ public HistogramAssert hasPointsSatisfying( .satisfiesExactlyInAnyOrder(AssertUtil.toConsumers(assertions, HistogramPointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the histogram satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final HistogramAssert containsPointsSatisfying( + Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the histogram satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + public HistogramAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new HistogramPointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongGaugeAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongGaugeAssert.java index f0c410096af..d470775f6bc 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongGaugeAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongGaugeAssert.java @@ -44,4 +44,30 @@ public LongGaugeAssert hasPointsSatisfying( .satisfiesExactlyInAnyOrder(AssertUtil.toConsumers(assertions, LongPointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the gauge satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final LongGaugeAssert containsPointsSatisfying(Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the gauge satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + public LongGaugeAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new LongPointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongSumAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongSumAssert.java index 128a95ebce8..767ecc5f633 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongSumAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/LongSumAssert.java @@ -87,4 +87,30 @@ public LongSumAssert hasPointsSatisfying( .satisfiesExactlyInAnyOrder(AssertUtil.toConsumers(assertions, LongPointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the sum satisfies it. Extra points + * that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final LongSumAssert containsPointsSatisfying(Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the sum satisfies it. Extra points + * that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + public LongSumAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new LongPointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/SummaryAssert.java b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/SummaryAssert.java index fc5cf20640c..cc902b403dc 100644 --- a/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/SummaryAssert.java +++ b/sdk/testing/src/main/java/io/opentelemetry/sdk/testing/assertj/SummaryAssert.java @@ -52,4 +52,30 @@ public SummaryAssert hasPointsSatisfying( .satisfiesExactlyInAnyOrder(AssertUtil.toConsumers(assertions, SummaryPointAssert::new)); return this; } + + /** + * Asserts that for each given assertion, at least one point in the summary satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + @SafeVarargs + @SuppressWarnings("varargs") + public final SummaryAssert containsPointsSatisfying(Consumer... assertions) { + return containsPointsSatisfying(Arrays.asList(assertions)); + } + + /** + * Asserts that for each given assertion, at least one point in the summary satisfies it. Extra + * points that match none of the assertions are allowed, and a single point may satisfy multiple + * assertions. + */ + public SummaryAssert containsPointsSatisfying( + Iterable> assertions) { + isNotNull(); + for (Consumer assertion : assertions) { + assertThat(actual.getPoints()) + .anySatisfy(point -> assertion.accept(new SummaryPointAssert(point))); + } + return this; + } } diff --git a/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/MetricAssertionsTest.java b/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/MetricAssertionsTest.java index 7c3691a7269..2cd6207f655 100644 --- a/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/MetricAssertionsTest.java +++ b/sdk/testing/src/test/java/io/opentelemetry/sdk/testing/assertj/MetricAssertionsTest.java @@ -1299,4 +1299,70 @@ void summary_failure() { point -> point.hasValuesSatisfying(value -> value.hasValue(3.0))))) .isInstanceOf(AssertionError.class); } + + @Test + void containsPointsSatisfying() { + assertThat(DOUBLE_GAUGE_METRIC) + .hasDoubleGaugeSatisfying( + gauge -> gauge.containsPointsSatisfying(point -> point.hasValue(3.0))); + assertThat(LONG_GAUGE_METRIC) + .hasLongGaugeSatisfying( + gauge -> + gauge.containsPointsSatisfying( + point -> point.hasValue(Long.MAX_VALUE), point -> point.hasValue(1))); + assertThat(DOUBLE_SUM_METRIC) + .hasDoubleSumSatisfying(sum -> sum.containsPointsSatisfying(point -> point.hasValue(3.0))); + assertThat(LONG_SUM_METRIC) + .hasLongSumSatisfying( + sum -> sum.containsPointsSatisfying(point -> point.hasValue(Long.MAX_VALUE))); + assertThat(HISTOGRAM_METRIC) + .hasHistogramSatisfying( + histogram -> histogram.containsPointsSatisfying(point -> point.hasSum(15))); + assertThat(EXPONENTIAL_HISTOGRAM_METRIC) + .hasExponentialHistogramSatisfying( + histogram -> histogram.containsPointsSatisfying(point -> point.hasSum(10.0))); + assertThat(SUMMARY_METRIC) + .hasSummarySatisfying( + summary -> summary.containsPointsSatisfying(point -> point.hasSum(2.0))); + } + + @Test + void containsPointsSatisfyingFailure() { + // single assertion that matches no point + assertThatThrownBy( + () -> + assertThat(DOUBLE_GAUGE_METRIC) + .hasDoubleGaugeSatisfying( + gauge -> gauge.containsPointsSatisfying(point -> point.hasValue(42.0)))) + .isInstanceOf(AssertionError.class); + // one of multiple assertions unmatched + assertThatThrownBy( + () -> + assertThat(LONG_GAUGE_METRIC) + .hasLongGaugeSatisfying( + gauge -> + gauge.containsPointsSatisfying( + point -> point.hasValue(Long.MAX_VALUE), + point -> point.hasValue(42)))) + .isInstanceOf(AssertionError.class); + assertThatThrownBy( + () -> + assertThat(HISTOGRAM_METRIC) + .hasHistogramSatisfying( + histogram -> histogram.containsPointsSatisfying(point -> point.hasSum(42)))) + .isInstanceOf(AssertionError.class); + assertThatThrownBy( + () -> + assertThat(EXPONENTIAL_HISTOGRAM_METRIC) + .hasExponentialHistogramSatisfying( + histogram -> + histogram.containsPointsSatisfying(point -> point.hasSum(42.0)))) + .isInstanceOf(AssertionError.class); + assertThatThrownBy( + () -> + assertThat(SUMMARY_METRIC) + .hasSummarySatisfying( + summary -> summary.containsPointsSatisfying(point -> point.hasSum(42.0)))) + .isInstanceOf(AssertionError.class); + } }