Coverage Report - org.apache.maven.surefire.booter.SurefireBooter
 
Classes in this File Line Coverage Branch Coverage Complexity
SurefireBooter
13%
53/397
20%
41/210
3.714
 
 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.ByteArrayInputStream;
 23  
 import java.io.ByteArrayOutputStream;
 24  
 import java.io.File;
 25  
 import java.io.FileInputStream;
 26  
 import java.io.FileNotFoundException;
 27  
 import java.io.FileOutputStream;
 28  
 import java.io.IOException;
 29  
 import java.lang.reflect.InvocationTargetException;
 30  
 import java.lang.reflect.Method;
 31  
 import java.net.MalformedURLException;
 32  
 import java.net.URL;
 33  
 import java.util.ArrayList;
 34  
 import java.util.Collections;
 35  
 import java.util.Enumeration;
 36  
 import java.util.Iterator;
 37  
 import java.util.List;
 38  
 import java.util.Map;
 39  
 import java.util.Properties;
 40  
 import java.util.SortedMap;
 41  
 import java.util.TreeMap;
 42  
 
 43  
 import org.apache.maven.surefire.Surefire;
 44  
 import org.apache.maven.surefire.booter.output.FileOutputConsumerProxy;
 45  
 import org.apache.maven.surefire.booter.output.ForkingStreamConsumer;
 46  
 import org.apache.maven.surefire.booter.output.OutputConsumer;
 47  
 import org.apache.maven.surefire.booter.output.StandardOutputConsumer;
 48  
 import org.apache.maven.surefire.booter.output.SupressFooterOutputConsumerProxy;
 49  
 import org.apache.maven.surefire.booter.output.SupressHeaderOutputConsumerProxy;
 50  
 import org.apache.maven.surefire.testset.TestSetFailedException;
 51  
 import org.apache.maven.surefire.util.NestedRuntimeException;
 52  
 import org.apache.maven.surefire.util.UrlUtils;
 53  
 import org.codehaus.plexus.util.IOUtil;
 54  
 import org.codehaus.plexus.util.StringUtils;
 55  
 import org.codehaus.plexus.util.cli.CommandLineException;
 56  
 import org.codehaus.plexus.util.cli.CommandLineUtils;
 57  
 import org.codehaus.plexus.util.cli.Commandline;
 58  
 import org.codehaus.plexus.util.cli.StreamConsumer;
 59  
 
 60  
 /**
 61  
  * @author Jason van Zyl
 62  
  * @author Emmanuel Venisse
 63  
  * @version $Id: SurefireBooter.java 980568 2010-07-29 20:56:29Z krosenvold $
 64  
  */
 65  
 public class SurefireBooter
 66  
 {
 67  
     private static final String TEST_SUITE_PROPERTY_PREFIX = "testSuite.";
 68  
     private static final String REPORT_PROPERTY_PREFIX = "report.";
 69  
     private static final String PARAMS_SUFIX = ".params";
 70  
     private static final String TYPES_SUFIX = ".types";
 71  
 
 72  0
     private List reports = new ArrayList();
 73  
 
 74  0
     private List classPathUrls = new ArrayList();
 75  
 
 76  0
     private List surefireClassPathUrls = new ArrayList();
 77  
 
 78  0
     private List surefireBootClassPathUrls = new ArrayList();
 79  
 
 80  0
     private List testSuites = new ArrayList();
 81  
     
 82  0
     private boolean failIfNoTests = false;
 83  
     
 84  0
     private int forkedProcessTimeoutInSeconds = 0;
 85  
 
 86  0
     private boolean redirectTestOutputToFile = false;
 87  
 
 88  
     // ----------------------------------------------------------------------
 89  
     //
 90  
     // ----------------------------------------------------------------------
 91  
 
 92  
     private ForkConfiguration forkConfiguration;
 93  
 
 94  
     public static final int TESTS_SUCCEEDED_EXIT_CODE = 0;
 95  
 
 96  
     public static final int TESTS_FAILED_EXIT_CODE = 255;
 97  
     
 98  
     public static final int NO_TESTS_EXIT_CODE = 254;
 99  
 
 100  
     private static Method assertionStatusMethod;
 101  
 
 102  
     /**
 103  
      * @deprecated because the IsolatedClassLoader is really isolated - no parent.
 104  
      */
 105  0
     private boolean childDelegation = true;
 106  
 
 107  
     private File reportsDirectory;
 108  
 
 109  
     /**
 110  
      * This field is set to true if it's running from main. It's used to help decide what classloader to use.
 111  
      */
 112  
     private final boolean isForked;
 113  
 
 114  
     /**
 115  
      * Whether to enable assertions or not (can be affected by the fork arguments, and the ability to do so based on the
 116  
      * JVM).
 117  
      */
 118  
     private boolean enableAssertions;
 119  
 
 120  
     static
 121  
     {
 122  
         try
 123  
         {
 124  4
             assertionStatusMethod =
 125  32
                 ClassLoader.class.getMethod( "setDefaultAssertionStatus", new Class[] { boolean.class } );
 126  
         }
 127  0
         catch ( NoSuchMethodException e )
 128  
         {
 129  0
             assertionStatusMethod = null;
 130  4
         }
 131  4
     }
 132  
 
 133  
     public SurefireBooter()
 134  0
     {
 135  0
         isForked = false;
 136  0
     }
 137  
 
 138  
     private SurefireBooter( boolean isForked )
 139  0
     {
 140  0
         this.isForked = isForked;
 141  0
     }
 142  
 
 143  
     // ----------------------------------------------------------------------
 144  
     // Accessors
 145  
     // ----------------------------------------------------------------------
 146  
 
 147  
     public void addReport( String report )
 148  
     {
 149  0
         addReport( report, null );
 150  0
     }
 151  
 
 152  
     public void addReport( String report, Object[] constructorParams )
 153  
     {
 154  0
         reports.add( new Object[] { report, constructorParams } );
 155  0
     }
 156  
 
 157  
     public void addTestSuite( String suiteClassName, Object[] constructorParams )
 158  
     {
 159  0
         testSuites.add( new Object[] { suiteClassName, constructorParams } );
 160  0
     }
 161  
 
 162  
     public void addClassPathUrl( String path )
 163  
     {
 164  0
         if ( !classPathUrls.contains( path ) )
 165  
         {
 166  0
             classPathUrls.add( path );
 167  
         }
 168  0
     }
 169  
 
 170  
     public void addSurefireClassPathUrl( String path )
 171  
     {
 172  0
         if ( !surefireClassPathUrls.contains( path ) )
 173  
         {
 174  0
             surefireClassPathUrls.add( path );
 175  
         }
 176  0
     }
 177  
 
 178  
     public void addSurefireBootClassPathUrl( String path )
 179  
     {
 180  0
         if ( !surefireBootClassPathUrls.contains( path ) )
 181  
         {
 182  0
             surefireBootClassPathUrls.add( path );
 183  
         }
 184  0
     }
 185  
 
 186  
     /**
 187  
      * Setting this to true will cause a failure if there are no tests to run
 188  
      *
 189  
      * @param redirectTestOutputToFile
 190  
      */
 191  
     public void setFailIfNoTests( boolean failIfNoTests )
 192  
     {
 193  0
         this.failIfNoTests = failIfNoTests;
 194  0
     }
 195  
     
 196  
     /**
 197  
      * When forking, setting this to true will make the test output to be saved in a file instead of showing it on the
 198  
      * standard output
 199  
      *
 200  
      * @param redirectTestOutputToFile
 201  
      */
 202  
     public void setRedirectTestOutputToFile( boolean redirectTestOutputToFile )
 203  
     {
 204  0
         this.redirectTestOutputToFile = redirectTestOutputToFile;
 205  0
     }
 206  
 
 207  
     /**
 208  
      * Set the directory where reports will be saved
 209  
      *
 210  
      * @param reportsDirectory the directory
 211  
      */
 212  
     public void setReportsDirectory( File reportsDirectory )
 213  
     {
 214  0
         this.reportsDirectory = reportsDirectory;
 215  0
     }
 216  
 
 217  
     /**
 218  
      * Get the directory where reports will be saved
 219  
      */
 220  
     public File getReportsDirectory()
 221  
     {
 222  0
         return reportsDirectory;
 223  
     }
 224  
 
 225  
     public void setForkConfiguration( ForkConfiguration forkConfiguration )
 226  
     {
 227  0
         this.forkConfiguration = forkConfiguration;
 228  0
     }
 229  
     
 230  
     public boolean isForking()
 231  
     {
 232  0
         return forkConfiguration.isForking();
 233  
     }
 234  
 
 235  
     public int run()
 236  
         throws SurefireBooterForkException, SurefireExecutionException
 237  
     {
 238  
         int result;
 239  
 
 240  0
         if (  ForkConfiguration.FORK_NEVER.equals( forkConfiguration.getForkMode() ) )
 241  
         {
 242  0
             result = runSuitesInProcess();
 243  
         }
 244  0
         else if ( ForkConfiguration.FORK_ONCE.equals( forkConfiguration.getForkMode() ) )
 245  
         {
 246  0
             result = runSuitesForkOnce();
 247  
         }
 248  0
         else if ( ForkConfiguration.FORK_ALWAYS.equals( forkConfiguration.getForkMode() ) )
 249  
         {
 250  0
             result = runSuitesForkPerTestSet();
 251  
         }
 252  
         else
 253  
         {
 254  0
             throw new SurefireExecutionException( "Unknown forkmode: " + forkConfiguration.getForkMode(), null );
 255  
         }
 256  0
         return result;
 257  
     }
 258  
 
 259  
     private int runSuitesInProcess( String testSet, Properties results )
 260  
         throws SurefireExecutionException
 261  
     {
 262  0
         if ( testSuites.size() != 1 )
 263  
         {
 264  0
             throw new IllegalArgumentException( "Cannot only specify testSet for single test suites" );
 265  
         }
 266  
 
 267  
         // TODO: replace with plexus
 268  
 
 269  
         // noinspection CatchGenericClass,OverlyBroadCatchBlock
 270  0
         ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
 271  
         try
 272  
         {
 273  0
             ClassLoader testsClassLoader =
 274  
                 useSystemClassLoader() ? ClassLoader.getSystemClassLoader() : createClassLoader( classPathUrls, null,
 275  
                                                                                                  childDelegation );
 276  
 
 277  
             // TODO: assertions = true shouldn't be required for this CL if we had proper separation (see TestNG)
 278  0
             ClassLoader surefireClassLoader = createClassLoader( surefireClassPathUrls, testsClassLoader );
 279  
 
 280  0
             Class surefireClass = surefireClassLoader.loadClass( Surefire.class.getName() );
 281  
 
 282  0
             Object surefire = surefireClass.newInstance();
 283  
 
 284  0
             Method run =
 285  
                 surefireClass.getMethod( "run", new Class[] { List.class, Object[].class, String.class,
 286  
                     ClassLoader.class, ClassLoader.class, Properties.class, Boolean.class } );
 287  
 
 288  0
             Thread.currentThread().setContextClassLoader( testsClassLoader );
 289  
 
 290  0
             Integer result =
 291  
                 (Integer) run.invoke( surefire, new Object[] { reports, testSuites.get( 0 ), testSet,
 292  
                     surefireClassLoader, testsClassLoader, results, new Boolean( failIfNoTests ) } );
 293  
 
 294  0
             return result.intValue();
 295  
         }
 296  0
         catch ( InvocationTargetException e )
 297  
         {
 298  0
             throw new SurefireExecutionException( e.getTargetException().getMessage(), e.getTargetException() );
 299  
         }
 300  0
         catch ( Exception e )
 301  
         {
 302  0
             throw new SurefireExecutionException( "Unable to instantiate and execute Surefire", e );
 303  
         }
 304  
         finally
 305  
         {
 306  0
             Thread.currentThread().setContextClassLoader( oldContextClassLoader );
 307  
         }
 308  
     }
 309  
 
 310  
     private int runSuitesInProcess()
 311  
         throws SurefireExecutionException
 312  
     {
 313  
         // TODO: replace with plexus
 314  
 
 315  
         // noinspection CatchGenericClass,OverlyBroadCatchBlock
 316  0
         ClassLoader oldContextClassLoader = Thread.currentThread().getContextClassLoader();
 317  
 
 318  
         try
 319  
         {
 320  
             // The test classloader must be constructed first to avoid issues with commons-logging until we properly
 321  
             // separate the TestNG classloader
 322  
             ClassLoader testsClassLoader;
 323  0
             String testClassPath = getTestClassPathAsString();
 324  0
             System.setProperty( "surefire.test.class.path", testClassPath );
 325  0
             if ( useManifestOnlyJar() )
 326  
             {
 327  0
                 testsClassLoader = getClass().getClassLoader(); // ClassLoader.getSystemClassLoader()
 328  
                 // SUREFIRE-459, trick the app under test into thinking its classpath was conventional
 329  
                 // (instead of a single manifest-only jar) 
 330  0
                 System.setProperty( "surefire.real.class.path", System.getProperty( "java.class.path" ) );
 331  0
                 System.setProperty( "java.class.path", testClassPath );
 332  
             }
 333  
             else
 334  
             {
 335  0
                 testsClassLoader = createClassLoader( classPathUrls, null, childDelegation );
 336  
             }
 337  
             
 338  0
             ClassLoader surefireClassLoader = createClassLoader( surefireClassPathUrls, testsClassLoader );
 339  
 
 340  0
             Class surefireClass = surefireClassLoader.loadClass( Surefire.class.getName() );
 341  
 
 342  0
             Object surefire = surefireClass.newInstance();
 343  
 
 344  0
             Method run =
 345  
                 surefireClass.getMethod( "run", new Class[] { List.class, List.class, ClassLoader.class,
 346  
                     ClassLoader.class, Boolean.class } );
 347  
 
 348  0
             Thread.currentThread().setContextClassLoader( testsClassLoader );
 349  
 
 350  0
             Integer result =
 351  
                 (Integer) run.invoke( surefire, new Object[] { reports, testSuites, surefireClassLoader,
 352  
                     testsClassLoader, new Boolean( failIfNoTests ) } );
 353  
 
 354  0
             return result.intValue();
 355  
         }
 356  0
         catch ( InvocationTargetException e )
 357  
         {
 358  0
             throw new SurefireExecutionException( e.getTargetException().getMessage(), e.getTargetException() );
 359  
         }
 360  0
         catch ( Exception e )
 361  
         {
 362  0
             throw new SurefireExecutionException( "Unable to instantiate and execute Surefire", e );
 363  
         }
 364  
         finally
 365  
         {
 366  0
             Thread.currentThread().setContextClassLoader( oldContextClassLoader );
 367  
         }
 368  
     }
 369  
 
 370  
     
 371  
     
 372  
     private String getTestClassPathAsString()
 373  
     {
 374  0
         StringBuffer sb = new StringBuffer();
 375  0
         for ( int i = 0; i < classPathUrls.size(); i++ )
 376  
         {
 377  0
             sb.append( classPathUrls.get( i ) ).append( File.pathSeparatorChar );
 378  
         }
 379  0
         return sb.toString();
 380  
     }
 381  
     
 382  
     private int runSuitesForkOnce()
 383  
         throws SurefireBooterForkException
 384  
     {
 385  0
         return forkSuites( testSuites, true, true );
 386  
     }
 387  
 
 388  
     private int runSuitesForkPerTestSet()
 389  
         throws SurefireBooterForkException
 390  
     {
 391  
         ClassLoader testsClassLoader;
 392  
         ClassLoader surefireClassLoader;
 393  
         try
 394  
         {
 395  0
             testsClassLoader = createClassLoader( classPathUrls, null, false );
 396  
             // TODO: assertions = true shouldn't be required if we had proper separation (see TestNG)
 397  0
             surefireClassLoader = createClassLoader( surefireClassPathUrls, testsClassLoader, false );
 398  
         }
 399  0
         catch ( MalformedURLException e )
 400  
         {
 401  0
             throw new SurefireBooterForkException( "Unable to create classloader to find test suites", e );
 402  0
         }
 403  
 
 404  0
         int globalResult = 0;
 405  
 
 406  0
         boolean showHeading = true;
 407  0
         Properties properties = new Properties();
 408  0
         for ( Iterator i = testSuites.iterator(); i.hasNext(); )
 409  
         {
 410  0
             Object[] testSuite = (Object[]) i.next();
 411  
 
 412  0
             Map testSets = getTestSets( testSuite, testsClassLoader, surefireClassLoader );
 413  
 
 414  0
             for ( Iterator j = testSets.keySet().iterator(); j.hasNext(); )
 415  
             {
 416  0
                 Object testSet = j.next();
 417  0
                 boolean showFooter = !j.hasNext() && !i.hasNext();
 418  0
                 int result = forkSuite( testSuite, testSet, showHeading, showFooter, properties );
 419  0
                 if ( result > globalResult )
 420  
                 {
 421  0
                     globalResult = result;
 422  
                 }
 423  0
                 showHeading = false;
 424  0
             }
 425  0
         }
 426  
 
 427  0
         return globalResult;
 428  
     }
 429  
 
 430  
     private Map getTestSets( Object[] testSuite, ClassLoader testsClassLoader, ClassLoader surefireClassLoader )
 431  
         throws SurefireBooterForkException
 432  
     {
 433  0
         String className = (String) testSuite[0];
 434  
 
 435  0
         Object[] params = (Object[]) testSuite[1];
 436  
 
 437  
         Object suite;
 438  
         try
 439  
         {
 440  0
             suite = Surefire.instantiateObject( className, params, surefireClassLoader );
 441  
         }
 442  0
         catch ( TestSetFailedException e )
 443  
         {
 444  0
             throw new SurefireBooterForkException( e.getMessage(), e.getCause() );
 445  
         }
 446  0
         catch ( ClassNotFoundException e )
 447  
         {
 448  0
             throw new SurefireBooterForkException( "Unable to find class for test suite '" + className + "'", e );
 449  
         }
 450  0
         catch ( NoSuchMethodException e )
 451  
         {
 452  0
             throw new SurefireBooterForkException( "Unable to find appropriate constructor for test suite '"
 453  
                 + className + "': " + e.getMessage(), e );
 454  0
         }
 455  
 
 456  
         Map testSets;
 457  
         try
 458  
         {
 459  0
             Method m = suite.getClass().getMethod( "locateTestSets", new Class[] { ClassLoader.class } );
 460  
 
 461  0
             testSets = (Map) m.invoke( suite, new Object[] { testsClassLoader } );
 462  
         }
 463  0
         catch ( IllegalAccessException e )
 464  
         {
 465  0
             throw new SurefireBooterForkException( "Error obtaining test sets", e );
 466  
         }
 467  0
         catch ( NoSuchMethodException e )
 468  
         {
 469  0
             throw new SurefireBooterForkException( "Error obtaining test sets", e );
 470  
         }
 471  0
         catch ( InvocationTargetException e )
 472  
         {
 473  0
             throw new SurefireBooterForkException( e.getTargetException().getMessage(), e.getTargetException() );
 474  0
         }
 475  0
         return testSets;
 476  
     }
 477  
 
 478  
     private int forkSuites( List testSuites, boolean showHeading, boolean showFooter )
 479  
         throws SurefireBooterForkException
 480  
     {
 481  0
         Properties properties = new Properties();
 482  
 
 483  0
         setForkProperties( testSuites, properties );
 484  
 
 485  0
         return fork( properties, showHeading, showFooter );
 486  
     }
 487  
 
 488  
     private int forkSuite( Object[] testSuite, Object testSet, boolean showHeading, boolean showFooter,
 489  
                                Properties properties )
 490  
         throws SurefireBooterForkException
 491  
     {
 492  0
         setForkProperties( Collections.singletonList( testSuite ), properties );
 493  
 
 494  0
         if ( testSet instanceof String )
 495  
         {
 496  0
             properties.setProperty( "testSet", (String) testSet );
 497  
         }
 498  
 
 499  0
         return fork( properties, showHeading, showFooter );
 500  
     }
 501  
 
 502  
     private void setForkProperties( List testSuites, Properties properties )
 503  
     {
 504  0
         addPropertiesForTypeHolder( reports, properties, REPORT_PROPERTY_PREFIX );
 505  0
         addPropertiesForTypeHolder( testSuites, properties, TEST_SUITE_PROPERTY_PREFIX );
 506  
 
 507  0
         for ( int i = 0; i < classPathUrls.size(); i++ )
 508  
         {
 509  0
             String url = (String) classPathUrls.get( i );
 510  0
             properties.setProperty( "classPathUrl." + i, url );
 511  
         }
 512  
 
 513  0
         for ( int i = 0; i < surefireClassPathUrls.size(); i++ )
 514  
         {
 515  0
             String url = (String) surefireClassPathUrls.get( i );
 516  0
             properties.setProperty( "surefireClassPathUrl." + i, url );
 517  
         }
 518  
 
 519  0
         properties.setProperty( "childDelegation", String.valueOf( childDelegation ) );
 520  0
         properties.setProperty( "enableAssertions", String.valueOf( enableAssertions ) );
 521  0
         properties.setProperty( "useSystemClassLoader", String.valueOf( useSystemClassLoader() ) );
 522  0
         properties.setProperty( "useManifestOnlyJar", String.valueOf( useManifestOnlyJar() ) );
 523  0
         properties.setProperty( "failIfNoTests", String.valueOf( failIfNoTests ) );
 524  0
     }
 525  
 
 526  
     private File writePropertiesFile( String name, Properties properties )
 527  
         throws IOException
 528  
     {
 529  0
         File file = File.createTempFile( name, "tmp" );
 530  0
         if ( !forkConfiguration.isDebug() )
 531  
         {
 532  0
             file.deleteOnExit();
 533  
         }
 534  
 
 535  0
         writePropertiesFile( file, name, properties );
 536  
 
 537  0
         return file;
 538  
     }
 539  
 
 540  
     private void writePropertiesFile( File file, String name, Properties properties )
 541  
         throws IOException
 542  
     {
 543  0
         FileOutputStream out = new FileOutputStream( file );
 544  
 
 545  
         try
 546  
         {
 547  0
             properties.store( out, name );
 548  
         }
 549  
         finally
 550  
         {
 551  0
             IOUtil.close( out );
 552  0
         }
 553  0
     }
 554  
 
 555  
     private void addPropertiesForTypeHolder( List typeHolderList, Properties properties, String propertyPrefix )
 556  
     {
 557  0
         for ( int i = 0; i < typeHolderList.size(); i++ )
 558  
         {
 559  0
             Object[] report = (Object[]) typeHolderList.get( i );
 560  
 
 561  0
             String className = (String) report[0];
 562  0
             Object[] params = (Object[]) report[1];
 563  
 
 564  0
             properties.setProperty( propertyPrefix + i, className );
 565  
 
 566  0
             if ( params != null )
 567  
             {
 568  0
                 String paramProperty = convert( params[0] );
 569  0
                 String typeProperty = params[0].getClass().getName();
 570  0
                 for ( int j = 1; j < params.length; j++ )
 571  
                 {
 572  0
                     paramProperty += "|";
 573  0
                     typeProperty += "|";
 574  0
                     if ( params[j] != null )
 575  
                     {
 576  0
                         paramProperty += convert( params[j] );
 577  0
                         typeProperty += params[j].getClass().getName();
 578  
                     }
 579  
                 }
 580  0
                 properties.setProperty( propertyPrefix + i + PARAMS_SUFIX, paramProperty );
 581  0
                 properties.setProperty( propertyPrefix + i + TYPES_SUFIX, typeProperty );
 582  
             }
 583  
         }
 584  0
     }
 585  
 
 586  
     private static String convert( Object param )
 587  
     {
 588  40
         if ( param instanceof File[] )
 589  
         {
 590  4
             File[] files = (File[]) param;
 591  4
             return "[" + StringUtils.join( files, "," ) + "]";
 592  
         }
 593  36
         else if ( param instanceof Properties )
 594  
         {
 595  12
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 596  
             try
 597  
             {
 598  12
                 ( (Properties) param ).store( baos, "" );
 599  12
                 return new String( baos.toByteArray(), "8859_1" );
 600  
             }
 601  0
             catch ( Exception e )
 602  
             {
 603  0
                 throw new RuntimeException ( "bug in property conversion", e );
 604  
             }
 605  
         }
 606  
         else
 607  
         {
 608  24
             return param.toString();
 609  
         }
 610  
     }
 611  
 
 612  
     private boolean useSystemClassLoader()
 613  
     {
 614  0
         return forkConfiguration.isUseSystemClassLoader() && ( isForked || forkConfiguration.isForking() );
 615  
     }
 616  
     
 617  
     private boolean useManifestOnlyJar()
 618  
     {
 619  0
         return forkConfiguration.isUseSystemClassLoader() && forkConfiguration.isUseManifestOnlyJar();
 620  
     }
 621  
 
 622  
     private int fork( Properties properties, boolean showHeading, boolean showFooter )
 623  
         throws SurefireBooterForkException
 624  
     {
 625  
         File surefireProperties;
 626  0
         File systemProperties = null;
 627  
         try
 628  
         {
 629  0
             surefireProperties = writePropertiesFile( "surefire", properties );
 630  0
             if ( forkConfiguration.getSystemProperties() != null )
 631  
             {
 632  0
                 systemProperties = writePropertiesFile( "surefire", forkConfiguration.getSystemProperties() );
 633  
             }
 634  
         }
 635  0
         catch ( IOException e )
 636  
         {
 637  0
             throw new SurefireBooterForkException( "Error creating properties files for forking", e );
 638  0
         }
 639  
 
 640  0
         List bootClasspath = new ArrayList( surefireBootClassPathUrls.size() + classPathUrls.size() );
 641  
 
 642  0
         bootClasspath.addAll( surefireBootClassPathUrls );
 643  
 
 644  0
         if ( useSystemClassLoader() )
 645  
         {
 646  0
             bootClasspath.addAll( classPathUrls );
 647  
         }
 648  
 
 649  0
         Commandline cli = forkConfiguration.createCommandLine( bootClasspath, useManifestOnlyJar() );
 650  
 
 651  0
         cli.createArg().setFile( surefireProperties );
 652  
 
 653  0
         if ( systemProperties != null )
 654  
         {
 655  0
             cli.createArg().setFile( systemProperties );
 656  
         }
 657  
 
 658  
         
 659  0
         ForkingStreamConsumer out = getForkingStreamConsumer( showHeading, showFooter, redirectTestOutputToFile );
 660  
 
 661  
         StreamConsumer err;
 662  
         
 663  0
         if ( redirectTestOutputToFile )
 664  
         {
 665  0
             err = out;
 666  
         }
 667  
         else
 668  
         { 
 669  0
             err = getForkingStreamConsumer( showHeading, showFooter, redirectTestOutputToFile );
 670  
         }
 671  
 
 672  0
         if ( forkConfiguration.isDebug() )
 673  
         {
 674  0
             System.out.println( "Forking command line: " + cli );
 675  
         }
 676  
 
 677  
         int returnCode;
 678  
 
 679  
         try
 680  
         {
 681  0
             returnCode = CommandLineUtils.executeCommandLine( cli, out, err, forkedProcessTimeoutInSeconds );
 682  
         }
 683  0
         catch ( CommandLineException e )
 684  
         {
 685  0
             throw new SurefireBooterForkException( "Error while executing forked tests.", e );
 686  0
         }
 687  
 
 688  0
         if ( redirectTestOutputToFile )
 689  
         {
 690  
             // ensure the FileOutputConsumerProxy flushes/closes the output file
 691  
             try
 692  
             {
 693  0
                 out.getOutputConsumer().testSetCompleted();
 694  
             }
 695  0
             catch ( Exception e )
 696  
             {
 697  
                 // the FileOutputConsumerProxy might throw an IllegalStateException but that's not of interest now
 698  0
             }
 699  
         }
 700  
 
 701  0
         if ( surefireProperties != null && surefireProperties.exists() )
 702  
         {
 703  0
             FileInputStream inStream = null;
 704  
             try
 705  
             {
 706  0
                 inStream = new FileInputStream( surefireProperties );
 707  
 
 708  0
                 properties.load( inStream );
 709  
             }
 710  0
             catch ( FileNotFoundException e )
 711  
             {
 712  0
                 throw new SurefireBooterForkException( "Unable to reload properties file from forked process", e );
 713  
             }
 714  0
             catch ( IOException e )
 715  
             {
 716  0
                 throw new SurefireBooterForkException( "Unable to reload properties file from forked process", e );
 717  
             }
 718  
             finally
 719  
             {
 720  0
                 IOUtil.close( inStream );
 721  0
             }
 722  
         }
 723  
 
 724  0
         return returnCode;
 725  
     }
 726  
 
 727  
     private ClassLoader createClassLoader( List classPathUrls, ClassLoader parent )
 728  
         throws MalformedURLException
 729  
     {
 730  0
         return createClassLoader( classPathUrls, parent, false );
 731  
     }
 732  
 
 733  
     private ClassLoader createClassLoader( List classPathUrls, ClassLoader parent, boolean childDelegation )
 734  
         throws MalformedURLException
 735  
     {
 736  0
         List urls = new ArrayList();
 737  
 
 738  0
         for ( Iterator i = classPathUrls.iterator(); i.hasNext(); )
 739  
         {
 740  0
             String url = (String) i.next();
 741  
 
 742  0
             if ( url != null )
 743  
             {
 744  0
                 File f = new File( url );
 745  0
                 urls.add( UrlUtils.getURL( f ) );
 746  
             }
 747  0
         }
 748  
 
 749  0
         IsolatedClassLoader classLoader = new IsolatedClassLoader( parent, childDelegation );
 750  0
         if ( assertionStatusMethod != null )
 751  
         {
 752  
             try
 753  
             {
 754  0
                 Object[] args = new Object[] { enableAssertions ? Boolean.TRUE : Boolean.FALSE };
 755  0
                 if ( parent != null )
 756  
                 {
 757  0
                     assertionStatusMethod.invoke( parent, args );
 758  
                 }
 759  0
                 assertionStatusMethod.invoke( classLoader, args );
 760  
             }
 761  0
             catch ( IllegalAccessException e )
 762  
             {
 763  0
                 throw new NestedRuntimeException( "Unable to access the assertion enablement method", e );
 764  
             }
 765  0
             catch ( InvocationTargetException e )
 766  
             {
 767  0
                 throw new NestedRuntimeException( "Unable to invoke the assertion enablement method", e );
 768  0
             }
 769  
         }
 770  0
         for ( Iterator iter = urls.iterator(); iter.hasNext(); )
 771  
         {
 772  0
             URL url = (URL) iter.next();
 773  0
             classLoader.addURL( url );
 774  0
         }
 775  0
         return classLoader;
 776  
     }
 777  
 
 778  
     private static List processStringList( String stringList )
 779  
     {
 780  8
         String sl = stringList;
 781  
 
 782  8
         if ( sl.startsWith( "[" ) && sl.endsWith( "]" ) )
 783  
         {
 784  8
             sl = sl.substring( 1, sl.length() - 1 );
 785  
         }
 786  
 
 787  8
         List list = new ArrayList();
 788  
 
 789  8
         String[] stringArray = StringUtils.split( sl, "," );
 790  
 
 791  12
         for ( int i = 0; i < stringArray.length; i++ )
 792  
         {
 793  4
             list.add( stringArray[i].trim() );
 794  
         }
 795  8
         return list;
 796  
     }
 797  
 
 798  
     private static Properties loadProperties( File file )
 799  
         throws IOException
 800  
     {
 801  0
         Properties p = new Properties();
 802  
 
 803  0
         if ( file != null && file.exists() )
 804  
         {
 805  0
             FileInputStream inStream = new FileInputStream( file );
 806  
             try
 807  
             {
 808  0
                 p.load( inStream );
 809  
             }
 810  
             finally
 811  
             {
 812  0
                 IOUtil.close( inStream );
 813  0
             }
 814  
         }
 815  
 
 816  0
         return p;
 817  
     }
 818  
 
 819  
     private static void setSystemProperties( File file )
 820  
         throws IOException
 821  
     {
 822  0
         Properties p = loadProperties( file );
 823  
 
 824  0
         for ( Iterator i = p.keySet().iterator(); i.hasNext(); )
 825  
         {
 826  0
             String key = (String) i.next();
 827  
 
 828  0
             System.setProperty( key, p.getProperty( key ) );
 829  0
         }
 830  0
     }
 831  
 
 832  
     private static Object[] constructParamObjects( String paramProperty, String typeProperty )
 833  
     {
 834  40
         Object[] paramObjects = null;
 835  40
         if ( paramProperty != null )
 836  
         {
 837  
             // bit of a glitch that it need sto be done twice to do an odd number of vertical bars (eg |||, |||||).
 838  40
             String[] params =
 839  
                 StringUtils.split( StringUtils.replace( StringUtils.replace( paramProperty, "||", "| |" ),
 840  
                                                         "||", "| |" ), "|" );
 841  40
             String[] types =
 842  
                 StringUtils.split( StringUtils.replace( StringUtils.replace( typeProperty, "||", "| |" ),
 843  
                                                         "||", "| |" ), "|" );
 844  
 
 845  40
             paramObjects = new Object[params.length];
 846  
 
 847  80
             for ( int i = 0; i < types.length; i++ )
 848  
             {
 849  40
                 if ( types[i].trim().length() == 0 )
 850  
                 {
 851  0
                     params[i] = null;
 852  
                 }
 853  40
                 else if ( types[i].equals( String.class.getName() ) )
 854  
                 {
 855  4
                     paramObjects[i] = params[i];
 856  
                 }
 857  36
                 else if ( types[i].equals( File.class.getName() ) )
 858  
                 {
 859  4
                     paramObjects[i] = new File( params[i] );
 860  
                 }
 861  32
                 else if ( types[i].equals( File[].class.getName() ) )
 862  
                 {
 863  4
                     List stringList = processStringList( params[i] );
 864  4
                     File[] fileList = new File[stringList.size()];
 865  8
                     for ( int j = 0; j < stringList.size(); j++ )
 866  
                     {
 867  4
                         fileList[j] = new File( (String) stringList.get( j ) );
 868  
                     }
 869  4
                     paramObjects[i] = fileList;
 870  4
                 }
 871  28
                 else if ( types[i].equals( ArrayList.class.getName() ) )
 872  
                 {
 873  4
                     paramObjects[i] = processStringList( params[i] );
 874  
                 }
 875  24
                 else if ( types[i].equals( Boolean.class.getName() ) )
 876  
                 {
 877  8
                     paramObjects[i] = Boolean.valueOf( params[i] );
 878  
                 }
 879  16
                 else if ( types[i].equals( Integer.class.getName() ) )
 880  
                 {
 881  4
                     paramObjects[i] = Integer.valueOf( params[i] );
 882  
                 }
 883  12
                 else if ( types[i].equals( Properties.class.getName() ) )
 884  
                 {
 885  12
                     final Properties result = new Properties();
 886  12
                     final String value = params[i];
 887  
                     try
 888  
                     {
 889  12
                         ByteArrayInputStream bais = new ByteArrayInputStream( value.getBytes( "8859_1" ) );
 890  12
                         result.load( bais );
 891  
                     }
 892  0
                     catch ( Exception e )
 893  
                     {
 894  0
                         throw new RuntimeException( "bug in property conversion", e );
 895  12
                     }
 896  12
                     paramObjects[i] = result;
 897  12
                 }
 898  
                 else
 899  
                 {
 900  
                     // TODO: could attempt to construct with a String constructor if needed
 901  0
                     throw new IllegalArgumentException( "Unknown parameter type: " + types[i] );
 902  
                 }
 903  
             }
 904  
         }
 905  40
         return paramObjects;
 906  
     }
 907  
 
 908  
     /**
 909  
      * This method is invoked when Surefire is forked - this method parses and organizes the arguments passed to it and
 910  
      * then calls the Surefire class' run method. <p/> The system exit code will be 1 if an exception is thrown.
 911  
      *
 912  
      * @param args
 913  
      */
 914  
     public static void main( String[] args )
 915  
         throws Throwable
 916  
     {
 917  
         // noinspection CatchGenericClass,OverlyBroadCatchBlock
 918  
         try
 919  
         {
 920  0
             if ( args.length > 1 )
 921  
             {
 922  0
                 setSystemProperties( new File( args[1] ) );
 923  
             }
 924  
 
 925  0
             File surefirePropertiesFile = new File( args[0] );
 926  0
             Properties p = loadProperties( surefirePropertiesFile );
 927  
 
 928  0
             SortedMap classPathUrls = new TreeMap();
 929  
 
 930  0
             SortedMap surefireClassPathUrls = new TreeMap();
 931  
 
 932  0
             SurefireBooter surefireBooter = new SurefireBooter( true );
 933  
 
 934  0
             ForkConfiguration forkConfiguration = new ForkConfiguration();
 935  0
             forkConfiguration.setForkMode( "never" );
 936  0
             surefireBooter.setForkConfiguration( forkConfiguration );
 937  
 
 938  0
             for ( Enumeration e = p.propertyNames(); e.hasMoreElements(); )
 939  
             {
 940  0
                 String name = (String) e.nextElement();
 941  
 
 942  0
                 if ( name.startsWith( REPORT_PROPERTY_PREFIX ) && !name.endsWith( PARAMS_SUFIX )
 943  
                                 && !name.endsWith( TYPES_SUFIX ) )
 944  
                 {
 945  0
                     String className = p.getProperty( name );
 946  
 
 947  0
                     String params = p.getProperty( name + PARAMS_SUFIX );
 948  0
                     String types = p.getProperty( name + TYPES_SUFIX );
 949  0
                     surefireBooter.addReport( className, constructParamObjects( params, types ) );
 950  0
                 }
 951  0
                 else if ( name.startsWith( TEST_SUITE_PROPERTY_PREFIX ) && !name.endsWith( PARAMS_SUFIX )
 952  
                                 && !name.endsWith( TYPES_SUFIX ) )
 953  
                 {
 954  0
                     String className = p.getProperty( name );
 955  
 
 956  0
                     String params = p.getProperty( name + PARAMS_SUFIX );
 957  0
                     String types = p.getProperty( name + TYPES_SUFIX );
 958  0
                     surefireBooter.addTestSuite( className, constructParamObjects( params, types ) );
 959  0
                 }
 960  0
                 else if ( name.startsWith( "classPathUrl." ) )
 961  
                 {
 962  0
                     classPathUrls.put( Integer.valueOf( name.substring( name.indexOf( '.' ) + 1 ) ),
 963  
                                        p.getProperty( name ) );
 964  
                 }
 965  0
                 else if ( name.startsWith( "surefireClassPathUrl." ) )
 966  
                 {
 967  0
                     surefireClassPathUrls.put( Integer.valueOf( name.substring( name.indexOf( '.' ) + 1 ) ),
 968  
                                                p.getProperty( name ) );
 969  
                 }
 970  0
                 else if ( name.startsWith( "surefireBootClassPathUrl." ) )
 971  
                 {
 972  0
                     surefireBooter.addSurefireBootClassPathUrl( p.getProperty( name ) );
 973  
                 }
 974  0
                 else if ( "childDelegation".equals( name ) )
 975  
                 {
 976  0
                     surefireBooter.childDelegation =
 977  
                         Boolean.valueOf( p.getProperty( "childDelegation" ) ).booleanValue();
 978  
                 }
 979  0
                 else if ( "enableAssertions".equals( name ) )
 980  
                 {
 981  0
                     surefireBooter.enableAssertions =
 982  
                         Boolean.valueOf( p.getProperty( "enableAssertions" ) ).booleanValue();
 983  
                 }
 984  0
                 else if ( "useSystemClassLoader".equals( name ) )
 985  
                 {
 986  0
                     boolean value = Boolean.valueOf( p.getProperty( "useSystemClassLoader" ) ).booleanValue();
 987  0
                     surefireBooter.forkConfiguration.setUseSystemClassLoader( value );
 988  0
                 }
 989  0
                 else if ( "useManifestOnlyJar".equals( name ) )
 990  
                 {
 991  0
                     boolean value = Boolean.valueOf( p.getProperty( "useManifestOnlyJar" ) ).booleanValue();
 992  0
                     surefireBooter.forkConfiguration.setUseManifestOnlyJar( value );
 993  0
                 }
 994  0
                 else if ( "failIfNoTests".equals( name ) )
 995  
                 {
 996  0
                     boolean value = Boolean.valueOf( p.getProperty( "failIfNoTests" ) ).booleanValue();
 997  0
                     surefireBooter.setFailIfNoTests( value );
 998  
                 }
 999  0
             }
 1000  
 
 1001  0
             for ( Iterator cpi = classPathUrls.keySet().iterator(); cpi.hasNext(); )
 1002  
             {
 1003  0
                 String url = (String) classPathUrls.get( cpi.next() );
 1004  0
                 surefireBooter.addClassPathUrl( url );
 1005  0
             }
 1006  
 
 1007  0
             for ( Iterator scpi = surefireClassPathUrls.keySet().iterator(); scpi.hasNext(); )
 1008  
             {
 1009  0
                 String url = (String) surefireClassPathUrls.get( scpi.next() );
 1010  0
                 surefireBooter.addSurefireClassPathUrl( url );
 1011  0
             }
 1012  
 
 1013  0
             String testSet = p.getProperty( "testSet" );
 1014  
             int result;
 1015  0
             if ( testSet != null )
 1016  
             {
 1017  0
                 result = surefireBooter.runSuitesInProcess( testSet, p );
 1018  
             }
 1019  
             else
 1020  
             {
 1021  0
                 result = surefireBooter.runSuitesInProcess();
 1022  
             }
 1023  
 
 1024  0
             surefireBooter.writePropertiesFile( surefirePropertiesFile, "surefire", p );
 1025  
 
 1026  
             // noinspection CallToSystemExit
 1027  0
             System.exit( result );
 1028  
         }
 1029  0
         catch ( Throwable t )
 1030  
         {
 1031  
             // Just throwing does getMessage() and a local trace - we want to call printStackTrace for a full trace
 1032  
             // noinspection UseOfSystemOutOrSystemErr
 1033  0
             t.printStackTrace( System.err );
 1034  
             // noinspection ProhibitedExceptionThrown,CallToSystemExit
 1035  0
             System.exit( 1 );
 1036  0
         }
 1037  0
     }
 1038  
 
 1039  
     public void setChildDelegation( boolean childDelegation )
 1040  
     {
 1041  0
         this.childDelegation = childDelegation;
 1042  0
     }
 1043  
 
 1044  
     private ForkingStreamConsumer getForkingStreamConsumer( boolean showHeading, boolean showFooter,
 1045  
                                                      boolean redirectTestOutputToFile )
 1046  
     {
 1047  0
         OutputConsumer outputConsumer = new StandardOutputConsumer();
 1048  
 
 1049  0
         if ( redirectTestOutputToFile )
 1050  
         {
 1051  0
             outputConsumer = new FileOutputConsumerProxy( outputConsumer, getReportsDirectory() );
 1052  
         }
 1053  
 
 1054  0
         if ( !showHeading )
 1055  
         {
 1056  0
             outputConsumer = new SupressHeaderOutputConsumerProxy( outputConsumer );
 1057  
         }
 1058  0
         if ( !showFooter )
 1059  
         {
 1060  0
             outputConsumer = new SupressFooterOutputConsumerProxy( outputConsumer );
 1061  
         }
 1062  
 
 1063  0
         return new ForkingStreamConsumer( outputConsumer );
 1064  
     }
 1065  
 
 1066  
     public void setEnableAssertions( boolean enableAssertions )
 1067  
     {
 1068  0
         this.enableAssertions = enableAssertions;
 1069  0
     }
 1070  
     
 1071  
     public void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds )
 1072  
     {
 1073  0
         this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
 1074  0
     }
 1075  
 }