Author: krosenvold Date: Thu Dec 22 21:55:05 2011 New Revision: 1222474 URL: http://svn.apache.org/viewvc?rev=1222474&view=rev Log: [SUREFIRE-799] Allow test parallelisation when forkMode=always
Patch by nkeyval, applied with review changes from me. Added: maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderParallelForksIT.java (with props) Modified: maven/surefire/trunk/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java maven/surefire/trunk/maven-surefire-common/pom.xml maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java maven/surefire/trunk/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderIT.java maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/pom.xml Modified: maven/surefire/trunk/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java (original) +++ maven/surefire/trunk/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java Thu Dec 22 21:55:05 2011 @@ -350,8 +350,8 @@ public class IntegrationTestMojo private Boolean failIfNoTests; /** - * Option to specify the forking mode. Can be "never", "once" or "always". "none" and "pertest" are also accepted - * for backwards compatibility. "always" forks for each test-class. + * Option to specify the forking mode. Can be "never", "once", "always" or "perthread". "none" and "pertest" are also accepted + * for backwards compatibility. "always" forks for each test-class. "perthread" will create "threadCount" parallel forks. * * @parameter expression="${forkMode}" default-value="once" * @since 2.1 @@ -472,8 +472,9 @@ public class IntegrationTestMojo private String testNGArtifactName; /** - * (TestNG/JUnit 4.7 provider only) The attribute thread-count allows you to specify how many threads should be - * allocated for this execution. Only makes sense to use in conjunction with the <code>parallel</code> parameter. + * (forkMode=perthread or TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be + * allocated for this execution. Only makes sense to use in conjunction with the <code>parallel</code> parameter. (forkMode=perthread + * does not support/require the <code>parallel</code> parameter) * * @parameter expression="${threadCount}" * @since 2.2 Modified: maven/surefire/trunk/maven-surefire-common/pom.xml URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/pom.xml?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/maven-surefire-common/pom.xml (original) +++ maven/surefire/trunk/maven-surefire-common/pom.xml Thu Dec 22 21:55:05 2011 @@ -97,24 +97,4 @@ </plugin> </plugins> </build> - - <profiles> - <!-- Force JDK 1.4 for this one, plugins can never be built on 1.3 --> - <profile> - <id>jdk1.3</id> - <build> - <pluginManagement> - <plugins> - <plugin> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <fork>false</fork> - <compilerVersion>1.4</compilerVersion> - </configuration> - </plugin> - </plugins> - </pluginManagement> - </build> - </profile> - </profiles> </project> Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java (original) +++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java Thu Dec 22 21:55:05 2011 @@ -260,7 +260,8 @@ public abstract class AbstractSurefireMo if ( this.getThreadCount() > 0 ) { - getProperties().setProperty( ProviderParameterNames.THREADCOUNT_PROP, Integer.toString( this.getThreadCount() ) ); + getProperties().setProperty( ProviderParameterNames.THREADCOUNT_PROP, + Integer.toString( this.getThreadCount() ) ); } if ( this.getObjectFactory() != null ) { @@ -317,7 +318,8 @@ public abstract class AbstractSurefireMo } if ( this.getThreadCount() > 0 ) { - getProperties().setProperty( ProviderParameterNames.THREADCOUNT_PROP, Integer.toString( this.getThreadCount() ) ); + getProperties().setProperty( ProviderParameterNames.THREADCOUNT_PROP, + Integer.toString( this.getThreadCount() ) ); } getProperties().setProperty( "perCoreThreadCount", Boolean.toString( getPerCoreThreadCount() ) ); getProperties().setProperty( "useUnlimitedThreads", Boolean.toString( getUseUnlimitedThreads() ) ); @@ -727,6 +729,15 @@ public abstract class AbstractSurefireMo setEnableAssertions( false ); } } + + if ( fork.getForkMode().equals( ForkConfiguration.FORK_PERTHREAD ) ) + { + fork.setThreadCount( getThreadCount() ); + } + else + { + fork.setThreadCount( 1 ); + } } return fork; } Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java (original) +++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkConfiguration.java Thu Dec 22 21:55:05 2011 @@ -54,6 +54,10 @@ public class ForkConfiguration public static final String FORK_NEVER = "never"; + public static final String FORK_PERTHREAD = "perthread"; + + private int threadCount; + private final Classpath bootClasspathConfiguration; private final String forkMode; @@ -96,7 +100,8 @@ public class ForkConfiguration { return FORK_NEVER; } - else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) || forkMode.equals( FORK_ALWAYS ) ) + else if ( forkMode.equals( FORK_NEVER ) || forkMode.equals( FORK_ONCE ) || + forkMode.equals( FORK_ALWAYS ) || forkMode.equals( FORK_PERTHREAD ) ) { return forkMode; } @@ -302,4 +307,14 @@ public class ForkConfiguration { return tempDirectory; } + + public int getThreadCount() + { + return threadCount; + } + + public void setThreadCount( int threadCount ) + { + this.threadCount = threadCount; + } } Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java (original) +++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java Thu Dec 22 21:55:05 2011 @@ -21,8 +21,16 @@ package org.apache.maven.plugin.surefire import java.io.File; import java.io.IOException; +import java.util.ArrayList; import java.util.Iterator; import java.util.Properties; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; import org.apache.maven.plugin.surefire.CommonReflector; import org.apache.maven.plugin.surefire.booterclient.output.ForkClient; import org.apache.maven.plugin.surefire.booterclient.output.ThreadedStreamConsumer; @@ -72,6 +80,13 @@ public class ForkStarter private final StartupReportConfiguration startupReportConfiguration; + private final FileReporterFactory fileReporterFactory; + + private static volatile int systemPropertiesFileCounter = 0; + + private static final Object lock = new Object(); + + public ForkStarter( ProviderConfiguration providerConfiguration, StartupConfiguration startupConfiguration, ForkConfiguration forkConfiguration, int forkedProcessTimeoutInSeconds, StartupReportConfiguration startupReportConfiguration ) @@ -81,28 +96,31 @@ public class ForkStarter this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds; this.startupConfiguration = startupConfiguration; this.startupReportConfiguration = startupReportConfiguration; + fileReporterFactory = new FileReporterFactory( startupReportConfiguration ); } public RunResult run() throws SurefireBooterForkException, SurefireExecutionException { final RunResult result; - final String requestedForkMode = forkConfiguration.getForkMode(); - final FileReporterFactory fileReporterFactory = new FileReporterFactory( startupReportConfiguration ); try { - final ForkClient forkClient = - new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() ); - final RunStatistics globalRunStatistics = fileReporterFactory.getGlobalRunStatistics(); if ( ForkConfiguration.FORK_ONCE.equals( requestedForkMode ) ) { - result = fork( null, providerConfiguration.getProviderProperties(), forkClient, globalRunStatistics ); + final ForkClient forkClient = + new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() ); + result = fork( null, providerConfiguration.getProviderProperties(), forkClient, + fileReporterFactory.getGlobalRunStatistics() ); } else if ( ForkConfiguration.FORK_ALWAYS.equals( requestedForkMode ) ) { - result = runSuitesForkPerTestSet( providerConfiguration.getProviderProperties(), forkClient, - globalRunStatistics ); + result = runSuitesForkPerTestSet( providerConfiguration.getProviderProperties(), 1 ); + } + else if ( ForkConfiguration.FORK_PERTHREAD.equals( requestedForkMode ) ) + { + result = runSuitesForkPerTestSet( providerConfiguration.getProviderProperties(), + forkConfiguration.getThreadCount() ); } else { @@ -116,24 +134,85 @@ public class ForkStarter return result; } - private RunResult runSuitesForkPerTestSet( Properties properties, ForkClient forkClient, - RunStatistics globalRunStatistics ) + private RunResult runSuitesForkPerTestSet( final Properties properties, int forkCount ) throws SurefireBooterForkException { - RunResult globalResult = new RunResult( 0, 0, 0, 0 ); - final Iterator suites = getSuitesIterator(); + ArrayList<Future<RunResult>> results = new ArrayList<Future<RunResult>>( 500 ); + ExecutorService executorService = new ThreadPoolExecutor( forkCount, forkCount, 60, TimeUnit.SECONDS, + new ArrayBlockingQueue<Runnable>( 500 ) ); - while ( suites.hasNext() ) + try { - Object testSet = suites.next(); - RunResult runResult = fork( testSet, properties, forkClient, globalRunStatistics ); - globalResult = globalResult.aggregate( runResult ); + // Ask to the executorService to run all tasks + RunResult globalResult = new RunResult( 0, 0, 0, 0 ); + final Iterator suites = getSuitesIterator(); + while ( suites.hasNext() ) + { + final Object testSet = suites.next(); + final ForkClient forkClient = + new ForkClient( fileReporterFactory, startupReportConfiguration.getTestVmSystemProperties() ); + Callable<RunResult> pf = new Callable<RunResult>() + { + public RunResult call() + throws Exception + { + return fork( testSet, properties, forkClient, fileReporterFactory.getGlobalRunStatistics() ); + } + }; + results.add( executorService.submit( pf ) ); + + } + + for ( Future<RunResult> result : results ) + { + try + { + RunResult cur = result.get(); + if ( cur != null ) + { + globalResult = globalResult.aggregate( cur ); + } + else + { + throw new SurefireBooterForkException( "No results for " + result.toString() ); + } + } + catch ( InterruptedException e ) + { + throw new SurefireBooterForkException( "Interrupted", e ); + } + catch ( ExecutionException e ) + { + throw new SurefireBooterForkException( "ExecutionException", e ); + } + } + return globalResult; + + } + finally + { + closeExecutor( executorService ); } - return globalResult; } + private void closeExecutor( ExecutorService executorService ) + throws SurefireBooterForkException + { + executorService.shutdown(); + try + { + // Should stop immediately, as we got all the results if we are here + executorService.awaitTermination( 60 * 60, TimeUnit.SECONDS ); + } + catch ( InterruptedException e ) + { + throw new SurefireBooterForkException( "Interrupted", e ); + } + } + + private RunResult fork( Object testSet, Properties properties, ForkClient forkClient, RunStatistics globalRunStatistics ) throws SurefireBooterForkException @@ -151,7 +230,9 @@ public class ForkStarter { systemProperties = SystemPropertyManager.writePropertiesFile( forkConfiguration.getSystemProperties(), forkConfiguration.getTempDirectory(), - "surefire", forkConfiguration.isDebug() ); + "surefire_" + + systemPropertiesFileCounter++, + forkConfiguration.isDebug() ); } } catch ( IOException e ) @@ -197,7 +278,7 @@ public class ForkStarter if ( result != RunResult.SUCCESS ) { - throw new SurefireBooterForkException( "Error occured in starting fork, check output in log" ); + throw new SurefireBooterForkException( "Error occurred in starting fork, check output in log" ); } threadedStreamConsumer.close(); forkClient.close(); @@ -241,5 +322,4 @@ public class ForkStarter } } - } Modified: maven/surefire/trunk/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java (original) +++ maven/surefire/trunk/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java Thu Dec 22 21:55:05 2011 @@ -319,8 +319,8 @@ public class SurefirePlugin private Boolean failIfNoTests; /** - * Option to specify the forking mode. Can be "never", "once" or "always". "none" and "pertest" are also accepted - * for backwards compatibility. "always" forks for each test-class. + * Option to specify the forking mode. Can be "never", "once", "always" or "perthread". "none" and "pertest" are also accepted + * for backwards compatibility. "always" forks for each test-class. "perthread" will create "threadCount" parallel forks. * * @parameter expression="${forkMode}" default-value="once" * @since 2.1 @@ -442,8 +442,9 @@ public class SurefirePlugin private String testNGArtifactName; /** - * (TestNG/JUnit 4.7 provider only) The attribute thread-count allows you to specify how many threads should be - * allocated for this execution. Only makes sense to use in conjunction with the <code>parallel</code> parameter. + * (forkMode=perthread or TestNG/JUnit 4.7 provider) The attribute thread-count allows you to specify how many threads should be + * allocated for this execution. Only makes sense to use in conjunction with the <code>parallel</code> parameter. (forkMode=perthread + * does not support/require the <code>parallel</code> parameter) * * @parameter expression="${threadCount}" * @since 2.2 Modified: maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderIT.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderIT.java?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderIT.java (original) +++ maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderIT.java Thu Dec 22 21:55:05 2011 @@ -19,19 +19,18 @@ package org.apache.maven.surefire.its; * under the License. */ -import org.apache.maven.it.VerificationException; -import org.apache.maven.it.Verifier; -import org.apache.maven.it.util.ResourceExtractor; -import org.apache.maven.surefire.its.misc.HelperAssertions; - import java.io.File; import java.io.IOException; import java.util.Calendar; import java.util.List; +import org.apache.maven.it.VerificationException; +import org.apache.maven.it.Verifier; +import org.apache.maven.it.util.ResourceExtractor; +import org.apache.maven.surefire.its.misc.HelperAssertions; /** * Verifies the runOrder setting and its effect - * + * * @author Kristian Rosenvold */ public class RunOrderIT @@ -45,7 +44,7 @@ public class RunOrderIT private File testDir; - private Verifier verifier; + protected Verifier verifier; public void setUp() throws IOException, VerificationException @@ -111,10 +110,17 @@ public class RunOrderIT HelperAssertions.assertTestSuiteResults( 3, 0, 0, 0, testDir ); } - private void executeTestsWithRunOrder( String runOrder ) + + protected String getForkMode() + { + return "once"; + } + + protected void executeTestsWithRunOrder( String runOrder ) throws VerificationException { List<String> goals = getInitialGoals(); + goals.add( "-DforkMode=" + getForkMode() ); goals.add( "-DrunOrder=" + runOrder ); goals.add( "test" ); executeGoals( verifier, goals ); @@ -129,7 +135,8 @@ public class RunOrderIT } } - private boolean testnamesAppearInSpecificOrder( String[] testnames ) throws VerificationException + private boolean testnamesAppearInSpecificOrder( String[] testnames ) + throws VerificationException { int i = 0; for ( String line : getLog() ) Added: maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderParallelForksIT.java URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderParallelForksIT.java?rev=1222474&view=auto ============================================================================== --- maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderParallelForksIT.java (added) +++ maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderParallelForksIT.java Thu Dec 22 21:55:05 2011 @@ -0,0 +1,31 @@ +package org.apache.maven.surefire.its; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +public class RunOrderParallelForksIT + extends RunOrderIT +{ + + @Override + protected String getForkMode() + { + return "perthread"; + } +} Propchange: maven/surefire/trunk/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/RunOrderParallelForksIT.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/pom.xml URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/pom.xml?rev=1222474&r1=1222473&r2=1222474&view=diff ============================================================================== --- maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/pom.xml (original) +++ maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/pom.xml Thu Dec 22 21:55:05 2011 @@ -11,7 +11,7 @@ <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>4.8.1</version> + <version>4.10</version> </dependency> </dependencies> @@ -34,7 +34,6 @@ <artifactId>maven-surefire-plugin</artifactId> <version>${surefire.version}</version> <configuration> - <forkMode>once</forkMode> <runOrder>${runOrder}</runOrder> <threadCount>2</threadCount> <perCoreThreadCount>false</perCoreThreadCount>