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 java.io.PrintStream;
23  import org.apache.maven.surefire.providerapi.SurefireProvider;
24  import org.apache.maven.surefire.report.ReporterException;
25  import org.apache.maven.surefire.suite.RunResult;
26  import org.apache.maven.surefire.testset.TestSetFailedException;
27  import org.apache.maven.surefire.util.NestedRuntimeException;
28  import org.apache.maven.surefire.util.ReflectionUtils;
29  
30  /**
31   * Invokes surefire with the correct classloader setup.
32   * <p/>
33   * This part of the booter is always guaranteed to be in the
34   * same vm as the tests will be run in.
35   *
36   * @author Jason van Zyl
37   * @author Brett Porter
38   * @author Emmanuel Venisse
39   * @author Dan Fabulich
40   * @author Kristian Rosenvold
41   * @version $Id$
42   */
43  public class SurefireStarter
44  {
45      private final ProviderConfiguration providerConfiguration;
46  
47      private final StartupConfiguration startupConfiguration;
48  
49      private final static String SUREFIRE_TEST_CLASSPATH = "surefire.test.class.path";
50  
51      private final StartupReportConfiguration startupReportConfiguration;
52  
53      public SurefireStarter( StartupConfiguration startupConfiguration, ProviderConfiguration providerConfiguration,
54                              StartupReportConfiguration startupReportConfiguration )
55      {
56          this.providerConfiguration = providerConfiguration;
57          this.startupConfiguration = startupConfiguration;
58          this.startupReportConfiguration = startupReportConfiguration;
59      }
60  
61      public RunResult runSuitesInProcessWhenForked( TypeEncodedValue testSet )
62          throws SurefireExecutionException
63      {
64          writeSurefireTestClasspathProperty();
65          final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
66  
67          // todo: Find out....
68          // Why is the classloader structure created differently when a testSet is specified ?
69          // Smells like a legacy bug. Need to check issue tracker.
70          ClassLoader testsClassLoader = classpathConfiguration.createTestClassLoaderConditionallySystem(
71              startupConfiguration.useSystemClassLoader() );
72  
73          ClassLoader surefireClassLoader = classpathConfiguration.createSurefireClassLoader( testsClassLoader );
74  
75          SurefireReflector surefireReflector = new SurefireReflector( surefireClassLoader );
76  
77          final Object forkingReporterFactory = createForkingReporterFactory( surefireReflector );
78  
79          Object test = testSet.getDecodedValue();
80  
81          return invokeProvider( test, testsClassLoader, surefireClassLoader, forkingReporterFactory );
82      }
83  
84      private Object createForkingReporterFactory( SurefireReflector surefireReflector )
85      {
86          final Boolean trimStackTrace = this.providerConfiguration.getReporterConfiguration().isTrimStackTrace();
87          final PrintStream originalSystemOut =
88              this.providerConfiguration.getReporterConfiguration().getOriginalSystemOut();
89          return surefireReflector.createForkingReporterFactory( trimStackTrace, originalSystemOut );
90      }
91  
92      // todo: Fix duplication in this method and runSuitesInProcess
93      // This should be fixed "at a higher level", because this whole way
94      // of organizing the code stinks.
95  
96      public RunResult runSuitesInProcessWhenForked()
97          throws SurefireExecutionException
98      {
99          ClassLoader testsClassLoader = createInProcessTestClassLoader();
100 
101         ClassLoader surefireClassLoader = createSurefireClassloader( testsClassLoader );
102 
103         SurefireReflector surefireReflector = new SurefireReflector( surefireClassLoader );
104 
105         final Object factory = createForkingReporterFactory( surefireReflector );
106 
107         return invokeProvider( null, testsClassLoader, surefireClassLoader, factory );
108     }
109 
110     public RunResult runSuitesInProcess()
111         throws SurefireExecutionException
112     {
113         // The test classloader must be constructed first to avoid issues with commons-logging until we properly
114         // separate the TestNG classloader
115         ClassLoader testsClassLoader = createInProcessTestClassLoader();
116 
117         ClassLoader surefireClassLoader = createSurefireClassloader( testsClassLoader );
118 
119         SurefireReflector surefireReflector = new SurefireReflector( surefireClassLoader );
120 
121         final Object factory = surefireReflector.createReportingReporterFactory( startupReportConfiguration );
122 
123         return invokeProvider( null, testsClassLoader, surefireClassLoader, factory );
124     }
125 
126     private ClassLoader createSurefireClassloader( ClassLoader testsClassLoader )
127         throws SurefireExecutionException
128     {
129         final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
130 
131         return classpathConfiguration.createSurefireClassLoader( testsClassLoader );
132     }
133 
134     private ClassLoader createInProcessTestClassLoader()
135         throws SurefireExecutionException
136     {
137         writeSurefireTestClasspathProperty();
138         ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
139         if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() )
140         {
141             ClassLoader testsClassLoader = getClass().getClassLoader(); // ClassLoader.getSystemClassLoader()
142             // SUREFIRE-459, trick the app under test into thinking its classpath was conventional
143             // (instead of a single manifest-only jar)
144             System.setProperty( "surefire.real.class.path", System.getProperty( "java.class.path" ) );
145             classpathConfiguration.getTestClasspath().writeToSystemProperty( "java.class.path" );
146             return testsClassLoader;
147         }
148         else
149         {
150             return classpathConfiguration.createTestClassLoader();
151         }
152     }
153 
154     private void writeSurefireTestClasspathProperty()
155     {
156         ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
157         classpathConfiguration.getTestClasspath().writeToSystemProperty( SUREFIRE_TEST_CLASSPATH );
158     }
159 
160     private RunResult invokeProvider( Object testSet, ClassLoader testsClassLoader, ClassLoader surefireClassLoader,
161                                       Object factory )
162     {
163         final PrintStream orgSystemOut = System.out;
164         final PrintStream orgSystemErr = System.err;
165         // Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
166         // in createProvider below. These are the same values as here.
167 
168         ProviderFactory providerFactory =
169             new ProviderFactory( startupConfiguration, providerConfiguration, surefireClassLoader, testsClassLoader,
170                                  factory );
171         final SurefireProvider provider = providerFactory.createProvider();
172 
173         try
174         {
175             return provider.invoke( testSet );
176         }
177         catch ( TestSetFailedException e )
178         {
179             throw new NestedRuntimeException( e );
180         }
181         catch ( ReporterException e )
182         {
183             throw new NestedRuntimeException( e );
184         }
185         finally
186         {
187             if ( System.getSecurityManager() == null )
188             {
189                 System.setOut( orgSystemOut );
190                 System.setErr( orgSystemErr );
191             }
192         }
193     }
194 }