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 = {File.class, testListResolver, int.class};
121             Constructor<?> constructor = getConstructor(testRequest, arguments);
122             return newInstance(
123                     constructor,
124                     suiteDefinition.getTestSourceDirectory(),
125                     resolver,
126                     suiteDefinition.getRerunFailingTestsCount());
127         }
128     }
129 
130     private Object createTestListResolver(TestListResolver resolver) {
131         if (resolver == null) {
132             return null;
133         } else {
134             Constructor<?> constructor = getConstructor(testListResolver, String.class);
135             return newInstance(constructor, resolver.getPluginParameterTest());
136         }
137     }
138 
139     private Object createDirectoryScannerParameters(DirectoryScannerParameters directoryScannerParameters) {
140         if (directoryScannerParameters == null) {
141             return null;
142         }
143         // Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
144         Class<?>[] arguments = {File.class, List.class, List.class, List.class, String.class};
145         Constructor<?> constructor = getConstructor(this.directoryScannerParameters, arguments);
146         return newInstance(
147                 constructor,
148                 directoryScannerParameters.getTestClassesDirectory(),
149                 directoryScannerParameters.getIncludes(),
150                 directoryScannerParameters.getExcludes(),
151                 directoryScannerParameters.getSpecificTests(),
152                 RunOrder.asString(directoryScannerParameters.getRunOrder()));
153     }
154 
155     private Object createRunOrderParameters(RunOrderParameters runOrderParameters) {
156         if (runOrderParameters == null) {
157             return null;
158         }
159         // Can't use the constructor with the RunOrder parameter. Using it causes some integration tests to fail.
160         Class<?>[] arguments = {String.class, File.class, Long.class};
161         Constructor<?> constructor = getConstructor(this.runOrderParameters, arguments);
162         File runStatisticsFile = runOrderParameters.getRunStatisticsFile();
163         return newInstance(
164                 constructor,
165                 RunOrder.asString(runOrderParameters.getRunOrder()),
166                 runStatisticsFile,
167                 runOrderParameters.getRunOrderRandomSeed());
168     }
169 
170     private Object createTestArtifactInfo(TestArtifactInfo testArtifactInfo) {
171         if (testArtifactInfo == null) {
172             return null;
173         }
174         Class<?>[] arguments = {String.class, String.class};
175         Constructor<?> constructor = getConstructor(this.testArtifactInfo, arguments);
176         return newInstance(constructor, testArtifactInfo.getVersion(), testArtifactInfo.getClassifier());
177     }
178 
179     private Object createReporterConfiguration(ReporterConfiguration reporterConfig) {
180         Constructor<?> constructor = getConstructor(reporterConfiguration, File.class, boolean.class);
181         return newInstance(constructor, reporterConfig.getReportsDirectory(), reporterConfig.isTrimStackTrace());
182     }
183 
184     public Object createBooterConfiguration(ClassLoader surefireClassLoader, boolean insideFork) {
185         return instantiateOneArg(surefireClassLoader, BaseProviderFactory.class.getName(), boolean.class, insideFork);
186     }
187 
188     public Object instantiateProvider(String providerClassName, Object booterParameters) {
189         return instantiateOneArg(surefireClassLoader, providerClassName, this.booterParameters, booterParameters);
190     }
191 
192     public void setIfDirScannerAware(Object o, DirectoryScannerParameters dirScannerParams) {
193         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
194             setDirectoryScannerParameters(o, dirScannerParams);
195         }
196     }
197 
198     public void setMainCliOptions(Object o, List<CommandLineOption> options) {
199         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
200             List<Enum<?>> newOptions = checkedList(new ArrayList<>(options.size()), commandLineOptionsClass);
201             Collection<Integer> ordinals = toOrdinals(options);
202             for (Enum<?> e : commandLineOptionsClass.getEnumConstants()) {
203                 if (ordinals.contains(e.ordinal())) {
204                     newOptions.add(e);
205                 }
206             }
207             invokeSetter(o, "setMainCliOptions", List.class, newOptions);
208         }
209     }
210 
211     public void setSkipAfterFailureCount(Object o, int skipAfterFailureCount) {
212         invokeSetter(o, "setSkipAfterFailureCount", int.class, skipAfterFailureCount);
213     }
214 
215     public void setSystemExitTimeout(Object o, Integer systemExitTimeout) {
216         invokeSetter(o, "setSystemExitTimeout", Integer.class, systemExitTimeout);
217     }
218 
219     void setDirectoryScannerParameters(Object o, DirectoryScannerParameters dirScannerParams) {
220         Object param = createDirectoryScannerParameters(dirScannerParams);
221         invokeSetter(o, "setDirectoryScannerParameters", directoryScannerParameters, param);
222     }
223 
224     public void setRunOrderParameters(Object o, RunOrderParameters runOrderParameters) {
225         Object param = createRunOrderParameters(runOrderParameters);
226         invokeSetter(o, "setRunOrderParameters", this.runOrderParameters, param);
227     }
228 
229     public void setTestSuiteDefinitionAware(Object o, TestRequest testSuiteDefinition2) {
230         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
231             setTestSuiteDefinition(o, testSuiteDefinition2);
232         }
233     }
234 
235     void setTestSuiteDefinition(Object o, TestRequest testSuiteDefinition1) {
236         Object param = createTestRequest(testSuiteDefinition1);
237         invokeSetter(o, "setTestRequest", testRequest, param);
238     }
239 
240     public void setProviderPropertiesAware(Object o, Map<String, String> properties) {
241         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
242             setProviderProperties(o, properties);
243         }
244     }
245 
246     void setProviderProperties(Object o, Map<String, String> providerProperties) {
247         invokeSetter(o, "setProviderProperties", Map.class, providerProperties);
248     }
249 
250     public void setReporterConfigurationAware(Object o, ReporterConfiguration reporterConfiguration1) {
251         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
252             setReporterConfiguration(o, reporterConfiguration1);
253         }
254     }
255 
256     private void setReporterConfiguration(Object o, ReporterConfiguration reporterConfiguration) {
257         Object param = createReporterConfiguration(reporterConfiguration);
258         invokeSetter(o, "setReporterConfiguration", this.reporterConfiguration, param);
259     }
260 
261     public void setTestClassLoaderAware(Object o, ClassLoader testClassLoader) {
262         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
263             setTestClassLoader(o, testClassLoader);
264         }
265     }
266 
267     void setTestClassLoader(Object o, ClassLoader testClassLoader) {
268         Method setter = getMethod(o, "setClassLoaders", ClassLoader.class);
269         invokeMethodWithArray(o, setter, testClassLoader);
270     }
271 
272     public void setTestArtifactInfoAware(Object o, TestArtifactInfo testArtifactInfo1) {
273         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
274             setTestArtifactInfo(o, testArtifactInfo1);
275         }
276     }
277 
278     void setTestArtifactInfo(Object o, TestArtifactInfo testArtifactInfo) {
279         Object param = createTestArtifactInfo(testArtifactInfo);
280         invokeSetter(o, "setTestArtifactInfo", this.testArtifactInfo, param);
281     }
282 
283     public void setReporterFactoryAware(Object o, Object reporterFactory) {
284         if (baseProviderFactory.isAssignableFrom(o.getClass())) {
285             setReporterFactory(o, reporterFactory);
286         }
287     }
288 
289     void setReporterFactory(Object o, Object reporterFactory) {
290         invokeSetter(o, "setReporterFactory", this.reporterFactory, reporterFactory);
291     }
292 
293     private boolean isRunResult(Object o) {
294         return runResult.isAssignableFrom(o.getClass());
295     }
296 
297     private static Collection<Integer> toOrdinals(Collection<? extends Enum<?>> enums) {
298         Collection<Integer> ordinals = new ArrayList<>(enums.size());
299         for (Enum<?> e : enums) {
300             ordinals.add(e.ordinal());
301         }
302         return ordinals;
303     }
304 }