Skip to content
Open
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
Expand Up @@ -150,12 +150,10 @@ private static void onTestStart(TestStarting event) {
String testName = event.testName();
Object testQualifier = null;
String testParameters = null;
Collection<String> categories;
TestIdentifier testIdentifier = new TestIdentifier(testSuiteName, testName, null);
Collection<String> categories = context.exclusionTags(testIdentifier);
if (context.itrUnskippable(testIdentifier)) {
categories = Collections.singletonList(CIConstants.Tags.ITR_UNSKIPPABLE_TAG);
} else {
categories = Collections.emptyList();
categories.add(CIConstants.Tags.ITR_UNSKIPPABLE_TAG);
}
Class<?> testClass = ScalatestUtils.getClass(event.suiteClassName());

Expand Down Expand Up @@ -234,6 +232,7 @@ private static void onTestIgnore(TestIgnored event) {
Class<?> testClass = ScalatestUtils.getClass(event.suiteClassName());

TestIdentifier skippableTest = new TestIdentifier(testSuiteName, testName, null);
categories = context.exclusionTags(skippableTest);
SkipReason reason = context.getSkipReason(skippableTest);

TestEventsHandler<TestSuiteDescriptor, TestDescriptor> eventHandler = context.getEventHandler();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,18 @@ public Collection<String> tags(TestIdentifier test) {
return tagsByTest.getOrDefault(test, Collections.emptyList());
}

/** Exclusions are the only new traits exposed by this integration. */
public Collection<String> exclusionTags(TestIdentifier test) {
Collection<String> exclusions = new ArrayList<>();
for (String tag : tags(test)) {
if ("datadog_no_auto_retry".equals(tag)
|| "datadog_prevent_failure_suppression".equals(tag)) {
exclusions.add(tag);
}
}
return exclusions;
}

public void populateTags(
String suiteId,
Map<String, Set<String>> tags,
Expand Down Expand Up @@ -116,7 +128,7 @@ private Tuple2<String, Boolean> skip(
String testName = testNameAndSkipStatus._1();
TestIdentifier test = new TestIdentifier(suiteId, testName, null);

SkipReason skipReason = eventHandler.skipReason(test);
SkipReason skipReason = eventHandler.skipReason(test, TestSourceData.UNKNOWN, tags(test));
if (skipReason == null || skipReason == SkipReason.ITR && itrUnskippable(test)) {
return testNameAndSkipStatus;
}
Expand All @@ -138,7 +150,7 @@ public void populateTags(TestIdentifier test, Map<String, Set<String>> tags) {
}

public boolean skip(TestIdentifier test, Map<String, Set<String>> tags) {
SkipReason skipReason = eventHandler.skipReason(test);
SkipReason skipReason = eventHandler.skipReason(test, TestSourceData.UNKNOWN, tags(test));
if (skipReason == null) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,16 @@ public static void apply(
@Advice.Argument(value = 1)
scala.collection.immutable.Map<String, scala.collection.immutable.Set<String>> tags,
@Advice.Argument(value = 2) String suiteId) {
if (filterResult == null // filter terminated exceptionally
|| filterResult._1() // test is filtered
|| filterResult._2() // test is ignored
) {
if (filterResult == null) {
return;
}
TestIdentifier test = new TestIdentifier(suiteId, testName, null);
RunContext runContext =
InstrumentationContext.get(Filter.class, RunContext.class).get(filter);
runContext.populateTags(test, tags);
Comment thread
calvinbayer marked this conversation as resolved.
if (filterResult._1() || filterResult._2()) {
return;
}

if (runContext.skip(test, tags)) {
filterResult = new Tuple2<>(false, true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import datadog.trace.civisibility.CiVisibilityInstrumentationTest
import datadog.trace.civisibility.diff.LineDiff
import datadog.trace.instrumentation.scalatest.ScalatestUtils
import org.example.TestFailed
import org.example.TestAutomationExcluded
import org.example.TestFailedParameterized
import org.example.TestFailedSuite
import org.example.TestFailedThenSucceed
Expand Down Expand Up @@ -32,6 +33,38 @@ import sbt.testing.TaskDef

class ScalatestTest extends CiVisibilityInstrumentationTest {

def "test automation exclusions preserve a blocking failure and execute once"() {
given:
def id = new TestFQN("org.example.TestAutomationExcluded", "protected_failure")
givenDisabledTests([id])
givenQuarantinedTests([id])
givenAttemptToFixTests([id])
givenFlakyRetryEnabled(true)
givenFlakyTests([id])
givenEarlyFlakinessDetectionEnabled(true)
givenKnownTests([])

when:
runTests([TestAutomationExcluded], false)

then:
TEST_WRITER.toList().flatten().count { it.getTag("test.name") == "protected_failure" } == 1
def testSpan = TEST_WRITER.toList().flatten().find { it.getTag("test.name") == "protected_failure" }
testSpan != null
testSpan.getTag("test.test_management.no_auto_retry") == true
testSpan.getTag("test.test_management.prevent_failure_suppression") == true
testSpan.getTag("test.test_management.is_test_disabled") == true
testSpan.getTag("test.test_management.is_quarantined") == true
testSpan.getTag("test.test_management.is_attempt_to_fix") == true
testSpan.getTag("test.test_management.attempt_to_fix_passed") == null
def skippedSpan = TEST_WRITER.toList().flatten().find { it.getTag("test.name") == "protected_skip" }
skippedSpan != null
skippedSpan.getTag("test.test_management.no_auto_retry") == true
skippedSpan.getTag("test.test_management.prevent_failure_suppression") == true
skippedSpan.getTag("test.status").toString() == "skip"
}


def "test #testcaseName"() {
runTests(tests, success)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.example

import datadog.trace.api.civisibility.TestAutomationExclusions
import org.scalatest.Tag
import org.scalatest.funsuite.AnyFunSuite

class TestAutomationExcluded extends AnyFunSuite {
private val noRetry = new Tag(TestAutomationExclusions.NO_AUTO_RETRY)
private val preventSuppression = new Tag(TestAutomationExclusions.PREVENT_FAILURE_SUPPRESSION)

test("protected_failure", noRetry, preventSuppression) {
assert(false)
}

ignore("protected_skip", noRetry, preventSuppression) {
assert(false)
}
}
6 changes: 6 additions & 0 deletions docs/test-automation-exclusions.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,9 @@ Tag the Scenario or Scenario Outline with `@datadog_no_auto_retry` and/or
`@datadog_prevent_failure_suppression` (the names from the public
`TestAutomationExclusions` constants). Both Karate 1.x and 2.x resolve these
scenario tags before Datadog skipping and retry policy selection.

## ScalaTest

Declare ScalaTest `Tag` instances from `TestAutomationExclusions.NO_AUTO_RETRY`
and `PREVENT_FAILURE_SUPPRESSION`, then pass them to the test declaration.
Native ignored tests remain ignored and carry their known exclusion tags.
Loading