Skip to content

Commit 95b3762

Browse files
Consider modified tests when applying fail-fast tests ordering
1 parent 19d8832 commit 95b3762

File tree

11 files changed

+98
-28
lines changed

11 files changed

+98
-28
lines changed

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ TestSuiteImpl testSuiteStart(
2929

3030
boolean isFlaky(@Nonnull TestIdentifier test);
3131

32-
boolean isModified(TestSourceData testSourceData);
32+
boolean isModified(@Nonnull TestSourceData testSourceData);
3333

3434
boolean isQuarantined(TestIdentifier test);
3535

@@ -49,5 +49,11 @@ TestSuiteImpl testSuiteStart(
4949
@Nonnull
5050
TestExecutionPolicy executionPolicy(TestIdentifier test, TestSourceData testSource);
5151

52+
/**
53+
* Returns the priority of the test execution that can be used for ordering tests. The higher the
54+
* value, the higher the priority, meaning that the test should be executed earlier.
55+
*/
56+
int executionPriority(@Nullable TestIdentifier test, @Nonnull TestSourceData testSource);
57+
5258
void end(Long startTime);
5359
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public boolean isFlaky(@Nonnull TestIdentifier test) {
100100
}
101101

102102
@Override
103-
public boolean isModified(TestSourceData testSourceData) {
103+
public boolean isModified(@Nonnull TestSourceData testSourceData) {
104104
return executionStrategy.isModified(testSourceData);
105105
}
106106

@@ -131,6 +131,11 @@ public TestExecutionPolicy executionPolicy(TestIdentifier test, TestSourceData t
131131
return executionStrategy.executionPolicy(test, testSource);
132132
}
133133

134+
@Override
135+
public int executionPriority(@Nullable TestIdentifier test, @Nonnull TestSourceData testSource) {
136+
return executionStrategy.executionPriority(test, testSource);
137+
}
138+
134139
@Override
135140
public void end(@Nullable Long endTime) {
136141
// we have no span locally,

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public boolean isFlaky(@Nonnull TestIdentifier test) {
8585
}
8686

8787
@Override
88-
public boolean isModified(TestSourceData testSourceData) {
88+
public boolean isModified(@Nonnull TestSourceData testSourceData) {
8989
return executionStrategy.isModified(testSourceData);
9090
}
9191

@@ -116,6 +116,11 @@ public TestExecutionPolicy executionPolicy(TestIdentifier test, TestSourceData t
116116
return executionStrategy.executionPolicy(test, testSource);
117117
}
118118

119+
@Override
120+
public int executionPriority(@Nullable TestIdentifier test, @Nonnull TestSourceData testSource) {
121+
return executionStrategy.executionPriority(test, testSource);
122+
}
123+
119124
@Override
120125
public void end(@Nullable Long endTime) {
121126
ExecutionSettings executionSettings = executionStrategy.getExecutionSettings();

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,12 @@ public boolean isFlaky(@Nonnull TestIdentifier test) {
114114
return false;
115115
}
116116

117+
@Override
118+
public int executionPriority(
119+
@Nullable TestIdentifier test, @Nonnull TestSourceData testSourceData) {
120+
return 0;
121+
}
122+
117123
@Override
118124
public void close() {
119125
// do nothing

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,11 @@ public TestExecutionPolicy executionPolicy(TestIdentifier test, TestSourceData t
299299
return testModule.executionPolicy(test, testSource);
300300
}
301301

302+
@Override
303+
public int executionPriority(@Nullable TestIdentifier test, @Nonnull TestSourceData testSource) {
304+
return testModule.executionPriority(test, testSource);
305+
}
306+
302307
@Override
303308
public boolean isNew(@Nonnull TestIdentifier test) {
304309
return testModule.isNew(test);

dd-java-agent/agent-ci-visibility/src/main/java/datadog/trace/civisibility/test/ExecutionStrategy.java

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ public boolean isEFDLimitReached() {
187187
return detectionsUsed > threshold;
188188
}
189189

190-
public boolean isModified(TestSourceData testSourceData) {
190+
public boolean isModified(@Nonnull TestSourceData testSourceData) {
191191
Class<?> testClass = testSourceData.getTestClass();
192192
if (testClass == null) {
193193
return false;
@@ -219,4 +219,28 @@ private LinesResolver.Lines getLines(Method testMethod) {
219219
return linesResolver.getMethodLines(testMethod);
220220
}
221221
}
222+
223+
/**
224+
* Returns the priority of the test execution that can be used for ordering tests. The higher the
225+
* value, the higher the priority, meaning that the test should be executed earlier.
226+
*/
227+
public int executionPriority(@Nullable TestIdentifier test, @Nonnull TestSourceData testSource) {
228+
if (test == null) {
229+
return 0;
230+
}
231+
if (isNew(test)) {
232+
// execute new tests first
233+
return 300;
234+
}
235+
if (isModified(testSource)) {
236+
// then modified tests
237+
return 200;
238+
}
239+
if (isFlaky(test)) {
240+
// then tests known to be flaky
241+
return 100;
242+
}
243+
// then the rest
244+
return 0;
245+
}
222246
}
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datadog.trace.instrumentation.junit5.order;
22

33
import datadog.trace.api.civisibility.config.TestIdentifier;
4+
import datadog.trace.api.civisibility.config.TestSourceData;
45
import datadog.trace.api.civisibility.events.TestEventsHandler;
56
import datadog.trace.instrumentation.junit5.JUnitPlatformUtils;
67
import java.util.Comparator;
@@ -15,41 +16,44 @@ public class FailFastClassOrderer implements ClassOrderer {
1516

1617
private final TestEventsHandler<TestDescriptor, TestDescriptor> testEventsHandler;
1718
private final @Nullable ClassOrderer delegate;
18-
private final Comparator<ClassDescriptor> knownTestSuitesComparator;
19+
private final Comparator<ClassDescriptor> executionOrderComparator;
1920

2021
public FailFastClassOrderer(
2122
TestEventsHandler<TestDescriptor, TestDescriptor> testEventsHandler,
2223
@Nullable ClassOrderer delegate) {
2324
this.testEventsHandler = testEventsHandler;
2425
this.delegate = delegate;
25-
this.knownTestSuitesComparator = Comparator.comparing(this::knownAndStableTestsPercentage);
26+
this.executionOrderComparator = Comparator.comparing(this::classExecutionPriority).reversed();
2627
}
2728

28-
private int knownAndStableTestsPercentage(ClassDescriptor classDescriptor) {
29+
private int classExecutionPriority(ClassDescriptor classDescriptor) {
2930
TestDescriptor testDescriptor = JUnit5OrderUtils.getTestDescriptor(classDescriptor);
30-
return 100 - unknownAndFlakyTestsPercentage(testDescriptor);
31+
return executionPriority(testDescriptor);
3132
}
3233

33-
private int unknownAndFlakyTestsPercentage(TestDescriptor testDescriptor) {
34+
private int executionPriority(TestDescriptor testDescriptor) {
3435
if (testDescriptor == null) {
3536
return 0;
3637
}
38+
3739
if (testDescriptor.isTest() || JUnitPlatformUtils.isParameterizedTest(testDescriptor)) {
3840
TestIdentifier testIdentifier = JUnitPlatformUtils.toTestIdentifier(testDescriptor);
39-
return testEventsHandler.isNew(testIdentifier) || testEventsHandler.isFlaky(testIdentifier)
40-
? 100
41-
: 0;
41+
TestSourceData testSourceData = JUnitPlatformUtils.toTestSourceData(testDescriptor);
42+
return testEventsHandler.executionPriority(testIdentifier, testSourceData);
4243
}
44+
4345
Set<? extends TestDescriptor> children = testDescriptor.getChildren();
4446
if (children.isEmpty()) {
4547
return 0;
4648
}
4749

48-
int uknownTestsPercentage = 0;
50+
// there's no specific meaning to the average of children priorities,
51+
// it is just a heuristic to try to favor classes with higher percentage of failure-prone tests
52+
int childrenPrioritySum = 0;
4953
for (TestDescriptor child : children) {
50-
uknownTestsPercentage += unknownAndFlakyTestsPercentage(child);
54+
childrenPrioritySum += executionPriority(child);
5155
}
52-
return uknownTestsPercentage / children.size();
56+
return childrenPrioritySum / children.size();
5357
}
5458

5559
@Override
@@ -60,6 +64,6 @@ public void orderClasses(ClassOrdererContext classOrdererContext) {
6064
}
6165
// then apply our ordering (since sorting is stable, delegate's ordering will be preserved for
6266
// classes with the same "known/stable" status)
63-
classOrdererContext.getClassDescriptors().sort(knownTestSuitesComparator);
67+
classOrdererContext.getClassDescriptors().sort(executionOrderComparator);
6468
}
6569
}

dd-java-agent/instrumentation/junit-5.3/junit-5.8/src/main/java/datadog/trace/instrumentation/junit5/order/FailFastMethodOrderer.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datadog.trace.instrumentation.junit5.order;
22

33
import datadog.trace.api.civisibility.config.TestIdentifier;
4+
import datadog.trace.api.civisibility.config.TestSourceData;
45
import datadog.trace.api.civisibility.events.TestEventsHandler;
56
import datadog.trace.instrumentation.junit5.JUnitPlatformUtils;
67
import java.util.Comparator;
@@ -14,23 +15,24 @@ public class FailFastMethodOrderer implements MethodOrderer {
1415

1516
private final TestEventsHandler<TestDescriptor, TestDescriptor> testEventsHandler;
1617
private final @Nullable MethodOrderer delegate;
17-
private final Comparator<MethodDescriptor> knownTestMethodsComparator;
18+
private final Comparator<MethodDescriptor> executionOrderComparator;
1819

1920
public FailFastMethodOrderer(
2021
TestEventsHandler<TestDescriptor, TestDescriptor> testEventsHandler,
2122
@Nullable MethodOrderer delegate) {
2223
this.testEventsHandler = testEventsHandler;
2324
this.delegate = delegate;
24-
this.knownTestMethodsComparator = Comparator.comparing(this::isKnownAndStable);
25+
this.executionOrderComparator = Comparator.comparing(this::executionPriority).reversed();
2526
}
2627

27-
private boolean isKnownAndStable(MethodDescriptor methodDescriptor) {
28+
private int executionPriority(MethodDescriptor methodDescriptor) {
2829
TestDescriptor testDescriptor = JUnit5OrderUtils.getTestDescriptor(methodDescriptor);
2930
if (testDescriptor == null) {
30-
return true;
31+
return 0;
3132
}
3233
TestIdentifier testIdentifier = JUnitPlatformUtils.toTestIdentifier(testDescriptor);
33-
return !testEventsHandler.isNew(testIdentifier) && !testEventsHandler.isFlaky(testIdentifier);
34+
TestSourceData testSourceData = JUnitPlatformUtils.toTestSourceData(testDescriptor);
35+
return testEventsHandler.executionPriority(testIdentifier, testSourceData);
3436
}
3537

3638
@Override
@@ -41,6 +43,6 @@ public void orderMethods(MethodOrdererContext methodOrdererContext) {
4143
}
4244
// then apply our ordering (since sorting is stable, delegate's ordering will be preserved for
4345
// methods with the same "known/stable" status)
44-
methodOrdererContext.getMethodDescriptors().sort(knownTestMethodsComparator);
46+
methodOrdererContext.getMethodDescriptors().sort(executionOrderComparator);
4547
}
4648
}

dd-java-agent/instrumentation/testng/src/main/java/datadog/trace/instrumentation/testng/TestNGUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.Collections;
1414
import java.util.List;
1515
import java.util.Properties;
16+
import javax.annotation.Nonnull;
1617
import org.slf4j.Logger;
1718
import org.slf4j.LoggerFactory;
1819
import org.testng.IClass;
@@ -234,6 +235,7 @@ public static IRetryAnalyzer getRetryAnalyzer(ITestResult result) {
234235
}
235236
}
236237

238+
@Nonnull
237239
public static TestIdentifier toTestIdentifier(
238240
Method method, Object instance, Object[] parameters) {
239241
Class<?> testClass = instance != null ? instance.getClass() : method.getDeclaringClass();
@@ -243,6 +245,7 @@ public static TestIdentifier toTestIdentifier(
243245
return new TestIdentifier(testSuiteName, testName, testParameters);
244246
}
245247

248+
@Nonnull
246249
public static TestIdentifier toTestIdentifier(ITestResult result) {
247250
String testSuiteName = result.getInstanceName();
248251
String testName =
@@ -251,6 +254,7 @@ public static TestIdentifier toTestIdentifier(ITestResult result) {
251254
return new TestIdentifier(testSuiteName, testName, testParameters);
252255
}
253256

257+
@Nonnull
254258
public static TestSuiteDescriptor toSuiteDescriptor(ITestClass testClass) {
255259
String testSuiteName = testClass.getName();
256260
Class<?> testSuiteClass = testClass.getRealClass();

dd-java-agent/instrumentation/testng/testng-7/src/main/java/datadog/trace/instrumentation/testng7/order/FailFastOrderInterceptor.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package datadog.trace.instrumentation.testng7.order;
22

33
import datadog.trace.api.civisibility.config.TestIdentifier;
4+
import datadog.trace.api.civisibility.config.TestSourceData;
45
import datadog.trace.api.civisibility.events.TestEventsHandler;
56
import datadog.trace.api.civisibility.events.TestSuiteDescriptor;
67
import datadog.trace.instrumentation.testng.TestNGUtils;
@@ -16,27 +17,29 @@
1617
public class FailFastOrderInterceptor implements IMethodInterceptor {
1718

1819
private final TestEventsHandler<TestSuiteDescriptor, ITestResult> testEventsHandler;
19-
private final Comparator<IMethodInstance> knownAndStableTestsComparator;
20+
private final Comparator<IMethodInstance> executionOrderComparator;
2021

2122
public FailFastOrderInterceptor(
2223
TestEventsHandler<TestSuiteDescriptor, ITestResult> testEventsHandler) {
2324
this.testEventsHandler = testEventsHandler;
24-
this.knownAndStableTestsComparator = Comparator.comparing(this::isKnownAndStable);
25+
this.executionOrderComparator = Comparator.comparing(this::executionPriority).reversed();
2526
}
2627

27-
private boolean isKnownAndStable(IMethodInstance methodInstance) {
28+
private int executionPriority(IMethodInstance methodInstance) {
2829
ITestNGMethod method = methodInstance.getMethod();
2930
if (method == null) {
30-
return true;
31+
// not expected to happen, just a safeguard
32+
return 0;
3133
}
3234
ITestResult testResult = TestResult.newTestResultFor(method);
3335
TestIdentifier testIdentifier = TestNGUtils.toTestIdentifier(testResult);
34-
return !testEventsHandler.isNew(testIdentifier) && !testEventsHandler.isFlaky(testIdentifier);
36+
TestSourceData testSourceData = TestNGUtils.toTestSourceData(testResult);
37+
return testEventsHandler.executionPriority(testIdentifier, testSourceData);
3538
}
3639

3740
@Override
3841
public List<IMethodInstance> intercept(List<IMethodInstance> list, ITestContext iTestContext) {
39-
list.sort(knownAndStableTestsComparator);
42+
list.sort(executionOrderComparator);
4043
return list;
4144
}
4245
}

internal-api/src/main/java/datadog/trace/api/civisibility/events/TestEventsHandler.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ void onTestIgnore(
9090
@Nonnull
9191
TestExecutionPolicy executionPolicy(TestIdentifier test, TestSourceData source);
9292

93+
/**
94+
* Returns the priority of the test execution that can be used for ordering tests. The higher the
95+
* value, the higher the priority, meaning that the test should be executed earlier.
96+
*/
97+
int executionPriority(@Nullable TestIdentifier test, @Nonnull TestSourceData testSourceData);
98+
9399
/**
94100
* Returns the reason for skipping a test IF it can be skipped.
95101
*

0 commit comments

Comments
 (0)