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