Skip to content

Commit 3827ccb

Browse files
committed
Lifecycle.PER_CLASS ⇒ ExecutionMode.SAME_THREAD
Tests in classes that use `Lifecycle.PER_CLASS` now use `ExecutionMode.SAME_THREAD` by default when parallel execution is enabled. In the case of nested classes, as soon as one enclosing class uses `Lifecycle.PER_CLASS` all contained nested classes and tests use `ExecutionMode.SAME_THREAD`. Fixes #1511.
1 parent d594ca6 commit 3827ccb

File tree

16 files changed

+234
-53
lines changed

16 files changed

+234
-53
lines changed

documentation/src/docs/asciidoc/release-notes/release-notes-5.3.0-RC1.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ on GitHub.
9898
class name and system hash code, separated by an `@` symbol.
9999
* New `getAs<Class>(index)` Kotlin extension method to make `ArgumentsAccessor` friendlier
100100
to use from Kotlin.
101+
* Tests in classes that use `Lifecycle.PER_CLASS` are now executed in the same thread by
102+
default when parallel execution is enabled.
101103

102104

103105
[[release-notes-5.3.0-RC1-junit-vintage]]

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/ClassTestDescriptor.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
import org.junit.platform.commons.util.Preconditions;
5252
import org.junit.platform.commons.util.ReflectionUtils;
5353
import org.junit.platform.commons.util.StringUtils;
54+
import org.junit.platform.engine.ConfigurationParameters;
5455
import org.junit.platform.engine.TestDescriptor;
5556
import org.junit.platform.engine.TestTag;
5657
import org.junit.platform.engine.UniqueId;
@@ -77,23 +78,25 @@ public class ClassTestDescriptor extends JupiterTestDescriptor {
7778

7879
private final Class<?> testClass;
7980
private final Set<TestTag> tags;
81+
protected final Lifecycle lifecycle;
8082

8183
private TestInstanceFactory testInstanceFactory;
8284
private List<Method> beforeAllMethods;
8385
private List<Method> afterAllMethods;
8486

85-
public ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass) {
86-
this(uniqueId, ClassTestDescriptor::generateDefaultDisplayName, testClass);
87+
public ClassTestDescriptor(UniqueId uniqueId, Class<?> testClass, ConfigurationParameters configurationParameters) {
88+
this(uniqueId, ClassTestDescriptor::generateDefaultDisplayName, testClass, configurationParameters);
8789
}
8890

8991
protected ClassTestDescriptor(UniqueId uniqueId, Function<Class<?>, String> defaultDisplayNameGenerator,
90-
Class<?> testClass) {
92+
Class<?> testClass, ConfigurationParameters configurationParameters) {
9193

9294
super(uniqueId, determineDisplayName(Preconditions.notNull(testClass, "Class must not be null"),
9395
defaultDisplayNameGenerator), ClassSource.from(testClass));
9496

9597
this.testClass = testClass;
9698
this.tags = getTags(testClass);
99+
this.lifecycle = getTestInstanceLifecycle(testClass, configurationParameters);
97100
}
98101

99102
// --- TestDescriptor ------------------------------------------------------
@@ -127,13 +130,18 @@ private static String generateDefaultDisplayName(Class<?> testClass) {
127130
// --- Node ----------------------------------------------------------------
128131

129132
@Override
130-
public ExecutionMode getExecutionMode() {
131-
return getExecutionMode(getTestClass());
133+
protected Optional<ExecutionMode> getExplicitExecutionMode() {
134+
return getExecutionModeFromAnnotation(getTestClass());
135+
}
136+
137+
@Override
138+
protected Optional<ExecutionMode> getDefaultChildExecutionMode() {
139+
return this.lifecycle == Lifecycle.PER_CLASS ? Optional.of(ExecutionMode.SAME_THREAD) : Optional.empty();
132140
}
133141

134142
@Override
135143
public Set<ExclusiveResource> getExclusiveResources() {
136-
return getExclusiveResources(getTestClass());
144+
return getExclusiveResourcesFromAnnotation(getTestClass());
137145
}
138146

139147
@Override
@@ -153,7 +161,6 @@ public JupiterEngineExecutionContext prepare(JupiterEngineExecutionContext conte
153161
registerBeforeEachMethodAdapters(registry);
154162
registerAfterEachMethodAdapters(registry);
155163

156-
Lifecycle lifecycle = getTestInstanceLifecycle(this.testClass, context.getConfigurationParameters());
157164
ThrowableCollector throwableCollector = new OpenTest4JAwareThrowableCollector();
158165
ClassExtensionContext extensionContext = new ClassExtensionContext(context.getExtensionContext(),
159166
context.getExecutionListener(), this, lifecycle, context.getConfigurationParameters(), throwableCollector);

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/DynamicNodeTestDescriptor.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010

1111
package org.junit.jupiter.engine.descriptor;
1212

13-
import static org.junit.platform.engine.support.hierarchical.Node.ExecutionMode.CONCURRENT;
14-
1513
import org.junit.jupiter.api.DynamicNode;
1614
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
1715
import org.junit.platform.engine.TestDescriptor;
@@ -32,11 +30,6 @@ abstract class DynamicNodeTestDescriptor extends JupiterTestDescriptor {
3230
this.index = index;
3331
}
3432

35-
@Override
36-
public ExecutionMode getExecutionMode() {
37-
return getParent().map(parent -> ((JupiterTestDescriptor) parent).getExecutionMode()).orElse(CONCURRENT);
38-
}
39-
4033
@Override
4134
public String getLegacyReportingName() {
4235
// @formatter:off

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/JupiterTestDescriptor.java

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.junit.platform.commons.logging.LoggerFactory;
4040
import org.junit.platform.commons.util.ExceptionUtils;
4141
import org.junit.platform.commons.util.StringUtils;
42+
import org.junit.platform.engine.TestDescriptor;
4243
import org.junit.platform.engine.TestSource;
4344
import org.junit.platform.engine.TestTag;
4445
import org.junit.platform.engine.UniqueId;
@@ -110,15 +111,41 @@ protected static <E extends AnnotatedElement> String determineDisplayName(E elem
110111

111112
// --- Node ----------------------------------------------------------------
112113

113-
protected ExecutionMode getExecutionMode(AnnotatedElement element) {
114+
@Override
115+
public final ExecutionMode getExecutionMode() {
116+
Optional<ExecutionMode> executionMode = getExplicitExecutionMode();
117+
if (executionMode.isPresent()) {
118+
return executionMode.get();
119+
}
120+
Optional<TestDescriptor> parent = getParent();
121+
while (parent.isPresent() && parent.get() instanceof JupiterTestDescriptor) {
122+
JupiterTestDescriptor jupiterParent = (JupiterTestDescriptor) parent.get();
123+
executionMode = jupiterParent.getExplicitExecutionMode();
124+
if (executionMode.isPresent()) {
125+
return executionMode.get();
126+
}
127+
executionMode = jupiterParent.getDefaultChildExecutionMode();
128+
if (executionMode.isPresent()) {
129+
return executionMode.get();
130+
}
131+
parent = jupiterParent.getParent();
132+
}
133+
return ExecutionMode.CONCURRENT;
134+
}
135+
136+
protected Optional<ExecutionMode> getExplicitExecutionMode() {
137+
return Optional.empty();
138+
}
139+
140+
protected Optional<ExecutionMode> getDefaultChildExecutionMode() {
141+
return Optional.empty();
142+
}
143+
144+
protected Optional<ExecutionMode> getExecutionModeFromAnnotation(AnnotatedElement element) {
114145
// @formatter:off
115146
return findAnnotation(element, Execution.class)
116147
.map(Execution::value)
117-
.map(JupiterTestDescriptor::toExecutionMode)
118-
.orElseGet(() -> getParent()
119-
.filter(parent -> parent instanceof Node)
120-
.map(parent -> ((Node<?>) parent).getExecutionMode())
121-
.orElse(ExecutionMode.CONCURRENT));
148+
.map(JupiterTestDescriptor::toExecutionMode);
122149
// @formatter:on
123150
}
124151

@@ -132,7 +159,7 @@ private static ExecutionMode toExecutionMode(org.junit.jupiter.api.parallel.Exec
132159
throw new JUnitException("Unknown ExecutionMode: " + mode);
133160
}
134161

135-
protected Set<ExclusiveResource> getExclusiveResources(AnnotatedElement element) {
162+
protected Set<ExclusiveResource> getExclusiveResourcesFromAnnotation(AnnotatedElement element) {
136163
// @formatter:off
137164
return findRepeatableAnnotations(element, ResourceLock.class).stream()
138165
.map(resource -> new ExclusiveResource(resource.value(), toLockMode(resource.mode())))

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/MethodBasedTestDescriptor.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import java.lang.reflect.Method;
1414
import java.util.LinkedHashSet;
15+
import java.util.Optional;
1516
import java.util.Set;
1617

1718
import org.junit.platform.commons.util.ClassUtils;
@@ -59,12 +60,11 @@ public final Set<TestTag> getTags() {
5960

6061
@Override
6162
public Set<ExclusiveResource> getExclusiveResources() {
62-
return getExclusiveResources(getTestMethod());
63+
return getExclusiveResourcesFromAnnotation(getTestMethod());
6364
}
6465

65-
@Override
66-
public ExecutionMode getExecutionMode() {
67-
return getExecutionMode(getTestMethod());
66+
protected Optional<ExecutionMode> getExplicitExecutionMode() {
67+
return getExecutionModeFromAnnotation(getTestMethod());
6868
}
6969

7070
public final Class<?> getTestClass() {

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/descriptor/NestedClassTestDescriptor.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.junit.jupiter.api.extension.ExtensionContext;
2121
import org.junit.jupiter.engine.execution.JupiterEngineExecutionContext;
2222
import org.junit.jupiter.engine.extension.ExtensionRegistry;
23+
import org.junit.platform.engine.ConfigurationParameters;
2324
import org.junit.platform.engine.TestDescriptor;
2425
import org.junit.platform.engine.TestTag;
2526
import org.junit.platform.engine.UniqueId;
@@ -42,8 +43,9 @@ public class NestedClassTestDescriptor extends ClassTestDescriptor {
4243
*/
4344
private final Set<TestTag> tags;
4445

45-
public NestedClassTestDescriptor(UniqueId uniqueId, Class<?> testClass) {
46-
super(uniqueId, Class::getSimpleName, testClass);
46+
public NestedClassTestDescriptor(UniqueId uniqueId, Class<?> testClass,
47+
ConfigurationParameters configurationParameters) {
48+
super(uniqueId, Class::getSimpleName, testClass, configurationParameters);
4749

4850
this.tags = getTags(testClass);
4951
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/DiscoverySelectorResolver.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.apiguardian.api.API;
2020
import org.junit.jupiter.engine.discovery.predicates.IsTestClassWithTests;
2121
import org.junit.platform.commons.util.ClassFilter;
22+
import org.junit.platform.engine.ConfigurationParameters;
2223
import org.junit.platform.engine.EngineDiscoveryRequest;
2324
import org.junit.platform.engine.TestDescriptor;
2425
import org.junit.platform.engine.discovery.ClassSelector;
@@ -51,7 +52,8 @@ public void resolveSelectors(EngineDiscoveryRequest request, TestDescriptor engi
5152
}
5253

5354
private void resolve(EngineDiscoveryRequest request, TestDescriptor engineDescriptor, ClassFilter classFilter) {
54-
JavaElementsResolver javaElementsResolver = createJavaElementsResolver(engineDescriptor, classFilter);
55+
JavaElementsResolver javaElementsResolver = createJavaElementsResolver(request.getConfigurationParameters(),
56+
engineDescriptor, classFilter);
5557

5658
request.getSelectorsByType(ClasspathRootSelector.class).forEach(javaElementsResolver::resolveClasspathRoot);
5759
request.getSelectorsByType(ModuleSelector.class).forEach(javaElementsResolver::resolveModule);
@@ -69,10 +71,11 @@ private void pruneTree(TestDescriptor rootDescriptor) {
6971
rootDescriptor.accept(TestDescriptor::prune);
7072
}
7173

72-
private JavaElementsResolver createJavaElementsResolver(TestDescriptor engineDescriptor, ClassFilter classFilter) {
74+
private JavaElementsResolver createJavaElementsResolver(ConfigurationParameters configurationParameters,
75+
TestDescriptor engineDescriptor, ClassFilter classFilter) {
7376
Set<ElementResolver> resolvers = new LinkedHashSet<>();
74-
resolvers.add(new TestContainerResolver());
75-
resolvers.add(new NestedTestsResolver());
77+
resolvers.add(new TestContainerResolver(configurationParameters));
78+
resolvers.add(new NestedTestsResolver(configurationParameters));
7679
resolvers.add(new TestMethodResolver());
7780
resolvers.add(new TestFactoryMethodResolver());
7881
resolvers.add(new TestTemplateMethodResolver());

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/NestedTestsResolver.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
1414
import org.junit.jupiter.engine.descriptor.NestedClassTestDescriptor;
1515
import org.junit.jupiter.engine.discovery.predicates.IsNestedTestClass;
16+
import org.junit.platform.engine.ConfigurationParameters;
1617
import org.junit.platform.engine.TestDescriptor;
1718
import org.junit.platform.engine.UniqueId;
1819

@@ -25,6 +26,10 @@ class NestedTestsResolver extends TestContainerResolver {
2526

2627
static final String SEGMENT_TYPE = "nested-class";
2728

29+
public NestedTestsResolver(ConfigurationParameters configurationParameters) {
30+
super(configurationParameters);
31+
}
32+
2833
@Override
2934
protected Class<? extends TestDescriptor> requiredParentType() {
3035
return ClassTestDescriptor.class;
@@ -52,7 +57,7 @@ protected boolean isPotentialCandidate(Class<?> element) {
5257

5358
@Override
5459
protected TestDescriptor resolveClass(Class<?> testClass, UniqueId uniqueId) {
55-
return new NestedClassTestDescriptor(uniqueId, testClass);
60+
return new NestedClassTestDescriptor(uniqueId, testClass, this.configurationParameters);
5661
}
5762

5863
}

junit-jupiter-engine/src/main/java/org/junit/jupiter/engine/discovery/TestContainerResolver.java

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import org.junit.jupiter.engine.descriptor.ClassTestDescriptor;
1919
import org.junit.jupiter.engine.discovery.predicates.IsPotentialTestContainer;
2020
import org.junit.platform.commons.util.ReflectionUtils;
21+
import org.junit.platform.engine.ConfigurationParameters;
2122
import org.junit.platform.engine.TestDescriptor;
2223
import org.junit.platform.engine.UniqueId;
2324

@@ -30,6 +31,12 @@ class TestContainerResolver implements ElementResolver {
3031

3132
static final String SEGMENT_TYPE = "class";
3233

34+
protected final ConfigurationParameters configurationParameters;
35+
36+
public TestContainerResolver(ConfigurationParameters configurationParameters) {
37+
this.configurationParameters = configurationParameters;
38+
}
39+
3340
@Override
3441
public Set<TestDescriptor> resolveElement(AnnotatedElement element, TestDescriptor parent) {
3542
if (!(element instanceof Class)) {
@@ -97,7 +104,7 @@ protected UniqueId createUniqueId(Class<?> testClass, TestDescriptor parent) {
97104
}
98105

99106
protected TestDescriptor resolveClass(Class<?> testClass, UniqueId uniqueId) {
100-
return new ClassTestDescriptor(uniqueId, testClass);
107+
return new ClassTestDescriptor(uniqueId, testClass, this.configurationParameters);
101108
}
102109

103110
}

0 commit comments

Comments
 (0)