Skip to content

Commit 5622636

Browse files
Fix test status propagation in headless mode (#9478)
1 parent fe3046f commit 5622636

File tree

126 files changed

+1109
-416
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

126 files changed

+1109
-416
lines changed

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/domain/SpanTagsPropagator.java

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -136,23 +136,19 @@ private void unsafePropagateStatus(AgentSpan childSpan) {
136136
return;
137137
}
138138

139+
Boolean childFailureSuppressed = (Boolean) childSpan.getTag(Tags.TEST_FAILURE_SUPPRESSED);
139140
TestStatus parentStatus = (TestStatus) parentSpan.getTag(Tags.TEST_STATUS);
140-
switch (childStatus) {
141-
case pass:
142-
if (parentStatus == null || TestStatus.skip.equals(parentStatus)) {
143-
parentSpan.setTag(Tags.TEST_STATUS, TestStatus.pass);
144-
}
145-
break;
146-
case fail:
147-
parentSpan.setTag(Tags.TEST_STATUS, TestStatus.fail);
148-
break;
149-
case skip:
150-
if (parentStatus == null) {
151-
parentSpan.setTag(Tags.TEST_STATUS, TestStatus.skip);
152-
}
153-
break;
154-
default:
155-
break;
141+
if (childStatus == TestStatus.pass
142+
|| (childFailureSuppressed != null && childFailureSuppressed)) {
143+
if (parentStatus == null || TestStatus.skip.equals(parentStatus)) {
144+
parentSpan.setTag(Tags.TEST_STATUS, TestStatus.pass);
145+
}
146+
} else if (childStatus == TestStatus.fail) {
147+
parentSpan.setTag(Tags.TEST_STATUS, TestStatus.fail);
148+
} else if (childStatus == TestStatus.skip) {
149+
if (parentStatus == null) {
150+
parentSpan.setTag(Tags.TEST_STATUS, TestStatus.skip);
151+
}
156152
}
157153
}
158154

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/NoOpTestEventsHandler.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,7 @@ public void onTestStart(
5858
@Nullable String testParameters,
5959
@Nullable Collection<String> categories,
6060
@Nonnull TestSourceData testSourceData,
61-
@Nullable Long startTime,
62-
@Nullable TestExecutionHistory testExecutionHistory) {
61+
@Nullable Long startTime) {
6362
// do nothing
6463
}
6564

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/events/TestEventsHandlerImpl.java

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,7 @@ public void onTestStart(
141141
final @Nullable String testParameters,
142142
final @Nullable Collection<String> categories,
143143
final @Nonnull TestSourceData testSourceData,
144-
final @Nullable Long startTime,
145-
final @Nullable TestExecutionHistory testExecutionHistory) {
144+
final @Nullable Long startTime) {
146145
if (skipTrace(testSourceData.getTestClass())) {
147146
return;
148147
}
@@ -180,14 +179,6 @@ public void onTestStart(
180179
test.setTag(Tags.TEST_TEST_MANAGEMENT_IS_ATTEMPT_TO_FIX, true);
181180
}
182181

183-
if (testExecutionHistory != null) {
184-
RetryReason retryReason = testExecutionHistory.currentExecutionRetryReason();
185-
if (retryReason != null) {
186-
test.setTag(Tags.TEST_IS_RETRY, true);
187-
test.setTag(Tags.TEST_RETRY_REASON, retryReason);
188-
}
189-
}
190-
191182
if (testFramework != null) {
192183
test.setTag(Tags.TEST_FRAMEWORK, testFramework);
193184
if (testFrameworkVersion != null) {
@@ -254,20 +245,28 @@ public void onTestFinish(
254245
return;
255246
}
256247

257-
TestIdentifier thisTest = test.getIdentifier();
258248
if (testExecutionHistory != null) {
259249
TestStatus testStatus = test.getStatus();
260-
testExecutionHistory.registerExecution(
261-
testStatus != null ? testStatus : TestStatus.skip, test.getDuration(endTime));
250+
TestExecutionHistory.ExecutionOutcome outcome =
251+
testExecutionHistory.registerExecution(
252+
testStatus != null ? testStatus : TestStatus.skip, test.getDuration(endTime));
262253

263-
if (testExecutionHistory.hasFailedAllRetries()) {
254+
RetryReason retryReason = outcome.retryReason();
255+
if (retryReason != null) {
256+
test.setTag(Tags.TEST_IS_RETRY, true);
257+
test.setTag(Tags.TEST_RETRY_REASON, retryReason);
258+
}
259+
260+
if (outcome.failedAllRetries()) {
264261
test.setTag(Tags.TEST_HAS_FAILED_ALL_RETRIES, true);
265262
}
266263

267-
if (testExecutionHistory.wasLastExecution() && testModule.isAttemptToFix(thisTest)) {
268-
test.setTag(
269-
Tags.TEST_TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED,
270-
testExecutionHistory.hasSucceededAllRetries());
264+
if (outcome.lastExecution() && testModule.isAttemptToFix(test.getIdentifier())) {
265+
test.setTag(Tags.TEST_TEST_MANAGEMENT_ATTEMPT_TO_FIX_PASSED, outcome.succeededAllRetries());
266+
}
267+
268+
if (outcome.failureSuppressed()) {
269+
test.setTag(Tags.TEST_FAILURE_SUPPRESSED, true);
271270
}
272271
}
273272

@@ -295,8 +294,7 @@ public void onTestIgnore(
295294
testParameters,
296295
categories,
297296
testSourceData,
298-
null,
299-
testExecutionHistory);
297+
null);
300298
onTestSkip(testDescriptor, reason);
301299
onTestFinish(testDescriptor, null, testExecutionHistory);
302300
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package datadog.trace.civisibility.execution;
2+
3+
import datadog.trace.api.civisibility.execution.TestExecutionHistory;
4+
import datadog.trace.api.civisibility.telemetry.tag.RetryReason;
5+
import org.jetbrains.annotations.Nullable;
6+
7+
class ExecutionOutcomeImpl implements TestExecutionHistory.ExecutionOutcome {
8+
9+
private final boolean failureSuppressed;
10+
private final boolean lastExecution;
11+
private final boolean failedAllRetries;
12+
private final boolean succeededAllRetries;
13+
private final RetryReason retryReason;
14+
15+
ExecutionOutcomeImpl(
16+
boolean failureSuppressed,
17+
boolean lastExecution,
18+
boolean failedAllRetries,
19+
boolean succeededAllRetries,
20+
RetryReason retryReason) {
21+
this.failureSuppressed = failureSuppressed;
22+
this.lastExecution = lastExecution;
23+
this.failedAllRetries = failedAllRetries;
24+
this.succeededAllRetries = succeededAllRetries;
25+
this.retryReason = retryReason;
26+
}
27+
28+
@Override
29+
public boolean failureSuppressed() {
30+
return failureSuppressed;
31+
}
32+
33+
@Override
34+
public boolean lastExecution() {
35+
return lastExecution;
36+
}
37+
38+
@Override
39+
public boolean failedAllRetries() {
40+
return failedAllRetries;
41+
}
42+
43+
@Override
44+
public boolean succeededAllRetries() {
45+
return succeededAllRetries;
46+
}
47+
48+
@Nullable
49+
@Override
50+
public RetryReason retryReason() {
51+
return retryReason;
52+
}
53+
}
Lines changed: 33 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package datadog.trace.civisibility.execution;
22

3+
import datadog.trace.api.civisibility.execution.TestExecutionHistory;
34
import datadog.trace.api.civisibility.execution.TestExecutionPolicy;
45
import datadog.trace.api.civisibility.execution.TestStatus;
56
import datadog.trace.api.civisibility.telemetry.tag.RetryReason;
@@ -13,11 +14,8 @@ public class Regular implements TestExecutionPolicy {
1314
private Regular() {}
1415

1516
@Override
16-
public void registerExecution(TestStatus status, long durationMillis) {}
17-
18-
@Override
19-
public boolean wasLastExecution() {
20-
return true;
17+
public ExecutionOutcome registerExecution(TestStatus status, long durationMillis) {
18+
return RegularExecutionOutcome.INSTANCE;
2119
}
2220

2321
@Override
@@ -30,19 +28,37 @@ public boolean suppressFailures() {
3028
return false;
3129
}
3230

33-
@Nullable
34-
@Override
35-
public RetryReason currentExecutionRetryReason() {
36-
return null;
37-
}
31+
private static final class RegularExecutionOutcome
32+
implements TestExecutionHistory.ExecutionOutcome {
3833

39-
@Override
40-
public boolean hasFailedAllRetries() {
41-
return false;
42-
}
34+
static final TestExecutionHistory.ExecutionOutcome INSTANCE = new RegularExecutionOutcome();
4335

44-
@Override
45-
public boolean hasSucceededAllRetries() {
46-
return false;
36+
private RegularExecutionOutcome() {}
37+
38+
@Override
39+
public boolean failureSuppressed() {
40+
return false;
41+
}
42+
43+
@Override
44+
public boolean lastExecution() {
45+
return false;
46+
}
47+
48+
@Override
49+
public boolean failedAllRetries() {
50+
return false;
51+
}
52+
53+
@Override
54+
public boolean succeededAllRetries() {
55+
return false;
56+
}
57+
58+
@Nullable
59+
@Override
60+
public RetryReason retryReason() {
61+
return null;
62+
}
4763
}
4864
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RetryUntilSuccessful.java

Lines changed: 15 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import datadog.trace.api.civisibility.execution.TestStatus;
55
import datadog.trace.api.civisibility.telemetry.tag.RetryReason;
66
import java.util.concurrent.atomic.AtomicInteger;
7-
import javax.annotation.Nullable;
87

98
/** Retries a test case if it failed, up to a maximum number of times. */
109
public class RetryUntilSuccessful implements TestExecutionPolicy {
@@ -26,54 +25,38 @@ public RetryUntilSuccessful(
2625
}
2726

2827
@Override
29-
public void registerExecution(TestStatus status, long durationMillis) {
28+
public ExecutionOutcome registerExecution(TestStatus status, long durationMillis) {
3029
++executions;
3130
successfulExecutionSeen |= (status != TestStatus.fail);
3231
if (executions > 1) {
3332
totalRetryCount.incrementAndGet();
3433
}
35-
}
3634

37-
@Override
38-
public boolean wasLastExecution() {
39-
return successfulExecutionSeen || executions == maxExecutions;
35+
boolean lastExecution = !retriesLeft();
36+
boolean retry = executions > 1; // first execution is not a retry
37+
return new ExecutionOutcomeImpl(
38+
status == TestStatus.fail && (!lastExecution || suppressFailures),
39+
lastExecution,
40+
lastExecution && !successfulExecutionSeen,
41+
false,
42+
retry ? RetryReason.atr : null);
4043
}
4144

42-
private boolean currentExecutionIsLast() {
43-
return executions == maxExecutions - 1;
45+
private boolean retriesLeft() {
46+
return !successfulExecutionSeen && executions < maxExecutions;
4447
}
4548

4649
@Override
4750
public boolean applicable() {
4851
// executions must always be registered, therefore consider it applicable as long as there are
4952
// retries left
50-
return !wasLastExecution();
53+
return retriesLeft();
5154
}
5255

5356
@Override
5457
public boolean suppressFailures() {
55-
// do not suppress failures for last execution
56-
// (unless flag to suppress all failures is set)
57-
return !currentExecutionIsLast() || suppressFailures;
58-
}
59-
60-
@Nullable
61-
@Override
62-
public RetryReason currentExecutionRetryReason() {
63-
return currentExecutionIsRetry() ? RetryReason.atr : null;
64-
}
65-
66-
private boolean currentExecutionIsRetry() {
67-
return executions > 0;
68-
}
69-
70-
@Override
71-
public boolean hasFailedAllRetries() {
72-
return wasLastExecution() && !successfulExecutionSeen;
73-
}
74-
75-
@Override
76-
public boolean hasSucceededAllRetries() {
77-
return false;
58+
// do not suppress failures for last execution (unless flag to suppress all failures is set);
59+
// the +1 is because this method is called _before_ subsequent execution is registered
60+
return executions + 1 < maxExecutions || suppressFailures;
7861
}
7962
}

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/execution/RunNTimes.java

Lines changed: 13 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import datadog.trace.api.civisibility.telemetry.tag.RetryReason;
66
import datadog.trace.civisibility.config.ExecutionsByDuration;
77
import java.util.List;
8-
import javax.annotation.Nullable;
98

109
/** Runs a test case N times (N depends on test duration) regardless of success or failure. */
1110
public class RunNTimes implements TestExecutionPolicy {
@@ -31,27 +30,35 @@ public RunNTimes(
3130
}
3231

3332
@Override
34-
public void registerExecution(TestStatus status, long durationMillis) {
33+
public ExecutionOutcome registerExecution(TestStatus status, long durationMillis) {
3534
lastStatus = status;
3635
++executions;
3736
if (status != TestStatus.fail) {
3837
++successfulExecutionsSeen;
3938
}
4039
int maxExecutionsForGivenDuration = getExecutions(durationMillis);
4140
maxExecutions = Math.min(maxExecutions, maxExecutionsForGivenDuration);
41+
42+
boolean lastExecution = !retriesLeft();
43+
boolean retry = executions > 1; // first execution is not a retry
44+
return new ExecutionOutcomeImpl(
45+
status == TestStatus.fail && suppressFailures(),
46+
lastExecution,
47+
lastExecution && successfulExecutionsSeen == 0,
48+
lastExecution && successfulExecutionsSeen == executions,
49+
retry ? retryReason : null);
4250
}
4351

44-
@Override
45-
public boolean wasLastExecution() {
52+
private boolean retriesLeft() {
4653
// skipped tests (either by the framework or DD) should not be retried
47-
return lastStatus == TestStatus.skip || executions >= maxExecutions;
54+
return lastStatus != TestStatus.skip && executions < maxExecutions;
4855
}
4956

5057
@Override
5158
public boolean applicable() {
5259
// executions must always be registered, therefore consider it applicable as long as there are
5360
// retries left
54-
return !wasLastExecution();
61+
return retriesLeft();
5562
}
5663

5764
@Override
@@ -67,24 +74,4 @@ private int getExecutions(long durationMillis) {
6774
}
6875
return 0;
6976
}
70-
71-
@Nullable
72-
@Override
73-
public RetryReason currentExecutionRetryReason() {
74-
return currentExecutionIsRetry() ? retryReason : null;
75-
}
76-
77-
private boolean currentExecutionIsRetry() {
78-
return executions > 0;
79-
}
80-
81-
@Override
82-
public boolean hasFailedAllRetries() {
83-
return wasLastExecution() && successfulExecutionsSeen == 0;
84-
}
85-
86-
@Override
87-
public boolean hasSucceededAllRetries() {
88-
return wasLastExecution() && successfulExecutionsSeen == executions;
89-
}
9077
}

0 commit comments

Comments
 (0)