diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestModule.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestModule.java index e87807d0ed0..6dfc5137337 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestModule.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestModule.java @@ -30,6 +30,7 @@ public abstract class AbstractTestModule { protected final Codeowners codeowners; protected final LinesResolver linesResolver; private final Consumer onSpanFinish; + protected final SpanTagsPropagator tagsPropagator; public AbstractTestModule( AgentSpanContext sessionSpanContext, @@ -63,6 +64,7 @@ public AbstractTestModule( } span = spanBuilder.start(); + tagsPropagator = new SpanTagsPropagator(span); span.setSpanType(InternalSpanTypes.TEST_MODULE_END); span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_TEST_MODULE); diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java index 4338a9a7ba8..15c3979104f 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/AbstractTestSession.java @@ -46,6 +46,7 @@ public abstract class AbstractTestSession { protected final SourcePathResolver sourcePathResolver; protected final Codeowners codeowners; protected final LinesResolver linesResolver; + protected final SpanTagsPropagator tagPropagator; public AbstractTestSession( String projectName, @@ -93,6 +94,7 @@ public AbstractTestSession( } span = spanBuilder.start(); + tagPropagator = new SpanTagsPropagator(span); span.setSpanType(InternalSpanTypes.TEST_SESSION_END); span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_TEST_SESSION); diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/utils/SpanUtils.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/SpanTagsPropagator.java similarity index 55% rename from dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/utils/SpanUtils.java rename to dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/SpanTagsPropagator.java index 64d154292b3..d5c4c002aed 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/utils/SpanUtils.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/SpanTagsPropagator.java @@ -1,4 +1,4 @@ -package datadog.trace.civisibility.utils; +package datadog.trace.civisibility.domain; import datadog.trace.api.civisibility.execution.TestStatus; import datadog.trace.bootstrap.instrumentation.api.AgentSpan; @@ -14,28 +14,51 @@ import java.util.function.BinaryOperator; import java.util.function.Consumer; -public class SpanUtils { - public static final Consumer DO_NOT_PROPAGATE_CI_VISIBILITY_TAGS = span -> {}; +public class SpanTagsPropagator { + public static final Consumer NOOP_PROPAGATOR = span -> {}; - public static Consumer propagateCiVisibilityTagsTo(AgentSpan parentSpan) { - return childSpan -> propagateCiVisibilityTags(parentSpan, childSpan); + private final AgentSpan parentSpan; + private final Object tagPropagationLock = new Object(); + + public SpanTagsPropagator(AgentSpan parentSpan) { + this.parentSpan = parentSpan; + } + + public void propagateCiVisibilityTags(AgentSpan childSpan) { + mergeTestFrameworks(getFrameworks(childSpan)); + propagateStatus(childSpan); + } + + public void propagateStatus(AgentSpan childSpan) { + synchronized (tagPropagationLock) { + unsafePropagateStatus(childSpan); + } + } + + public void mergeTestFrameworks(Collection testFrameworks) { + synchronized (tagPropagationLock) { + unsafeMergeTestFrameworks(testFrameworks); + } } - public static void propagateCiVisibilityTags(AgentSpan parentSpan, AgentSpan childSpan) { - mergeTestFrameworks(parentSpan, getFrameworks(childSpan)); - propagateStatus(parentSpan, childSpan); + public void propagateTags(AgentSpan childSpan, TagMergeSpec... specs) { + synchronized (tagPropagationLock) { + for (TagMergeSpec spec : specs) { + unsafePropagateTag(childSpan, spec); + } + } } - public static void mergeTestFrameworks(AgentSpan span, Collection testFrameworks) { - Collection spanFrameworks = getFrameworks(span); - Collection merged = merge(spanFrameworks, testFrameworks); - setFrameworks(span, merged); + private void unsafeMergeTestFrameworks(Collection childFrameworks) { + Collection parentFrameworks = getFrameworks(parentSpan); + Collection merged = merge(parentFrameworks, childFrameworks); + setFrameworks(merged); } - private static Collection getFrameworks(AgentSpan span) { + static Collection getFrameworks(AgentSpan span) { Object nameTag = span.getTag(Tags.TEST_FRAMEWORK); Object versionTag = span.getTag(Tags.TEST_FRAMEWORK_VERSION); - if (nameTag == null && versionTag == null) { + if (nameTag == null) { return Collections.emptyList(); } @@ -45,9 +68,11 @@ private static Collection getFrameworks(AgentSpan span) { } else if (nameTag instanceof Collection) { Iterator names = ((Collection) nameTag).iterator(); - Iterator versions = ((Collection) versionTag).iterator(); + Iterator versions = + versionTag != null ? ((Collection) versionTag).iterator() : null; while (names.hasNext()) { - frameworks.add(new TestFramework(names.next(), versions.next())); + String version = (versions != null && versions.hasNext()) ? versions.next() : null; + frameworks.add(new TestFramework(names.next(), version)); } } else { @@ -79,7 +104,7 @@ private static Collection merge( return merged; } - private static void setFrameworks(AgentSpan span, Collection frameworks) { + private void setFrameworks(Collection frameworks) { if (frameworks.isEmpty()) { return; } @@ -90,7 +115,7 @@ private static void setFrameworks(AgentSpan span, Collection fram if (framework.getVersion() != null) { tags.put(Tags.TEST_FRAMEWORK_VERSION, framework.getVersion()); } - span.setAllTags(tags); + parentSpan.setAllTags(tags); return; } Collection names = new ArrayList<>(frameworks.size()); @@ -102,10 +127,10 @@ private static void setFrameworks(AgentSpan span, Collection fram Map> tags = new HashMap<>(); tags.put(Tags.TEST_FRAMEWORK, names); tags.put(Tags.TEST_FRAMEWORK_VERSION, versions); - span.setAllTags(tags); + parentSpan.setAllTags(tags); } - private static void propagateStatus(AgentSpan parentSpan, AgentSpan childSpan) { + private void unsafePropagateStatus(AgentSpan childSpan) { TestStatus childStatus = (TestStatus) childSpan.getTag(Tags.TEST_STATUS); if (childStatus == null) { return; @@ -131,25 +156,32 @@ private static void propagateStatus(AgentSpan parentSpan, AgentSpan childSpan) { } } - public static void propagateTags(AgentSpan parentSpan, AgentSpan childSpan, String... tagNames) { - for (String tagName : tagNames) { - parentSpan.setTag(tagName, childSpan.getTag(tagName)); + public static class TagMergeSpec { + private final String tagKey; + private final BinaryOperator mergeFunction; + + TagMergeSpec(String tagKey, BinaryOperator mergeFunction) { + this.tagKey = tagKey; + this.mergeFunction = mergeFunction; + } + + public static TagMergeSpec of(String key, BinaryOperator mergeFunction) { + return new TagMergeSpec<>(key, mergeFunction); } - } - public static void propagateTag(AgentSpan parentSpan, AgentSpan childSpan, String tagName) { - propagateTag(parentSpan, childSpan, tagName, (p, c) -> c); + public static TagMergeSpec of(String tagKey) { + return new TagMergeSpec<>(tagKey, (parent, child) -> child); + } } - public static void propagateTag( - AgentSpan parentSpan, AgentSpan childSpan, String tagName, BinaryOperator mergeStrategy) { - T childTag = (T) childSpan.getTag(tagName); + private void unsafePropagateTag(AgentSpan childSpan, TagMergeSpec spec) { + T childTag = (T) childSpan.getTag(spec.tagKey); if (childTag != null) { - T parentTag = (T) parentSpan.getTag(tagName); + T parentTag = (T) parentSpan.getTag(spec.tagKey); if (parentTag == null) { - parentSpan.setTag(tagName, childTag); + parentSpan.setTag(spec.tagKey, childTag); } else { - parentSpan.setTag(tagName, mergeStrategy.apply(parentTag, childTag)); + parentSpan.setTag(spec.tagKey, spec.mergeFunction.apply(parentTag, childTag)); } } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java index 24319318968..c29fa175229 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/TestSuiteImpl.java @@ -24,7 +24,6 @@ import datadog.trace.civisibility.source.SourcePathResolver; import datadog.trace.civisibility.source.SourceResolutionException; import datadog.trace.civisibility.test.ExecutionResults; -import datadog.trace.civisibility.utils.SpanUtils; import java.lang.reflect.Method; import java.util.Collection; import java.util.function.Consumer; @@ -56,6 +55,7 @@ public class TestSuiteImpl implements DDTestSuite { private final boolean parallelized; private final Collection capabilities; private final Consumer onSpanFinish; + private final SpanTagsPropagator tagsPropagator; public TestSuiteImpl( AgentSpanContext moduleSpanContext, @@ -106,6 +106,7 @@ public TestSuiteImpl( } span = spanBuilder.start(); + tagsPropagator = new SpanTagsPropagator(span); span.setSpanType(InternalSpanTypes.TEST_SUITE_END); span.setTag(Tags.SPAN_KIND, Tags.SPAN_KIND_TEST_SUITE); @@ -264,6 +265,6 @@ public TestImpl testStart( coverageStoreFactory, executionResults, capabilities, - SpanUtils.propagateCiVisibilityTagsTo(span)); + tagsPropagator::propagateStatus); } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java index 4fc0c27f59a..525f82baf50 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemModuleImpl.java @@ -30,7 +30,6 @@ import datadog.trace.civisibility.ipc.SignalType; import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.SourcePathResolver; -import datadog.trace.civisibility.utils.SpanUtils; import datadog.trace.util.Strings; import java.net.InetSocketAddress; import java.nio.file.Path; @@ -289,7 +288,7 @@ private SignalResponse onModuleExecutionResultReceived(ModuleExecutionResult res testsSkipped.add(result.getTestsSkippedTotal()); - SpanUtils.mergeTestFrameworks(span, result.getTestFrameworks()); + tagsPropagator.mergeTestFrameworks(result.getTestFrameworks()); return AckResponse.INSTANCE; } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemSessionImpl.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemSessionImpl.java index c8a289caa7a..330d2aa8167 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemSessionImpl.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/buildsystem/BuildSystemSessionImpl.java @@ -1,6 +1,7 @@ package datadog.trace.civisibility.domain.buildsystem; import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.startSpan; +import static datadog.trace.civisibility.domain.SpanTagsPropagator.TagMergeSpec; import datadog.trace.api.Config; import datadog.trace.api.DDTags; @@ -35,7 +36,6 @@ import datadog.trace.civisibility.source.SourcePathResolver; import datadog.trace.civisibility.source.index.RepoIndex; import datadog.trace.civisibility.source.index.RepoIndexProvider; -import datadog.trace.civisibility.utils.SpanUtils; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collection; @@ -54,7 +54,6 @@ public class BuildSystemSessionImpl extends Abstra private final CoverageCalculator.Factory coverageCalculatorFactory; private final T coverageCalculator; private final BuildSessionSettings settings; - private final Object tagPropagationLock = new Object(); public BuildSystemSessionImpl( String projectName, @@ -183,22 +182,17 @@ public AgentSpan testTaskStart(String taskName) { private void onModuleFinish(AgentSpan moduleSpan) { // multiple modules can finish in parallel - synchronized (tagPropagationLock) { - SpanUtils.propagateCiVisibilityTags(span, moduleSpan); - - SpanUtils.propagateTag(span, moduleSpan, Tags.TEST_EARLY_FLAKE_ENABLED, Boolean::logicalOr); - SpanUtils.propagateTag(span, moduleSpan, Tags.TEST_EARLY_FLAKE_ABORT_REASON); - - SpanUtils.propagateTag(span, moduleSpan, Tags.TEST_CODE_COVERAGE_ENABLED, Boolean::logicalOr); - SpanUtils.propagateTag( - span, moduleSpan, Tags.TEST_ITR_TESTS_SKIPPING_ENABLED, Boolean::logicalOr); - SpanUtils.propagateTag(span, moduleSpan, Tags.TEST_ITR_TESTS_SKIPPING_TYPE); - SpanUtils.propagateTag(span, moduleSpan, Tags.TEST_ITR_TESTS_SKIPPING_COUNT, Long::sum); - SpanUtils.propagateTag(span, moduleSpan, DDTags.CI_ITR_TESTS_SKIPPED, Boolean::logicalOr); - - SpanUtils.propagateTag( - span, moduleSpan, Tags.TEST_TEST_MANAGEMENT_ENABLED, Boolean::logicalOr); - } + tagPropagator.propagateCiVisibilityTags(moduleSpan); + tagPropagator.propagateTags( + moduleSpan, + TagMergeSpec.of(Tags.TEST_EARLY_FLAKE_ENABLED, Boolean::logicalOr), + TagMergeSpec.of(Tags.TEST_EARLY_FLAKE_ABORT_REASON), + TagMergeSpec.of(Tags.TEST_CODE_COVERAGE_ENABLED, Boolean::logicalOr), + TagMergeSpec.of(Tags.TEST_ITR_TESTS_SKIPPING_ENABLED, Boolean::logicalOr), + TagMergeSpec.of(Tags.TEST_ITR_TESTS_SKIPPING_TYPE), + TagMergeSpec.of(Tags.TEST_ITR_TESTS_SKIPPING_COUNT, Long::sum), + TagMergeSpec.of(DDTags.CI_ITR_TESTS_SKIPPED, Boolean::logicalOr), + TagMergeSpec.of(Tags.TEST_TEST_MANAGEMENT_ENABLED, Boolean::logicalOr)); } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestModule.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestModule.java index 145a5a25ff8..a338c36f0af 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestModule.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestModule.java @@ -27,7 +27,6 @@ import datadog.trace.civisibility.source.SourcePathResolver; import datadog.trace.civisibility.test.ExecutionResults; import datadog.trace.civisibility.test.ExecutionStrategy; -import datadog.trace.civisibility.utils.SpanUtils; import java.util.Collection; import java.util.function.Consumer; import javax.annotation.Nonnull; @@ -183,6 +182,6 @@ public TestSuiteImpl testSuiteStart( coverageStoreFactory, executionResults, capabilities, - SpanUtils.propagateCiVisibilityTagsTo(span)); + tagsPropagator::propagateCiVisibilityTags); } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestSession.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestSession.java index ccafd04a41d..926fdbba4e9 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestSession.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/headless/HeadlessTestSession.java @@ -1,5 +1,7 @@ package datadog.trace.civisibility.domain.headless; +import static datadog.trace.civisibility.domain.SpanTagsPropagator.TagMergeSpec; + import datadog.trace.api.Config; import datadog.trace.api.DDTags; import datadog.trace.api.civisibility.config.LibraryCapability; @@ -19,7 +21,6 @@ import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.SourcePathResolver; import datadog.trace.civisibility.test.ExecutionStrategy; -import datadog.trace.civisibility.utils.SpanUtils; import java.util.Collection; import java.util.Collections; import javax.annotation.Nonnull; @@ -82,22 +83,21 @@ public HeadlessTestModule testModuleStart(String moduleName, @Nullable Long star coverageStoreFactory, executionStrategy, capabilities, - this::propagateModuleTags); + this::onModuleFinish); } - private void propagateModuleTags(AgentSpan moduleSpan) { - SpanUtils.propagateCiVisibilityTags(span, moduleSpan); - SpanUtils.propagateTags( - span, + private void onModuleFinish(AgentSpan moduleSpan) { + tagPropagator.propagateCiVisibilityTags(moduleSpan); + tagPropagator.propagateTags( moduleSpan, - Tags.TEST_CODE_COVERAGE_ENABLED, - Tags.TEST_ITR_TESTS_SKIPPING_ENABLED, - Tags.TEST_ITR_TESTS_SKIPPING_TYPE, - Tags.TEST_ITR_TESTS_SKIPPING_COUNT, - Tags.TEST_EARLY_FLAKE_ENABLED, - Tags.TEST_EARLY_FLAKE_ABORT_REASON, - DDTags.CI_ITR_TESTS_SKIPPED, - Tags.TEST_TEST_MANAGEMENT_ENABLED); + TagMergeSpec.of(Tags.TEST_CODE_COVERAGE_ENABLED), + TagMergeSpec.of(Tags.TEST_ITR_TESTS_SKIPPING_ENABLED), + TagMergeSpec.of(Tags.TEST_ITR_TESTS_SKIPPING_TYPE), + TagMergeSpec.of(Tags.TEST_ITR_TESTS_SKIPPING_COUNT), + TagMergeSpec.of(Tags.TEST_EARLY_FLAKE_ENABLED), + TagMergeSpec.of(Tags.TEST_EARLY_FLAKE_ABORT_REASON), + TagMergeSpec.of(DDTags.CI_ITR_TESTS_SKIPPED), + TagMergeSpec.of(Tags.TEST_TEST_MANAGEMENT_ENABLED)); } @Override diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestModule.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestModule.java index 217409521c7..22dbe30994a 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestModule.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestModule.java @@ -15,7 +15,6 @@ import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.SourcePathResolver; import datadog.trace.civisibility.test.ExecutionResults; -import datadog.trace.civisibility.utils.SpanUtils; import java.util.Collections; import java.util.function.Consumer; import javax.annotation.Nullable; @@ -81,6 +80,6 @@ public TestSuiteImpl testSuiteStart( coverageStoreFactory, executionResults, Collections.emptyList(), - SpanUtils.propagateCiVisibilityTagsTo(span)); + tagsPropagator::propagateCiVisibilityTags); } } diff --git a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestSession.java b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestSession.java index 283e13f513b..917bf9ddbc0 100644 --- a/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestSession.java +++ b/dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/manualapi/ManualApiTestSession.java @@ -11,7 +11,6 @@ import datadog.trace.civisibility.domain.InstrumentationType; import datadog.trace.civisibility.source.LinesResolver; import datadog.trace.civisibility.source.SourcePathResolver; -import datadog.trace.civisibility.utils.SpanUtils; import javax.annotation.Nullable; /** @@ -60,6 +59,6 @@ public ManualApiTestModule testModuleStart(String moduleName, @Nullable Long sta codeowners, linesResolver, coverageStoreFactory, - SpanUtils.propagateCiVisibilityTagsTo(span)); + tagPropagator::propagateCiVisibilityTags); } } diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/SpanTagsPropagatorTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/SpanTagsPropagatorTest.groovy new file mode 100644 index 00000000000..f55cc33a753 --- /dev/null +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/SpanTagsPropagatorTest.groovy @@ -0,0 +1,175 @@ +package datadog.trace.civisibility.domain + +import static datadog.trace.civisibility.domain.SpanTagsPropagator.TagMergeSpec + +import datadog.trace.api.civisibility.execution.TestStatus +import datadog.trace.bootstrap.instrumentation.api.Tags +import datadog.trace.civisibility.ipc.TestFramework +import datadog.trace.core.DDSpan +import java.util.concurrent.CountDownLatch +import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit +import spock.lang.Specification + +class SpanTagsPropagatorTest extends Specification { + def "test getFrameworks"() { + when: + def span = Stub(DDSpan) + span.getTag(Tags.TEST_FRAMEWORK) >> frameworkTag + span.getTag(Tags.TEST_FRAMEWORK_VERSION) >> frameworkVersionTag + + def frameworks = SpanTagsPropagator.getFrameworks(span) + + then: + frameworks == expected + + where: + frameworkTag | frameworkVersionTag | expected + "name" | "version" | [new TestFramework("name", "version")] + "name" | null | [new TestFramework("name", null)] + null | "version" | [] + ["nameA", "nameB"] | ["versionA", "versionB"] | [new TestFramework("nameA", "versionA"), new TestFramework("nameB", "versionB")] + ["nameA", "nameB"] | null | [new TestFramework("nameA", null), new TestFramework("nameB", null)] + ["nameA", "nameB"] | ["versionA", null] | [new TestFramework("nameA", "versionA"), new TestFramework("nameB", null)] + } + + def "test status propagation: #childStatus to #parentStatus"() { + given: + def parentSpan = Mock(DDSpan) + parentSpan.getTag(Tags.TEST_STATUS) >> parentStatus + + def childSpan = Mock(DDSpan) + childSpan.getTag(Tags.TEST_STATUS) >> childStatus + + def propagator = new SpanTagsPropagator(parentSpan) + + when: + propagator.propagateStatus(childSpan) + + then: + if (expectedStatus != null) { + 1 * parentSpan.setTag(Tags.TEST_STATUS, expectedStatus) + } else { + 0 * parentSpan.setTag(Tags.TEST_STATUS, _) + } + + where: + childStatus | parentStatus | expectedStatus + TestStatus.pass | null | TestStatus.pass + TestStatus.pass | TestStatus.skip | TestStatus.pass + TestStatus.pass | TestStatus.pass | null // no change + TestStatus.pass | TestStatus.fail | null // no change + TestStatus.fail | null | TestStatus.fail + TestStatus.fail | TestStatus.pass | TestStatus.fail + TestStatus.fail | TestStatus.skip | TestStatus.fail + TestStatus.fail | TestStatus.fail | TestStatus.fail + TestStatus.skip | null | TestStatus.skip + TestStatus.skip | TestStatus.pass | null // no change + TestStatus.skip | TestStatus.fail | null // no change + TestStatus.skip | TestStatus.skip | null // no change + null | TestStatus.pass | null // no change + } + + def "test framework merging: #childFrameworks and #parentFrameworks"() { + given: + def parentSpan = Mock(DDSpan) + parentSpan.getTag(Tags.TEST_FRAMEWORK) >> parentFrameworks.collect(it -> it.getName()) + parentSpan.getTag(Tags.TEST_FRAMEWORK_VERSION) >> parentFrameworks.collect(it -> it.getVersion()) + + def propagator = new SpanTagsPropagator(parentSpan) + + def expectedNames = expectedFrameworks.collect(it -> it.getName()) + def expectedVersions = expectedFrameworks.collect(it -> it.getVersion()) + + when: + propagator.mergeTestFrameworks(childFrameworks) + + then: + 1 * parentSpan.setAllTags([ + (Tags.TEST_FRAMEWORK) : expectedNames, + (Tags.TEST_FRAMEWORK_VERSION): expectedVersions + ]) + + where: + childFrameworks | parentFrameworks | expectedFrameworks + [] | [new TestFramework("JUnit", "5.8.0"), new TestFramework("TestNG", "7.4.0")] | [new TestFramework("JUnit", "5.8.0"), new TestFramework("TestNG", "7.4.0")] + [new TestFramework("JUnit", "5.8.0"), new TestFramework("TestNG", "7.4.0")] | [] | [new TestFramework("JUnit", "5.8.0"), new TestFramework("TestNG", "7.4.0")] + [new TestFramework("JUnit", "5.8.0"), new TestFramework("TestNG", "7.4.0")] | [new TestFramework("Spock", "2.3")] | [new TestFramework("JUnit", "5.8.0"), new TestFramework("Spock", "2.3"), new TestFramework("TestNG", "7.4.0")] + } + + def "test tag propagation: #childValue and #parentValue with spec #tagSpec"() { + given: + def parentSpan = Mock(DDSpan) + parentSpan.getTag("tag") >> parentValue + + def childSpan = Mock(DDSpan) + childSpan.getTag("tag") >> childValue + + def propagator = new SpanTagsPropagator(parentSpan) + + when: + propagator.propagateTags(childSpan, tagSpec) + + then: + if (expectedChange) { + 1 * parentSpan.setTag("tag", expectedValue) + } else { + 0 * parentSpan.setTag("tag", _) + } + + where: + tagSpec | childValue | parentValue | expectedChange | expectedValue + TagMergeSpec.of("tag") | "a" | "b" | true | "a" + TagMergeSpec.of("tag") | null | "b" | false | "b" + TagMergeSpec.of("tag") | null | null | false | null + TagMergeSpec.of("tag", Boolean::logicalOr) | true | false | true | true + TagMergeSpec.of("tag", Boolean::logicalOr) | false | false | true | false + } + + def "test synchronized propagation"() { + given: + def parentSpan = Mock(DDSpan) + def propagator = new SpanTagsPropagator(parentSpan) + def numThreads = 9 + def latch = new CountDownLatch(numThreads) + def executor = Executors.newFixedThreadPool(numThreads) + def exceptions = Collections.synchronizedList([]) + + when: + numThreads.times { i -> + executor.submit { + try { + switch (i % 3) { + case 0: + def childSpan = Mock(DDSpan) + childSpan.getTag(Tags.TEST_STATUS) >> TestStatus.fail + propagator.propagateStatus(childSpan) + break + case 1: + def frameworks = [new TestFramework("JUnit${i}", "5.${i}")] + propagator.mergeTestFrameworks(frameworks) + break + case 2: + def childSpan = Mock(DDSpan) + childSpan.getTag("custom.tag.${i}") >> "value${i}" + propagator.propagateTags(childSpan, TagMergeSpec.of("custom.tag.${i}")) + break + } + } catch (Exception e) { + exceptions.add(e) + } finally { + latch.countDown() + } + } + } + + latch.await(5, TimeUnit.SECONDS) + executor.shutdown() + + then: + exceptions.isEmpty() + 3 * parentSpan.setTag(Tags.TEST_STATUS, TestStatus.fail) + 3 * parentSpan.setAllTags(_) + 3 * parentSpan.setTag({ it.startsWith("custom.tag.") }, { it.startsWith("value") }) + } +} diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestImplTest.groovy index eac92fa8474..56b1a8ab640 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestImplTest.groovy @@ -18,7 +18,6 @@ import datadog.trace.civisibility.source.LinesResolver import datadog.trace.civisibility.source.NoOpSourcePathResolver import datadog.trace.civisibility.telemetry.CiVisibilityMetricCollectorImpl import datadog.trace.civisibility.test.ExecutionResults -import datadog.trace.civisibility.utils.SpanUtils class TestImplTest extends SpanWriterTest { def "test span is generated and tags populated"() { @@ -143,7 +142,7 @@ class TestImplTest extends SpanWriterTest { coverageStoreFactory, executionResults, libraryCapabilities, - SpanUtils.DO_NOT_PROPAGATE_CI_VISIBILITY_TAGS + SpanTagsPropagator.NOOP_PROPAGATOR ) } } diff --git a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestSuiteImplTest.groovy b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestSuiteImplTest.groovy index aed8ffa88db..c7819669a85 100644 --- a/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestSuiteImplTest.groovy +++ b/dd-java-agent/agent-ci-visibility/src/test/groovy/datadog/trace/civisibility/domain/TestSuiteImplTest.groovy @@ -15,7 +15,6 @@ import datadog.trace.civisibility.source.LinesResolver import datadog.trace.civisibility.source.SourcePathResolver import datadog.trace.civisibility.telemetry.CiVisibilityMetricCollectorImpl import datadog.trace.civisibility.test.ExecutionResults -import datadog.trace.civisibility.utils.SpanUtils class TestSuiteImplTest extends SpanWriterTest { def "test suite span is generated and tags populated"() { @@ -86,7 +85,7 @@ class TestSuiteImplTest extends SpanWriterTest { coverageStoreFactory, executionResults, [], - SpanUtils.DO_NOT_PROPAGATE_CI_VISIBILITY_TAGS + SpanTagsPropagator.NOOP_PROPAGATOR ) } }