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.providerapi.SurefireProvider;
23  import org.apache.maven.surefire.report.ReporterConfiguration;
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  
29  import java.io.File;
30  import java.io.IOException;
31  import java.io.PrintStream;
32  import java.util.Properties;
33  
34  /**
35   * Invokes surefire with the correct classloader setup.
36   * <p/>
37   * This part of the booter is always guaranteed to be in the
38   * same vm as the tests will be run in.
39   *
40   * @author Jason van Zyl
41   * @author Brett Porter
42   * @author Emmanuel Venisse
43   * @author Dan Fabulich
44   * @author Kristian Rosenvold
45   * @version $Id$
46   */
47  public class SurefireStarter
48  {
49      private static final int NO_TESTS = 254;
50  
51      private final ProviderConfiguration providerConfiguration;
52  
53      private final StartupConfiguration startupConfiguration;
54  
55      private final String SUREFIRE_TEST_CLASSPATH = "surefire.test.class.path";
56  
57      public SurefireStarter( StartupConfiguration startupConfiguration, ProviderConfiguration providerConfiguration )
58      {
59          this.providerConfiguration = providerConfiguration;
60          this.startupConfiguration = startupConfiguration;
61      }
62  
63      public int runSuitesInProcess( Object testSet, File surefirePropertiesFile, Properties p )
64          throws SurefireExecutionException, IOException
65      {
66          writeSurefireTestClasspathProperty();
67          final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
68  
69          ClassLoader testsClassLoader = classpathConfiguration.createTestClassLoaderConditionallySystem(
70              startupConfiguration.useSystemClassLoader() );
71  
72          ClassLoader surefireClassLoader = classpathConfiguration.createSurefireClassLoader( testsClassLoader );
73  
74          final RunResult runResult = invokeProvider( testSet, testsClassLoader, surefireClassLoader );
75          updateResultsProperties( runResult, p );
76          SystemPropertyManager.writePropertiesFile( surefirePropertiesFile, "surefire", p );
77          return processRunCount( runResult );
78      }
79  
80      public int runSuitesInProcess()
81          throws SurefireExecutionException
82      {
83          // The test classloader must be constructed first to avoid issues with commons-logging until we properly
84          // separate the TestNG classloader
85          ClassLoader testsClassLoader = createInProcessTestClassLoader();
86  
87          final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
88  
89          ClassLoader surefireClassLoader = classpathConfiguration.createSurefireClassLoader( testsClassLoader );
90  
91          final RunResult runResult = invokeProvider( null, testsClassLoader, surefireClassLoader );
92          return processRunCount( runResult);
93      }
94  
95      private ClassLoader createInProcessTestClassLoader()
96          throws SurefireExecutionException
97      {
98          writeSurefireTestClasspathProperty();
99          ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
100         if ( startupConfiguration.isManifestOnlyJarRequestedAndUsable() )
101         {
102             ClassLoader testsClassLoader = getClass().getClassLoader(); // ClassLoader.getSystemClassLoader()
103             // SUREFIRE-459, trick the app under test into thinking its classpath was conventional
104             // (instead of a single manifest-only jar)
105             System.setProperty( "surefire.real.class.path", System.getProperty( "java.class.path" ) );
106             classpathConfiguration.getTestClasspath().writeToSystemProperty( "java.class.path" );
107             return testsClassLoader;
108         }
109         else
110         {
111             return classpathConfiguration.createTestClassLoader();
112         }
113     }
114 
115     private void writeSurefireTestClasspathProperty()
116     {
117         ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
118         classpathConfiguration.getTestClasspath().writeToSystemProperty( SUREFIRE_TEST_CLASSPATH );
119     }
120 
121     private static final String RESULTS_ERRORS = "errors";
122 
123     private static final String RESULTS_COMPLETED_COUNT = "completedCount";
124 
125     private static final String RESULTS_FAILURES = "failures";
126 
127     private static final String RESULTS_SKIPPED = "skipped";
128 
129 
130     private void updateResultsProperties( RunResult runResult, Properties results )
131     {
132         results.setProperty( RESULTS_ERRORS, String.valueOf( runResult.getErrors() ) );
133         results.setProperty( RESULTS_COMPLETED_COUNT, String.valueOf( runResult.getCompletedCount() ) );
134         results.setProperty( RESULTS_FAILURES, String.valueOf( runResult.getFailures() ) );
135         results.setProperty( RESULTS_SKIPPED, String.valueOf( runResult.getSkipped() ) );
136     }
137 
138     private RunResult invokeProvider( Object testSet, ClassLoader testsClassLoader, ClassLoader surefireClassLoader )
139     {
140         final PrintStream orgSystemOut = System.out;
141         final PrintStream orgSystemErr = System.err;
142         // Note that System.out/System.err are also read in the "ReporterConfiguration" instatiation
143         // in createProvider below. These are the same values as here.
144         ProviderFactory providerFactory =
145             new ProviderFactory( startupConfiguration, providerConfiguration, surefireClassLoader, testsClassLoader );
146         final SurefireProvider provider = providerFactory.createProvider( );
147 
148         try
149         {
150             return provider.invoke( testSet );
151         }
152         catch ( TestSetFailedException e )
153         {
154             throw new NestedRuntimeException( e );
155         }
156         catch ( ReporterException e )
157         {
158             throw new NestedRuntimeException( e );
159         }
160         finally
161         {
162             System.setOut( orgSystemOut );
163             System.setErr( orgSystemErr );
164         }
165     }
166 
167     /**
168      * Returns the process return code based on the RunResult
169      *
170      * @param runCount The run result
171      * @return The process result code
172      * @throws SurefireExecutionException When an exception is found
173      */
174     private int processRunCount( RunResult runCount )
175         throws SurefireExecutionException
176     {
177 
178         if ( runCount.getCompletedCount() == 0 && providerConfiguration.isFailIfNoTests().booleanValue() )
179         {
180             return NO_TESTS;
181         }
182 
183         return runCount.getBooterCode();
184     }
185 }