Skip to content

Commit 29b2b09

Browse files
committed
use testing extension for metrics assertions
1 parent edcc15e commit 29b2b09

File tree

1 file changed

+91
-115
lines changed

1 file changed

+91
-115
lines changed

instrumentation/jmx-metrics/library/src/test/java/io/opentelemetry/instrumentation/jmx/engine/MetricAggregationTest.java

Lines changed: 91 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,21 @@
66
package io.opentelemetry.instrumentation.jmx.engine;
77

88
import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat;
9-
import static org.assertj.core.groups.Tuple.tuple;
109
import static org.junit.jupiter.params.ParameterizedInvocationConstants.ARGUMENTS_PLACEHOLDER;
1110

1211
import io.opentelemetry.api.common.AttributeKey;
1312
import io.opentelemetry.api.common.Attributes;
14-
import io.opentelemetry.sdk.OpenTelemetrySdk;
15-
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
16-
import io.opentelemetry.sdk.metrics.data.LongPointData;
17-
import io.opentelemetry.sdk.metrics.data.MetricData;
18-
import io.opentelemetry.sdk.metrics.data.PointData;
19-
import io.opentelemetry.sdk.testing.exporter.InMemoryMetricReader;
13+
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
14+
import io.opentelemetry.instrumentation.testing.junit.LibraryInstrumentationExtension;
15+
import io.opentelemetry.sdk.testing.assertj.LongPointAssert;
16+
import io.opentelemetry.sdk.testing.assertj.MetricAssert;
2017
import java.util.ArrayList;
2118
import java.util.Arrays;
22-
import java.util.Collection;
2319
import java.util.Collections;
2420
import java.util.List;
21+
import java.util.Locale;
22+
import java.util.concurrent.atomic.AtomicInteger;
23+
import java.util.function.Consumer;
2524
import javax.annotation.Nullable;
2625
import javax.management.InstanceNotFoundException;
2726
import javax.management.MBeanRegistrationException;
@@ -32,7 +31,7 @@
3231
import org.junit.jupiter.api.AfterAll;
3332
import org.junit.jupiter.api.AfterEach;
3433
import org.junit.jupiter.api.BeforeAll;
35-
import org.junit.jupiter.api.BeforeEach;
34+
import org.junit.jupiter.api.extension.RegisterExtension;
3635
import org.junit.jupiter.params.ParameterizedTest;
3736
import org.junit.jupiter.params.provider.MethodSource;
3837

@@ -58,6 +57,9 @@ public int getValue() {
5857
}
5958
}
6059

60+
@RegisterExtension
61+
static final InstrumentationExtension testing = LibraryInstrumentationExtension.create();
62+
6163
private static final String DOMAIN = "otel.jmx.test";
6264
private static MBeanServer theServer;
6365

@@ -71,15 +73,6 @@ static void tearDown() {
7173
MBeanServerFactory.releaseMBeanServer(theServer);
7274
}
7375

74-
@BeforeEach
75-
void before() {
76-
reader = InMemoryMetricReader.createDelta();
77-
sdk =
78-
OpenTelemetrySdk.builder()
79-
.setMeterProvider(SdkMeterProvider.builder().registerMetricReader(reader).build())
80-
.build();
81-
}
82-
8376
@AfterEach
8477
void after() throws Exception {
8578
ObjectName objectName = new ObjectName(DOMAIN + ":type=" + Hello.class.getSimpleName() + ",*");
@@ -93,15 +86,8 @@ void after() throws Exception {
9386
throw new RuntimeException(e);
9487
}
9588
});
96-
97-
if (sdk != null) {
98-
sdk.getSdkMeterProvider().close();
99-
}
10089
}
10190

102-
private InMemoryMetricReader reader;
103-
private OpenTelemetrySdk sdk;
104-
10591
private static ObjectName getObjectName(@Nullable String a, @Nullable String b)
10692
throws MalformedObjectNameException {
10793
StringBuilder parts = new StringBuilder();
@@ -126,8 +112,10 @@ void singleInstance(MetricInfo.Type metricType) throws Exception {
126112
ObjectName bean = getObjectName(null, null);
127113
theServer.registerMBean(new Hello(42), bean);
128114

129-
Collection<MetricData> data = testMetric(bean.toString(), Collections.emptyList(), metricType);
130-
checkSingleValue(data, 42, metricType);
115+
String metricName = generateMetricName(metricType);
116+
startTestMetric(metricName, bean.toString(), Collections.emptyList(), metricType);
117+
waitAndAssertMetric(
118+
metricName, metricType, point -> point.hasValue(42).hasAttributes(Attributes.empty()));
131119
}
132120

133121
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
@@ -137,14 +125,17 @@ void aggregateOneParam(MetricInfo.Type metricType) throws Exception {
137125
theServer.registerMBean(new Hello(37), getObjectName("value2", null));
138126

139127
String bean = getObjectName("*", null).toString();
140-
Collection<MetricData> data = testMetric(bean, Collections.emptyList(), metricType);
141-
int expected = 79;
142-
if (metricType == MetricInfo.Type.GAUGE) {
143-
// last-value aggregation produces unpredictable result unless a single mbean instance is used
144-
// test here is only used as a way to document behavior and should not be considered a feature
145-
expected = 37;
146-
}
147-
checkSingleValue(data, expected, metricType);
128+
String metricName = generateMetricName(metricType);
129+
startTestMetric(metricName, bean, Collections.emptyList(), metricType);
130+
131+
// last-value aggregation produces an unpredictable result unless a single mbean instance is
132+
// used
133+
// test here is only used as a way to document behavior and should not be considered a feature
134+
long expected = metricType == MetricInfo.Type.GAUGE ? 37 : 79;
135+
waitAndAssertMetric(
136+
metricName,
137+
metricType,
138+
point -> point.hasValue(expected).hasAttributes(Attributes.empty()));
148139
}
149140

150141
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
@@ -156,15 +147,17 @@ void aggregateMultipleParams(MetricInfo.Type metricType) throws Exception {
156147
theServer.registerMBean(new Hello(4), getObjectName("4", "y"));
157148

158149
String bean = getObjectName("*", "*").toString();
159-
Collection<MetricData> data = testMetric(bean, Collections.emptyList(), metricType);
160-
161-
int expected = 10;
162-
if (metricType == MetricInfo.Type.GAUGE) {
163-
// last-value aggregation produces unpredictable result unless a single mbean instance is used
164-
// test here is only used as a way to document behavior and should not be considered a feature
165-
expected = 1;
166-
}
167-
checkSingleValue(data, expected, metricType);
150+
String metricName = generateMetricName(metricType);
151+
startTestMetric(metricName, bean, Collections.emptyList(), metricType);
152+
153+
// last-value aggregation produces an unpredictable result unless a single mbean instance is
154+
// used
155+
// test here is only used as a way to document behavior and should not be considered a feature
156+
long expected = metricType == MetricInfo.Type.GAUGE ? 1 : 10;
157+
waitAndAssertMetric(
158+
metricName,
159+
metricType,
160+
point -> point.hasValue(expected).hasAttributes(Attributes.empty()));
168161
}
169162

170163
@ParameterizedTest(name = ARGUMENTS_PLACEHOLDER)
@@ -181,44 +174,66 @@ void partialAggregateMultipleParams(MetricInfo.Type metricType) throws Exception
181174
Collections.singletonList(
182175
new MetricAttribute(
183176
"test.metric.param", MetricAttributeExtractor.fromObjectNameParameter("b")));
184-
Collection<MetricData> data = testMetric(bean, attributes, metricType);
185-
186-
assertThat(data)
187-
.isNotEmpty()
188-
.satisfiesExactly(
189-
metric -> {
190-
assertThat(metric.getName()).isEqualTo("test.metric");
191-
AttributeKey<String> metricAttribute = AttributeKey.stringKey("test.metric.param");
192-
193-
if (metricType == MetricInfo.Type.GAUGE) {
194-
// last-value aggregation produces unpredictable result unless a single mbean
195-
// instance is used
196-
// test here is only used as a way to document behavior and should not be considered
197-
// a feature
198-
assertThat(metric.getLongGaugeData().getPoints())
199-
.extracting(LongPointData::getValue, PointData::getAttributes)
200-
.containsExactlyInAnyOrder(
201-
tuple(4L, Attributes.of(metricAttribute, "y")),
202-
tuple(1L, Attributes.of(metricAttribute, "x")));
203-
} else {
204-
// sum aggregation
205-
assertThat(metric.getLongSumData().getPoints())
206-
.extracting(LongPointData::getValue, PointData::getAttributes)
207-
.containsExactlyInAnyOrder(
208-
tuple(6L, Attributes.of(metricAttribute, "y")),
209-
tuple(4L, Attributes.of(metricAttribute, "x")));
210-
}
211-
});
177+
String metricName = generateMetricName(metricType);
178+
startTestMetric(metricName, bean, attributes, metricType);
179+
180+
AttributeKey<String> metricAttribute = AttributeKey.stringKey("test.metric.param");
181+
if (metricType == MetricInfo.Type.GAUGE) {
182+
waitAndAssertMetric(
183+
metricName,
184+
metricType,
185+
point -> point.hasValue(1).hasAttribute(metricAttribute, "x"),
186+
point -> point.hasValue(4).hasAttribute(metricAttribute, "y"));
187+
} else {
188+
waitAndAssertMetric(
189+
metricName,
190+
metricType,
191+
point -> point.hasValue(4).hasAttribute(metricAttribute, "x"),
192+
point -> point.hasValue(6).hasAttribute(metricAttribute, "y"));
193+
}
194+
}
195+
196+
private static final AtomicInteger metricCounter = new AtomicInteger(0);
197+
198+
private static String generateMetricName(MetricInfo.Type metricType) {
199+
// generate a sequential metric name that prevents naming conflicts and unexpected behaviors
200+
return "test.metric"
201+
+ metricCounter.incrementAndGet()
202+
+ "."
203+
+ metricType.name().toLowerCase(Locale.ROOT);
212204
}
213205

214-
private Collection<MetricData> testMetric(
215-
String mbean, List<MetricAttribute> attributes, MetricInfo.Type metricType)
206+
@SafeVarargs
207+
@SuppressWarnings("varargs")
208+
private static void waitAndAssertMetric(
209+
String metricName, MetricInfo.Type metricType, Consumer<LongPointAssert>... pointAsserts) {
210+
211+
testing.waitAndAssertMetrics(
212+
"io.opentelemetry.jmx",
213+
metricName,
214+
metrics ->
215+
metrics.anySatisfy(
216+
metricData -> {
217+
MetricAssert metricAssert =
218+
assertThat(metricData).hasDescription("description").hasUnit("1");
219+
if (metricType == MetricInfo.Type.GAUGE) {
220+
metricAssert.hasLongGaugeSatisfying(
221+
gauge -> gauge.hasPointsSatisfying(pointAsserts));
222+
} else {
223+
metricAssert.hasLongSumSatisfying(sum -> sum.hasPointsSatisfying(pointAsserts));
224+
}
225+
}));
226+
}
227+
228+
private static void startTestMetric(
229+
String metricName, String mbean, List<MetricAttribute> attributes, MetricInfo.Type metricType)
216230
throws MalformedObjectNameException {
217-
JmxMetricInsight metricInsight = JmxMetricInsight.createService(sdk, 0);
231+
JmxMetricInsight metricInsight =
232+
JmxMetricInsight.createService(testing.getOpenTelemetrySdk(), 0);
218233
MetricConfiguration metricConfiguration = new MetricConfiguration();
219234
List<MetricExtractor> extractors = new ArrayList<>();
220235

221-
MetricInfo metricInfo = new MetricInfo("test.metric", "description", null, "1", metricType);
236+
MetricInfo metricInfo = new MetricInfo(metricName, "description", null, "1", metricType);
222237
BeanAttributeExtractor beanExtractor = BeanAttributeExtractor.fromName("Value");
223238
MetricExtractor extractor = new MetricExtractor(beanExtractor, metricInfo, attributes);
224239
extractors.add(extractor);
@@ -227,44 +242,5 @@ private Collection<MetricData> testMetric(
227242

228243
metricConfiguration.addMetricDef(metricDef);
229244
metricInsight.startLocal(metricConfiguration);
230-
231-
return waitMetricsReceived();
232-
}
233-
234-
private static void checkSingleValue(
235-
Collection<MetricData> data, long expectedValue, MetricInfo.Type metricType) {
236-
assertThat(data)
237-
.isNotEmpty()
238-
.satisfiesExactlyInAnyOrder(
239-
metric -> {
240-
assertThat(metric.getName()).isEqualTo("test.metric");
241-
242-
Collection<LongPointData> points;
243-
if (metricType == MetricInfo.Type.GAUGE) {
244-
points = metric.getLongGaugeData().getPoints();
245-
} else {
246-
points = metric.getLongSumData().getPoints();
247-
}
248-
assertThat(points)
249-
.extracting(LongPointData::getValue, PointData::getAttributes)
250-
.containsExactlyInAnyOrder(tuple(expectedValue, Attributes.empty()));
251-
});
252-
}
253-
254-
private Collection<MetricData> waitMetricsReceived() {
255-
int retries = 100;
256-
Collection<MetricData> data;
257-
do {
258-
try {
259-
Thread.sleep(100);
260-
} catch (InterruptedException e) {
261-
throw new RuntimeException(e);
262-
}
263-
data = reader.collectAllMetrics();
264-
} while (data.isEmpty() && retries-- > 0);
265-
if (data.isEmpty()) {
266-
throw new RuntimeException("timed out waiting for metrics received");
267-
}
268-
return data;
269245
}
270246
}

0 commit comments

Comments
 (0)