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.junit.platform.commons.util.StringUtils.isBlank;
31  import static org.junit.platform.engine.discovery.DiscoverySelectors.selectClass;
32  import static org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder.request;
33  
34  import java.io.IOException;
35  import java.io.StringReader;
36  import java.io.UncheckedIOException;
37  import java.util.ArrayList;
38  import java.util.HashMap;
39  import java.util.List;
40  import java.util.Map;
41  import java.util.Optional;
42  import java.util.Properties;
43  import java.util.logging.Logger;
44  
45  import org.apache.maven.surefire.providerapi.AbstractProvider;
46  import org.apache.maven.surefire.providerapi.ProviderParameters;
47  import org.apache.maven.surefire.report.ConsoleOutputCapture;
48  import org.apache.maven.surefire.report.ConsoleOutputReceiver;
49  import org.apache.maven.surefire.report.ReporterException;
50  import org.apache.maven.surefire.report.ReporterFactory;
51  import org.apache.maven.surefire.report.RunListener;
52  import org.apache.maven.surefire.suite.RunResult;
53  import org.apache.maven.surefire.testset.TestListResolver;
54  import org.apache.maven.surefire.testset.TestSetFailedException;
55  import org.apache.maven.surefire.util.ScanResult;
56  import org.apache.maven.surefire.util.TestsToRun;
57  import org.junit.platform.commons.util.StringUtils;
58  import org.junit.platform.engine.Filter;
59  import org.junit.platform.launcher.Launcher;
60  import org.junit.platform.launcher.LauncherDiscoveryRequest;
61  import org.junit.platform.launcher.TagFilter;
62  import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
63  import org.junit.platform.launcher.core.LauncherFactory;
64  
65  /**
66   * JUnit 5 Platform Provider.
67   *
68   * @since 2.22.0
69   */
70  public class JUnitPlatformProvider
71      extends AbstractProvider
72  {
73      static final String CONFIGURATION_PARAMETERS = "configurationParameters";
74  
75      private final ProviderParameters parameters;
76  
77      private final Launcher launcher;
78  
79      private final Filter<?>[] filters;
80  
81      private final Map<String, String> configurationParameters;
82  
83      public JUnitPlatformProvider( ProviderParameters parameters )
84      {
85          this( parameters, LauncherFactory.create() );
86      }
87  
88      JUnitPlatformProvider( ProviderParameters parameters, Launcher launcher )
89      {
90          this.parameters = parameters;
91          this.launcher = launcher;
92          filters = newFilters();
93          configurationParameters = newConfigurationParameters();
94          Logger.getLogger( "org.junit" ).setLevel( WARNING );
95      }
96  
97      @Override
98      public Iterable<Class<?>> getSuites()
99      {
100         return scanClasspath();
101     }
102 
103     @Override
104     public RunResult invoke( Object forkTestSet )
105                     throws TestSetFailedException, ReporterException
106     {
107         if ( forkTestSet instanceof TestsToRun )
108         {
109             return invokeAllTests( (TestsToRun) forkTestSet );
110         }
111         else if ( forkTestSet instanceof Class )
112         {
113             return invokeAllTests( TestsToRun.fromClass( (Class<?>) forkTestSet ) );
114         }
115         else if ( forkTestSet == null )
116         {
117             return invokeAllTests( scanClasspath() );
118         }
119         else
120         {
121             throw new IllegalArgumentException( "Unexpected value of forkTestSet: " + forkTestSet );
122         }
123     }
124 
125     private TestsToRun scanClasspath()
126     {
127         TestPlanScannerFilter filter = new TestPlanScannerFilter( launcher, filters );
128         ScanResult scanResult = parameters.getScanResult();
129         TestsToRun scannedClasses = scanResult.applyFilter( filter, parameters.getTestClassLoader() );
130         return parameters.getRunOrderCalculator().orderTestClasses( scannedClasses );
131     }
132 
133     private RunResult invokeAllTests( TestsToRun testsToRun )
134     {
135         RunResult runResult;
136         ReporterFactory reporterFactory = parameters.getReporterFactory();
137         try
138         {
139             RunListener runListener = reporterFactory.createReporter();
140             ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) runListener );
141             LauncherDiscoveryRequest discoveryRequest = buildLauncherDiscoveryRequest( testsToRun );
142             launcher.execute( discoveryRequest, new RunListenerAdapter( runListener ) );
143         }
144         finally
145         {
146             runResult = reporterFactory.close();
147         }
148         return runResult;
149     }
150 
151     private LauncherDiscoveryRequest buildLauncherDiscoveryRequest( TestsToRun testsToRun )
152     {
153         LauncherDiscoveryRequestBuilder builder =
154                         request().filters( filters ).configurationParameters( configurationParameters );
155         for ( Class<?> testClass : testsToRun )
156         {
157             builder.selectors( selectClass( testClass ) );
158         }
159         return builder.build();
160     }
161 
162     private Filter<?>[] newFilters()
163     {
164         List<Filter<?>> filters = new ArrayList<>();
165 
166         getPropertiesList( TESTNG_GROUPS_PROP )
167                 .map( TagFilter::includeTags )
168                 .ifPresent( filters::add );
169 
170         getPropertiesList( TESTNG_EXCLUDEDGROUPS_PROP )
171                 .map( TagFilter::excludeTags )
172                 .ifPresent( filters::add );
173 
174         TestListResolver testListResolver = parameters.getTestRequest().getTestListResolver();
175         if ( !testListResolver.isEmpty() )
176         {
177             filters.add( new TestMethodFilter( testListResolver ) );
178         }
179 
180         return filters.toArray( new Filter<?>[ filters.size() ] );
181     }
182 
183     Filter<?>[] getFilters()
184     {
185         return filters;
186     }
187 
188     private Map<String, String> newConfigurationParameters()
189     {
190         String content = parameters.getProviderProperties().get( CONFIGURATION_PARAMETERS );
191         if ( content == null )
192         {
193             return emptyMap();
194         }
195         try ( StringReader reader = new StringReader( content ) )
196         {
197             Map<String, String> result = new HashMap<>();
198             Properties props = new Properties();
199             props.load( reader );
200             props.stringPropertyNames()
201                     .forEach( key -> result.put( key, props.getProperty( key ) ) );
202             return result;
203         }
204         catch ( IOException e )
205         {
206             throw new UncheckedIOException( "Error reading " + CONFIGURATION_PARAMETERS, e );
207         }
208     }
209 
210     Map<String, String> getConfigurationParameters()
211     {
212         return configurationParameters;
213     }
214 
215     private Optional<List<String>> getPropertiesList( String key )
216     {
217         String property = parameters.getProviderProperties().get( key );
218         return isBlank( property ) ? empty()
219                         : of( stream( property.split( "[,]+" ) )
220                                               .filter( StringUtils::isNotBlank )
221                                               .map( String::trim )
222                                               .collect( toList() ) );
223     }
224 }