1010
1111package org .junit .platform .launcher .core ;
1212
13- import java .util .HashSet ;
14- import java .util .Optional ;
15- import java .util .Set ;
16- import java .util .function .Consumer ;
17-
18- import org .junit .platform .commons .JUnitException ;
19- import org .junit .platform .commons .logging .Logger ;
20- import org .junit .platform .commons .logging .LoggerFactory ;
21- import org .junit .platform .commons .util .BlacklistedExceptions ;
2213import org .junit .platform .commons .util .Preconditions ;
23- import org .junit .platform .engine .ConfigurationParameters ;
24- import org .junit .platform .engine .EngineExecutionListener ;
25- import org .junit .platform .engine .ExecutionRequest ;
26- import org .junit .platform .engine .FilterResult ;
27- import org .junit .platform .engine .TestDescriptor ;
2814import org .junit .platform .engine .TestEngine ;
29- import org .junit .platform .engine .TestExecutionResult ;
30- import org .junit .platform .engine .UniqueId ;
31- import org .junit .platform .launcher .EngineDiscoveryResult ;
3215import org .junit .platform .launcher .Launcher ;
33- import org .junit .platform .launcher .LauncherDiscoveryListener ;
3416import org .junit .platform .launcher .LauncherDiscoveryRequest ;
3517import org .junit .platform .launcher .TestExecutionListener ;
3618import org .junit .platform .launcher .TestPlan ;
4729 */
4830class DefaultLauncher implements Launcher {
4931
50- private static final Logger logger = LoggerFactory .getLogger (DefaultLauncher .class );
51-
5232 private final TestExecutionListenerRegistry listenerRegistry = new TestExecutionListenerRegistry ();
53- private final EngineDiscoveryResultValidator discoveryResultValidator = new EngineDiscoveryResultValidator ( );
54- private final Iterable < TestEngine > testEngines ;
33+ private final EngineExecutionOrchestrator executionOrchestrator = new EngineExecutionOrchestrator ( listenerRegistry );
34+ private final EngineDiscoveryOrchestrator discoveryOrchestrator ;
5535
5636 /**
5737 * Construct a new {@code DefaultLauncher} with the supplied test engines.
@@ -62,53 +42,7 @@ class DefaultLauncher implements Launcher {
6242 Preconditions .condition (testEngines != null && testEngines .iterator ().hasNext (),
6343 () -> "Cannot create Launcher without at least one TestEngine; "
6444 + "consider adding an engine implementation JAR to the classpath" );
65- this .testEngines = validateEngineIds (testEngines );
66- }
67-
68- private static Iterable <TestEngine > validateEngineIds (Iterable <TestEngine > testEngines ) {
69- Set <String > ids = new HashSet <>();
70- for (TestEngine testEngine : testEngines ) {
71- // check usage of reserved id prefix
72- if (!validateReservedIds (testEngine )) {
73- logger .warn (() -> String .format (
74- "Third-party TestEngine implementations are forbidden to use the reserved 'junit-' prefix for their ID: '%s'" ,
75- testEngine .getId ()));
76- }
77-
78- // check uniqueness
79- if (!ids .add (testEngine .getId ())) {
80- throw new JUnitException (String .format (
81- "Cannot create Launcher for multiple engines with the same ID '%s'." , testEngine .getId ()));
82- }
83- }
84- return testEngines ;
85- }
86-
87- // https://github.com/junit-team/junit5/issues/1557
88- private static boolean validateReservedIds (TestEngine testEngine ) {
89- String engineId = testEngine .getId ();
90- if (!engineId .startsWith ("junit-" )) {
91- return true ;
92- }
93- if (engineId .equals ("junit-jupiter" )) {
94- validateWellKnownClassName (testEngine , "org.junit.jupiter.engine.JupiterTestEngine" );
95- return true ;
96- }
97- if (engineId .equals ("junit-vintage" )) {
98- validateWellKnownClassName (testEngine , "org.junit.vintage.engine.VintageTestEngine" );
99- return true ;
100- }
101- return false ;
102- }
103-
104- private static void validateWellKnownClassName (TestEngine testEngine , String expectedClassName ) {
105- String actualClassName = testEngine .getClass ().getName ();
106- if (actualClassName .equals (expectedClassName )) {
107- return ;
108- }
109- throw new JUnitException (
110- String .format ("Third-party TestEngine '%s' is forbidden to use the reserved '%s' TestEngine ID." ,
111- actualClassName , testEngine .getId ()));
45+ this .discoveryOrchestrator = new EngineDiscoveryOrchestrator (EngineIdValidator .validate (testEngines ));
11246 }
11347
11448 @ Override
@@ -121,15 +55,15 @@ public void registerTestExecutionListeners(TestExecutionListener... listeners) {
12155 @ Override
12256 public TestPlan discover (LauncherDiscoveryRequest discoveryRequest ) {
12357 Preconditions .notNull (discoveryRequest , "LauncherDiscoveryRequest must not be null" );
124- return InternalTestPlan .from (discoverRoot (discoveryRequest , "discovery" ));
58+ return InternalTestPlan .from (discover (discoveryRequest , "discovery" ));
12559 }
12660
12761 @ Override
12862 public void execute (LauncherDiscoveryRequest discoveryRequest , TestExecutionListener ... listeners ) {
12963 Preconditions .notNull (discoveryRequest , "LauncherDiscoveryRequest must not be null" );
13064 Preconditions .notNull (listeners , "TestExecutionListener array must not be null" );
13165 Preconditions .containsNoNullElements (listeners , "individual listeners must not be null" );
132- execute (InternalTestPlan .from (discoverRoot (discoveryRequest , "execution" )), listeners );
66+ execute (InternalTestPlan .from (discover (discoveryRequest , "execution" )), listeners );
13367 }
13468
13569 @ Override
@@ -145,114 +79,12 @@ TestExecutionListenerRegistry getTestExecutionListenerRegistry() {
14579 return listenerRegistry ;
14680 }
14781
148- private Root discoverRoot (LauncherDiscoveryRequest discoveryRequest , String phase ) {
149- Root root = new Root (discoveryRequest .getConfigurationParameters ());
150-
151- for (TestEngine testEngine : this .testEngines ) {
152- // @formatter:off
153- boolean engineIsExcluded = discoveryRequest .getEngineFilters ().stream ()
154- .map (engineFilter -> engineFilter .apply (testEngine ))
155- .anyMatch (FilterResult ::excluded );
156- // @formatter:on
157-
158- if (engineIsExcluded ) {
159- logger .debug (() -> String .format (
160- "Test discovery for engine '%s' was skipped due to an EngineFilter in phase '%s'." ,
161- testEngine .getId (), phase ));
162- continue ;
163- }
164-
165- logger .debug (() -> String .format ("Discovering tests during Launcher %s phase in engine '%s'." , phase ,
166- testEngine .getId ()));
167-
168- TestDescriptor rootDescriptor = discoverEngineRoot (testEngine , discoveryRequest );
169- root .add (testEngine , rootDescriptor );
170- }
171- root .applyPostDiscoveryFilters (discoveryRequest );
172- root .prune ();
173- return root ;
174- }
175-
176- private TestDescriptor discoverEngineRoot (TestEngine testEngine , LauncherDiscoveryRequest discoveryRequest ) {
177- LauncherDiscoveryListener discoveryListener = discoveryRequest .getDiscoveryListener ();
178- UniqueId uniqueEngineId = UniqueId .forEngine (testEngine .getId ());
179- try {
180- discoveryListener .engineDiscoveryStarted (uniqueEngineId );
181- TestDescriptor engineRoot = testEngine .discover (discoveryRequest , uniqueEngineId );
182- discoveryResultValidator .validate (testEngine , engineRoot );
183- discoveryListener .engineDiscoveryFinished (uniqueEngineId , EngineDiscoveryResult .successful ());
184- return engineRoot ;
185- }
186- catch (Throwable throwable ) {
187- BlacklistedExceptions .rethrowIfBlacklisted (throwable );
188- String message = String .format ("TestEngine with ID '%s' failed to discover tests" , testEngine .getId ());
189- JUnitException cause = new JUnitException (message , throwable );
190- discoveryListener .engineDiscoveryFinished (uniqueEngineId , EngineDiscoveryResult .failed (cause ));
191- return new EngineDiscoveryErrorDescriptor (uniqueEngineId , testEngine , cause );
192- }
82+ private LauncherDiscoveryResult discover (LauncherDiscoveryRequest discoveryRequest , String phase ) {
83+ return discoveryOrchestrator .discover (discoveryRequest , phase );
19384 }
19485
19586 private void execute (InternalTestPlan internalTestPlan , TestExecutionListener [] listeners ) {
196- Root root = internalTestPlan .getRoot ();
197- ConfigurationParameters configurationParameters = root .getConfigurationParameters ();
198- TestExecutionListenerRegistry listenerRegistry = buildListenerRegistryForExecution (listeners );
199- withInterceptedStreams (configurationParameters , listenerRegistry , testExecutionListener -> {
200- testExecutionListener .testPlanExecutionStarted (internalTestPlan );
201- ExecutionListenerAdapter engineExecutionListener = new ExecutionListenerAdapter (internalTestPlan ,
202- testExecutionListener );
203- for (TestEngine testEngine : root .getTestEngines ()) {
204- TestDescriptor engineDescriptor = root .getTestDescriptorFor (testEngine );
205- if (engineDescriptor instanceof EngineDiscoveryErrorDescriptor ) {
206- engineExecutionListener .executionStarted (engineDescriptor );
207- engineExecutionListener .executionFinished (engineDescriptor ,
208- TestExecutionResult .failed (((EngineDiscoveryErrorDescriptor ) engineDescriptor ).getCause ()));
209- }
210- else {
211- execute (engineDescriptor , engineExecutionListener , configurationParameters , testEngine );
212- }
213- }
214- testExecutionListener .testPlanExecutionFinished (internalTestPlan );
215- });
216- }
217-
218- private void withInterceptedStreams (ConfigurationParameters configurationParameters ,
219- TestExecutionListenerRegistry listenerRegistry , Consumer <TestExecutionListener > action ) {
220-
221- TestExecutionListener testExecutionListener = listenerRegistry .getCompositeTestExecutionListener ();
222- Optional <StreamInterceptingTestExecutionListener > streamInterceptingTestExecutionListener = StreamInterceptingTestExecutionListener .create (
223- configurationParameters , testExecutionListener ::reportingEntryPublished );
224- streamInterceptingTestExecutionListener .ifPresent (listenerRegistry ::registerListeners );
225- try {
226- action .accept (testExecutionListener );
227- }
228- finally {
229- streamInterceptingTestExecutionListener .ifPresent (StreamInterceptingTestExecutionListener ::unregister );
230- }
231- }
232-
233- private TestExecutionListenerRegistry buildListenerRegistryForExecution (TestExecutionListener ... listeners ) {
234- if (listeners .length == 0 ) {
235- return this .listenerRegistry ;
236- }
237- TestExecutionListenerRegistry registry = new TestExecutionListenerRegistry (this .listenerRegistry );
238- registry .registerListeners (listeners );
239- return registry ;
240- }
241-
242- private void execute (TestDescriptor engineDescriptor , EngineExecutionListener listener ,
243- ConfigurationParameters configurationParameters , TestEngine testEngine ) {
244-
245- OutcomeDelayingEngineExecutionListener delayingListener = new OutcomeDelayingEngineExecutionListener (listener ,
246- engineDescriptor );
247- try {
248- testEngine .execute (new ExecutionRequest (engineDescriptor , delayingListener , configurationParameters ));
249- delayingListener .reportEngineOutcome ();
250- }
251- catch (Throwable throwable ) {
252- BlacklistedExceptions .rethrowIfBlacklisted (throwable );
253- delayingListener .reportEngineFailure (new JUnitException (
254- String .format ("TestEngine with ID '%s' failed to execute tests" , testEngine .getId ()), throwable ));
255- }
87+ executionOrchestrator .execute (internalTestPlan , listeners );
25688 }
25789
25890}
0 commit comments