Skip to content

Commit 0eee8e3

Browse files
committed
Report test plan as containing tests in the presence of critical issues
This avoids the test from getting skipped if a tool (like Surefire does) inspect the test plan to decide whether a class is a test class.
1 parent 721d61a commit 0eee8e3

16 files changed

+179
-96
lines changed

junit-platform-launcher/src/main/java/org/junit/platform/launcher/TestPlan.java

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,13 +63,10 @@
6363
public class TestPlan {
6464

6565
private final Set<TestIdentifier> roots = synchronizedSet(new LinkedHashSet<>(4));
66-
6766
private final Map<UniqueId, Set<TestIdentifier>> children = new ConcurrentHashMap<>(32);
68-
6967
private final Map<UniqueId, TestIdentifier> allIdentifiers = new ConcurrentHashMap<>(32);
7068

7169
private final boolean containsTests;
72-
7370
private final ConfigurationParameters configurationParameters;
7471
private final OutputDirectoryProvider outputDirectoryProvider;
7572

@@ -80,6 +77,7 @@ public class TestPlan {
8077
* <p>Each supplied {@code TestDescriptor} is expected to be a descriptor
8178
* for a {@link org.junit.platform.engine.TestEngine TestEngine}.
8279
*
80+
* @param containsTests whether the test plan contains tests
8381
* @param engineDescriptors the engine test descriptors from which the test
8482
* plan should be created; never {@code null}
8583
* @param configurationParameters the {@code ConfigurationParameters} for
@@ -88,13 +86,12 @@ public class TestPlan {
8886
* this test plan; never {@code null}
8987
* @return a new test plan
9088
*/
91-
@API(status = INTERNAL, since = "1.12")
92-
public static TestPlan from(Collection<TestDescriptor> engineDescriptors,
89+
@API(status = INTERNAL, since = "1.13")
90+
public static TestPlan from(boolean containsTests, Collection<TestDescriptor> engineDescriptors,
9391
ConfigurationParameters configurationParameters, OutputDirectoryProvider outputDirectoryProvider) {
9492
Preconditions.notNull(engineDescriptors, "Cannot create TestPlan from a null collection of TestDescriptors");
9593
Preconditions.notNull(configurationParameters, "Cannot create TestPlan from null ConfigurationParameters");
96-
TestPlan testPlan = new TestPlan(engineDescriptors.stream().anyMatch(TestDescriptor::containsTests),
97-
configurationParameters, outputDirectoryProvider);
94+
TestPlan testPlan = new TestPlan(containsTests, configurationParameters, outputDirectoryProvider);
9895
TestDescriptor.Visitor visitor = descriptor -> testPlan.addInternal(TestIdentifier.from(descriptor));
9996
engineDescriptors.forEach(engineDescriptor -> engineDescriptor.accept(visitor));
10097
return testPlan;

junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/DiscoveryIssueNotifier.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ private DiscoveryIssueNotifier(List<DiscoveryIssue> criticalIssues, List<Discove
5555
this.nonCriticalIssues = nonCriticalIssues;
5656
}
5757

58+
boolean hasCriticalIssues() {
59+
return !criticalIssues.isEmpty();
60+
}
61+
5862
void logCriticalIssues(TestEngine testEngine) {
5963
logIssues(testEngine, criticalIssues, "critical");
6064
}

junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/InternalTestPlan.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ class InternalTestPlan extends TestPlan {
3030
private final TestPlan delegate;
3131

3232
static InternalTestPlan from(LauncherDiscoveryResult discoveryResult) {
33-
TestPlan delegate = TestPlan.from(discoveryResult.getEngineTestDescriptors(),
34-
discoveryResult.getConfigurationParameters(), discoveryResult.getOutputDirectoryProvider());
33+
TestPlan delegate = TestPlan.from(discoveryResult.containsCriticalIssuesOrContainsTests(),
34+
discoveryResult.getEngineTestDescriptors(), discoveryResult.getConfigurationParameters(),
35+
discoveryResult.getOutputDirectoryProvider());
3536
return new InternalTestPlan(discoveryResult, delegate);
3637
}
3738

junit-platform-launcher/src/main/java/org/junit/platform/launcher/core/LauncherDiscoveryResult.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ public Collection<TestEngine> getTestEngines() {
6767
return this.testEngineResults.keySet();
6868
}
6969

70+
boolean containsCriticalIssuesOrContainsTests() {
71+
return this.testEngineResults.values().stream() //
72+
.anyMatch(EngineResultInfo::containsCriticalIssuesOrContainsTests);
73+
}
74+
7075
Collection<TestDescriptor> getEngineTestDescriptors() {
7176
return this.testEngineResults.values().stream() //
7277
.map(EngineResultInfo::getRootDescriptor) //
@@ -126,6 +131,11 @@ Optional<Throwable> getCause() {
126131
return Optional.ofNullable(this.cause);
127132
}
128133

134+
boolean containsCriticalIssuesOrContainsTests() {
135+
return cause != null //
136+
|| discoveryIssueNotifier.hasCriticalIssues() //
137+
|| TestDescriptor.containsTests(rootDescriptor);
138+
}
129139
}
130140

131141
}

platform-tests/src/test/java/org/junit/platform/console/tasks/ColorPaletteTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ void flat_single_color() {
187187

188188
private void demoTestRun(TestExecutionListener listener) {
189189
TestDescriptor testDescriptor = new TestDescriptorStub(UniqueId.forEngine("demo-engine"), "My Test");
190-
TestPlan testPlan = TestPlan.from(List.of(testDescriptor), mock(), dummyOutputDirectoryProvider());
190+
TestPlan testPlan = TestPlan.from(true, List.of(testDescriptor), mock(), dummyOutputDirectoryProvider());
191191
listener.testPlanExecutionStarted(testPlan);
192192
listener.executionStarted(TestIdentifier.from(testDescriptor));
193193
listener.executionFinished(TestIdentifier.from(testDescriptor), TestExecutionResult.successful());

platform-tests/src/test/java/org/junit/platform/console/tasks/TestFeedPrintingListenerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void prepareListener() {
4545
"%c ool test");
4646
engineDescriptor.addChild(testDescriptor);
4747

48-
testPlan = TestPlan.from(Collections.singleton(engineDescriptor), mock(), dummyOutputDirectoryProvider());
48+
testPlan = TestPlan.from(true, Collections.singleton(engineDescriptor), mock(), dummyOutputDirectoryProvider());
4949
testIdentifier = testPlan.getTestIdentifier(testDescriptor.getUniqueId());
5050

5151
listener.testPlanExecutionStarted(testPlan);

platform-tests/src/test/java/org/junit/platform/launcher/TestPlanTests.java

Lines changed: 1 addition & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,15 @@
1010

1111
package org.junit.platform.launcher;
1212

13-
import static org.assertj.core.api.Assertions.assertThat;
1413
import static org.junit.platform.launcher.core.OutputDirectoryProviders.dummyOutputDirectoryProvider;
1514
import static org.mockito.Mockito.inOrder;
1615
import static org.mockito.Mockito.mock;
1716

1817
import java.util.List;
19-
import java.util.Set;
2018

2119
import org.junit.jupiter.api.Test;
2220
import org.junit.platform.engine.ConfigurationParameters;
2321
import org.junit.platform.engine.UniqueId;
24-
import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
2522
import org.junit.platform.engine.support.descriptor.EngineDescriptor;
2623
import org.junit.platform.fakes.TestDescriptorStub;
2724

@@ -31,56 +28,6 @@ class TestPlanTests {
3128

3229
private final EngineDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine("foo"), "Foo");
3330

34-
@Test
35-
void doesNotContainTestsForEmptyContainers() {
36-
engineDescriptor.addChild(
37-
new AbstractTestDescriptor(engineDescriptor.getUniqueId().append("test", "bar"), "Bar") {
38-
@Override
39-
public Type getType() {
40-
return Type.CONTAINER;
41-
}
42-
});
43-
44-
var testPlan = TestPlan.from(Set.of(engineDescriptor), configParams, dummyOutputDirectoryProvider());
45-
46-
assertThat(testPlan.containsTests()).as("contains tests").isFalse();
47-
}
48-
49-
@Test
50-
void containsTestsForTests() {
51-
engineDescriptor.addChild(
52-
new AbstractTestDescriptor(engineDescriptor.getUniqueId().append("test", "bar"), "Bar") {
53-
@Override
54-
public Type getType() {
55-
return Type.TEST;
56-
}
57-
});
58-
59-
var testPlan = TestPlan.from(Set.of(engineDescriptor), configParams, dummyOutputDirectoryProvider());
60-
61-
assertThat(testPlan.containsTests()).as("contains tests").isTrue();
62-
}
63-
64-
@Test
65-
void containsTestsForContainersThatMayRegisterTests() {
66-
engineDescriptor.addChild(
67-
new AbstractTestDescriptor(engineDescriptor.getUniqueId().append("test", "bar"), "Bar") {
68-
@Override
69-
public Type getType() {
70-
return Type.CONTAINER;
71-
}
72-
73-
@Override
74-
public boolean mayRegisterTests() {
75-
return true;
76-
}
77-
});
78-
79-
var testPlan = TestPlan.from(Set.of(engineDescriptor), configParams, dummyOutputDirectoryProvider());
80-
81-
assertThat(testPlan.containsTests()).as("contains tests").isTrue();
82-
}
83-
8431
@Test
8532
void acceptsVisitorsInDepthFirstOrder() {
8633
var container = new TestDescriptorStub(engineDescriptor.getUniqueId().append("container", "bar"), "Bar");
@@ -94,7 +41,7 @@ void acceptsVisitorsInDepthFirstOrder() {
9441
engineDescriptor2.addChild(test2);
9542
engineDescriptor2.addChild(test3);
9643

97-
var testPlan = TestPlan.from(List.of(engineDescriptor, engineDescriptor2), configParams,
44+
var testPlan = TestPlan.from(true, List.of(engineDescriptor, engineDescriptor2), configParams,
9845
dummyOutputDirectoryProvider());
9946
var visitor = mock(TestPlan.Visitor.class);
10047

platform-tests/src/test/java/org/junit/platform/launcher/core/CompositeTestExecutionListenerTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ private void assertThatTestListenerErrorLogged(LogRecordListener logRecordListen
207207
}
208208

209209
private static TestPlan anyTestPlan() {
210-
return TestPlan.from(Set.of(anyTestDescriptor()), mock(), dummyOutputDirectoryProvider());
210+
return TestPlan.from(true, Set.of(anyTestDescriptor()), mock(), dummyOutputDirectoryProvider());
211211
}
212212

213213
private static DemoMethodTestDescriptor anyTestDescriptor() {

platform-tests/src/test/java/org/junit/platform/launcher/core/DefaultLauncherTests.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,9 @@ void reportsEngineExecutionFailureForCriticalDiscoveryIssuesAndLogsRemaining(
715715
var result = execute(new TestEngineStub("engine-id") {
716716
@Override
717717
public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId uniqueId) {
718-
var listener1 = discoveryRequest.getDiscoveryListener();
719-
listener1.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.ERROR, "error"));
720-
listener1.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.WARNING, "warning"));
718+
var listener = discoveryRequest.getDiscoveryListener();
719+
listener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.ERROR, "error"));
720+
listener.issueEncountered(uniqueId, DiscoveryIssue.create(Severity.WARNING, "warning"));
721721
return new EngineDescriptor(uniqueId, "Engine") {
722722
@Override
723723
public Set<TestTag> getTags() {
@@ -727,6 +727,8 @@ public Set<TestTag> getTags() {
727727
}
728728
});
729729

730+
assertThat(result.testPlan().containsTests()).isTrue();
731+
730732
assertThat(result.testIdentifier().getDisplayName()).isEqualTo("Engine");
731733
assertThat(result.testIdentifier().getTags()).containsExactly(TestTag.create("custom-tag"));
732734

@@ -788,6 +790,8 @@ public TestDescriptor discover(EngineDiscoveryRequest discoveryRequest, UniqueId
788790
}
789791
});
790792

793+
assertThat(result.testPlan().containsTests()).isTrue();
794+
791795
assertThat(result.testIdentifier().getDisplayName()).isEqualTo("engine-id");
792796
assertThat(result.testExecutionResult().getStatus()).isEqualTo(Status.FAILED);
793797
assertThat(result.testExecutionResult().getThrowable().orElseThrow()) //
@@ -860,7 +864,8 @@ private static ReportedData execute(TestEngine engine) {
860864
.build();
861865
var launcher = createLauncher(engine);
862866

863-
launcher.execute(request, executionListener);
867+
var testPlan = launcher.discover(request);
868+
launcher.execute(testPlan, executionListener);
864869

865870
var inOrder = inOrder(executionListener);
866871
var testIdentifier = ArgumentCaptor.forClass(TestIdentifier.class);
@@ -871,7 +876,7 @@ private static ReportedData execute(TestEngine engine) {
871876
inOrder.verify(executionListener).testPlanExecutionFinished(any());
872877
inOrder.verifyNoMoreInteractions();
873878

874-
return new ReportedData(testIdentifier.getValue(), testExecutionResult.getValue(),
879+
return new ReportedData(testPlan, testIdentifier.getValue(), testExecutionResult.getValue(),
875880
requireNonNull(startTime.get()), requireNonNull(finishTime.get()));
876881
}
877882

@@ -881,8 +886,8 @@ private static LogRecord findFirstDiscoveryIssueLogRecord(LogRecordListener list
881886
.orElseThrow();
882887
}
883888

884-
private record ReportedData(TestIdentifier testIdentifier, TestExecutionResult testExecutionResult,
885-
Instant startTime, Instant finishTime) {
889+
private record ReportedData(TestPlan testPlan, TestIdentifier testIdentifier,
890+
TestExecutionResult testExecutionResult, Instant startTime, Instant finishTime) {
886891
}
887892

888893
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Copyright 2015-2025 the original author or authors.
3+
*
4+
* All rights reserved. This program and the accompanying materials are
5+
* made available under the terms of the Eclipse Public License v2.0 which
6+
* accompanies this distribution and is available at
7+
*
8+
* https://www.eclipse.org/legal/epl-v20.html
9+
*/
10+
11+
package org.junit.platform.launcher.core;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
import static org.junit.platform.launcher.core.OutputDirectoryProviders.dummyOutputDirectoryProvider;
15+
import static org.mockito.Mockito.mock;
16+
17+
import java.util.List;
18+
import java.util.Map;
19+
20+
import org.junit.jupiter.api.Test;
21+
import org.junit.platform.engine.ConfigurationParameters;
22+
import org.junit.platform.engine.DiscoveryIssue;
23+
import org.junit.platform.engine.DiscoveryIssue.Severity;
24+
import org.junit.platform.engine.TestEngine;
25+
import org.junit.platform.engine.UniqueId;
26+
import org.junit.platform.engine.support.descriptor.AbstractTestDescriptor;
27+
import org.junit.platform.engine.support.descriptor.EngineDescriptor;
28+
import org.junit.platform.launcher.core.LauncherDiscoveryResult.EngineResultInfo;
29+
30+
public class InternalTestPlanTests {
31+
32+
private final ConfigurationParameters configParams = mock();
33+
34+
private final EngineDescriptor engineDescriptor = new EngineDescriptor(UniqueId.forEngine("foo"), "Foo");
35+
36+
@Test
37+
void doesNotContainTestsForEmptyContainers() {
38+
engineDescriptor.addChild(
39+
new AbstractTestDescriptor(engineDescriptor.getUniqueId().append("test", "bar"), "Bar") {
40+
@Override
41+
public Type getType() {
42+
return Type.CONTAINER;
43+
}
44+
});
45+
46+
var testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(
47+
EngineResultInfo.completed(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES)));
48+
49+
assertThat(testPlan.containsTests()).as("contains tests").isFalse();
50+
}
51+
52+
@Test
53+
void containsTestsForTests() {
54+
engineDescriptor.addChild(
55+
new AbstractTestDescriptor(engineDescriptor.getUniqueId().append("test", "bar"), "Bar") {
56+
@Override
57+
public Type getType() {
58+
return Type.TEST;
59+
}
60+
});
61+
62+
var testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(
63+
EngineResultInfo.completed(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES)));
64+
65+
assertThat(testPlan.containsTests()).as("contains tests").isTrue();
66+
}
67+
68+
@Test
69+
void containsTestsForContainersThatMayRegisterTests() {
70+
engineDescriptor.addChild(
71+
new AbstractTestDescriptor(engineDescriptor.getUniqueId().append("test", "bar"), "Bar") {
72+
@Override
73+
public Type getType() {
74+
return Type.CONTAINER;
75+
}
76+
77+
@Override
78+
public boolean mayRegisterTests() {
79+
return true;
80+
}
81+
});
82+
83+
var testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(
84+
EngineResultInfo.completed(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES)));
85+
86+
assertThat(testPlan.containsTests()).as("contains tests").isTrue();
87+
}
88+
89+
@Test
90+
void containsTestsForEnginesWithDiscoveryError() {
91+
var testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(
92+
EngineResultInfo.errored(engineDescriptor, DiscoveryIssueNotifier.NO_ISSUES, new RuntimeException())));
93+
94+
assertThat(testPlan.containsTests()).as("contains tests").isTrue();
95+
}
96+
97+
@Test
98+
void containsTestsForEnginesWithCriticalDiscoveryIssues() {
99+
var testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(EngineResultInfo.completed(engineDescriptor,
100+
DiscoveryIssueNotifier.from(Severity.ERROR, List.of(DiscoveryIssue.create(Severity.ERROR, "error"))))));
101+
102+
assertThat(testPlan.containsTests()).as("contains tests").isTrue();
103+
}
104+
105+
@Test
106+
void doesNotContainTestsForEnginesWithNonCriticalDiscoveryIssues() {
107+
var testPlan = InternalTestPlan.from(createLauncherDiscoveryResult(EngineResultInfo.completed(engineDescriptor,
108+
DiscoveryIssueNotifier.from(Severity.ERROR, List.of(DiscoveryIssue.create(Severity.WARNING, "warning"))))));
109+
110+
assertThat(testPlan.containsTests()).as("contains tests").isFalse();
111+
}
112+
113+
private LauncherDiscoveryResult createLauncherDiscoveryResult(EngineResultInfo result) {
114+
var testEngineResults = Map.of(mock(TestEngine.class), result);
115+
return new LauncherDiscoveryResult(testEngineResults, configParams, dummyOutputDirectoryProvider());
116+
}
117+
118+
}

platform-tests/src/test/java/org/junit/platform/launcher/listeners/SummaryGenerationTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
class SummaryGenerationTests {
4141

4242
private final SummaryGeneratingListener listener = new SummaryGeneratingListener();
43-
private final TestPlan testPlan = TestPlan.from(List.of(), mock(), dummyOutputDirectoryProvider());
43+
private final TestPlan testPlan = TestPlan.from(true, List.of(), mock(), dummyOutputDirectoryProvider());
4444

4545
@Test
4646
void emptyReport() {

0 commit comments

Comments
 (0)