1 package org.apache.maven.plugin.surefire.booterclient;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugin.surefire.booterclient.output.FileOutputConsumerProxy;
23 import org.apache.maven.plugin.surefire.booterclient.output.NullOutputConsumer;
24 import org.apache.maven.plugin.surefire.booterclient.output.OutputConsumer;
25 import org.apache.maven.plugin.surefire.booterclient.output.StandardOutputConsumer;
26 import org.apache.maven.plugin.surefire.booterclient.output.SupressFooterOutputConsumerProxy;
27 import org.apache.maven.plugin.surefire.booterclient.output.SupressHeaderOutputConsumerProxy;
28 import org.apache.maven.plugin.surefire.booterclient.output.SynchronizedOutputConsumer;
29 import org.apache.maven.surefire.booter.Classpath;
30 import org.apache.maven.surefire.booter.ProviderConfiguration;
31 import org.apache.maven.surefire.booter.ProviderFactory;
32 import org.apache.maven.surefire.booter.StartupConfiguration;
33 import org.apache.maven.surefire.booter.SurefireBooterForkException;
34 import org.apache.maven.surefire.booter.SurefireExecutionException;
35 import org.apache.maven.surefire.booter.SurefireStarter;
36 import org.apache.maven.surefire.booter.SystemPropertyManager;
37 import org.apache.maven.surefire.providerapi.SurefireProvider;
38 import org.apache.maven.surefire.suite.RunResult;
39 import org.codehaus.plexus.util.IOUtil;
40 import org.codehaus.plexus.util.cli.CommandLineException;
41 import org.codehaus.plexus.util.cli.CommandLineTimeOutException;
42 import org.codehaus.plexus.util.cli.CommandLineUtils;
43 import org.codehaus.plexus.util.cli.Commandline;
44 import org.codehaus.plexus.util.cli.StreamConsumer;
45
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.IOException;
50 import java.util.Iterator;
51 import java.util.Properties;
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 public class ForkStarter
70 {
71 private final int forkedProcessTimeoutInSeconds;
72
73 private final ProviderConfiguration providerConfiguration;
74
75 private final StartupConfiguration startupConfiguration;
76
77 private final ForkConfiguration forkConfiguration;
78
79 private final File reportsDirectory;
80
81 private final boolean printSummary;
82
83 public ForkStarter( ProviderConfiguration providerConfiguration, StartupConfiguration startupConfiguration,
84 File reportsDirectory, ForkConfiguration forkConfiguration, int forkedProcessTimeoutInSeconds,
85 boolean printSummary )
86 {
87 this.forkConfiguration = forkConfiguration;
88 this.providerConfiguration = providerConfiguration;
89 this.reportsDirectory = reportsDirectory;
90 this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
91 this.startupConfiguration = startupConfiguration;
92 this.printSummary = printSummary;
93 }
94
95 public int run()
96 throws SurefireBooterForkException, SurefireExecutionException
97 {
98 final int result;
99
100 final String requestedForkMode = forkConfiguration.getForkMode();
101 if ( ForkConfiguration.FORK_NEVER.equals( requestedForkMode ) )
102 {
103 SurefireStarter surefireStarter = new SurefireStarter( startupConfiguration, providerConfiguration );
104 result = surefireStarter.runSuitesInProcess();
105 }
106 else if ( ForkConfiguration.FORK_ONCE.equals( requestedForkMode ) )
107 {
108 result = runSuitesForkOnce();
109 }
110 else if ( ForkConfiguration.FORK_ALWAYS.equals( requestedForkMode ) )
111 {
112 result = runSuitesForkPerTestSet();
113 }
114 else
115 {
116 throw new SurefireExecutionException( "Unknown forkmode: " + requestedForkMode, null );
117 }
118 return result;
119 }
120
121 private int runSuitesForkOnce()
122 throws SurefireBooterForkException
123 {
124 return fork( null, providerConfiguration.getProviderProperties(), true, true );
125 }
126
127 private int runSuitesForkPerTestSet()
128 throws SurefireBooterForkException
129 {
130 int globalResult = 0;
131
132 ClassLoader testsClassLoader;
133 ClassLoader surefireClassLoader;
134 try
135 {
136 testsClassLoader = startupConfiguration.getClasspathConfiguration().createTestClassLoader( false );
137
138 surefireClassLoader =
139 startupConfiguration.getClasspathConfiguration().createSurefireClassLoader( testsClassLoader );
140 }
141 catch ( SurefireExecutionException e )
142 {
143 throw new SurefireBooterForkException( "Unable to create classloader to find test suites", e );
144 }
145
146 boolean showHeading = true;
147 final ProviderFactory providerFactory =
148 new ProviderFactory( startupConfiguration, providerConfiguration, surefireClassLoader, testsClassLoader );
149 SurefireProvider surefireProvider = providerFactory.createProvider();
150
151 Properties properties = new Properties();
152
153 final Iterator suites = surefireProvider.getSuites();
154 while ( suites.hasNext() )
155 {
156 Object testSet = suites.next();
157 boolean showFooter = !suites.hasNext();
158 int result = fork( testSet, properties, showHeading, showFooter );
159
160 if ( result > globalResult )
161 {
162 globalResult = result;
163 }
164 showHeading = false;
165 }
166
167 return globalResult;
168 }
169
170 private int fork( Object testSet, Properties properties, boolean showHeading, boolean showFooter )
171 throws SurefireBooterForkException
172 {
173 File surefireProperties;
174 File systemProperties = null;
175 try
176 {
177 BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration, properties );
178
179 surefireProperties = booterSerializer.serialize( providerConfiguration, startupConfiguration, testSet );
180
181 if ( forkConfiguration.getSystemProperties() != null )
182 {
183 systemProperties = SystemPropertyManager.writePropertiesFile( forkConfiguration.getSystemProperties(),
184 forkConfiguration.getTempDirectory(),
185 "surefire", forkConfiguration.isDebug() );
186 }
187 }
188 catch ( IOException e )
189 {
190 throw new SurefireBooterForkException( "Error creating properties files for forking", e );
191 }
192
193 final Classpath bootClasspathConfiguration = forkConfiguration.getBootClasspath();
194 final Classpath additionlClassPathUrls = startupConfiguration.useSystemClassLoader()
195 ? startupConfiguration.getClasspathConfiguration().getTestClasspath()
196 : null;
197
198 Classpath bootClasspath = Classpath.join( bootClasspathConfiguration, additionlClassPathUrls );
199
200 Commandline cli = forkConfiguration.createCommandLine( bootClasspath.getClassPath(),
201 startupConfiguration.getClassLoaderConfiguration(),
202 startupConfiguration.isShadefire() );
203
204 cli.createArg().setFile( surefireProperties );
205
206 if ( systemProperties != null )
207 {
208 cli.createArg().setFile( systemProperties );
209 }
210
211 final boolean willBeSharingConsumer = startupConfiguration.isRedirectTestOutputToFile();
212
213 ForkingStreamConsumer out =
214 getForkingStreamConsumer( showHeading, showFooter, startupConfiguration.isRedirectTestOutputToFile(),
215 willBeSharingConsumer, printSummary );
216
217 StreamConsumer err = willBeSharingConsumer
218 ? out
219 : getForkingStreamConsumer( showHeading, showFooter, startupConfiguration.isRedirectTestOutputToFile(),
220 false, printSummary );
221
222 if ( forkConfiguration.isDebug() )
223 {
224 System.out.println( "Forking command line: " + cli );
225 }
226
227 int returnCode;
228
229 try
230 {
231 returnCode = CommandLineUtils.executeCommandLine( cli, out, err, forkedProcessTimeoutInSeconds > 0 ?
232 forkedProcessTimeoutInSeconds: 0 );
233 }
234 catch ( CommandLineTimeOutException e )
235 {
236 returnCode = RunResult.FAILURE;
237 }
238 catch ( CommandLineException e )
239 {
240 throw new SurefireBooterForkException( "Error while executing forked tests.", e.getCause() );
241 }
242
243
244
245
246
247
248
249
250 if ( startupConfiguration.isRedirectTestOutputToFile() )
251 {
252
253 try
254 {
255 out.getOutputConsumer().testSetCompleted();
256 }
257 catch ( Exception e )
258 {
259
260 }
261 }
262
263 if ( surefireProperties != null && surefireProperties.exists() )
264 {
265 FileInputStream inStream = null;
266 try
267 {
268 inStream = new FileInputStream( surefireProperties );
269
270 properties.load( inStream );
271 }
272 catch ( FileNotFoundException e )
273 {
274 throw new SurefireBooterForkException( "Unable to reload properties file from forked process", e );
275 }
276 catch ( IOException e )
277 {
278 throw new SurefireBooterForkException( "Unable to reload properties file from forked process", e );
279 }
280 finally
281 {
282 IOUtil.close( inStream );
283 }
284 }
285
286 return returnCode;
287 }
288
289
290 private ForkingStreamConsumer getForkingStreamConsumer( boolean showHeading, boolean showFooter,
291 boolean redirectTestOutputToFile, boolean mustBeThreadSafe,
292 boolean printSummary )
293 {
294 OutputConsumer outputConsumer =
295 printSummary ? new StandardOutputConsumer() : (OutputConsumer) new NullOutputConsumer();
296
297 if ( redirectTestOutputToFile )
298 {
299 outputConsumer = new FileOutputConsumerProxy( outputConsumer, reportsDirectory );
300 }
301
302 if ( !showHeading )
303 {
304 outputConsumer = new SupressHeaderOutputConsumerProxy( outputConsumer );
305 }
306
307 if ( !showFooter )
308 {
309 outputConsumer = new SupressFooterOutputConsumerProxy( outputConsumer );
310 }
311
312 if ( mustBeThreadSafe )
313 {
314 outputConsumer = new SynchronizedOutputConsumer( outputConsumer );
315 }
316
317 return new ForkingStreamConsumer( outputConsumer );
318 }
319 }