View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.surefire.booter;
20  
21  import java.io.File;
22  import java.lang.reflect.Constructor;
23  import java.lang.reflect.Method;
24  import java.util.ArrayList;
25  import java.util.Collection;
26  import java.util.List;
27  import java.util.Map;
28  
29  import org.apache.maven.surefire.api.booter.BaseProviderFactory;
30  import org.apache.maven.surefire.api.cli.CommandLineOption;
31  import org.apache.maven.surefire.api.provider.ProviderParameters;
32  import org.apache.maven.surefire.api.report.ReporterConfiguration;
33  import org.apache.maven.surefire.api.report.ReporterFactory;
34  import org.apache.maven.surefire.api.suite.RunResult;
35  import org.apache.maven.surefire.api.testset.DirectoryScannerParameters;
36  import org.apache.maven.surefire.api.testset.RunOrderParameters;
37  import org.apache.maven.surefire.api.testset.TestArtifactInfo;
38  import org.apache.maven.surefire.api.testset.TestListResolver;
39  import org.apache.maven.surefire.api.testset.TestRequest;
40  import org.apache.maven.surefire.api.util.RunOrder;
41  import org.apache.maven.surefire.api.util.SurefireReflectionException;
42  
43  import static java.util.Collections.checkedList;
44  import static org.apache.maven.surefire.api.util.ReflectionUtils.getConstructor;
45  import static org.apache.maven.surefire.api.util.ReflectionUtils.getMethod;
46  import static org.apache.maven.surefire.api.util.ReflectionUtils.instantiateOneArg;
47  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeGetter;
48  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeMethodWithArray;
49  import static org.apache.maven.surefire.api.util.ReflectionUtils.invokeSetter;
50  import static org.apache.maven.surefire.api.util.ReflectionUtils.newInstance;
51  
52  /**
53   * Does reflection based invocation of the surefire methods.
54   * <br>
55   * This is to avoid complications with linkage issues
56   *
57   * @author Kristian Rosenvold
58   */
59  public final class SurefireReflector {
60      private final ClassLoader surefireClassLoader;
61  
62      private final Class<?> reporterConfiguration;
63  
64      private final Class<?> testRequest;
65  
66      private final Class<?> testArtifactInfo;
67  
68      private final Class<?> directoryScannerParameters;
69  
70      private final Class<?> runOrderParameters;
71  
72      private final Class<?> baseProviderFactory;
73  
74      private final Class<?> runResult;
75  
76      private final Class<?> booterParameters;
77  
78      private final Class<?> reporterFactory;
79  
80      private final Class<?> testListResolver;
81  
82      private final Class<Enum<?>> commandLineOptionsClass;
83  
84      @SuppressWarnings("unchecked")
85      public SurefireReflector(ClassLoader surefireClassLoader) {
86          this.surefireClassLoader = surefireClassLoader;
87          try {
88              reporterConfiguration = surefireClassLoader.loadClass(ReporterConfiguration.class.getName());
89              testRequest = surefireClassLoader.loadClass(TestRequest.class.getName());
90              testArtifactInfo = surefireClassLoader.loadClass(TestArtifactInfo.class.getName());
91              directoryScannerParameters = surefireClassLoader.loadClass(DirectoryScannerParameters.class.getName());
92              runOrderParameters = surefireClassLoader.loadClass(RunOrderParameters.class.getName());
93              baseProviderFactory = surefireClassLoader.loadClass(BaseProviderFactory.class.getName());
94              reporterFactory = surefireClassLoader.loadClass(ReporterFactory.class.getName());
95              runResult = surefireClassLoader.loadClass(RunResult.class.getName());
96              booterParameters = surefireClassLoader.loadClass(ProviderParameters.class.getName());
97              testListResolver = surefireClassLoader.loadClass(TestListResolver.class.getName());
98              commandLineOptionsClass = (Class<Enum<?>>) surefireClassLoader.loadClass(CommandLineOption.class.getName());
99          } catch (ClassNotFoundException e) {
100             throw new SurefireReflectionException(e);
101         }
102     }
103 
104     public Object convertIfRunResult(Object result) {
105         if (result == null || !isRunResult(result)) {
106             return result;
107         }
108         int getCompletedCount1 = invokeGetter(result, "getCompletedCount");
109         int getErrors = invokeGetter(result, "getErrors");
110         int getSkipped = invokeGetter(result, "getSkipped");
111         int getFailures = invokeGetter(result, "getFailures");
112         return new RunResult(getCompletedCount1, getErrors, getFailures, getSkipped);
113     }
114 
115     private Object createTestRequest(TestRequest suiteDefinition) {
116         if (suiteDefinition == null) {
117             return null;
118         } else {
119             Object resolver = createTestListResolver(suiteDefinition.getTestListResolver());
120             Class<?>[] arguments = {List.class, File.class, testListResolver, int.class};
121             Constructor<?> constructor = getConstructor(testRequest, arguments);
122             return newInstance(
123                     constructor,
124                     suiteDefinition.getSuiteXmlFiles(),
125                     suiteDefinition.getTestSourceDirectory(),
126                     resolver,
127                     suiteDefinition.getRerunFailingTestsCount());
128         }
129     }
130 
131     private Object createTestListResolver(TestListResolver resolver) {
132         if (resolver == null) {
133             return null;
134         } else {
135             Constructor<?> constructor = getConstructor(testListResolver, String.class);
136             return newInstance(constructor, resolver.getPluginParameterTest());
137         }
138     }
139 
140     private Object createDirectoryScannerParameters(DirectoryScannerParameters directoryScannerParameters) {
141         if (directoryScannerParameters == null) {
142             return null;
143         }
144         // Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
145         Class<?>[] arguments = {File.class, List.class, List.class, List.class, String.class};
146         Constructor<?> constructor = getConstructor(this.directoryScannerParameters, arguments);
147         return newInstance(
148                 constructor,
149                 directoryScannerParameters.getTestClassesDirectory(),
150                 directoryScannerParameters.getIncludes(),
151                 directoryScannerParameters.getExcludes(),
152                 directoryScannerParameters.getSpecificTests(),
153                 RunOrder.asString(directoryScannerParameters.getRunOrder()));
154     }
155 
156     private Object createRunOrderParameters(RunOrderParameters runOrderParameters) {
157         if (runOrderParameters == null) {
158             return null;
159         }
160         // Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
161         Class<?>[] arguments = {String.class, File.class, Long.class};
162         Constructor<?> constructor = getConstructor(this.runOrderParameters, arguments);
163         File runStatisticsFile = runOrderParameters.getRunStatisticsFile();
164         return newInstance(
165                 constructor,
166                 RunOrder.asString(runOrderParameters.getRunOrder()),
167                 runStatisticsFile,
168                 runOrderParameters.getRunOrderRandomSeed());
169     }
170 
171     private Object createTestArtifactInfo(TestArtifactInfo testArtifactInfo) {
172         if (testArtifactInfo == null) {
173             return null;
174         }
175         Class<?>[] arguments = {String.class, String.class};
176         Constructor<?> constructor = getConstructor(this.testArtifactInfo, arguments);
177         return newInstance(constructor, testArtifactInfo.getVersion(), testArtifactInfo.getClassifier());
178     }
179 
180     private Object createReporterConfiguration(ReporterConfiguration reporterConfig) {
181         Constructor<?> constructor = getConstructor(reporterConfiguration, File.class, boolean.class);
182         return newInstance(constructor, reporterConfig.getReportsDirectory(), reporterConfig.isTrimStackTrace());
183     }
184 
185     public Object createBooterConfiguration(ClassLoader surefireClassLoader, boolean insideFork) {
186         return instantiateOneArg(surefireClassLoader, BaseProviderFactory.class.getName(), boolean.class, insideFork);
187     }
188 
189     public Object instantiateProvider(String providerClassName, Object booterParameters) {
190         return instantiateOneArg(surefireClassLoader, providerClassName, this.booterParameters, booterParameters);
191     }
192 
193     public void setIfDirScannerAware(Object o, DirectoryScannerParameters dirScannerParams) {
194         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
195             setDirectoryScannerParameters(o, dirScannerParams);
196         }
197     }
198 
199     public void setMainCliOptions(Object o, List<CommandLineOption> options) {
200         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
201             List<Enum<?>> newOptions = checkedList(new ArrayList<>(options.size()), commandLineOptionsClass);
202             Collection<Integer> ordinals = toOrdinals(options);
203             for (Enum<?> e : commandLineOptionsClass.getEnumConstants()) {
204                 if (ordinals.contains(e.ordinal())) {
205                     newOptions.add(e);
206                 }
207             }
208             invokeSetter(o, "setMainCliOptions", List.class, newOptions);
209         }
210     }
211 
212     public void setSkipAfterFailureCount(Object o, int skipAfterFailureCount) {
213         invokeSetter(o, "setSkipAfterFailureCount", int.class, skipAfterFailureCount);
214     }
215 
216     public void setSystemExitTimeout(Object o, Integer systemExitTimeout) {
217         invokeSetter(o, "setSystemExitTimeout", Integer.class, systemExitTimeout);
218     }
219 
220     void setDirectoryScannerParameters(Object o, DirectoryScannerParameters dirScannerParams) {
221         Object param = createDirectoryScannerParameters(dirScannerParams);
222         invokeSetter(o, "setDirectoryScannerParameters", directoryScannerParameters, param);
223     }
224 
225     public void setRunOrderParameters(Object o, RunOrderParameters runOrderParameters) {
226         Object param = createRunOrderParameters(runOrderParameters);
227         invokeSetter(o, "setRunOrderParameters", this.runOrderParameters, param);
228     }
229 
230     public void setTestSuiteDefinitionAware(Object o, TestRequest testSuiteDefinition2) {
231         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
232             setTestSuiteDefinition(o, testSuiteDefinition2);
233         }
234     }
235 
236     void setTestSuiteDefinition(Object o, TestRequest testSuiteDefinition1) {
237         Object param = createTestRequest(testSuiteDefinition1);
238         invokeSetter(o, "setTestRequest", testRequest, param);
239     }
240 
241     public void setProviderPropertiesAware(Object o, Map<String, String> properties) {
242         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
243             setProviderProperties(o, properties);
244         }
245     }
246 
247     void setProviderProperties(Object o, Map<String, String> providerProperties) {
248         invokeSetter(o, "setProviderProperties", Map.class, providerProperties);
249     }
250 
251     public void setReporterConfigurationAware(Object o, ReporterConfiguration reporterConfiguration1) {
252         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
253             setReporterConfiguration(o, reporterConfiguration1);
254         }
255     }
256 
257     private void setReporterConfiguration(Object o, ReporterConfiguration reporterConfiguration) {
258         Object param = createReporterConfiguration(reporterConfiguration);
259         invokeSetter(o, "setReporterConfiguration", this.reporterConfiguration, param);
260     }
261 
262     public void setTestClassLoaderAware(Object o, ClassLoader testClassLoader) {
263         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
264             setTestClassLoader(o, testClassLoader);
265         }
266     }
267 
268     void setTestClassLoader(Object o, ClassLoader testClassLoader) {
269         Method setter = getMethod(o, "setClassLoaders", ClassLoader.class);
270         invokeMethodWithArray(o, setter, testClassLoader);
271     }
272 
273     public void setTestArtifactInfoAware(Object o, TestArtifactInfo testArtifactInfo1) {
274         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
275             setTestArtifactInfo(o, testArtifactInfo1);
276         }
277     }
278 
279     void setTestArtifactInfo(Object o, TestArtifactInfo testArtifactInfo) {
280         Object param = createTestArtifactInfo(testArtifactInfo);
281         invokeSetter(o, "setTestArtifactInfo", this.testArtifactInfo, param);
282     }
283 
284     public void setReporterFactoryAware(Object o, Object reporterFactory) {
285         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
286             setReporterFactory(o, reporterFactory);
287         }
288     }
289 
290     void setReporterFactory(Object o, Object reporterFactory) {
291         invokeSetter(o, "setReporterFactory", this.reporterFactory, reporterFactory);
292     }
293 
294     private boolean isRunResult(Object o) {
295         return runResult.isAssignableFrom(o.getClass());
296     }
297 
298     private static Collection<Integer> toOrdinals(Collection<? extends Enum<?>> enums) {
299         Collection<Integer> ordinals = new ArrayList<>(enums.size());
300         for (Enum<?> e : enums) {
301             ordinals.add(e.ordinal());
302         }
303         return ordinals;
304     }
305 }