View Javadoc
1   package org.apache.maven.surefire.booter;
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 org.apache.maven.surefire.api.booter.BaseProviderFactory;
23  import org.apache.maven.surefire.api.cli.CommandLineOption;
24  import org.apache.maven.surefire.api.provider.ProviderParameters;
25  import org.apache.maven.surefire.api.report.ReporterConfiguration;
26  import org.apache.maven.surefire.api.report.ReporterFactory;
27  import org.apache.maven.surefire.api.suite.RunResult;
28  import org.apache.maven.surefire.api.testset.DirectoryScannerParameters;
29  import org.apache.maven.surefire.api.testset.RunOrderParameters;
30  import org.apache.maven.surefire.api.testset.TestArtifactInfo;
31  import org.apache.maven.surefire.api.testset.TestListResolver;
32  import org.apache.maven.surefire.api.testset.TestRequest;
33  import org.apache.maven.surefire.api.util.RunOrder;
34  import org.apache.maven.surefire.api.util.SurefireReflectionException;
35  
36  import java.io.File;
37  import java.lang.reflect.Constructor;
38  import java.lang.reflect.Method;
39  import java.util.ArrayList;
40  import java.util.Collection;
41  import java.util.List;
42  import java.util.Map;
43  
44  import static java.util.Collections.checkedList;
45  import static org.apache.maven.surefire.api.util.ReflectionUtils.getConstructor;
46  import static org.apache.maven.surefire.api.util.ReflectionUtils.getMethod;
47  import static org.apache.maven.surefire.api.util.ReflectionUtils.instantiateOneArg;
48  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeGetter;
49  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray;
50  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeSetter;
51  import static org.apache.maven.surefire.api.util.ReflectionUtils.newInstance;
52  
53  /**
54   * Does reflection based invocation of the surefire methods.
55   * <br>
56   * This is to avoid complications with linkage issues
57   *
58   * @author Kristian Rosenvold
59   */
60  public final class SurefireReflector
61  {
62      private final ClassLoader surefireClassLoader;
63  
64      private final Class<?> reporterConfiguration;
65  
66      private final Class<?> testRequest;
67  
68      private final Class<?> testArtifactInfo;
69  
70      private final Class<?> directoryScannerParameters;
71  
72      private final Class<?> runOrderParameters;
73  
74      private final Class<?> baseProviderFactory;
75  
76      private final Class<?> runResult;
77  
78      private final Class<?> booterParameters;
79  
80      private final Class<?> reporterFactory;
81  
82      private final Class<?> testListResolver;
83  
84      private final Class<Enum<?>> commandLineOptionsClass;
85  
86      @SuppressWarnings( "unchecked" )
87      public SurefireReflector( ClassLoader surefireClassLoader )
88      {
89          this.surefireClassLoader = surefireClassLoader;
90          try
91          {
92              reporterConfiguration = surefireClassLoader.loadClass( ReporterConfiguration.class.getName() );
93              testRequest = surefireClassLoader.loadClass( TestRequest.class.getName() );
94              testArtifactInfo = surefireClassLoader.loadClass( TestArtifactInfo.class.getName() );
95              directoryScannerParameters = surefireClassLoader.loadClass( DirectoryScannerParameters.class.getName() );
96              runOrderParameters = surefireClassLoader.loadClass( RunOrderParameters.class.getName() );
97              baseProviderFactory = surefireClassLoader.loadClass( BaseProviderFactory.class.getName() );
98              reporterFactory = surefireClassLoader.loadClass( ReporterFactory.class.getName() );
99              runResult = surefireClassLoader.loadClass( RunResult.class.getName() );
100             booterParameters = surefireClassLoader.loadClass( ProviderParameters.class.getName() );
101             testListResolver = surefireClassLoader.loadClass( TestListResolver.class.getName() );
102             commandLineOptionsClass =
103                 (Class<Enum<?>>) surefireClassLoader.loadClass( CommandLineOption.class.getName() );
104         }
105         catch ( ClassNotFoundException e )
106         {
107             throw new SurefireReflectionException( e );
108         }
109     }
110 
111     public Object convertIfRunResult( Object result )
112     {
113         if ( result == null || !isRunResult( result ) )
114         {
115             return result;
116         }
117         int getCompletedCount1 = (Integer) invokeGetter( result, "getCompletedCount" );
118         int getErrors = (Integer) invokeGetter( result, "getErrors" );
119         int getSkipped = (Integer) invokeGetter( result, "getSkipped" );
120         int getFailures = (Integer) invokeGetter( result, "getFailures" );
121         return new RunResult( getCompletedCount1, getErrors, getFailures, getSkipped );
122     }
123 
124     private Object createTestRequest( TestRequest suiteDefinition )
125     {
126         if ( suiteDefinition == null )
127         {
128             return null;
129         }
130         else
131         {
132             Object resolver = createTestListResolver( suiteDefinition.getTestListResolver() );
133             Class<?>[] arguments = { List.class, File.class, testListResolver, int.class };
134             Constructor<?> constructor = getConstructor( testRequest, arguments );
135             return newInstance( constructor,
136                                 suiteDefinition.getSuiteXmlFiles(),
137                                 suiteDefinition.getTestSourceDirectory(),
138                                 resolver,
139                                 suiteDefinition.getRerunFailingTestsCount() );
140         }
141     }
142 
143     private Object createTestListResolver( TestListResolver resolver )
144     {
145         if ( resolver == null )
146         {
147             return null;
148         }
149         else
150         {
151             Constructor<?> constructor = getConstructor( testListResolver, String.class );
152             return newInstance( constructor, resolver.getPluginParameterTest() );
153         }
154     }
155 
156     private Object createDirectoryScannerParameters( DirectoryScannerParameters directoryScannerParameters )
157     {
158         if ( directoryScannerParameters == null )
159         {
160             return null;
161         }
162         //Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
163         Class<?>[] arguments = { File.class, List.class, List.class, List.class, boolean.class, String.class };
164         Constructor<?> constructor = getConstructor( this.directoryScannerParameters, arguments );
165         return newInstance( constructor,
166                             directoryScannerParameters.getTestClassesDirectory(),
167                             directoryScannerParameters.getIncludes(),
168                             directoryScannerParameters.getExcludes(),
169                             directoryScannerParameters.getSpecificTests(),
170                             directoryScannerParameters.isFailIfNoTests(),
171                             RunOrder.asString( directoryScannerParameters.getRunOrder() ) );
172     }
173 
174     private Object createRunOrderParameters( RunOrderParameters runOrderParameters )
175     {
176         if ( runOrderParameters == null )
177         {
178             return null;
179         }
180         //Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
181         Class<?>[] arguments = { String.class, File.class };
182         Constructor<?> constructor = getConstructor( this.runOrderParameters, arguments );
183         File runStatisticsFile = runOrderParameters.getRunStatisticsFile();
184         return newInstance( constructor, RunOrder.asString( runOrderParameters.getRunOrder() ), runStatisticsFile );
185     }
186 
187     private Object createTestArtifactInfo( TestArtifactInfo testArtifactInfo )
188     {
189         if ( testArtifactInfo == null )
190         {
191             return null;
192         }
193         Class<?>[] arguments = { String.class, String.class };
194         Constructor<?> constructor = getConstructor( this.testArtifactInfo, arguments );
195         return newInstance( constructor, testArtifactInfo.getVersion(), testArtifactInfo.getClassifier() );
196     }
197 
198     private Object createReporterConfiguration( ReporterConfiguration reporterConfig )
199     {
200         Constructor<?> constructor = getConstructor( reporterConfiguration, File.class, boolean.class );
201         return newInstance( constructor, reporterConfig.getReportsDirectory(), reporterConfig.isTrimStackTrace() );
202     }
203 
204     public Object createBooterConfiguration( ClassLoader surefireClassLoader, boolean insideFork )
205     {
206         return instantiateOneArg( surefireClassLoader, BaseProviderFactory.class.getName(), boolean.class, insideFork );
207     }
208 
209     public Object instantiateProvider( String providerClassName, Object booterParameters )
210     {
211         return instantiateOneArg( surefireClassLoader, providerClassName, this.booterParameters, booterParameters );
212     }
213 
214     public void setIfDirScannerAware( Object o, DirectoryScannerParameters dirScannerParams )
215     {
216         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
217         {
218             setDirectoryScannerParameters( o, dirScannerParams );
219         }
220     }
221 
222     public void setMainCliOptions( Object o, List<CommandLineOption> options )
223     {
224         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
225         {
226             List<Enum<?>> newOptions = checkedList( new ArrayList<Enum<?>>( options.size() ), commandLineOptionsClass );
227             Collection<Integer> ordinals = toOrdinals( options );
228             for ( Enum<?> e : commandLineOptionsClass.getEnumConstants() )
229             {
230                 if ( ordinals.contains( e.ordinal() ) )
231                 {
232                     newOptions.add( e );
233                 }
234             }
235             invokeSetter( o, "setMainCliOptions", List.class, newOptions );
236         }
237     }
238 
239     public void setSkipAfterFailureCount( Object o, int skipAfterFailureCount )
240     {
241         invokeSetter( o, "setSkipAfterFailureCount", int.class, skipAfterFailureCount );
242     }
243 
244     public void setSystemExitTimeout( Object o, Integer systemExitTimeout )
245     {
246         invokeSetter( o, "setSystemExitTimeout", Integer.class, systemExitTimeout );
247     }
248 
249     void setDirectoryScannerParameters( Object o, DirectoryScannerParameters dirScannerParams )
250     {
251         Object param = createDirectoryScannerParameters( dirScannerParams );
252         invokeSetter( o, "setDirectoryScannerParameters", directoryScannerParameters, param );
253     }
254 
255     public void setRunOrderParameters( Object o, RunOrderParameters runOrderParameters )
256     {
257         Object param = createRunOrderParameters( runOrderParameters );
258         invokeSetter( o, "setRunOrderParameters", this.runOrderParameters, param );
259     }
260 
261     public void setTestSuiteDefinitionAware( Object o, TestRequest testSuiteDefinition2 )
262     {
263         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
264         {
265             setTestSuiteDefinition( o, testSuiteDefinition2 );
266         }
267     }
268 
269     void setTestSuiteDefinition( Object o, TestRequest testSuiteDefinition1 )
270     {
271         Object param = createTestRequest( testSuiteDefinition1 );
272         invokeSetter( o, "setTestRequest", testRequest, param );
273     }
274 
275     public void setProviderPropertiesAware( Object o, Map<String, String> properties )
276     {
277         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
278         {
279             setProviderProperties( o, properties );
280         }
281     }
282 
283     void setProviderProperties( Object o, Map<String, String> providerProperties )
284     {
285         invokeSetter( o, "setProviderProperties", Map.class, providerProperties );
286     }
287 
288     public void setReporterConfigurationAware( Object o, ReporterConfiguration reporterConfiguration1 )
289     {
290         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
291         {
292             setReporterConfiguration( o, reporterConfiguration1 );
293         }
294     }
295 
296     private void setReporterConfiguration( Object o, ReporterConfiguration reporterConfiguration )
297     {
298         Object param = createReporterConfiguration( reporterConfiguration );
299         invokeSetter( o, "setReporterConfiguration", this.reporterConfiguration, param );
300     }
301 
302     public void setTestClassLoaderAware( Object o, ClassLoader testClassLoader )
303     {
304         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
305         {
306             setTestClassLoader( o, testClassLoader );
307         }
308     }
309 
310     void setTestClassLoader( Object o, ClassLoader testClassLoader )
311     {
312         Method setter = getMethod( o, "setClassLoaders", ClassLoader.class );
313         invokeMethodWithArray( o, setter, testClassLoader );
314     }
315 
316     public void setTestArtifactInfoAware( Object o, TestArtifactInfo testArtifactInfo1 )
317     {
318         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
319         {
320             setTestArtifactInfo( o, testArtifactInfo1 );
321         }
322     }
323 
324     void setTestArtifactInfo( Object o, TestArtifactInfo testArtifactInfo )
325     {
326         Object param = createTestArtifactInfo( testArtifactInfo );
327         invokeSetter( o, "setTestArtifactInfo", this.testArtifactInfo, param );
328     }
329 
330     public void setReporterFactoryAware( Object o, Object reporterFactory )
331     {
332         if ( baseProviderFactory.isAssignableFrom( o.getClass() ) )
333         {
334             setReporterFactory( o, reporterFactory );
335         }
336     }
337 
338     void setReporterFactory( Object o, Object reporterFactory )
339     {
340         invokeSetter( o, "setReporterFactory", this.reporterFactory, reporterFactory );
341     }
342 
343     private boolean isRunResult( Object o )
344     {
345         return runResult.isAssignableFrom( o.getClass() );
346     }
347 
348     private static Collection<Integer> toOrdinals( Collection<? extends Enum<?>> enums )
349     {
350         Collection<Integer> ordinals = new ArrayList<>( enums.size() );
351         for ( Enum<?> e : enums )
352         {
353             ordinals.add( e.ordinal() );
354         }
355         return ordinals;
356     }
357 }