11package datadog .trace .agent .test .assertions ;
22
3+ import static datadog .trace .agent .test .assertions .Matchers .assertValue ;
34import 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 ;
48import static datadog .trace .agent .test .assertions .Matchers .matches ;
59import static datadog .trace .agent .test .assertions .Matchers .nonNull ;
610import 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 ;
916import datadog .trace .core .DDSpan ;
1017import java .time .Duration ;
11- import java .util .Optional ;
18+ import java .util .List ;
1219import java .util .function .Predicate ;
1320import java .util .regex .Pattern ;
1421import org .opentest4j .AssertionFailedError ;
1522
23+ /*
24+ * TODO: Dev notes
25+ * - inconsistency with "<field><matching-rule>()" vs "has<field>()"
26+ * - hasServiceName / withError / withoutError
27+ */
28+
1629public 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