|
5 | 5 |
|
6 | 6 | package io.opentelemetry.contrib.jmxscraper.assertions; |
7 | 7 |
|
| 8 | +import static io.opentelemetry.contrib.jmxscraper.assertions.DataPointAttributes.attributeGroup; |
| 9 | + |
8 | 10 | import com.google.errorprone.annotations.CanIgnoreReturnValue; |
9 | 11 | import io.opentelemetry.proto.common.v1.KeyValue; |
10 | 12 | import io.opentelemetry.proto.metrics.v1.Metric; |
11 | 13 | import io.opentelemetry.proto.metrics.v1.NumberDataPoint; |
12 | 14 | import java.util.Arrays; |
13 | 15 | import java.util.Collection; |
14 | | -import java.util.HashMap; |
15 | 16 | import java.util.HashSet; |
16 | 17 | import java.util.List; |
17 | 18 | import java.util.Map; |
|
21 | 22 | import org.assertj.core.api.AbstractAssert; |
22 | 23 | import org.assertj.core.internal.Integers; |
23 | 24 | import org.assertj.core.internal.Iterables; |
24 | | -import org.assertj.core.internal.Maps; |
25 | 25 | import org.assertj.core.internal.Objects; |
26 | 26 |
|
27 | 27 | public class MetricAssert extends AbstractAssert<MetricAssert, Metric> { |
28 | 28 |
|
29 | 29 | private static final Objects objects = Objects.instance(); |
30 | 30 | private static final Iterables iterables = Iterables.instance(); |
31 | 31 | private static final Integers integers = Integers.instance(); |
32 | | - private static final Maps maps = Maps.instance(); |
33 | 32 |
|
34 | 33 | private boolean strict; |
35 | 34 |
|
@@ -59,7 +58,7 @@ private void strictCheck( |
59 | 58 | if (!strict) { |
60 | 59 | return; |
61 | 60 | } |
62 | | - String failMsgPrefix = expectedCheckStatus ? "duplicate" : "missing"; |
| 61 | + String failMsgPrefix = expectedCheckStatus ? "Missing" : "Duplicate"; |
63 | 62 | info.description( |
64 | 63 | "%s assertion on %s for metric '%s'", failMsgPrefix, metricProperty, actual.getName()); |
65 | 64 | objects.assertEqual(info, actualCheckStatus, expectedCheckStatus); |
@@ -122,8 +121,8 @@ private MetricAssert hasSum(boolean monotonic) { |
122 | 121 | info.description("sum expected for metric '%s'", actual.getName()); |
123 | 122 | objects.assertEqual(info, actual.hasSum(), true); |
124 | 123 |
|
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); |
127 | 126 | objects.assertEqual(info, actual.getSum().getIsMonotonic(), monotonic); |
128 | 127 | return this; |
129 | 128 | } |
@@ -156,6 +155,11 @@ public MetricAssert isUpDownCounter() { |
156 | 155 | return this; |
157 | 156 | } |
158 | 157 |
|
| 158 | + /** |
| 159 | + * Verifies that there is no attribute in any of data points. |
| 160 | + * |
| 161 | + * @return this |
| 162 | + */ |
159 | 163 | @CanIgnoreReturnValue |
160 | 164 | public MetricAssert hasDataPointsWithoutAttributes() { |
161 | 165 | isNotNull(); |
@@ -195,6 +199,7 @@ private MetricAssert checkDataPoints(Consumer<List<NumberDataPoint>> listConsume |
195 | 199 | return this; |
196 | 200 | } |
197 | 201 |
|
| 202 | + // TODO: To be removed and calls will be replaced with hasDataPointsWithAttributes() |
198 | 203 | @CanIgnoreReturnValue |
199 | 204 | public MetricAssert hasTypedDataPoints(Collection<String> types) { |
200 | 205 | return checkDataPoints( |
@@ -229,102 +234,61 @@ private void dataPointsCommonCheck(List<NumberDataPoint> dataPoints) { |
229 | 234 | } |
230 | 235 |
|
231 | 236 | /** |
232 | | - * Verifies that all data points have all the expected attributes |
| 237 | + * Verifies that all metric data points have the same expected one attribute |
233 | 238 | * |
234 | | - * @param attributes expected attributes |
| 239 | + * @param expectedAttribute attribute matcher to validate data points attributes |
235 | 240 | * @return this |
236 | 241 | */ |
237 | | - @SafeVarargs |
238 | 242 | @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(attributeGroup(expectedAttribute)); |
258 | 245 | } |
259 | 246 |
|
260 | 247 | /** |
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 data point attributes is matched exactly by one of the matcher groups |
| 249 | + * provided. Also, each matcher group must match at least one data point attributes set. Data |
| 250 | + * point attributes are matched by matcher group if each attribute is matched by one matcher and |
| 251 | + * each matcher matches one attribute. In other words: number of attributes is the same as number |
| 252 | + * of matchers and there is 1:1 matching between them. |
263 | 253 | * |
264 | | - * @param attributeSets sets of attributes as maps |
| 254 | + * @param matcherGroups array of attribute matcher groups |
265 | 255 | * @return this |
266 | 256 | */ |
267 | | - @SafeVarargs |
268 | 257 | @CanIgnoreReturnValue |
269 | | - @SuppressWarnings("varargs") // required to avoid warning |
270 | | - public final MetricAssert hasDataPointsAttributes(Map<String, String>... attributeSets) { |
| 258 | + public final MetricAssert hasDataPointsWithAttributes(AttributeMatcherGroup... matcherGroups) { |
271 | 259 | return checkDataPoints( |
272 | 260 | dataPoints -> { |
273 | 261 | dataPointsCommonCheck(dataPoints); |
274 | 262 |
|
275 | | - boolean[] matchedSets = new boolean[attributeSets.length]; |
| 263 | + boolean[] matchedSets = new boolean[matcherGroups.length]; |
276 | 264 |
|
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 |
278 | 266 | for (NumberDataPoint dataPoint : dataPoints) { |
279 | | - Map<String, String> map = toMap(dataPoint.getAttributesList()); |
280 | | - |
| 267 | + Map<String, String> dataPointAttributes = |
| 268 | + dataPoint.getAttributesList().stream() |
| 269 | + .collect( |
| 270 | + Collectors.toMap(KeyValue::getKey, kv -> kv.getValue().getStringValue())); |
281 | 271 | int matchCount = 0; |
282 | | - for (int i = 0; i < attributeSets.length; i++) { |
283 | | - if (mapEquals(map, attributeSets[i])) { |
| 272 | + for (int i = 0; i < matcherGroups.length; i++) { |
| 273 | + if (matcherGroups[i].matches(dataPointAttributes)) { |
284 | 274 | matchedSets[i] = true; |
285 | 275 | matchCount++; |
286 | 276 | } |
287 | 277 | } |
288 | 278 |
|
289 | 279 | info.description( |
290 | 280 | "data point attributes '%s' for metric '%s' must match exactly one of the attribute sets '%s'", |
291 | | - map, actual.getName(), Arrays.asList(attributeSets)); |
| 281 | + dataPointAttributes, actual.getName(), Arrays.asList(matcherGroups)); |
292 | 282 | integers.assertEqual(info, matchCount, 1); |
293 | 283 | } |
294 | 284 |
|
295 | 285 | // check that all attribute sets matched at least once |
296 | 286 | for (int i = 0; i < matchedSets.length; i++) { |
297 | 287 | info.description( |
298 | 288 | "no data point matched attribute set '%s' for metric '%s'", |
299 | | - attributeSets[i], actual.getName()); |
| 289 | + matcherGroups[i], actual.getName()); |
300 | 290 | objects.assertEqual(info, matchedSets[i], true); |
301 | 291 | } |
302 | 292 | }); |
303 | 293 | } |
304 | | - |
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) { |
327 | | - return list.stream() |
328 | | - .collect(Collectors.toMap(KeyValue::getKey, kv -> kv.getValue().getStringValue())); |
329 | | - } |
330 | 294 | } |
0 commit comments