View Javadoc

1   package org.apache.maven.surefire;
2   
3   /*
4    * Copyright 2001-2005 The Codehaus.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * 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, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
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     // Accessors
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     // Forking options
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             // Only show the heading for the first run
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             //both cli's working directory and  system property "user.dir" must have the same value
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             // This class comes from a different classloader then the isolated classloader.
456             // This is the battery class that is from a different loader.
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             // Exclude the surefire booter
490             // Exclude the surefire booter
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