Skip to content

Commit 1bf9c7f

Browse files
committed
feat(test): Add instrumentation test for JUnit
1 parent 4654d52 commit 1bf9c7f

File tree

10 files changed

+281
-33
lines changed

10 files changed

+281
-33
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package datadog.trace.agent.test.assertions;
2+
3+
import java.util.Optional;
4+
5+
public class Any<T> implements Matcher<T> {
6+
@Override
7+
public Optional<T> expected() {
8+
return Optional.empty();
9+
}
10+
11+
@Override
12+
public String message() {
13+
return "";
14+
}
15+
16+
@Override
17+
public boolean test(T t) {
18+
return true;
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package datadog.trace.agent.test.assertions;
2+
3+
import java.util.Optional;
4+
5+
public class IsFalse implements Matcher<Boolean> {
6+
@Override
7+
public Optional<Boolean> expected() {
8+
return Optional.of(false);
9+
}
10+
11+
@Override
12+
public String message() {
13+
return "False expected";
14+
}
15+
16+
@Override
17+
public boolean test(Boolean t) {
18+
return !t;
19+
}
20+
}

dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/assertions/NonNull.java renamed to dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/assertions/IsNonNull.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import java.util.Optional;
44

5-
public class NonNull<T> implements Matcher<T> {
5+
public class IsNonNull<T> implements Matcher<T> {
66
@Override
77
public Optional<T> expected() {
88
return Optional.empty();
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package datadog.trace.agent.test.assertions;
2+
3+
import java.util.Optional;
4+
5+
public class IsNull<T> implements Matcher<T> {
6+
@Override
7+
public Optional<T> expected() {
8+
return Optional.empty();
9+
}
10+
11+
@Override
12+
public String message() {
13+
return "Null value expected";
14+
}
15+
16+
@Override
17+
public boolean test(T t) {
18+
return t == null;
19+
}
20+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package datadog.trace.agent.test.assertions;
2+
3+
import java.util.Optional;
4+
5+
public class IsTrue implements Matcher<Boolean> {
6+
@Override
7+
public Optional<Boolean> expected() {
8+
return Optional.of(true);
9+
}
10+
11+
@Override
12+
public String message() {
13+
return "True expected";
14+
}
15+
16+
@Override
17+
public boolean test(Boolean t) {
18+
return t;
19+
}
20+
}

dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/assertions/Matchers.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
11
package datadog.trace.agent.test.assertions;
22

3+
import java.util.Optional;
34
import java.util.function.Predicate;
45
import java.util.regex.Pattern;
6+
import org.opentest4j.AssertionFailedError;
7+
8+
/*
9+
* TODO: Dev notes
10+
* - introduce as few as possible matchers
11+
* - only have matchers for generic purpose, don't introduce feature / produce / use-case specific matchers
12+
* - name "ignores" as "any"?
13+
*/
514

615
public class Matchers {
716
public static <T> Matcher<T> is(T expected) {
817
return new Is<>(expected);
918
}
1019

20+
public static <T> Matcher<T> isNull() {
21+
return new IsNull<>();
22+
}
23+
1124
public static <T> Matcher<T> nonNull() {
12-
return new NonNull<>();
25+
return new IsNonNull<>();
26+
}
27+
28+
public static Matcher<Boolean> isTrue() {
29+
return new IsTrue();
30+
}
31+
32+
public static Matcher<Boolean> isFalse() {
33+
return new IsFalse();
1334
}
1435

1536
public static Matcher<CharSequence> matches(String regex) {
@@ -23,4 +44,19 @@ public static Matcher<CharSequence> matches(Pattern pattern) {
2344
public static <T> Matcher<T> validates(Predicate<T> validator) {
2445
return new Validates<>(validator);
2546
}
47+
48+
public static <T> Matcher<T> any() {
49+
return new Any<>();
50+
}
51+
52+
static <T> void assertValue(Matcher<T> matcher, T value, String message) {
53+
if (matcher != null && !matcher.test(value)) {
54+
Optional<T> expected = matcher.expected();
55+
if (expected.isPresent()) {
56+
throw new AssertionFailedError(message + ". " + matcher.message(), expected.get(), value);
57+
} else {
58+
throw new AssertionFailedError(message + ": " + value + ". " + matcher.message());
59+
}
60+
}
61+
}
2662
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package datadog.trace.agent.test.assertions;
2+
3+
import static datadog.trace.agent.test.assertions.Matchers.assertValue;
4+
import static datadog.trace.agent.test.assertions.Matchers.is;
5+
import static datadog.trace.bootstrap.instrumentation.api.AgentSpanLink.DEFAULT_FLAGS;
6+
import static datadog.trace.bootstrap.instrumentation.api.SpanAttributes.EMPTY;
7+
8+
import datadog.trace.api.DDTraceId;
9+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanContext;
10+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
11+
import datadog.trace.bootstrap.instrumentation.api.SpanAttributes;
12+
import datadog.trace.core.DDSpan;
13+
14+
public final class SpanLinkMatcher {
15+
private final Matcher<DDTraceId> traceIdMatcher;
16+
private final Matcher<Long> spanIdMatcher;
17+
private Matcher<Byte> traceFlagsMatcher;
18+
private Matcher<SpanAttributes> spanAttributesMatcher;
19+
private Matcher<String> traceStateMatcher;
20+
21+
private SpanLinkMatcher(Matcher<DDTraceId> traceIdMatcher, Matcher<Long> spanIdMatcher) {
22+
this.traceIdMatcher = traceIdMatcher;
23+
this.spanIdMatcher = spanIdMatcher;
24+
this.traceFlagsMatcher = is(DEFAULT_FLAGS);
25+
this.spanAttributesMatcher = is(EMPTY);
26+
this.traceStateMatcher = is("");
27+
}
28+
29+
public static SpanLinkMatcher from(DDSpan span) {
30+
return from(span.context());
31+
}
32+
33+
public static SpanLinkMatcher from(AgentSpanContext spanContext) {
34+
return from(spanContext.getTraceId(), spanContext.getSpanId());
35+
}
36+
37+
public static SpanLinkMatcher from(DDTraceId traceId, long spanId) {
38+
return new SpanLinkMatcher(is(traceId), is(spanId));
39+
}
40+
41+
public static SpanLinkMatcher any() {
42+
return new SpanLinkMatcher(Matchers.any(), Matchers.any());
43+
}
44+
45+
public SpanLinkMatcher traceFlags(byte traceFlags) {
46+
this.traceFlagsMatcher = is(traceFlags);
47+
return this;
48+
}
49+
50+
public SpanLinkMatcher attributes(SpanAttributes spanAttributes) {
51+
this.spanAttributesMatcher = is(spanAttributes);
52+
return this;
53+
}
54+
55+
public SpanLinkMatcher traceState(String traceState) {
56+
this.traceStateMatcher = is(traceState);
57+
return this;
58+
}
59+
60+
void assertLink(AgentSpanLink link) {
61+
// Assert link values
62+
assertValue(this.traceIdMatcher, link.traceId(), "Expected trace identifier");
63+
assertValue(this.spanIdMatcher, link.spanId(), "Expected span identifier");
64+
assertValue(this.traceFlagsMatcher, link.traceFlags(), "Expected trace flags");
65+
assertValue(this.spanAttributesMatcher, link.attributes(), "Expected attributes");
66+
assertValue(this.traceStateMatcher, link.traceState(), "Expected trace state");
67+
}
68+
}

dd-java-agent/instrumentation-testing/src/main/java/datadog/trace/agent/test/assertions/SpanMatcher.java

Lines changed: 73 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,55 @@
11
package datadog.trace.agent.test.assertions;
22

3+
import static datadog.trace.agent.test.assertions.Matchers.assertValue;
34
import static datadog.trace.agent.test.assertions.Matchers.is;
5+
import static datadog.trace.agent.test.assertions.Matchers.isFalse;
6+
import static datadog.trace.agent.test.assertions.Matchers.isNull;
7+
import static datadog.trace.agent.test.assertions.Matchers.isTrue;
48
import static datadog.trace.agent.test.assertions.Matchers.matches;
59
import static datadog.trace.agent.test.assertions.Matchers.nonNull;
610
import static datadog.trace.agent.test.assertions.Matchers.validates;
7-
import static java.time.temporal.ChronoUnit.NANOS;
11+
import static datadog.trace.core.DDSpanAccessor.spanLinks;
12+
import static java.time.Duration.ofNanos;
813

14+
import datadog.trace.api.TagMap;
15+
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
916
import datadog.trace.core.DDSpan;
1017
import java.time.Duration;
11-
import java.util.Optional;
18+
import java.util.List;
1219
import java.util.function.Predicate;
1320
import java.util.regex.Pattern;
1421
import org.opentest4j.AssertionFailedError;
1522

23+
/*
24+
* TODO: Dev notes
25+
* - inconsistency with "<field><matching-rule>()" vs "has<field>()"
26+
* - hasServiceName / withError / withoutError
27+
*/
28+
1629
public final class SpanMatcher {
1730
private Matcher<Long> idMatcher;
1831
private Matcher<Long> parentIdMatcher;
1932
private Matcher<String> serviceNameMatcher;
2033
private Matcher<CharSequence> operationNameMatcher;
2134
private Matcher<CharSequence> resourceNameMatcher;
2235
private Matcher<Duration> durationMatcher;
36+
private Matcher<String> typeMatcher;
37+
private Matcher<Boolean> errorMatcher;
38+
private SpanLinkMatcher[] linkMatchers;
2339

2440
private static final Matcher<Long> CHILD_OF_PREVIOUS_MATCHER = is(0L);
2541

26-
private SpanMatcher() {}
42+
private SpanMatcher() {
43+
this.serviceNameMatcher = validates(s -> s != null && !s.isEmpty());
44+
this.typeMatcher = isNull();
45+
this.errorMatcher = isFalse();
46+
}
2747

2848
public static SpanMatcher span() {
2949
return new SpanMatcher();
3050
}
3151

32-
public SpanMatcher withId(long id) {
52+
public SpanMatcher id(long id) {
3353
this.idMatcher = is(id);
3454
return this;
3555
}
@@ -53,32 +73,32 @@ public SpanMatcher hasServiceName() {
5373
return this;
5474
}
5575

56-
public SpanMatcher withServiceName(String serviceName) {
76+
public SpanMatcher serviceName(String serviceName) {
5777
this.serviceNameMatcher = is(serviceName);
5878
return this;
5979
}
6080

61-
public SpanMatcher withOperationName(String operationName) {
81+
public SpanMatcher operationName(String operationName) {
6282
this.operationNameMatcher = is(operationName);
6383
return this;
6484
}
6585

66-
public SpanMatcher operationNameMatching(Pattern pattern) {
86+
public SpanMatcher operationName(Pattern pattern) {
6787
this.operationNameMatcher = matches(pattern);
6888
return this;
6989
}
7090

71-
public SpanMatcher withResourceName(String resourceName) {
91+
public SpanMatcher resourceName(String resourceName) {
7292
this.resourceNameMatcher = is(resourceName);
7393
return this;
7494
}
7595

76-
public SpanMatcher resourceNameMatching(Pattern pattern) {
96+
public SpanMatcher resourceName(Pattern pattern) {
7797
this.resourceNameMatcher = matches(pattern);
7898
return this;
7999
}
80100

81-
public SpanMatcher resourceNameMatching(Predicate<CharSequence> validator) {
101+
public SpanMatcher resourceName(Predicate<CharSequence> validator) {
82102
this.resourceNameMatcher = validates(validator);
83103
return this;
84104
}
@@ -93,12 +113,32 @@ public SpanMatcher durationLongerThan(Duration duration) {
93113
return this;
94114
}
95115

96-
public SpanMatcher durationMatching(Predicate<Duration> validator) {
116+
public SpanMatcher duration(Predicate<Duration> validator) {
97117
this.durationMatcher = validates(validator);
98118
return this;
99119
}
100120

101-
public void assertSpan(DDSpan span, DDSpan previousSpan) {
121+
public SpanMatcher type(String type) {
122+
this.typeMatcher = is(type);
123+
return this;
124+
}
125+
126+
public SpanMatcher withError() {
127+
this.errorMatcher = isTrue();
128+
return this;
129+
}
130+
131+
public SpanMatcher withoutError() {
132+
this.errorMatcher = isFalse();
133+
return this;
134+
}
135+
136+
public SpanMatcher links(SpanLinkMatcher... matchers) {
137+
this.linkMatchers = matchers;
138+
return this;
139+
}
140+
141+
void assertSpan(DDSpan span, DDSpan previousSpan) {
102142
// Apply parent id matcher from the previous span
103143
if (this.parentIdMatcher == CHILD_OF_PREVIOUS_MATCHER) {
104144
this.parentIdMatcher = is(previousSpan.getSpanId());
@@ -109,19 +149,27 @@ public void assertSpan(DDSpan span, DDSpan previousSpan) {
109149
assertValue(this.serviceNameMatcher, span.getServiceName(), "Expected service name");
110150
assertValue(this.operationNameMatcher, span.getOperationName(), "Expected operation name");
111151
assertValue(this.resourceNameMatcher, span.getResourceName(), "Expected resource name");
112-
assertValue(
113-
this.durationMatcher, Duration.of(span.getDurationNano(), NANOS), "Expected duration");
114-
// TODO Add more values to test (tags, links, ...)
115-
}
116-
117-
private <T> void assertValue(Matcher<T> matcher, T value, String message) {
118-
if (matcher != null && !matcher.test(value)) {
119-
Optional<T> expected = matcher.expected();
120-
if (expected.isPresent()) {
121-
throw new AssertionFailedError(message + ". " + matcher.message(), expected.get(), value);
122-
} else {
123-
throw new AssertionFailedError(message + ": " + value + ". " + matcher.message());
124-
}
152+
assertValue(this.durationMatcher, ofNanos(span.getDurationNano()), "Expected duration");
153+
assertValue(this.typeMatcher, span.getSpanType(), "Expected span type");
154+
assertValue(this.errorMatcher, span.isError(), "Expected error status");
155+
assertSpanTags(span.getTags());
156+
assertSpanLinks(spanLinks(span));
157+
}
158+
159+
private void assertSpanTags(TagMap tags) {
160+
// TODO Implement span tag assertions
161+
}
162+
163+
private void assertSpanLinks(List<AgentSpanLink> links) {
164+
int linkCount = links == null ? 0 : links.size();
165+
int expectedLinkCount = this.linkMatchers == null ? 0 : this.linkMatchers.length;
166+
if (linkCount != expectedLinkCount) {
167+
throw new AssertionFailedError("Unexpected span link count", expectedLinkCount, linkCount);
168+
}
169+
for (int i = 0; i < expectedLinkCount; i++) {
170+
SpanLinkMatcher linkMatcher = this.linkMatchers[expectedLinkCount];
171+
AgentSpanLink link = links.get(i);
172+
linkMatcher.assertLink(link);
125173
}
126174
}
127175
}

0 commit comments

Comments
 (0)