1 package org.apache.maven.surefire;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 import org.codehaus.plexus.util.StringUtils;
20 import org.codehaus.plexus.util.cli.CommandLineException;
21 import org.codehaus.plexus.util.cli.CommandLineUtils;
22 import org.codehaus.plexus.util.cli.Commandline;
23 import org.codehaus.plexus.util.cli.StreamConsumer;
24
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.OutputStreamWriter;
30 import java.io.Writer;
31 import java.lang.reflect.Method;
32 import java.net.URL;
33 import java.net.URLClassLoader;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Properties;
40
41 /***
42 * @author Jason van Zyl
43 * @author Emmanuel Venisse
44 * @version $Id: SurefireBooter.java 387790 2006-03-22 08:14:59Z carlos $
45 */
46 public class SurefireBooter
47 {
48 protected static final String EOL = System.getProperty( "line.separator" );
49
50 protected static final String PS = System.getProperty( "path.separator" );
51
52 private static final String RUNNER = "org.apache.maven.surefire.SurefireBooter";
53
54 private static final String BATTERY_EXECUTOR = "org.apache.maven.surefire.Surefire";
55
56 private static final String SINGLE_TEST_BATTERY = "org.apache.maven.surefire.battery.JUnitBattery";
57
58 private List batteries = new ArrayList();
59
60 private List reports = new ArrayList();
61
62 private List classpathUrls = new ArrayList();
63
64 private String reportsDirectory;
65
66 private String forkMode;
67
68 private String basedir;
69
70 private String jvm;
71
72 private Properties systemProperties;
73
74 private String argLine;
75
76 private Map environmentVariables;
77
78 private File workingDirectory;
79
80 private boolean childDelegation;
81
82 private boolean debug;
83
84 private String surefireBooterJar;
85
86 private String plexusUtilsJar;
87
88
89
90
91
92 public static final String FORK_ONCE = "once";
93
94 public static final String FORK_PERTEST = "pertest";
95
96 public static final String FORK_NONE = "none";
97
98 public static final String SUREFIRE_PROPERTIES = "surefire.properties";
99
100 public static final String SYSTEM_PROPERTIES = "surefire-system.properties";
101
102 public static final String CLASSLOADER_PROPERTIES = "surefire-classloader.properties";
103
104 static int TESTS_SUCCEEDED = 0;
105
106 static int TESTS_FAILED = 255;
107
108 static int ILLEGAL_ARGUMENT_EXCEPTION = 100;
109
110 static int OTHER_EXCEPTION = 200;
111
112
113
114
115
116 public void setReportsDirectory( String reportsDirectory )
117 {
118 this.reportsDirectory = reportsDirectory;
119 }
120
121 public String getReportsDirectory()
122 {
123 return this.reportsDirectory;
124 }
125
126 public void addBattery( String battery, Object[] params )
127 {
128 batteries.add( new Object[]{battery, params} );
129 }
130
131 public void addBattery( String battery )
132 {
133 batteries.add( new Object[]{battery, null} );
134 }
135
136 public void addReport( String report )
137 {
138 reports.add( report );
139 }
140
141 public void addClassPathUrl( String path )
142 {
143 if ( !classpathUrls.contains( path ) )
144 {
145 classpathUrls.add( path );
146 }
147 }
148
149 public void setClassPathUrls( List classpathUrls )
150 {
151 this.classpathUrls = classpathUrls;
152 }
153
154
155
156
157
158 public void setForkMode( String forkMode )
159 {
160 if ( forkMode.equals( FORK_NONE ) || forkMode.equals( FORK_ONCE ) || forkMode.equals( FORK_PERTEST ) )
161 {
162 this.forkMode = forkMode;
163 }
164 else
165 {
166 throw new IllegalArgumentException( "Fork mode " + forkMode + " is not a legal value" );
167 }
168 }
169
170 public void setJvm( String jvm )
171 {
172 this.jvm = jvm;
173 }
174
175 public void setSystemProperties( Properties systemProperties )
176 {
177 this.systemProperties = systemProperties;
178 }
179
180 public void setArgLine( String argLine )
181 {
182 this.argLine = argLine;
183 }
184
185 public void setEnvironmentVariables( Map environmentVariables )
186 {
187 this.environmentVariables = environmentVariables;
188 }
189
190 public void setBasedir( String basedir )
191 {
192 this.basedir = basedir;
193 }
194
195 public void setWorkingDirectory( File dir )
196 {
197 this.workingDirectory = dir;
198 }
199
200 public void setChildDelegation( boolean childDelegation )
201 {
202 this.childDelegation = childDelegation;
203 }
204
205 public void setDebug( boolean debug )
206 {
207 this.debug = debug;
208 }
209
210
211
212
213
214 public boolean run()
215 throws Exception
216 {
217 boolean result = false;
218
219 if ( forkMode.equals( FORK_NONE ) )
220 {
221 result = runTestsInProcess();
222 }
223 else if ( forkMode.equals( FORK_ONCE ) )
224 {
225 result = runTestsForkOnce();
226 }
227 else if ( forkMode.equals( FORK_PERTEST ) )
228 {
229 result = runTestsForkEach();
230 }
231
232 return result;
233 }
234
235 private ClassLoader createClassLoader() throws Exception
236 {
237 return createClassLoader( classpathUrls, childDelegation );
238 }
239
240 static private ClassLoader createClassLoader( List classpathUrls, boolean childDelegation ) throws Exception
241 {
242 ArrayList urls = new ArrayList();
243
244 for ( Iterator i = classpathUrls.iterator(); i.hasNext(); )
245 {
246 String url = (String) i.next();
247
248 if ( url == null )
249 {
250 continue;
251 }
252
253 File f = new File( url );
254 urls.add( f.toURL() );
255 }
256
257 if ( childDelegation )
258 {
259 IsolatedClassLoader surefireClassLoader = new IsolatedClassLoader( ClassLoader.getSystemClassLoader(), true );
260 for ( Iterator iter = urls.iterator(); iter.hasNext(); )
261 {
262 URL url = (URL) iter.next();
263 surefireClassLoader.addURL( url );
264 }
265 return surefireClassLoader;
266 }
267 else
268 {
269 URL u[] = new URL[urls.size()];
270 urls.toArray( u );
271 return new URLClassLoader( u, ClassLoader.getSystemClassLoader() );
272 }
273 }
274
275 private static ClassLoader createForkingClassLoader( String basedir )
276 throws Exception
277 {
278 Properties p = loadProperties( basedir, CLASSLOADER_PROPERTIES );
279
280 String cp = p.getProperty( "classpath" );
281
282 boolean childDelegation = "true".equals( p.getProperty( "childDelegation", "false" ) );
283
284 List urls;
285 if ( cp == null )
286 {
287 urls = new ArrayList(0);
288 }
289 else
290 {
291 urls = Arrays.asList( cp.split( PS ) );
292 }
293
294 return createClassLoader( urls, childDelegation );
295 }
296
297
298 private boolean runTestsInProcess()
299 throws Exception
300 {
301 ClassLoader surefireClassLoader = createClassLoader();
302
303 Class batteryExecutorClass = surefireClassLoader.loadClass( BATTERY_EXECUTOR );
304
305 Object batteryExecutor = batteryExecutorClass.newInstance();
306
307 Method run = batteryExecutorClass.getMethod( "run", new Class[]{List.class, List.class, ClassLoader.class, String.class} );
308
309 ClassLoader oldContextClassLoader = Thread.currentThread() .getContextClassLoader();
310
311 Thread.currentThread().setContextClassLoader( surefireClassLoader );
312
313 Boolean result = (Boolean) run.invoke( batteryExecutor, new Object[]{reports, batteries, surefireClassLoader, reportsDirectory} );
314
315 Thread.currentThread().setContextClassLoader( oldContextClassLoader );
316
317 return result.booleanValue();
318 }
319
320 private boolean runTestsForkOnce()
321 throws Exception
322 {
323 getForkOnceArgs();
324
325 return fork( true );
326 }
327
328 private boolean runTestsForkEach()
329 throws Exception
330 {
331 boolean noFailures = true;
332
333 List testClasses = getTestClasses();
334
335 for ( int i = 0; i < testClasses.size(); i++ )
336 {
337 String testClass = (String) testClasses.get( i );
338
339 getForkPerTestArgs( testClass );
340
341
342 boolean result = fork( i == 0 );
343
344 if ( !result )
345 {
346 noFailures = false;
347 }
348 }
349
350 return noFailures;
351 }
352
353 private boolean fork( boolean showHeading )
354 throws Exception
355 {
356 Commandline cli = new Commandline();
357
358 cli.setWorkingDirectory( basedir );
359
360 cli.setExecutable( jvm );
361
362 if ( argLine != null )
363 {
364 cli.addArguments( StringUtils.split( argLine, " " ) );
365 }
366
367 if ( environmentVariables != null )
368 {
369 Iterator iter = environmentVariables.keySet().iterator();
370
371 while ( iter.hasNext() )
372 {
373 String key = (String) iter.next();
374
375 String value = (String) environmentVariables.get( key );
376
377 cli.addEnvironment( key, value );
378
379 if ( debug )
380 {
381 System.out.println( "Environment: " + key + "=" + value + " added." );
382 }
383
384 }
385
386 }
387
388 cli.createArgument().setValue( "-classpath" );
389
390 cli.createArgument().setValue( surefireBooterJar + PS + plexusUtilsJar );
391
392 cli.createArgument().setValue( RUNNER );
393
394 cli.createArgument().setValue( basedir );
395
396 if ( workingDirectory != null )
397 {
398
399 cli.setWorkingDirectory( workingDirectory.getAbsolutePath() );
400
401 cli.createArgument().setValue( workingDirectory.getAbsolutePath() );
402 }
403
404 if ( debug )
405 {
406 System.out.println( Commandline.toString( cli.getCommandline() ) );
407 }
408
409 Writer consoleWriter = new OutputStreamWriter( System.out );
410
411 StreamConsumer out = new ForkingWriterStreamConsumer( consoleWriter, showHeading );
412
413 StreamConsumer err = new ForkingWriterStreamConsumer( consoleWriter, showHeading );
414
415 int returnCode;
416
417 try
418 {
419 returnCode = CommandLineUtils.executeCommandLine( cli, out, err );
420 }
421 catch ( CommandLineException e )
422 {
423 throw new Exception( "Error while executing forked tests.", e );
424 }
425 catch ( Exception e )
426 {
427 throw new SurefireBooterForkException( "Error while executing forked tests.", e );
428 }
429
430 if ( returnCode != 0 )
431 {
432 return false;
433 }
434
435 return true;
436 }
437
438 private List getTestClasses()
439 throws Exception
440 {
441 ClassLoader classLoader = createClassLoader();
442
443 List instantiatedBatteries = Surefire.instantiateBatteries( batteries, classLoader );
444
445 List testClasses = new ArrayList();
446
447 for ( Iterator i = instantiatedBatteries.iterator(); i.hasNext(); )
448 {
449 Object o = i.next();
450
451 Method m = o.getClass().getMethod( "getSubBatteryClassNames", new Class[]{} );
452
453 List tests = (List) m.invoke( o, new Object[]{} );
454
455
456
457
458 testClasses.addAll( tests );
459 }
460
461 return testClasses;
462 }
463
464 private void getForkOnceArgs()
465 throws Exception
466 {
467 getForkArgs( getStringArrayFromBatteries()[0] );
468 }
469
470 private void getForkPerTestArgs( String testClass )
471 throws Exception
472 {
473 getForkArgs( SINGLE_TEST_BATTERY + "|" + testClass );
474 }
475
476
477 private void getForkArgs( String batteryConfig )
478 throws Exception
479 {
480 String reportClassNames = getListOfStringsAsString( reports, "," );
481
482 Properties p = new Properties();
483
484 String cp = "";
485 for ( int i = 0; i < classpathUrls.size(); i++ )
486 {
487 String url = (String) classpathUrls.get( i );
488
489
490
491 if ( url.indexOf( "surefire-booter" ) > 0 )
492 {
493 surefireBooterJar = url;
494 }
495 else if ( url.indexOf( "plexus-utils" ) > 0 )
496 {
497 plexusUtilsJar = url;
498 }
499 else
500 {
501 if ( cp.length() == 0 )
502 {
503 cp = url;
504 }
505 else
506 {
507 cp += PS + url;
508 }
509 }
510 }
511
512 p.setProperty( "classpath", cp );
513
514 p.setProperty( "childDelegation", "" + childDelegation );
515
516 FileOutputStream fos = new FileOutputStream( new File( basedir, CLASSLOADER_PROPERTIES ) );
517
518 p.store( fos, "classpath entries" );
519
520 fos.close();
521
522 if ( systemProperties != null )
523 {
524 File f = new File( basedir, SYSTEM_PROPERTIES );
525
526 fos = new FileOutputStream( f );
527
528 systemProperties.store( fos, "system properties" );
529
530 fos.close();
531 }
532
533 p = new Properties();
534
535 p.setProperty( "reportClassNames", reportClassNames );
536
537 p.setProperty( "reportsDirectory", reportsDirectory );
538
539 p.setProperty( "batteryExecutorName", BATTERY_EXECUTOR );
540
541 p.setProperty( "forkMode", forkMode );
542
543 p.setProperty( "batteryConfig", batteryConfig );
544
545 p.setProperty( "debug", "" + debug );
546
547 fos = new FileOutputStream( new File( basedir, SUREFIRE_PROPERTIES ) );
548
549 p.store( fos, "surefire properties" );
550
551 fos.close();
552
553 }
554
555 public void reset()
556 {
557 batteries.clear();
558
559 reports.clear();
560
561 classpathUrls.clear();
562 }
563
564 private String getListOfStringsAsString( List listOfStrings, String delimiterParm )
565 {
566 StringBuffer stringBuffer = new StringBuffer();
567
568 Iterator listOfStringsIterator = listOfStrings.iterator();
569
570 String delimiter = "";
571
572 while ( listOfStringsIterator.hasNext() )
573 {
574 String string = (String) listOfStringsIterator.next();
575
576 stringBuffer.append( delimiter );
577
578 stringBuffer.append( string );
579
580 delimiter = delimiterParm;
581 }
582
583 return new String( stringBuffer );
584 }
585
586 private String[] getStringArrayFromBatteries()
587 {
588 String[] batteryConfig = new String[batteries.size()];
589
590 StringBuffer batteryBuffer = new StringBuffer();
591
592 int batteryCounter = 0;
593
594 for ( Iterator j = batteries.iterator(); j.hasNext(); )
595 {
596 Object[] batteryArray = (Object[]) j.next();
597
598 batteryBuffer.append( (String) batteryArray[0] );
599
600 if ( batteryArray[1] != null )
601 {
602 Object[] batteryParms = (Object[]) batteryArray[1];
603
604 for ( int i = 0; i < 3; i++ )
605 {
606 batteryBuffer.append( "|" );
607
608 batteryBuffer.append( batteryParms[i] );
609 }
610 }
611
612 batteryConfig[batteryCounter++] = new String( batteryBuffer );
613 }
614
615 return batteryConfig;
616 }
617
618
619
620
621
622 private static Properties loadProperties( String basedir, String file )
623 throws Exception
624 {
625 File f = new File( basedir, file );
626
627 Properties p = new Properties();
628
629 if ( !f.exists() )
630 {
631 throw new FileNotFoundException( f.getAbsolutePath() );
632 }
633
634 f.deleteOnExit();
635
636 p.load( new FileInputStream( f ) );
637
638 return p;
639 }
640
641 private static Properties getSurefireProperties( String basedir )
642 throws Exception
643 {
644 return loadProperties( basedir, SUREFIRE_PROPERTIES );
645 }
646
647 private static void setSystemProperties( String basedir )
648 throws Exception
649 {
650 Properties p = loadProperties( basedir, SYSTEM_PROPERTIES );
651
652 for ( Iterator i = p.keySet().iterator(); i.hasNext(); )
653 {
654 String key = (String) i.next();
655
656 System.setProperty( key, p.getProperty( key ) );
657 }
658 }
659
660 /***
661 * This method is invoked when Surefire is forked - this method parses and
662 * organizes the arguments passed to it and then calls the Surefire class'
663 * run method.
664 *
665 * @param args
666 * @throws Exception
667 */
668 public static void main( String[] args )
669 throws Exception
670 {
671 String basedir = args[0];
672
673 String workingDirectory = null;
674
675 if ( args.length == 2 )
676 {
677 workingDirectory = args[1];
678 }
679
680 ClassLoader classLoader = createForkingClassLoader( basedir );
681
682
683 Thread.currentThread().setContextClassLoader( classLoader );
684
685 setSystemProperties( basedir );
686
687 if ( workingDirectory != null )
688 {
689 System.setProperty( "user.dir", workingDirectory );
690 }
691
692 Properties p = getSurefireProperties( basedir );
693
694 boolean debug = "true".equals( p.getProperty( "debug", "false" ) );
695 if ( debug )
696 {
697 logClassLoader( classLoader );
698 }
699
700 String batteryExecutorName = p.getProperty( "batteryExecutorName" );
701
702 Class batteryExecutorClass = classLoader.loadClass( batteryExecutorName );
703
704 Object batteryExecutor = batteryExecutorClass.newInstance();
705
706 String reports = p.getProperty( "reportClassNames" );
707
708 String[] reportClasses = reports.split( "," );
709
710 List reportList = Arrays.asList( reportClasses );
711
712 String batteryConfig = p.getProperty( "batteryConfig" );
713
714 String[] batteryParts = batteryConfig.split( "//|" );
715
716 String batteryClassName = batteryParts[0];
717
718 Object[] batteryParms;
719
720 String forkMode = p.getProperty( "forkMode" );
721
722 if ( forkMode.equals( FORK_ONCE ) )
723 {
724 batteryParms = new Object[batteryParts.length - 1];
725
726 batteryParms[0] = new File( batteryParts[1] );
727
728 String stringList = batteryParts[2];
729
730 if ( stringList.startsWith( "[" ) && stringList.endsWith( "]" ) )
731 {
732 stringList = stringList.substring( 1, stringList.length() - 1 );
733 }
734
735 ArrayList includesList = new ArrayList();
736
737 String[] stringArray = stringList.split( "," );
738
739 for ( int i = 0; i < stringArray.length; i++ )
740 {
741 includesList.add( stringArray[i].trim() );
742 }
743
744 batteryParms[1] = includesList;
745
746 stringList = batteryParts[3];
747
748 ArrayList excludesList = new ArrayList();
749
750 if ( stringList.startsWith( "[" ) && stringList.endsWith( "]" ) )
751 {
752 stringList = stringList.substring( 1, stringList.length() - 1 );
753 }
754
755 stringArray = stringList.split( "," );
756
757 for ( int i = 0; i < stringArray.length; i++ )
758 {
759 excludesList.add( stringArray[i].trim() );
760 }
761
762 batteryParms[2] = excludesList;
763 }
764 else
765 {
766 batteryParms = new Object[1];
767
768 batteryParms[0] = batteryParts[1];
769 }
770
771 List batteryHolders = new ArrayList();
772
773 batteryHolders.add( new Object[]{batteryClassName, batteryParms} );
774
775 String reportsDirectory = p.getProperty( "reportsDirectory" );
776
777 Method run = batteryExecutorClass.getMethod( "run", new Class[]{List.class, List.class, String.class} );
778
779 Object[] parms = new Object[]{reportList, batteryHolders, reportsDirectory};
780
781 int returnCode = TESTS_FAILED;
782
783 try
784 {
785 boolean result = ( (Boolean) run.invoke( batteryExecutor, parms ) ).booleanValue();
786
787 if ( result )
788 {
789 returnCode = TESTS_SUCCEEDED;
790 }
791
792 }
793 catch ( IllegalArgumentException e )
794 {
795 returnCode = ILLEGAL_ARGUMENT_EXCEPTION;
796 }
797 catch ( Exception e )
798 {
799 e.printStackTrace();
800
801 returnCode = OTHER_EXCEPTION;
802 }
803
804 System.exit( returnCode );
805 }
806
807
808 private static void logClassLoader( ClassLoader classLoader )
809 {
810 if ( classLoader.getParent() != null )
811 {
812 logClassLoader( classLoader.getParent() );
813 }
814
815 if ( classLoader instanceof URLClassLoader )
816 {
817 System.out.println( "ClassLoader: type" + classLoader.getClass() + ", value=" + classLoader );
818
819 URLClassLoader ucl = (URLClassLoader) classLoader;
820
821 URL[] u = ucl.getURLs();
822
823 for ( int i = 0; i < u.length; i++ )
824 {
825 System.out.println( " : " + u[i] );
826 }
827 }
828 else
829 {
830 System.out.println( "ClassLoader: type" + classLoader.getClass() + ", value=" + classLoader );
831 }
832 }
833 }
834