Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package datadog.trace.agent.test.assertions;

import static datadog.trace.test.junit.utils.assertions.Matchers.assertValue;
import static datadog.trace.test.junit.utils.assertions.Matchers.is;
import static datadog.trace.test.junit.utils.assertions.Matchers.validates;
import static java.util.Collections.emptyMap;

import datadog.trace.core.DDSpan;
import datadog.trace.core.DDSpanEvent;
import datadog.trace.test.junit.utils.assertions.Matcher;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
* Provides matchers for span events based on their properties such as name, time, and attributes.
*/
public final class SpanEventMatcher {
private final Matcher<String> nameMatcher;
private Matcher<Long> timeMatcher;
private Matcher<Map<String, ?>> attributesMatcher;

private SpanEventMatcher(Matcher<String> nameMatcher) {
this.nameMatcher = nameMatcher;
this.attributesMatcher = is(emptyMap());
}

/**
* Creates a {@code SpanEventMatcher} that matches a span event with the given name. By default,
* the event must have no attribute and its time must be within the span start and end times.
*
* @param name The span event name to match against.
* @return A {@code SpanEventMatcher} that matches a span event with the given name.
*/
public static SpanEventMatcher event(String name) {
return new SpanEventMatcher(is(name));
}

/**
* Sets the event time to match against.
*
* @param timestamp The event time to match against.
* @param unit The event time unit.
* @return The updated {@code SpanEventMatcher} instance with the new time constraint.
*/
public SpanEventMatcher time(long timestamp, TimeUnit unit) {
this.timeMatcher = is(unit.toNanos(timestamp));
return this;
}

/**
* Sets the event attributes to match against.
*
* @param attributes The event attributes to match against, with values being {@link String},
* {@link Boolean}, {@link Long}, {@link Double}, or a {@link List} of them.
* @return The updated {@code SpanEventMatcher} instance with the new attributes constraint.
*/
public SpanEventMatcher attributes(Map<String, ?> attributes) {
this.attributesMatcher = is(attributes);
return this;
}

void assertEvent(DDSpan span, DDSpanEvent event) {
Matcher<Long> timeMatcher = this.timeMatcher;
if (timeMatcher == null) {
long startTime = span.getStartTime();
long endTime = startTime + span.getDurationNano();
timeMatcher = validates(time -> startTime <= time && time <= endTime);
}
// Assert event values
assertValue(this.nameMatcher, event.name(), "Unexpected event name");
assertValue(timeMatcher, event.timeUnixNano(), "Unexpected event time");
assertValue(this.attributesMatcher, event.attributes(), "Unexpected event attributes");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import datadog.trace.api.TagMap;
import datadog.trace.bootstrap.instrumentation.api.AgentSpanLink;
import datadog.trace.core.DDSpan;
import datadog.trace.core.DDSpanEvent;
import datadog.trace.test.junit.utils.assertions.Any;
import datadog.trace.test.junit.utils.assertions.IsNull;
import datadog.trace.test.junit.utils.assertions.Matcher;
Expand Down Expand Up @@ -53,6 +54,7 @@
* <li>span top-level status with {@link #topLevel()} and {@link #topLevel(boolean)}
* <li>span tags with {@link #tags(TagsMatcher...)}
* <li>span links with {@link #links(SpanLinkMatcher...)}
* <li>span events with {@link #events(SpanEventMatcher...)}
* </ul>
*/
public final class SpanMatcher {
Expand All @@ -70,6 +72,7 @@ public final class SpanMatcher {
private Matcher<Boolean> topLevelMatcher;
private TagsMatcher[] tagMatchers;
private SpanLinkMatcher[] linkMatchers;
private SpanEventMatcher[] eventMatchers;

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

Expand Down Expand Up @@ -372,6 +375,19 @@ public SpanMatcher links(SpanLinkMatcher... matchers) {
return this;
}

/**
* Checks the span events structure.
*
* @param matchers The {@link SpanEventMatcher} to verify the span events structure, one per
* event.
* @return The current {@link SpanMatcher} instance updated with the specified span event
* constraints.
*/
public SpanMatcher events(SpanEventMatcher... matchers) {
this.eventMatchers = matchers;
return this;
}

void assertSpan(List<DDSpan> trace, int spanIndex) {
DDSpan span = trace.get(spanIndex);
// Apply parent span index
Expand Down Expand Up @@ -400,6 +416,7 @@ else if (this.parentIdMatcher == CHILD_OF_PREVIOUS_MATCHER) {
assertValue(this.topLevelMatcher, span.isTopLevel(), "Unexpected top-level status");
assertSpanTags(span.getTags());
assertSpanLinks(spanLinks(span));
assertSpanEvents(span);
}

private void assertSpanTags(TagMap tags) {
Expand Down Expand Up @@ -461,4 +478,27 @@ private void assertSpanLinks(List<AgentSpanLink> links) {
linkMatcher.assertLink(link);
}
}

/*
* Right now, it's expecting to have as many matchers as events, and in the same order.
*/
private void assertSpanEvents(DDSpan span) {
// Check if events should be asserted at all
if (this.eventMatchers == null) {
return;
}
List<DDSpanEvent> events = span.getEvents();
int eventCount = events.size();
int expectedEventCount = this.eventMatchers.length;
if (eventCount != expectedEventCount) {
throw assertionFailure()
.message("Unexpected span event count")
.expected(expectedEventCount)
.actual(eventCount)
.build();
}
for (int i = 0; i < expectedEventCount; i++) {
this.eventMatchers[i].assertEvent(span, events.get(i));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import static datadog.trace.api.DDTags.REQUIRED_CODE_ORIGIN_TAGS;
import static datadog.trace.api.DDTags.RUNTIME_ID_TAG;
import static datadog.trace.api.DDTags.SCHEMA_VERSION_TAG_KEY;
import static datadog.trace.api.DDTags.SPAN_EVENTS;
import static datadog.trace.api.DDTags.SPAN_LINKS;
import static datadog.trace.api.DDTags.THREAD_ID;
import static datadog.trace.api.DDTags.THREAD_NAME;
Expand Down Expand Up @@ -56,6 +57,7 @@ public static TagsMatcher defaultTags() {
tagMatchers.put(DJM_ENABLED, any());
tagMatchers.put(PARENT_ID, any());
tagMatchers.put(SPAN_LINKS, any()); // this is checked by LinksAsserter
tagMatchers.put(SPAN_EVENTS, any()); // this is checked by SpanEventMatcher
Comment thread
PerfectSlayer marked this conversation as resolved.
tagMatchers.put(DD_INTEGRATION, any());
tagMatchers.put(TRACER_HOST, any());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,5 @@ dependencies {
testImplementation project(path: ':dd-java-agent:agent-otel:otel-bootstrap', configuration: 'shadow')

testImplementation group: 'io.opentelemetry', name: 'opentelemetry-api', version: openTelemetryVersion
testImplementation group: 'org.skyscreamer', name: 'jsonassert', version: '1.5.1'
latestDepTestImplementation group: 'io.opentelemetry', name: 'opentelemetry-api', version: '1+'
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ com.squareup.okhttp3:logging-interceptor:3.12.12=latestDepTestCompileClasspath,l
com.squareup.okhttp3:okhttp:3.12.12=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.squareup.okio:okio:1.17.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
com.thoughtworks.qdox:qdox:1.12.1=codenarc
com.vaadin.external.google:android-json:0.0.20131108.vaadin1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-fileupload:commons-fileupload:1.5=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-io:commons-io:2.11.0=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
commons-io:commons-io:2.21.0=spotbugs
Expand All @@ -74,7 +73,7 @@ org.apache.commons:commons-lang3:3.20.0=spotbugs
org.apache.commons:commons-text:1.15.0=spotbugs
org.apache.logging.log4j:log4j-api:2.26.1=spotbugs
org.apache.logging.log4j:log4j-core:2.26.1=spotbugs
org.apiguardian:apiguardian-api:1.1.2=latestDepTestCompileClasspath,testCompileClasspath
org.apiguardian:apiguardian-api:1.1.2=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.checkerframework:checker-qual:3.33.0=annotationProcessor,latestDepTestAnnotationProcessor,testAnnotationProcessor
org.codehaus.groovy:groovy-ant:3.0.23=codenarc
org.codehaus.groovy:groovy-docgenerator:3.0.23=codenarc
Expand Down Expand Up @@ -114,7 +113,6 @@ org.ow2.asm:asm-tree:9.10.1=latestDepTestRuntimeClasspath,spotbugs,testRuntimeCl
org.ow2.asm:asm-util:9.10.1=spotbugs
org.ow2.asm:asm-util:9.7.1=latestDepTestRuntimeClasspath,testRuntimeClasspath
org.ow2.asm:asm:9.10.1=buildTimeInstrumentationPlugin,compileClasspath,latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,muzzleTooling,runtimeClasspath,spotbugs,testCompileClasspath,testRuntimeClasspath
org.skyscreamer:jsonassert:1.5.1=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:jcl-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:jul-to-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
org.slf4j:log4j-over-slf4j:1.7.30=latestDepTestCompileClasspath,latestDepTestRuntimeClasspath,testCompileClasspath,testRuntimeClasspath
Expand Down
Loading
Loading