View Javadoc
1   package org.apache.maven.surefire.junitplatform;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import static java.util.Arrays.stream;
23  import static java.util.Collections.emptyMap;
24  import static java.util.Optional.empty;
25  import static java.util.Optional.of;
26  import static java.util.logging.Level.WARNING;
27  import static java.util.stream.Collectors.toList;
28  import static org.apache.maven.surefire.booter.ProviderParameterNames.TESTNG_EXCLUDEDGROUPS_PROP;
29  import static org.apache.maven.surefire.booter.ProviderParameterNames.TESTNG_GROUPS_PROP;
30  import static org.apache.maven.surefire.report.ConsoleOutputCapture.startCapture;
31  import static org.apache.maven.surefire.util.TestsToRun.fromClass;
32  import static org.junit.platform.commons.util.StringUtils.isBlank;
33  import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
34  import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
35  
36  import java.io.IOException;
37  import java.io.StringReader;
38  import java.io.UncheckedIOException;
39  import java.util.ArrayList;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  import java.util.Optional;
44  import java.util.Properties;
45  import java.util.logging.Logger;
46  
47  import org.apache.maven.surefire.providerapi.AbstractProvider;
48  import org.apache.maven.surefire.providerapi.ProviderParameters;
49  import org.apache.maven.surefire.report.ConsoleOutputReceiver;
50  import org.apache.maven.surefire.report.ReporterException;
51  import org.apache.maven.surefire.report.ReporterFactory;
52  import org.apache.maven.surefire.report.RunListener;
53  import org.apache.maven.surefire.suite.RunResult;
54  import org.apache.maven.surefire.testset.TestListResolver;
55  import org.apache.maven.surefire.testset.TestSetFailedException;
56  import org.apache.maven.surefire.util.ScanResult;
57  import org.apache.maven.surefire.util.TestsToRun;
58  import org.junit.platform.commons.util.StringUtils;
59  import org.junit.platform.engine.Filter;
60  import org.junit.platform.launcher.Launcher;
61  import org.junit.platform.launcher.LauncherDiscoveryRequest;
62  import org.junit.platform.launcher.TagFilter;
63  import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
64  import org.junit.platform.launcher.core.LauncherFactory;
65  
66  /**
67   * JUnit 5 Platform Provider.
68   *
69   * @since 2.22.0
70   */
71  public class JUnitPlatformProvider
72      extends AbstractProvider
73  {
74      static final String CONFIGURATION_PARAMETERS = "configurationParameters";
75  
76      private final ProviderParameters parameters;
77  
78      private final Launcher launcher;
79  
80      private final Filter<?>[] filters;
81  
82      private final Map<String, String> configurationParameters;
83  
84      public JUnitPlatformProvider( ProviderParameters parameters )
85      {
86          this( parameters, LauncherFactory.create() );
87      }
88  
89      JUnitPlatformProvider( ProviderParameters parameters, Launcher launcher )
90      {
91          this.parameters = parameters;
92          this.launcher = launcher;
93          filters = newFilters();
94          configurationParameters = newConfigurationParameters();
95          Logger.getLogger( "org.junit" ).setLevel( WARNING );
96      }
97  
98      @Override
99      public Iterable<Class<?>> getSuites()
100     {
101         return scanClasspath();
102     }
103 
104     @Override
105     public RunResult invoke( Object forkTestSet )
106                     throws TestSetFailedException, ReporterException
107     {
108         ReporterFactory reporterFactory = parameters.getReporterFactory();
109         final RunResult runResult;
110         try
111         {
112             RunListener runListener = reporterFactory.createReporter();
113             startCapture( ( ConsoleOutputReceiver ) runListener );
114             if ( forkTestSet instanceof TestsToRun )
115             {
116                 invokeAllTests( (TestsToRun) forkTestSet, runListener );
117             }
118             else if ( forkTestSet instanceof Class )
119             {
120                 invokeAllTests( fromClass( ( Class<?> ) forkTestSet ), runListener );
121             }
122             else if ( forkTestSet == null )
123             {
124                 invokeAllTests( scanClasspath(), runListener );
125             }
126             else
127             {
128                 throw new IllegalArgumentException(
129                         "Unexpected value of forkTestSet: " + forkTestSet );
130             }
131         }
132         finally
133         {
134             runResult = reporterFactory.close();
135         }
136         return runResult;
137     }
138 
139     private TestsToRun scanClasspath()
140     {
141         TestPlanScannerFilter filter = new TestPlanScannerFilter( launcher, filters );
142         ScanResult scanResult = parameters.getScanResult();
143         TestsToRun scannedClasses = scanResult.applyFilter( filter, parameters.getTestClassLoader() );
144         return parameters.getRunOrderCalculator().orderTestClasses( scannedClasses );
145     }
146 
147     private void invokeAllTests( TestsToRun testsToRun, RunListener runListener )
148     {
149         LauncherDiscoveryRequest discoveryRequest = buildLauncherDiscoveryRequest( testsToRun );
150         launcher.execute( discoveryRequest, new RunListenerAdapter( runListener ) );
151     }
152 
153     private LauncherDiscoveryRequest buildLauncherDiscoveryRequest( TestsToRun testsToRun )
154     {
155         LauncherDiscoveryRequestBuilder builder =
156                         request().filters( filters ).configurationParameters( configurationParameters );
157         for ( Class<?> testClass : testsToRun )
158         {
159             builder.selectors( selectClass( testClass ) );
160         }
161         return builder.build();
162     }
163 
164     private Filter<?>[] newFilters()
165     {
166         List<Filter<?>> filters = new ArrayList<>();
167 
168         getPropertiesList( TESTNG_GROUPS_PROP )
169                 .map( TagFilter::includeTags )
170                 .ifPresent( filters::add );
171 
172         getPropertiesList( TESTNG_EXCLUDEDGROUPS_PROP )
173                 .map( TagFilter::excludeTags )
174                 .ifPresent( filters::add );
175 
176         TestListResolver testListResolver = parameters.getTestRequest().getTestListResolver();
177         if ( !testListResolver.isEmpty() )
178         {
179             filters.add( new TestMethodFilter( testListResolver ) );
180         }
181 
182         return filters.toArray( new Filter<?>[ filters.size() ] );
183     }
184 
185     Filter<?>[] getFilters()
186     {
187         return filters;
188     }
189 
190     private Map<String, String> newConfigurationParameters()
191     {
192         String content = parameters.getProviderProperties().get( CONFIGURATION_PARAMETERS );
193         if ( content == null )
194         {
195             return emptyMap();
196         }
197         try ( StringReader reader = new StringReader( content ) )
198         {
199             Map<String, String> result = new HashMap<>();
200             Properties props = new Properties();
201             props.load( reader );
202             props.stringPropertyNames()
203                     .forEach( key -> result.put( key, props.getProperty( key ) ) );
204             return result;
205         }
206         catch ( IOException e )
207         {
208             throw new UncheckedIOException( "Error reading " + CONFIGURATION_PARAMETERS, e );
209         }
210     }
211 
212     Map<String, String> getConfigurationParameters()
213     {
214         return configurationParameters;
215     }
216 
217     private Optional<List<String>> getPropertiesList( String key )
218     {
219         String property = parameters.getProviderProperties().get( key );
220         return isBlank( property ) ? empty()
221                         : of( stream( property.split( "[,]+" ) )
222                                               .filter( StringUtils::isNotBlank )
223                                               .map( String::trim )
224                                               .collect( toList() ) );
225     }
226 }