Skip to content

Commit fd82042

Browse files
committed
Refactoring of data point attribute assertions
1 parent fddc5fc commit fd82042

File tree

8 files changed

+764
-561
lines changed

8 files changed

+764
-561
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.jmxscraper.assertions;
7+
8+
import static io.opentelemetry.contrib.jmxscraper.assertions.AttributeValueMatcher.ANY_VALUE_MATCHER;
9+
10+
import java.util.Objects;
11+
12+
/** Implements functionality of matching data point attributes. */
13+
public class AttributeMatcher {
14+
private final String name;
15+
private final AttributeValueMatcher attributeValueMatcher;
16+
17+
/**
18+
* Create instance used to match data point attribute with te same name and with any value.
19+
*
20+
* @param name attribute name
21+
*/
22+
AttributeMatcher(String name) {
23+
this.name = name;
24+
this.attributeValueMatcher = ANY_VALUE_MATCHER;
25+
}
26+
27+
/**
28+
* Create instance used to match data point attribute with te same name and with the same value.
29+
*
30+
* @param name attribute name
31+
* @param value attribute value
32+
*/
33+
AttributeMatcher(String name, String value) {
34+
this.name = name;
35+
this.attributeValueMatcher = new AttributeValueMatcher(value);
36+
}
37+
38+
public String getName() {
39+
return name;
40+
}
41+
42+
@Override
43+
public boolean equals(Object o) {
44+
if (this == o) {
45+
return true;
46+
}
47+
if (!(o instanceof AttributeMatcher)) {
48+
return false;
49+
}
50+
AttributeMatcher other = (AttributeMatcher) o;
51+
return Objects.equals(name, other.name)
52+
&& attributeValueMatcher.matchValue(other.attributeValueMatcher);
53+
}
54+
55+
@Override
56+
public int hashCode() {
57+
// Do not use value matcher here to support value wildcards
58+
return Objects.hash(name);
59+
}
60+
61+
@Override
62+
public String toString() {
63+
return attributeValueMatcher.getValue() == null
64+
? '{' + name + '}'
65+
: '{' + name + '=' + attributeValueMatcher.getValue() + '}';
66+
}
67+
;
68+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.jmxscraper.assertions;
7+
8+
import java.util.Objects;
9+
10+
class AttributeValueMatcher {
11+
static final AttributeValueMatcher ANY_VALUE_MATCHER = new AttributeValueMatcher();
12+
13+
private final String value;
14+
15+
AttributeValueMatcher() {
16+
this(null);
17+
}
18+
19+
AttributeValueMatcher(String value) {
20+
this.value = value;
21+
}
22+
23+
String getValue() {
24+
return value;
25+
}
26+
27+
/**
28+
* Match the value held by this and the other AttributeValueMatcher instances. Null value means
29+
* "any value", that's why if any of the values is null they are considered as matching. If both
30+
* values are not null then they must be equal.
31+
*
32+
* @param other the other matcher to compare value with
33+
* @return true if values held by both matchers are matching, false otherwise.
34+
*/
35+
boolean matchValue(AttributeValueMatcher other) {
36+
if ((value == null) || (other.value == null)) {
37+
return true;
38+
}
39+
return Objects.equals(value, other.value);
40+
}
41+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.contrib.jmxscraper.assertions;
7+
8+
import java.util.Arrays;
9+
import java.util.Set;
10+
import java.util.stream.Collectors;
11+
12+
/**
13+
* Utility class implementing convenience static methods to construct data point attribute matchers
14+
* and sets of matchers.
15+
*/
16+
public class DataPointAttributes {
17+
private DataPointAttributes() {}
18+
19+
public static AttributeMatcher attribute(String name, String value) {
20+
return new AttributeMatcher(name, value);
21+
}
22+
23+
public static AttributeMatcher attributeWithAnyValue(String name) {
24+
return new AttributeMatcher(name);
25+
}
26+
27+
public static Set<AttributeMatcher> attributeSet(AttributeMatcher... attributes) {
28+
return Arrays.stream(attributes).collect(Collectors.toSet());
29+
}
30+
}

jmx-scraper/src/integrationTest/java/io/opentelemetry/contrib/jmxscraper/assertions/MetricAssert.java

Lines changed: 28 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,30 @@
55

66
package io.opentelemetry.contrib.jmxscraper.assertions;
77

8+
import static io.opentelemetry.contrib.jmxscraper.assertions.DataPointAttributes.attribute;
9+
810
import com.google.errorprone.annotations.CanIgnoreReturnValue;
911
import io.opentelemetry.proto.common.v1.KeyValue;
1012
import io.opentelemetry.proto.metrics.v1.Metric;
1113
import io.opentelemetry.proto.metrics.v1.NumberDataPoint;
1214
import java.util.Arrays;
1315
import java.util.Collection;
14-
import java.util.HashMap;
16+
import java.util.Collections;
1517
import java.util.HashSet;
1618
import java.util.List;
17-
import java.util.Map;
1819
import java.util.Set;
1920
import java.util.function.Consumer;
2021
import java.util.stream.Collectors;
2122
import org.assertj.core.api.AbstractAssert;
2223
import org.assertj.core.internal.Integers;
2324
import org.assertj.core.internal.Iterables;
24-
import org.assertj.core.internal.Maps;
2525
import org.assertj.core.internal.Objects;
2626

2727
public class MetricAssert extends AbstractAssert<MetricAssert, Metric> {
2828

2929
private static final Objects objects = Objects.instance();
3030
private static final Iterables iterables = Iterables.instance();
3131
private static final Integers integers = Integers.instance();
32-
private static final Maps maps = Maps.instance();
3332

3433
private boolean strict;
3534

@@ -59,7 +58,7 @@ private void strictCheck(
5958
if (!strict) {
6059
return;
6160
}
62-
String failMsgPrefix = expectedCheckStatus ? "duplicate" : "missing";
61+
String failMsgPrefix = expectedCheckStatus ? "Missing" : "Duplicate";
6362
info.description(
6463
"%s assertion on %s for metric '%s'", failMsgPrefix, metricProperty, actual.getName());
6564
objects.assertEqual(info, actualCheckStatus, expectedCheckStatus);
@@ -122,8 +121,8 @@ private MetricAssert hasSum(boolean monotonic) {
122121
info.description("sum expected for metric '%s'", actual.getName());
123122
objects.assertEqual(info, actual.hasSum(), true);
124123

125-
String prefix = monotonic ? "monotonic" : "non-monotonic";
126-
info.description(prefix + " sum expected for metric '%s'", actual.getName());
124+
String sumType = monotonic ? "monotonic" : "non-monotonic";
125+
info.description("sum for metric '%s' is expected to be %s", actual.getName(), sumType);
127126
objects.assertEqual(info, actual.getSum().getIsMonotonic(), monotonic);
128127
return this;
129128
}
@@ -156,6 +155,11 @@ public MetricAssert isUpDownCounter() {
156155
return this;
157156
}
158157

158+
/**
159+
* Verifies that there is no attribute in any of data points.
160+
*
161+
* @return this
162+
*/
159163
@CanIgnoreReturnValue
160164
public MetricAssert hasDataPointsWithoutAttributes() {
161165
isNotNull();
@@ -195,6 +199,7 @@ private MetricAssert checkDataPoints(Consumer<List<NumberDataPoint>> listConsume
195199
return this;
196200
}
197201

202+
// TODO: To be removed and calls will be replaced with hasDataPointsWithAttributes()
198203
@CanIgnoreReturnValue
199204
public MetricAssert hasTypedDataPoints(Collection<String> types) {
200205
return checkDataPoints(
@@ -229,66 +234,48 @@ private void dataPointsCommonCheck(List<NumberDataPoint> dataPoints) {
229234
}
230235

231236
/**
232-
* Verifies that all data points have all the expected attributes
237+
* Verifies that all data points have the same expected one attribute
233238
*
234-
* @param attributes expected attributes
239+
* @param expectedAttribute expected attribute
235240
* @return this
236241
*/
237-
@SafeVarargs
238242
@CanIgnoreReturnValue
239-
public final MetricAssert hasDataPointsAttributes(Map.Entry<String, String>... attributes) {
240-
return checkDataPoints(
241-
dataPoints -> {
242-
dataPointsCommonCheck(dataPoints);
243-
244-
Map<String, String> attributesMap = new HashMap<>();
245-
for (Map.Entry<String, String> attributeEntry : attributes) {
246-
attributesMap.put(attributeEntry.getKey(), attributeEntry.getValue());
247-
}
248-
for (NumberDataPoint dataPoint : dataPoints) {
249-
Map<String, String> dataPointAttributes = toMap(dataPoint.getAttributesList());
250-
251-
// all attributes must match
252-
info.description(
253-
"missing/unexpected data points attributes for metric '%s'", actual.getName());
254-
containsExactly(dataPointAttributes, attributes);
255-
maps.assertContainsAllEntriesOf(info, dataPointAttributes, attributesMap);
256-
}
257-
});
243+
public final MetricAssert hasDataPointsWithOneAttribute(AttributeMatcher expectedAttribute) {
244+
return hasDataPointsWithAttributes(Collections.singleton(expectedAttribute));
258245
}
259246

260247
/**
261-
* Verifies that all data points have their attributes match one of the attributes set and that
262-
* all provided attributes sets matched at least once.
248+
* Verifies that every provided attribute matcher set matches attributes of at least one metric
249+
* data point. Attribute matcher set is considered matching data point attributes if each matcher
250+
* matches exactly one data point attribute
263251
*
264-
* @param attributeSets sets of attributes as maps
252+
* @param attributeSets array of attribute matcher sets
265253
* @return this
266254
*/
267255
@SafeVarargs
268256
@CanIgnoreReturnValue
269257
@SuppressWarnings("varargs") // required to avoid warning
270-
public final MetricAssert hasDataPointsAttributes(Map<String, String>... attributeSets) {
258+
public final MetricAssert hasDataPointsWithAttributes(Set<AttributeMatcher>... attributeSets) {
271259
return checkDataPoints(
272260
dataPoints -> {
273261
dataPointsCommonCheck(dataPoints);
274262

275263
boolean[] matchedSets = new boolean[attributeSets.length];
276264

277-
// validate each datapoint attributes match exactly one of the provided attributes set
265+
// validate each datapoint attributes match exactly one of the provided attributes sets
278266
for (NumberDataPoint dataPoint : dataPoints) {
279-
Map<String, String> map = toMap(dataPoint.getAttributesList());
280-
267+
Set<AttributeMatcher> dataPointAttributes = toSet(dataPoint.getAttributesList());
281268
int matchCount = 0;
282269
for (int i = 0; i < attributeSets.length; i++) {
283-
if (mapEquals(map, attributeSets[i])) {
270+
if (attributeSets[i].equals(dataPointAttributes)) {
284271
matchedSets[i] = true;
285272
matchCount++;
286273
}
287274
}
288275

289276
info.description(
290277
"data point attributes '%s' for metric '%s' must match exactly one of the attribute sets '%s'",
291-
map, actual.getName(), Arrays.asList(attributeSets));
278+
dataPointAttributes, actual.getName(), Arrays.asList(attributeSets));
292279
integers.assertEqual(info, matchCount, 1);
293280
}
294281

@@ -302,29 +289,9 @@ public final MetricAssert hasDataPointsAttributes(Map<String, String>... attribu
302289
});
303290
}
304291

305-
/**
306-
* Map equality utility
307-
*
308-
* @param m1 first map
309-
* @param m2 second map
310-
* @return true if the maps have exactly the same keys and values
311-
*/
312-
private static boolean mapEquals(Map<String, String> m1, Map<String, String> m2) {
313-
if (m1.size() != m2.size()) {
314-
return false;
315-
}
316-
return m1.entrySet().stream().allMatch(e -> e.getValue().equals(m2.get(e.getKey())));
317-
}
318-
319-
@SafeVarargs
320-
@SuppressWarnings("varargs") // required to avoid warning
321-
private final void containsExactly(
322-
Map<String, String> map, Map.Entry<String, String>... entries) {
323-
maps.assertContainsExactly(info, map, entries);
324-
}
325-
326-
private static Map<String, String> toMap(List<KeyValue> list) {
292+
private static Set<AttributeMatcher> toSet(List<KeyValue> list) {
327293
return list.stream()
328-
.collect(Collectors.toMap(KeyValue::getKey, kv -> kv.getValue().getStringValue()));
294+
.map(entry -> attribute(entry.getKey(), entry.getValue().getStringValue()))
295+
.collect(Collectors.toSet());
329296
}
330297
}

0 commit comments

Comments
 (0)