Coverage Report - org.apache.maven.plugin.surefire.booterclient.ForkStarter
 
Classes in this File Line Coverage Branch Coverage Complexity
ForkStarter
0%
0/141
0%
0/52
5
ForkStarter$1
0%
0/4
N/A
5
ForkStarter$2
0%
0/3
N/A
5
ForkStarter$InputStreamCloser
0%
0/9
0%
0/2
5
 
 1  
 package org.apache.maven.plugin.surefire.booterclient;
 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.File;
 23  
 import java.io.IOException;
 24  
 import java.io.InputStream;
 25  
 import java.util.ArrayList;
 26  
 import java.util.Iterator;
 27  
 import java.util.List;
 28  
 import java.util.Properties;
 29  
 import java.util.Queue;
 30  
 import java.util.concurrent.ArrayBlockingQueue;
 31  
 import java.util.concurrent.Callable;
 32  
 import java.util.concurrent.ConcurrentLinkedQueue;
 33  
 import java.util.concurrent.ExecutionException;
 34  
 import java.util.concurrent.ExecutorService;
 35  
 import java.util.concurrent.Future;
 36  
 import java.util.concurrent.LinkedBlockingQueue;
 37  
 import java.util.concurrent.ThreadPoolExecutor;
 38  
 import java.util.concurrent.TimeUnit;
 39  
 
 40  
 import org.apache.maven.plugin.surefire.AbstractSurefireMojo;
 41  
 import org.apache.maven.plugin.surefire.CommonReflector;
 42  
 import org.apache.maven.plugin.surefire.StartupReportConfiguration;
 43  
 import org.apache.maven.plugin.surefire.SurefireProperties;
 44  
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
 45  
 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
 46  
 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
 47  
 import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer;
 48  
 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
 49  
 import org.apache.maven.shared.utils.cli.CommandLineException;
 50  
 import org.apache.maven.shared.utils.cli.CommandLineTimeOutException;
 51  
 import org.apache.maven.shared.utils.cli.CommandLineUtils;
 52  
 import org.apache.maven.shared.utils.cli.ShutdownHookUtils;
 53  
 import org.apache.maven.surefire.booter.Classpath;
 54  
 import org.apache.maven.surefire.booter.ClasspathConfiguration;
 55  
 import org.apache.maven.surefire.booter.KeyValueSource;
 56  
 import org.apache.maven.surefire.booter.PropertiesWrapper;
 57  
 import org.apache.maven.surefire.booter.ProviderConfiguration;
 58  
 import org.apache.maven.surefire.booter.ProviderFactory;
 59  
 import org.apache.maven.surefire.booter.StartupConfiguration;
 60  
 import org.apache.maven.surefire.booter.SurefireBooterForkException;
 61  
 import org.apache.maven.surefire.booter.SurefireExecutionException;
 62  
 import org.apache.maven.surefire.booter.SystemPropertyManager;
 63  
 import org.apache.maven.surefire.providerapi.SurefireProvider;
 64  
 import org.apache.maven.surefire.report.StackTraceWriter;
 65  
 import org.apache.maven.surefire.suite.RunResult;
 66  
 import org.apache.maven.surefire.util.DefaultScanResult;
 67  
 
 68  
 /**
 69  
  * Starts the fork or runs in-process.
 70  
  * <p/>
 71  
  * Lives only on the plugin-side (not present in remote vms)
 72  
  * <p/>
 73  
  * Knows how to fork new vms and also how to delegate non-forking invocation to SurefireStarter directly
 74  
  * 
 75  
  * @author Jason van Zyl
 76  
  * @author Emmanuel Venisse
 77  
  * @author Brett Porter
 78  
  * @author Dan Fabulich
 79  
  * @author Carlos Sanchez
 80  
  * @author Kristian Rosenvold
 81  
  */
 82  0
 public class ForkStarter
 83  
 {
 84  
     /**
 85  
      * Closes an InputStream
 86  
      */
 87  
     private final class InputStreamCloser
 88  
         implements Runnable
 89  
     {
 90  
         private InputStream testProvidingInputStream;
 91  
 
 92  
         public InputStreamCloser( InputStream testProvidingInputStream )
 93  0
         {
 94  0
             this.testProvidingInputStream = testProvidingInputStream;
 95  0
         }
 96  
 
 97  
         public synchronized void run()
 98  
         {
 99  0
             if ( testProvidingInputStream != null )
 100  
             {
 101  
                 try
 102  
                 {
 103  0
                     testProvidingInputStream.close();
 104  
                 }
 105  0
                 catch ( IOException e )
 106  
                 {
 107  
                     // ignore
 108  0
                 }
 109  0
                 testProvidingInputStream = null;
 110  
             }
 111  0
         }
 112  
     }
 113  
 
 114  
     private final int forkedProcessTimeoutInSeconds;
 115  
 
 116  
     private final ProviderConfiguration providerConfiguration;
 117  
 
 118  
     private final StartupConfiguration startupConfiguration;
 119  
 
 120  
     private final ForkConfiguration forkConfiguration;
 121  
 
 122  
     private final StartupReportConfiguration startupReportConfiguration;
 123  
 
 124  
     private final DefaultReporterFactory defaultReporterFactory;
 125  
 
 126  0
     private static volatile int systemPropertiesFileCounter = 0;
 127  
 
 128  
     public ForkStarter( ProviderConfiguration providerConfiguration, StartupConfiguration startupConfiguration,
 129  
                         ForkConfiguration forkConfiguration, int forkedProcessTimeoutInSeconds,
 130  
                         StartupReportConfiguration startupReportConfiguration )
 131  0
     {
 132  0
         this.forkConfiguration = forkConfiguration;
 133  0
         this.providerConfiguration = providerConfiguration;
 134  0
         this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
 135  0
         this.startupConfiguration = startupConfiguration;
 136  0
         this.startupReportConfiguration = startupReportConfiguration;
 137  0
         defaultReporterFactory = new DefaultReporterFactory( startupReportConfiguration );
 138  0
     }
 139  
 
 140  
     public RunResult run( SurefireProperties effectiveSystemProperties, DefaultScanResult scanResult )
 141  
         throws SurefireBooterForkException, SurefireExecutionException
 142  
     {
 143  
         final RunResult result;
 144  
         try
 145  
         {
 146  0
             Properties providerProperties = providerConfiguration.getProviderProperties();
 147  0
             scanResult.writeTo( providerProperties );
 148  0
             if ( isForkOnce() )
 149  
             {
 150  0
                 final ForkClient forkClient =
 151  
                     new ForkClient( defaultReporterFactory, startupReportConfiguration.getTestVmSystemProperties() );
 152  0
                 result =
 153  
                     fork( null, new PropertiesWrapper( providerProperties ), forkClient, effectiveSystemProperties,
 154  
                           null );
 155  0
             }
 156  
             else
 157  
             {
 158  0
                 if ( forkConfiguration.isReuseForks() )
 159  
                 {
 160  0
                     result = runSuitesForkOnceMultiple( effectiveSystemProperties, forkConfiguration.getForkCount() );
 161  
                 }
 162  
                 else
 163  
                 {
 164  0
                     result = runSuitesForkPerTestSet( effectiveSystemProperties, forkConfiguration.getForkCount() );
 165  
                 }
 166  
             }
 167  
         }
 168  
         finally
 169  
         {
 170  0
             defaultReporterFactory.close();
 171  0
         }
 172  0
         return result;
 173  
     }
 174  
 
 175  
     private boolean isForkOnce()
 176  
     {
 177  0
         return forkConfiguration.isReuseForks() && 1 == forkConfiguration.getForkCount();
 178  
     }
 179  
 
 180  
     private RunResult runSuitesForkOnceMultiple( final SurefireProperties effectiveSystemProperties, int forkCount )
 181  
         throws SurefireBooterForkException
 182  
     {
 183  
 
 184  0
         ArrayList<Future<RunResult>> results = new ArrayList<Future<RunResult>>( forkCount );
 185  0
         ExecutorService executorService =
 186  
             new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS,
 187  
                                     new ArrayBlockingQueue<Runnable>( forkCount ) );
 188  
 
 189  
         try
 190  
         {
 191  
             // Ask to the executorService to run all tasks
 192  0
             RunResult globalResult = new RunResult( 0, 0, 0, 0 );
 193  
 
 194  0
             List<Class<?>> suites = new ArrayList<Class<?>>();
 195  0
             Iterator<Class<?>> suitesIterator = getSuitesIterator();
 196  0
             while ( suitesIterator.hasNext() )
 197  
             {
 198  0
                 suites.add( suitesIterator.next() );
 199  
             }
 200  0
             final Queue<String> messageQueue = new ConcurrentLinkedQueue<String>();
 201  0
             for ( Class<?> clazz : suites )
 202  
             {
 203  0
                 messageQueue.add( clazz.getName() );
 204  
             }
 205  
 
 206  0
             for ( int forkNum = 0; forkNum < forkCount && forkNum < suites.size(); forkNum++ )
 207  
             {
 208  0
                 Callable<RunResult> pf = new Callable<RunResult>()
 209  0
                 {
 210  
                     public RunResult call()
 211  
                         throws Exception
 212  
                     {
 213  0
                         TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream( messageQueue );
 214  
 
 215  0
                         ForkClient forkClient =
 216  
                             new ForkClient( defaultReporterFactory,
 217  
                                             startupReportConfiguration.getTestVmSystemProperties(),
 218  
                                             testProvidingInputStream );
 219  
 
 220  0
                         return fork( null, new PropertiesWrapper( providerConfiguration.getProviderProperties() ),
 221  
                                      forkClient, effectiveSystemProperties, testProvidingInputStream );
 222  
                     }
 223  
                 };
 224  
 
 225  0
                 results.add( executorService.submit( pf ) );
 226  
             }
 227  
 
 228  0
             for ( Future<RunResult> result : results )
 229  
             {
 230  
                 try
 231  
                 {
 232  0
                     RunResult cur = result.get();
 233  0
                     if ( cur != null )
 234  
                     {
 235  0
                         globalResult = globalResult.aggregate( cur );
 236  
                     }
 237  
                     else
 238  
                     {
 239  0
                         throw new SurefireBooterForkException( "No results for " + result.toString() );
 240  
                     }
 241  
                 }
 242  0
                 catch ( InterruptedException e )
 243  
                 {
 244  0
                     throw new SurefireBooterForkException( "Interrupted", e );
 245  
                 }
 246  0
                 catch ( ExecutionException e )
 247  
                 {
 248  0
                     throw new SurefireBooterForkException( "ExecutionException", e );
 249  0
                 }
 250  
             }
 251  0
             return globalResult;
 252  
 
 253  
         }
 254  
         finally
 255  
         {
 256  0
             closeExecutor( executorService );
 257  
         }
 258  
 
 259  
     }
 260  
 
 261  
     private RunResult runSuitesForkPerTestSet( final SurefireProperties effectiveSystemProperties, final int forkCount )
 262  
         throws SurefireBooterForkException
 263  
     {
 264  
 
 265  0
         ArrayList<Future<RunResult>> results = new ArrayList<Future<RunResult>>( 500 );
 266  0
         ExecutorService executorService =
 267  
             new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() );
 268  
 
 269  
         try
 270  
         {
 271  
             // Ask to the executorService to run all tasks
 272  0
             RunResult globalResult = new RunResult( 0, 0, 0, 0 );
 273  0
             final Iterator<Class<?>> suites = getSuitesIterator();
 274  0
             while ( suites.hasNext() )
 275  
             {
 276  0
                 final Object testSet = suites.next();
 277  0
                 Callable<RunResult> pf = new Callable<RunResult>()
 278  0
                 {
 279  
                     public RunResult call()
 280  
                         throws Exception
 281  
                     {
 282  0
                         ForkClient forkClient =
 283  
                             new ForkClient( defaultReporterFactory,
 284  
                                             startupReportConfiguration.getTestVmSystemProperties() );
 285  0
                         return fork( testSet, new PropertiesWrapper( providerConfiguration.getProviderProperties() ),
 286  
                                      forkClient, effectiveSystemProperties, null );
 287  
                     }
 288  
                 };
 289  0
                 results.add( executorService.submit( pf ) );
 290  
 
 291  0
             }
 292  
 
 293  0
             for ( Future<RunResult> result : results )
 294  
             {
 295  
                 try
 296  
                 {
 297  0
                     RunResult cur = result.get();
 298  0
                     if ( cur != null )
 299  
                     {
 300  0
                         globalResult = globalResult.aggregate( cur );
 301  
                     }
 302  
                     else
 303  
                     {
 304  0
                         throw new SurefireBooterForkException( "No results for " + result.toString() );
 305  
                     }
 306  
                 }
 307  0
                 catch ( InterruptedException e )
 308  
                 {
 309  0
                     throw new SurefireBooterForkException( "Interrupted", e );
 310  
                 }
 311  0
                 catch ( ExecutionException e )
 312  
                 {
 313  0
                     throw new SurefireBooterForkException( "ExecutionException", e );
 314  0
                 }
 315  
             }
 316  0
             return globalResult;
 317  
 
 318  
         }
 319  
         finally
 320  
         {
 321  0
             closeExecutor( executorService );
 322  
         }
 323  
 
 324  
     }
 325  
 
 326  
     private void closeExecutor( ExecutorService executorService )
 327  
         throws SurefireBooterForkException
 328  
     {
 329  0
         executorService.shutdown();
 330  
         try
 331  
         {
 332  
             // Should stop immediately, as we got all the results if we are here
 333  0
             executorService.awaitTermination( 60 * 60, TimeUnit.SECONDS );
 334  
         }
 335  0
         catch ( InterruptedException e )
 336  
         {
 337  0
             throw new SurefireBooterForkException( "Interrupted", e );
 338  0
         }
 339  0
     }
 340  
 
 341  
     private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkClient forkClient,
 342  
                             SurefireProperties effectiveSystemProperties,
 343  
                             TestProvidingInputStream testProvidingInputStream )
 344  
         throws SurefireBooterForkException
 345  
     {
 346  0
         int forkNumber = ForkNumberBucket.drawNumber();
 347  
         try
 348  
         {
 349  0
             return fork( testSet, providerProperties, forkClient, effectiveSystemProperties, forkNumber,
 350  
                          testProvidingInputStream );
 351  
         }
 352  
         finally
 353  
         {
 354  0
             ForkNumberBucket.returnNumber( forkNumber );
 355  
         }
 356  
     }
 357  
 
 358  
     private RunResult fork( Object testSet, KeyValueSource providerProperties, ForkClient forkClient,
 359  
                             SurefireProperties effectiveSystemProperties, int forkNumber,
 360  
                             TestProvidingInputStream testProvidingInputStream )
 361  
         throws SurefireBooterForkException
 362  
     {
 363  
         File surefireProperties;
 364  0
         File systPropsFile = null;
 365  
         try
 366  
         {
 367  0
             BooterSerializer booterSerializer = new BooterSerializer( forkConfiguration );
 368  
 
 369  0
             surefireProperties =
 370  
                 booterSerializer.serialize( providerProperties, providerConfiguration, startupConfiguration, testSet,
 371  
                                             null != testProvidingInputStream );
 372  
 
 373  0
             if ( effectiveSystemProperties != null )
 374  
             {
 375  0
                 SurefireProperties filteredProperties =
 376  
                     AbstractSurefireMojo.createCopyAndReplaceForkNumPlaceholder( effectiveSystemProperties, forkNumber );
 377  0
                 systPropsFile =
 378  
                     SystemPropertyManager.writePropertiesFile( filteredProperties,
 379  
                                                                forkConfiguration.getTempDirectory(), "surefire_"
 380  
                                                                    + systemPropertiesFileCounter++,
 381  
                                                                forkConfiguration.isDebug() );
 382  
             }
 383  
         }
 384  0
         catch ( IOException e )
 385  
         {
 386  0
             throw new SurefireBooterForkException( "Error creating properties files for forking", e );
 387  0
         }
 388  
 
 389  0
         final Classpath bootClasspathConfiguration = forkConfiguration.getBootClasspath();
 390  
 
 391  0
         final Classpath additionlClassPathUrls =
 392  
             startupConfiguration.useSystemClassLoader() ? startupConfiguration.getClasspathConfiguration().getTestClasspath()
 393  
                             : null;
 394  
 
 395  
         // Surefire-booter + all test classes if "useSystemClassloader"
 396  
         // Surefire-booter if !useSystemClassLoader
 397  0
         Classpath bootClasspath = Classpath.join( bootClasspathConfiguration, additionlClassPathUrls );
 398  
 
 399  
         @SuppressWarnings( "unchecked" )
 400  0
         OutputStreamFlushableCommandline cli =
 401  
             forkConfiguration.createCommandLine( bootClasspath.getClassPath(),
 402  
                                                  startupConfiguration.getClassLoaderConfiguration(),
 403  
                                                  startupConfiguration.isShadefire(), forkNumber );
 404  
 
 405  
         final InputStreamCloser inputStreamCloser;
 406  
         final Thread inputStreamCloserHook;
 407  0
         if ( testProvidingInputStream != null )
 408  
         {
 409  0
             testProvidingInputStream.setFlushReceiverProvider( cli );
 410  0
             inputStreamCloser = new InputStreamCloser( testProvidingInputStream );
 411  0
             inputStreamCloserHook = new Thread( inputStreamCloser );
 412  0
             ShutdownHookUtils.addShutDownHook( inputStreamCloserHook );
 413  
         }
 414  
         else
 415  
         {
 416  0
             inputStreamCloser = null;
 417  0
             inputStreamCloserHook = null;
 418  
         }
 419  
 
 420  0
         cli.createArg().setFile( surefireProperties );
 421  
 
 422  0
         if ( systPropsFile != null )
 423  
         {
 424  0
             cli.createArg().setFile( systPropsFile );
 425  
         }
 426  
 
 427  0
         ThreadedStreamConsumer threadedStreamConsumer = new ThreadedStreamConsumer( forkClient );
 428  
 
 429  0
         if ( forkConfiguration.isDebug() )
 430  
         {
 431  0
             System.out.println( "Forking command line: " + cli );
 432  
         }
 433  
 
 434  0
         RunResult runResult = null;
 435  
 
 436  
         try
 437  
         {
 438  0
             final int timeout = forkedProcessTimeoutInSeconds > 0 ? forkedProcessTimeoutInSeconds : 0;
 439  0
             final int result =
 440  
                 CommandLineUtils.executeCommandLine( cli, testProvidingInputStream, threadedStreamConsumer,
 441  
                                                      threadedStreamConsumer, timeout, inputStreamCloser );
 442  0
             if ( result != RunResult.SUCCESS )
 443  
             {
 444  0
                 throw new SurefireBooterForkException( "Error occurred in starting fork, check output in log" );
 445  
             }
 446  
 
 447  0
         }
 448  0
         catch ( CommandLineTimeOutException e )
 449  
         {
 450  0
             runResult = RunResult.timeout( defaultReporterFactory.getGlobalRunStatistics().getRunResult() );
 451  0
         }
 452  0
         catch ( CommandLineException e )
 453  
         {
 454  0
             runResult = RunResult.failure( defaultReporterFactory.getGlobalRunStatistics().getRunResult(), e );
 455  0
             throw new SurefireBooterForkException( "Error while executing forked tests.", e.getCause() );
 456  
         }
 457  
         finally
 458  
         {
 459  0
             threadedStreamConsumer.close();
 460  0
             if ( inputStreamCloser != null )
 461  
             {
 462  0
                 inputStreamCloser.run();
 463  0
                 ShutdownHookUtils.removeShutdownHook( inputStreamCloserHook );
 464  
             }
 465  0
             if ( runResult == null )
 466  
             {
 467  0
                 runResult = defaultReporterFactory.getGlobalRunStatistics().getRunResult();
 468  
             }
 469  0
             if ( !runResult.isTimeout() )
 470  
             {
 471  0
                 StackTraceWriter errorInFork = forkClient.getErrorInFork();
 472  0
                 if ( errorInFork != null )
 473  
                 {
 474  
                     // noinspection ThrowFromFinallyBlock
 475  0
                     throw new RuntimeException( "There was an error in the forked process\n"
 476  
                         + errorInFork.writeTraceToString() );
 477  
                 }
 478  0
                 if ( !forkClient.isSaidGoodBye() )
 479  
                 {
 480  
                     // noinspection ThrowFromFinallyBlock
 481  0
                     throw new RuntimeException(
 482  
                                                 "The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?"
 483  
                                                     + "\nCommand was" + cli.toString() );
 484  
                 }
 485  
 
 486  
             }
 487  0
             forkClient.close( runResult.isTimeout() );
 488  0
         }
 489  
 
 490  0
         return runResult;
 491  
     }
 492  
 
 493  
     @SuppressWarnings( "unchecked" )
 494  
     private Iterator<Class<?>> getSuitesIterator()
 495  
         throws SurefireBooterForkException
 496  
     {
 497  
         try
 498  
         {
 499  0
             final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
 500  0
             ClassLoader testsClassLoader = classpathConfiguration.createTestClassLoader( false );
 501  0
             ClassLoader surefireClassLoader = classpathConfiguration.createInprocSurefireClassLoader( testsClassLoader );
 502  
 
 503  0
             CommonReflector commonReflector = new CommonReflector( surefireClassLoader );
 504  0
             Object reporterFactory = commonReflector.createReportingReporterFactory( startupReportConfiguration );
 505  
 
 506  0
             final ProviderFactory providerFactory =
 507  
                 new ProviderFactory( startupConfiguration, providerConfiguration, surefireClassLoader,
 508  
                                      testsClassLoader, reporterFactory );
 509  0
             SurefireProvider surefireProvider = providerFactory.createProvider( false );
 510  0
             return surefireProvider.getSuites();
 511  
         }
 512  0
         catch ( SurefireExecutionException e )
 513  
         {
 514  0
             throw new SurefireBooterForkException( "Unable to create classloader to find test suites", e );
 515  
         }
 516  
     }
 517  
 }