You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ag...@apache.org on 2013/08/09 21:20:59 UTC
[3/3] git commit: [SUREFIRE-1021] New Parallel Computer for JUnit
4.7+ Submitted by: Tibor17
[SUREFIRE-1021] New Parallel Computer for JUnit 4.7+
Submitted by: Tibor17
o Applied without any changes (just rebased w/o any conflicts and squashed)
Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/49c4a625
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/49c4a625
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/49c4a625
Branch: refs/heads/master
Commit: 49c4a62597383317a6d4abd8320d27d4a7cba354
Parents: e224e8b
Author: Tibor Digana <ti...@lycos.com>
Authored: Wed Jul 31 20:42:16 2013 +0200
Committer: Andreas Gudian <ag...@apache.org>
Committed: Fri Aug 9 21:18:18 2013 +0200
----------------------------------------------------------------------
.../plugin/failsafe/IntegrationTestMojo.java | 37 +
.../plugin/surefire/AbstractSurefireMojo.java | 275 +++++-
.../surefire/SurefireExecutionParameters.java | 8 +
.../maven/plugin/surefire/SurefirePlugin.java | 37 +
.../surefire/booter/BaseProviderFactory.java | 2 +-
.../surefire/booter/ProviderParameterNames.java | 10 +
.../surefire/its/Junit47WithCucumberIT.java | 7 +-
.../maven/surefire/its/TestSingleMethodIT.java | 6 +-
.../surefire/its/fixture/SurefireLauncher.java | 5 +
.../its/jiras/Surefire943ReportContentIT.java | 9 +-
.../src/test/resources/junit44-dep/pom.xml | 1 +
.../src/test/java/junit44Dep/BasicTest.java | 1 +
.../surefire/junitcore/JUnitCoreParameters.java | 99 +-
.../surefire/junitcore/JUnitCoreProvider.java | 17 +-
.../surefire/junitcore/JUnitCoreWrapper.java | 166 +++-
.../junitcore/ParallelComputerFactory.java | 368 ++++++++
.../pc/AbstractThreadPoolStrategy.java | 111 +++
.../maven/surefire/junitcore/pc/Balancer.java | 49 +
.../surefire/junitcore/pc/BalancerFactory.java | 68 ++
.../surefire/junitcore/pc/InvokerStrategy.java | 61 ++
.../pc/NonSharedThreadPoolStrategy.java | 54 ++
.../surefire/junitcore/pc/NullBalancer.java | 44 +
.../surefire/junitcore/pc/ParallelComputer.java | 83 ++
.../junitcore/pc/ParallelComputerBuilder.java | 416 +++++++++
.../maven/surefire/junitcore/pc/Scheduler.java | 335 +++++++
.../junitcore/pc/SchedulingStrategies.java | 73 ++
.../junitcore/pc/SchedulingStrategy.java | 105 +++
.../junitcore/pc/SharedThreadPoolStrategy.java | 84 ++
.../junitcore/pc/ThreadResourcesBalancer.java | 90 ++
.../surefire/junitcore/JUnit4SuiteTest.java | 49 +
.../junitcore/JUnitCoreParametersTest.java | 15 +-
.../junitcore/ParallelComputerFactoryTest.java | 907 +++++++++++++++++++
.../surefire/junitcore/Surefire746Test.java | 25 +-
.../pc/ParallelComputerBuilderTest.java | 436 +++++++++
.../surefire/junitcore/pc/RangeMatcher.java | 56 ++
.../junitcore/pc/SchedulingStrategiesTest.java | 155 ++++
.../maven/surefire/junitcore/pc/Stopwatch.java | 45 +
37 files changed, 4219 insertions(+), 90 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
----------------------------------------------------------------------
diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index 23f8dc1..41278c3 100644
--- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
+++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
@@ -137,6 +137,27 @@ public class IntegrationTestMojo
*/
@Parameter( property = "failsafe.timeout" )
private int forkedProcessTimeoutInSeconds;
+
+ /**
+ * Stop executing queued parallel JUnit tests after a certain number of seconds.
+ * If set to 0, wait forever, never timing out.
+ * Makes sense with specified <code>parallel</code> different from "none".
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "failsafe.parallel.timeout" )
+ private int parallelTestsTimeoutInSeconds;
+
+ /**
+ * Stop executing queued parallel JUnit tests
+ * and <em>interrupt</em> currently running tests after a certain number of seconds.
+ * If set to 0, wait forever, never timing out.
+ * Makes sense with specified <code>parallel</code> different from "none".
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "failsafe.parallel.forcedTimeout" )
+ private int parallelTestsTimeoutForcedInSeconds;
/**
* A list of <include> elements specifying the tests (by pattern) that should be included in testing. When not
@@ -433,6 +454,22 @@ public class IntegrationTestMojo
this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
}
+ public int getParallelTestsTimeoutInSeconds() {
+ return parallelTestsTimeoutInSeconds;
+ }
+
+ public void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ) {
+ this.parallelTestsTimeoutInSeconds = parallelTestsTimeoutInSeconds;
+ }
+
+ public int getParallelTestsTimeoutForcedInSeconds() {
+ return parallelTestsTimeoutForcedInSeconds;
+ }
+
+ public void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ) {
+ this.parallelTestsTimeoutForcedInSeconds = parallelTestsTimeoutForcedInSeconds;
+ }
+
public boolean isUseSystemClassLoader()
{
return useSystemClassLoader;
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index 677920e..5975d7d 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -483,7 +483,13 @@ public abstract class AbstractSurefireMojo
* respect their order of execution.
* <p/>
* (JUnit 4.7 provider) Supports values "classes"/"methods"/"both" to run in separate threads, as controlled by
- * <code>threadCount</code>.
+ * <code>threadCount</code>.<br/>
+ * <br/>
+ * Since version 2.16 (JUnit 4.7 provider), the value "both" is <strong>DEPRECATED</strong>.
+ * Use <strong>"classesAndMethods"</strong> instead.<br/>
+ * <br/>
+ * Since version 2.16 (JUnit 4.7 provider), additional vales are available
+ * "suites"/"suitesAndClasses"/"suitesAndMethods"/"classesAndMethods"/"all".
*
* @since 2.2
*/
@@ -491,6 +497,75 @@ public abstract class AbstractSurefireMojo
protected String parallel;
/**
+ * (JUnit 4.7 provider) The attribute thread-count-suites allows you to specify the concurrency in test suites, i.e.:
+ * <ul>
+ * <li>number of threads executing JUnit test suites if <code>threadCount</code> is 0 or unspecified</li>
+ * <li>In a special case <code>threadCountSuites</code> and <code>threadCount</code> are specified
+ * without <code>threadCountMethods</code> for <code>parallel</code>=<code>suitesAndMethods</code>.
+ * <br/>Example1: threadCount=8 and threadCountSuites=3, the number of parallel methods is varying from 5 to 7 in your tests.
+ * <br/>In another special case when <code>parallel</code>=<code>all</code> and the only <code>threadCountMethods</code>
+ * is unspecified, then threads from suites and classes are reused in favor of methods.
+ * <br/>Example2: parallel=all, threadCount=16 , threadCountSuites=2 , threadCountClasses=5,
+ * the number of parallel methods is varying from 9 to 14 in your tests.
+ * </li>
+ * <li>integer number which represents the weight in ratio between
+ * <em>threadCountSuites</em>:<code>threadCountClasses</code>:<code>threadCountMethods</code>.
+ * As an example 2 is 20% of <code>threadCount</code> if the ratio is <em>2</em>:3:5</li>
+ * <li>You can impose limitation on parallel suites if <code>useUnlimitedThreads</code> is specified.</li>
+ * </ul>
+ *
+ * Only makes sense to use in conjunction with the <code>parallel</code> parameter.
+ * The default value <code>0</code> behaves same as unspecified one.
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "threadCountSuites", defaultValue = "0" )
+ protected int threadCountSuites;
+
+ /**
+ * (JUnit 4.7 provider) The attribute thread-count-classes allows you to specify the concurrency in test classes, i.e.:
+ * <ul>
+ * <li>number of threads executing JUnit test classes if <code>threadCount</code> is 0 or unspecified</li>
+ * <li>In a special case <code>threadCountClasses</code> and <code>threadCount</code> are specified
+ * without <code>threadCountMethods</code> for <code>parallel</code>=<code>classesAndMethods</code>.
+ * <br/>Example1: threadCount=8 and threadCountClasses=3, the number of parallel methods is varying from 5 to 7 in your tests.
+ * <br/>In another special case when <code>parallel</code>=<code>all</code> and the only <code>threadCountMethods</code>
+ * is unspecified, then threads from suites and classes are reused in favor of methods.
+ * <br/>Example2: parallel=all, threadCount=16 , threadCountSuites=2 , threadCountClasses=5,
+ * the number of parallel methods is varying from 9 to 14 in your tests.
+ * </li>
+ * <li>integer number which represents the weight in ratio between
+ * <code>threadCountSuites</code>:<em>threadCountClasses</em>:<code>threadCountMethods</code>.
+ * As an example 3 is 30% of <code>threadCount</code> if the ratio is 2:<em>3</em>:5</li>
+ * <li>You can impose limitation on parallel classes if <code>useUnlimitedThreads</code> is specified.</li>
+ * </ul>
+ *
+ * Only makes sense to use in conjunction with the <code>parallel</code> parameter.
+ * The default value <code>0</code> behaves same as unspecified one.
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "threadCountClasses", defaultValue = "0" )
+ protected int threadCountClasses;
+
+ /**
+ * (JUnit 4.7 provider) The attribute thread-count-methods allows you to specify the concurrency in test methods, i.e.:
+ * <ul>
+ * <li>number of threads executing JUnit test methods if <code>threadCount</code> is 0 or unspecified;</li>
+ * <li>integer number which represents the weight in ratio between <code>threadCountSuites</code>:<code>threadCountClasses</code>:<em>threadCountMethods</em>.
+ * As an example 5 is 50% of <code>threadCount</code> if the ratio is 2:3:<em>5</em>.</li>
+ * <li>You can impose limitation on parallel methods if <code>useUnlimitedThreads</code> is specified.</li>
+ * </ul>
+ *
+ * Only makes sense to use in conjunction with the <code>parallel</code> parameter.
+ * The default value <code>0</code> behaves same as unspecified one.
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "threadCountMethods", defaultValue = "0" )
+ protected int threadCountMethods;
+
+ /**
* Whether to trim the stack trace in the reports to just the lines within the test, or show the full trace.
*
* @since 2.2
@@ -1046,23 +1121,171 @@ public abstract class AbstractSurefireMojo
* Converts old JUnit configuration parameters over to new properties based configuration
* method. (if any are defined the old way)
*/
- private void convertJunitCoreParameters()
+ private void convertJunitCoreParameters() throws MojoExecutionException
{
+ checkThreadCountEntity( getThreadCountSuites(), "suites" );
+ checkThreadCountEntity( getThreadCountClasses(), "classes" );
+ checkThreadCountEntity( getThreadCountMethods(), "methods" );
+
String usedParallel = ( getParallel() != null ) ? getParallel() : "none";
- String usedThreadCount = ( getThreadCount() > 0 ) ? Integer.toString( getThreadCount() ) : "2";
+ if ( !"none".equals( usedParallel ))
+ {
+ checkNonForkedThreads( parallel );
+ }
+
+ String usedThreadCount = Integer.toString( getThreadCount() );
getProperties().setProperty( ProviderParameterNames.PARALLEL_PROP, usedParallel );
getProperties().setProperty( ProviderParameterNames.THREADCOUNT_PROP, usedThreadCount );
getProperties().setProperty( "perCoreThreadCount", Boolean.toString( getPerCoreThreadCount() ) );
getProperties().setProperty( "useUnlimitedThreads", Boolean.toString( getUseUnlimitedThreads() ) );
+ getProperties().setProperty( ProviderParameterNames.THREADCOUNTSUITES_PROP, Integer.toString( getThreadCountSuites() ) );
+ getProperties().setProperty( ProviderParameterNames.THREADCOUNTCLASSES_PROP, Integer.toString( getThreadCountClasses() ) );
+ getProperties().setProperty( ProviderParameterNames.THREADCOUNTMETHODS_PROP, Integer.toString( getThreadCountMethods() ) );
+ getProperties().setProperty( ProviderParameterNames.PARALLEL_TIMEOUT_PROP,
+ Integer.toString( getParallelTestsTimeoutInSeconds() ) );
+ getProperties().setProperty( ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP,
+ Integer.toString( getParallelTestsTimeoutForcedInSeconds() ) );
String message =
"parallel='" + usedParallel + '\'' + ", perCoreThreadCount=" + getPerCoreThreadCount() + ", threadCount="
- + usedThreadCount + ", useUnlimitedThreads=" + getUseUnlimitedThreads();
+ + usedThreadCount + ", useUnlimitedThreads=" + getUseUnlimitedThreads() +
+ ", threadCountSuites=" + getThreadCountSuites() + ", threadCountClasses=" + getThreadCountClasses() +
+ ", threadCountMethods=" + getThreadCountMethods();
getLog().info( message );
}
+ private void checkNonForkedThreads( String parallel ) throws MojoExecutionException
+ {
+ if ( "suites".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountSuites() > 0 ) )
+ {
+ throw new MojoExecutionException( "Use threadCount or threadCountSuites > 0 or useUnlimitedThreads=true " +
+ "for parallel='suites'" );
+ }
+ setThreadCountClasses( 0 );
+ setThreadCountMethods( 0 );
+ }
+ else if ( "classes".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountClasses() > 0 ) )
+ {
+ throw new MojoExecutionException( "Use threadCount or threadCountClasses > 0 or useUnlimitedThreads=true " +
+ "for parallel='classes'" );
+ }
+ setThreadCountSuites( 0 );
+ setThreadCountMethods( 0 );
+ }
+ else if ( "methods".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads() || getThreadCount() > 0 ^ getThreadCountMethods() > 0 ) )
+ {
+ throw new MojoExecutionException( "Use threadCount or threadCountMethods > 0 or useUnlimitedThreads=true " +
+ "for parallel='methods'" );
+ }
+ setThreadCountSuites( 0 );
+ setThreadCountClasses( 0 );
+ }
+ else if ( "suitesAndClasses".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads()
+ || onlyThreadCount()
+ || getThreadCountSuites() > 0 && getThreadCountClasses() > 0
+ && getThreadCount() == 0 && getThreadCountMethods() == 0
+ || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountClasses() > 0
+ && getThreadCountMethods() == 0
+ || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCount() > getThreadCountSuites()
+ && getThreadCountClasses() == 0 && getThreadCountMethods() == 0 ) )
+ {
+ throw new MojoExecutionException( "Use useUnlimitedThreads=true, " +
+ "or only threadCount > 0, " +
+ "or (threadCountSuites > 0 and threadCountClasses > 0), " +
+ "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0) " +
+ "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " +
+ "for parallel='suitesAndClasses' or 'both'" );
+ }
+ setThreadCountMethods( 0 );
+ }
+ else if ( "suitesAndMethods".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads()
+ || onlyThreadCount()
+ || getThreadCountSuites() > 0 && getThreadCountMethods() > 0
+ && getThreadCount() == 0 && getThreadCountClasses() == 0
+ || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountMethods() > 0
+ && getThreadCountClasses() == 0
+ || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCount() > getThreadCountSuites()
+ && getThreadCountClasses() == 0 && getThreadCountMethods() == 0 ) )
+ {
+ throw new MojoExecutionException( "Use useUnlimitedThreads=true, " +
+ "or only threadCount > 0, " +
+ "or (threadCountSuites > 0 and threadCountMethods > 0), " +
+ "or (threadCount > 0 and threadCountSuites > 0 and threadCountMethods > 0), " +
+ "or (threadCount > 0 and threadCountSuites > 0 and threadCount > threadCountSuites) " +
+ "for parallel='suitesAndMethods'" );
+ }
+ setThreadCountClasses( 0 );
+ }
+ else if ( "both".equals( parallel ) || "classesAndMethods".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads()
+ || onlyThreadCount()
+ || getThreadCountClasses() > 0 && getThreadCountMethods() > 0
+ && getThreadCount() == 0 && getThreadCountSuites() == 0
+ || getThreadCount() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0
+ && getThreadCountSuites() == 0
+ || getThreadCount() > 0 && getThreadCountClasses() > 0 && getThreadCount() > getThreadCountClasses()
+ && getThreadCountSuites() == 0 && getThreadCountMethods() == 0 ) )
+ {
+ throw new MojoExecutionException( "Use useUnlimitedThreads=true, " +
+ "or only threadCount > 0, " +
+ "or (threadCountClasses > 0 and threadCountMethods > 0), " +
+ "or (threadCount > 0 and threadCountClasses > 0 and threadCountMethods > 0), " +
+ "or (threadCount > 0 and threadCountClasses > 0 and threadCount > threadCountClasses) " +
+ "for parallel='both' or parallel='classesAndMethods'" );
+ }
+ setThreadCountSuites( 0 );
+ }
+ else if ( "all".equals( parallel ) )
+ {
+ if ( !( getUseUnlimitedThreads()
+ || onlyThreadCount()
+ || getThreadCountSuites() > 0 && getThreadCountClasses() > 0 && getThreadCountMethods() > 0
+ || getThreadCount() > 0 && getThreadCountSuites() > 0 && getThreadCountClasses() > 0
+ && getThreadCountMethods() == 0
+ && getThreadCount() > ( getThreadCountSuites() + getThreadCountClasses() ) ) )
+ {
+ throw new MojoExecutionException( "Use useUnlimitedThreads=true, " +
+ "or only threadCount > 0, " +
+ "or (threadCountSuites > 0 and threadCountClasses > 0 and threadCountMethods > 0), " +
+ "or every thread-count is specified, " +
+ "or (threadCount > 0 and threadCountSuites > 0 and threadCountClasses > 0 " +
+ "and threadCount > threadCountSuites + threadCountClasses) " +
+ "for parallel='all'" );
+ }
+ }
+ else
+ {
+ throw new MojoExecutionException( "Illegal parallel='" + parallel + "'" );
+ }
+ }
+
+ private boolean onlyThreadCount()
+ {
+ return getThreadCount() > 0 && getThreadCountSuites() == 0 && getThreadCountClasses() == 0
+ && getThreadCountMethods() == 0;
+ }
+
+ private static void checkThreadCountEntity(int count, String entity) throws MojoExecutionException
+ {
+ if ( count < 0 )
+ {
+ throw new MojoExecutionException("parallel maven execution does not allow negative thread-count" + entity);
+ }
+ }
+
private boolean isJunit47Compatible( Artifact artifact )
{
return dependencyResolver.isWithinVersionSpec( artifact, "[4.7,)" );
@@ -1668,6 +1891,8 @@ public abstract class AbstractSurefireMojo
checksum.add( getArgLine() );
checksum.add( getDebugForkedProcess() );
checksum.add( getForkedProcessTimeoutInSeconds() );
+ checksum.add( getParallelTestsTimeoutInSeconds() );
+ checksum.add( getParallelTestsTimeoutForcedInSeconds() );
checksum.add( getEnvironmentVariables() );
checksum.add( getWorkingDirectory() );
checksum.add( isChildDelegation() );
@@ -1677,6 +1902,9 @@ public abstract class AbstractSurefireMojo
checksum.add( getJunitArtifact() );
checksum.add( getTestNGArtifactName() );
checksum.add( getThreadCount() );
+ checksum.add( getThreadCountSuites() );
+ checksum.add( getThreadCountClasses() );
+ checksum.add( getThreadCountMethods() );
checksum.add( getPerCoreThreadCount() );
checksum.add( getUseUnlimitedThreads() );
checksum.add( getParallel() );
@@ -2054,7 +2282,7 @@ public abstract class AbstractSurefireMojo
return true;
}
- public void addProviderProperties()
+ public void addProviderProperties() throws MojoExecutionException
{
}
@@ -2093,7 +2321,7 @@ public abstract class AbstractSurefireMojo
return junitDepArtifact != null || isAnyJunit4( junitArtifact );
}
- public void addProviderProperties()
+ public void addProviderProperties() throws MojoExecutionException
{
}
@@ -2137,7 +2365,7 @@ public abstract class AbstractSurefireMojo
return isAny47ProvidersForcers && ( isJunitArtifact47 || is47CompatibleJunitDep() );
}
- public void addProviderProperties()
+ public void addProviderProperties() throws MojoExecutionException
{
convertJunitCoreParameters();
convertGroupParameters();
@@ -2494,6 +2722,39 @@ public abstract class AbstractSurefireMojo
this.parallel = parallel;
}
+ public int getThreadCountSuites()
+ {
+ return threadCountSuites;
+ }
+
+ @SuppressWarnings( "UnusedDeclaration" )
+ public void setThreadCountSuites( int threadCountSuites )
+ {
+ this.threadCountSuites = threadCountSuites;
+ }
+
+ public int getThreadCountClasses()
+ {
+ return threadCountClasses;
+ }
+
+ @SuppressWarnings( "UnusedDeclaration" )
+ public void setThreadCountClasses( int threadCountClasses )
+ {
+ this.threadCountClasses = threadCountClasses;
+ }
+
+ public int getThreadCountMethods()
+ {
+ return threadCountMethods;
+ }
+
+ @SuppressWarnings( "UnusedDeclaration" )
+ public void setThreadCountMethods( int threadCountMethods )
+ {
+ this.threadCountMethods = threadCountMethods;
+ }
+
public boolean isTrimStackTrace()
{
return trimStackTrace;
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
----------------------------------------------------------------------
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
index 44c7d19..0b5a1af 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
@@ -101,6 +101,14 @@ public interface SurefireExecutionParameters
void setForkedProcessTimeoutInSeconds( int forkedProcessTimeoutInSeconds );
+ int getParallelTestsTimeoutInSeconds();
+
+ void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds );
+
+ int getParallelTestsTimeoutForcedInSeconds();
+
+ void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds );
+
boolean isUseSystemClassLoader();
void setUseSystemClassLoader( boolean useSystemClassLoader );
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index f0ac255..189161b 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -119,6 +119,27 @@ public class SurefirePlugin
*/
@Parameter( property = "surefire.timeout" )
private int forkedProcessTimeoutInSeconds;
+
+ /**
+ * Stop executing queued parallel JUnit tests after a certain number of seconds.
+ * If set to 0, wait forever, never timing out.
+ * Makes sense with specified <code>parallel</code> different from "none".
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "surefire.parallel.timeout" )
+ private int parallelTestsTimeoutInSeconds;
+
+ /**
+ * Stop executing queued parallel JUnit tests
+ * and <em>interrupt</em> currently running tests after a certain number of seconds.
+ * If set to 0, wait forever, never timing out.
+ * Makes sense with specified <code>parallel</code> different from "none".
+ *
+ * @since 2.16
+ */
+ @Parameter( property = "surefire.parallel.forcedTimeout" )
+ private int parallelTestsTimeoutForcedInSeconds;
/**
* A list of <include> elements specifying the tests (by pattern) that should be included in testing. When not
@@ -426,6 +447,22 @@ public class SurefirePlugin
this.forkedProcessTimeoutInSeconds = forkedProcessTimeoutInSeconds;
}
+ public int getParallelTestsTimeoutInSeconds() {
+ return parallelTestsTimeoutInSeconds;
+ }
+
+ public void setParallelTestsTimeoutInSeconds( int parallelTestsTimeoutInSeconds ) {
+ this.parallelTestsTimeoutInSeconds = parallelTestsTimeoutInSeconds;
+ }
+
+ public int getParallelTestsTimeoutForcedInSeconds() {
+ return parallelTestsTimeoutForcedInSeconds;
+ }
+
+ public void setParallelTestsTimeoutForcedInSeconds( int parallelTestsTimeoutForcedInSeconds ) {
+ this.parallelTestsTimeoutForcedInSeconds = parallelTestsTimeoutForcedInSeconds;
+ }
+
public void setTest( String test )
{
this.test = test;
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
index a5e7daf..dee42aa 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
@@ -92,7 +92,7 @@ public class BaseProviderFactory
private int getThreadCount()
{
final String threadcount = (String) providerProperties.get( ProviderParameterNames.THREADCOUNT_PROP );
- return threadcount == null ? 1 : Integer.parseInt( threadcount );
+ return threadcount == null ? 1 : Math.max( Integer.parseInt( threadcount ), 1 );
}
public RunOrderCalculator getRunOrderCalculator()
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java
index b70fbc9..72c16ac 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/ProviderParameterNames.java
@@ -31,4 +31,14 @@ public class ProviderParameterNames
public static final String PARALLEL_PROP = "parallel";
+ public static final String THREADCOUNTSUITES_PROP = "threadcountsuites";
+
+ public static final String THREADCOUNTCLASSES_PROP = "threadcountclasses";
+
+ public static final String THREADCOUNTMETHODS_PROP = "threadcountmethods";
+
+ public static final String PARALLEL_TIMEOUT_PROP = "paralleltimeout";
+
+ public static final String PARALLEL_TIMEOUTFORCED_PROP = "paralleltimeoutforced";
+
}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java
index 08f6f6c..09e857a 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/Junit47WithCucumberIT.java
@@ -60,7 +60,10 @@ public class Junit47WithCucumberIT
private void doTest( String parallel, int total )
{
- unpack( "junit47-cucumber" ).sysProp( "parallel", parallel ).executeTest().assertTestSuiteResults( total, 0, 2,
- 0 );
+ unpack( "junit47-cucumber" )
+ .sysProp( "parallel", parallel )
+ .sysProp( "threadCount", "2" )
+ .executeTest()
+ .assertTestSuiteResults( total, 0, 2, 0 );
}
}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java
index d96f480..39d2ab9 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/TestSingleMethodIT.java
@@ -54,8 +54,10 @@ public class TestSingleMethodIT
public void testJunit48parallel()
throws Exception
{
- unpack( "junit48-single-method" ).parallelClasses().executeTest().verifyErrorFreeLog().assertTestSuiteResults(
- 1, 0, 0, 0 );
+ unpack( "junit48-single-method" )
+ .executeTest()
+ .verifyErrorFreeLog()
+ .assertTestSuiteResults( 1, 0, 0, 0 );
}
@Test
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
index fffde85..7b65783 100755
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/fixture/SurefireLauncher.java
@@ -321,6 +321,11 @@ public class SurefireLauncher
}
+ public SurefireLauncher parallelSuites()
+ {
+ return parallel( "suites" );
+ }
+
public SurefireLauncher parallelClasses()
{
return parallel( "classes" );
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java
index fe72087..32aedbf 100644
--- a/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java
+++ b/surefire-integration-tests/src/test/java/org/apache/maven/surefire/its/jiras/Surefire943ReportContentIT.java
@@ -49,8 +49,13 @@ public class Surefire943ReportContentIT
private void doTest( String parallelMode )
throws Exception
{
- OutputValidator validator =
- unpack( "surefire-943-report-content" ).maven().sysProp( "parallel", parallelMode ).withFailure().executeTest();
+ OutputValidator validator = unpack( "surefire-943-report-content" )
+ .maven()
+ .sysProp( "parallel", parallelMode )
+ .sysProp( "threadCount", 4 )
+ .withFailure()
+ .executeTest();
+
validator.assertTestSuiteResults( 9, 0, 3, 3 );
validate( validator, "org.sample.module.My1Test", 1 );
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml b/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml
index 7f77872..6e6d476 100644
--- a/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml
+++ b/surefire-integration-tests/src/test/resources/junit44-dep/pom.xml
@@ -66,6 +66,7 @@
<version>${surefire.version}</version>
<configuration>
<parallel>classes</parallel>
+ <threadCount>1</threadCount>
</configuration>
</plugin>
</plugins>
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java
----------------------------------------------------------------------
diff --git a/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java b/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java
index 3db5d19..219b8da 100644
--- a/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java
+++ b/surefire-integration-tests/src/test/resources/junit44-dep/src/test/java/junit44Dep/BasicTest.java
@@ -54,6 +54,7 @@ public class BasicTest
public void testSetUp()
{
Assert.assertTrue( "setUp was not called", setUpCalled );
+ Assert.assertFalse( "tearDown was called", tearDownCalled );
Assert.assertThat( true, Is.is( true ) );
}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java
index a2dcea2..157ed58 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreParameters.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.junitcore;
* under the License.
*/
+import java.util.ArrayList;
+import java.util.Collection;
import java.util.Properties;
import org.apache.maven.surefire.booter.ProviderParameterNames;
@@ -33,6 +35,16 @@ class JUnitCoreParameters
private final int threadCount;
+ private final int threadCountSuites;
+
+ private final int threadCountClasses;
+
+ private final int threadCountMethods;
+
+ private final int parallelTestsTimeoutInSeconds;
+
+ private final int parallelTestsTimeoutForcedInSeconds;
+
private final Boolean useUnlimitedThreads;
public static final String PARALLEL_KEY = ProviderParameterNames.PARALLEL_PROP;
@@ -41,29 +53,70 @@ class JUnitCoreParameters
public static final String THREADCOUNT_KEY = ProviderParameterNames.THREADCOUNT_PROP;
+ public static final String THREADCOUNTSUITES_KEY = ProviderParameterNames.THREADCOUNTSUITES_PROP;
+
+ public static final String THREADCOUNTCLASSES_KEY = ProviderParameterNames.THREADCOUNTCLASSES_PROP;
+
+ public static final String THREADCOUNTMETHODS_KEY = ProviderParameterNames.THREADCOUNTMETHODS_PROP;
+
public static final String USEUNLIMITEDTHREADS_KEY = "useUnlimitedThreads";
+ public static final String PARALLEL_TIMEOUT_KEY = ProviderParameterNames.PARALLEL_TIMEOUT_PROP;
+
+ public static final String PARALLEL_TIMEOUTFORCED_KEY = ProviderParameterNames.PARALLEL_TIMEOUTFORCED_PROP;
+
public JUnitCoreParameters( Properties properties )
{
- this.parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase();
- this.perCoreThreadCount = Boolean.valueOf( properties.getProperty( PERCORETHREADCOUNT_KEY, "true" ) );
- this.threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "2" ) );
- this.useUnlimitedThreads = Boolean.valueOf( properties.getProperty( USEUNLIMITEDTHREADS_KEY, "false" ) );
+ parallel = properties.getProperty( PARALLEL_KEY, "none" ).toLowerCase();
+ perCoreThreadCount = Boolean.valueOf( properties.getProperty( PERCORETHREADCOUNT_KEY, "true" ) );
+ threadCount = Integer.valueOf( properties.getProperty( THREADCOUNT_KEY, "0" ) );
+ threadCountMethods = Integer.valueOf( properties.getProperty( THREADCOUNTMETHODS_KEY, "0" ) );
+ threadCountClasses = Integer.valueOf( properties.getProperty( THREADCOUNTCLASSES_KEY, "0" ) );
+ threadCountSuites = Integer.valueOf( properties.getProperty( THREADCOUNTSUITES_KEY, "0" ) );
+ useUnlimitedThreads = Boolean.valueOf( properties.getProperty( USEUNLIMITEDTHREADS_KEY, "false" ) );
+ parallelTestsTimeoutInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUT_KEY, "0" ) );
+ parallelTestsTimeoutForcedInSeconds = Integer.valueOf( properties.getProperty( PARALLEL_TIMEOUTFORCED_KEY, "0" ) );
+ }
+
+ private static Collection<String> lowerCase( String... elements )
+ {
+ ArrayList<String> lowerCase = new ArrayList<String>();
+ for ( String element : elements )
+ {
+ lowerCase.add( element.toLowerCase() );
+ }
+ return lowerCase;
+ }
+
+ private boolean isAllParallel()
+ {
+ return "all".equals( parallel );
}
public boolean isParallelMethod()
{
- return "methods".equals( parallel );
+ return isAllParallel()
+ || lowerCase( "both", "methods", "suitesAndMethods", "classesAndMethods" ).contains( parallel );
}
public boolean isParallelClasses()
{
- return "classes".equals( parallel );
+ return isAllParallel()
+ || lowerCase( "both", "classes", "suitesAndClasses", "classesAndMethods" ).contains( parallel );
+ }
+
+ public boolean isParallelSuites()
+ {
+ return isAllParallel() || lowerCase( "suites", "suitesAndClasses", "suitesAndMethods" ).contains( parallel );
}
+ /**
+ * @deprecated Instead use the expression ( {@link #isParallelMethod()} && {@link #isParallelClasses()} ).
+ */
+ @Deprecated
public boolean isParallelBoth()
{
- return "both".equals( parallel );
+ return isParallelMethod() && isParallelClasses();
}
public Boolean isPerCoreThreadCount()
@@ -76,25 +129,51 @@ class JUnitCoreParameters
return threadCount;
}
+ public int getThreadCountMethods()
+ {
+ return threadCountMethods;
+ }
+
+ public int getThreadCountClasses()
+ {
+ return threadCountClasses;
+ }
+
+ public int getThreadCountSuites()
+ {
+ return threadCountSuites;
+ }
+
public Boolean isUseUnlimitedThreads()
{
return useUnlimitedThreads;
}
+ public int getParallelTestsTimeoutInSeconds()
+ {
+ return parallelTestsTimeoutInSeconds;
+ }
+
+ public int getParallelTestsTimeoutForcedInSeconds()
+ {
+ return parallelTestsTimeoutForcedInSeconds;
+ }
+
public boolean isNoThreading()
{
- return !( isParallelClasses() || isParallelMethod() || isParallelBoth() );
+ return !isAnyParallelitySelected();
}
public boolean isAnyParallelitySelected()
{
- return !isNoThreading();
+ return isParallelSuites() || isParallelClasses() || isParallelMethod();
}
@Override
public String toString()
{
return "parallel='" + parallel + '\'' + ", perCoreThreadCount=" + perCoreThreadCount + ", threadCount="
- + threadCount + ", useUnlimitedThreads=" + useUnlimitedThreads;
+ + threadCount + ", useUnlimitedThreads=" + useUnlimitedThreads + ", threadCountSuites=" + threadCountSuites
+ + ", threadCountClasses=" + threadCountClasses + ", threadCountMethods=" + threadCountMethods;
}
}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
index 14af56c..de0673c 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
@@ -100,8 +100,7 @@ public class JUnitCoreProvider
private boolean isSingleThreaded()
{
- return !jUnitCoreParameters.isAnyParallelitySelected()
- || ( testsToRun.containsExactly( 1 ) && !jUnitCoreParameters.isParallelMethod() );
+ return jUnitCoreParameters.isNoThreading();
}
public RunResult invoke( Object forkTestSet )
@@ -153,8 +152,8 @@ public class JUnitCoreProvider
RunListener listener =
ConcurrentRunListener.createInstance( testSetMap, reporterFactory,
- jUnitCoreParameters.isParallelClasses(),
- jUnitCoreParameters.isParallelBoth(), consoleLogger );
+ isParallelTypes(),
+ isParallelMethodsAndTypes(), consoleLogger );
ConsoleOutputCapture.startCapture( (ConsoleOutputReceiver) listener );
jUnit4RunListener = new JUnitCoreRunListener( listener, testSetMap );
@@ -162,6 +161,16 @@ public class JUnitCoreProvider
return jUnit4RunListener;
}
+ private boolean isParallelMethodsAndTypes()
+ {
+ return jUnitCoreParameters.isParallelMethod() && isParallelTypes();
+ }
+
+ private boolean isParallelTypes()
+ {
+ return jUnitCoreParameters.isParallelClasses() || jUnitCoreParameters.isParallelSuites();
+ }
+
private Filter createJUnit48Filter()
{
final FilterFactory filterFactory = new FilterFactory( testClassLoader );
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
index 4473063..45cf34c 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreWrapper.java
@@ -19,14 +19,20 @@ package org.apache.maven.surefire.junitcore;
* under the License.
*/
-import java.util.Iterator;
+import java.util.Collection;
+import java.util.Collections;
import java.util.List;
-import java.util.concurrent.ExecutionException;
+import java.util.TreeSet;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
+import org.apache.maven.surefire.junitcore.pc.ParallelComputer;
import org.apache.maven.surefire.testset.TestSetFailedException;
import org.apache.maven.surefire.util.TestsToRun;
import org.junit.runner.Computer;
+import org.junit.runner.Description;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;
@@ -73,24 +79,21 @@ class JUnitCoreWrapper
List<RunListener> listeners, Filter filter )
throws TestSetFailedException
{
- Computer computer = getComputer( jUnitCoreParameters );
-
+ ComputerWrapper computerWrapper = createComputer( jUnitCoreParameters );
JUnitCore junitCore = createJUnitCore( listeners );
-
- try
+ if ( testsToRun.allowEagerReading() )
{
- if ( testsToRun.allowEagerReading() )
- {
- executeEager( testsToRun, filter, computer, junitCore );
- }
- else
- {
- exeuteLazy( testsToRun, filter, computer, junitCore );
- }
+ executeEager( testsToRun, filter, computerWrapper.getComputer(), junitCore );
}
- finally
+ else
+ {
+ exeuteLazy( testsToRun, filter, computerWrapper.getComputer(), junitCore );
+ }
+
+ String timeoutMessage = computerWrapper.describeElapsedTimeout();
+ if ( timeoutMessage.length() != 0 )
{
- closeIfConfigurable( computer );
+ throw new TestSetFailedException( timeoutMessage );
}
}
@@ -108,21 +111,20 @@ class JUnitCoreWrapper
throws TestSetFailedException
{
Class[] tests = testsToRun.getLocatedClasses();
- createReqestAndRun( filter, computer, junitCore, tests );
+ createRequestAndRun( filter, computer, junitCore, tests );
}
private static void exeuteLazy(TestsToRun testsToRun, Filter filter, Computer computer, JUnitCore junitCore)
throws TestSetFailedException
{
// in order to support LazyTestsToRun, the iterator must be used
- Iterator<?> classIter = testsToRun.iterator();
- while ( classIter.hasNext() )
+ for ( Class clazz : testsToRun )
{
- createReqestAndRun( filter, computer, junitCore, new Class[]{ (Class<?>) classIter.next() } );
+ createRequestAndRun( filter, computer, junitCore, clazz );
}
}
- private static void createReqestAndRun( Filter filter, Computer computer, JUnitCore junitCore, Class<?>[] classesToRun )
+ private static void createRequestAndRun( Filter filter, Computer computer, JUnitCore junitCore, Class<?>... classesToRun )
throws TestSetFailedException
{
Request req = Request.classes( computer, classesToRun );
@@ -140,46 +142,110 @@ class JUnitCoreWrapper
JUnit4RunListener.rethrowAnyTestMechanismFailures( run );
}
- private static void closeIfConfigurable( Computer computer )
+ private static ComputerWrapper createComputer( JUnitCoreParameters parameters )
throws TestSetFailedException
{
- if ( computer instanceof ConfigurableParallelComputer )
- {
- try
- {
- ( (ConfigurableParallelComputer) computer ).close();
- }
- catch ( ExecutionException e )
- {
- throw new TestSetFailedException( e );
- }
- }
+ return parameters.isNoThreading() ? new ComputerWrapper( Computer.serial() ) : createParallelComputer( parameters );
}
- private static Computer getComputer( JUnitCoreParameters jUnitCoreParameters )
- throws TestSetFailedException
+ private static ComputerWrapper createParallelComputer( JUnitCoreParameters parameters )
+ throws TestSetFailedException
{
- if ( jUnitCoreParameters.isNoThreading() )
- {
- return new Computer();
- }
- return getConfigurableParallelComputer( jUnitCoreParameters );
+ ParallelComputer pc = ParallelComputerFactory.createParallelComputer( parameters );
+
+ int timeout = parameters.getParallelTestsTimeoutInSeconds();
+
+ int timeoutForced = parameters.getParallelTestsTimeoutForcedInSeconds();
+
+ Future<Collection<Description>> testsBeforeShutdown =
+ timeout > 0 ? pc.scheduleShutdown( timeout, TimeUnit.SECONDS ) : null;
+
+ Future<Collection<Description>> testsBeforeForcedShutdown =
+ timeoutForced > 0 ? pc.scheduleForcedShutdown( timeoutForced, TimeUnit.SECONDS ) : null;
+
+ return new ComputerWrapper( pc, timeout, testsBeforeShutdown, timeoutForced, testsBeforeForcedShutdown );
}
- private static Computer getConfigurableParallelComputer( JUnitCoreParameters jUnitCoreParameters )
- throws TestSetFailedException
+ private static class ComputerWrapper
{
- if ( jUnitCoreParameters.isUseUnlimitedThreads() )
+ private final Computer computer;
+ private final int timeout;
+ private final int timeoutForced;
+ private final Future<Collection<Description>> testsBeforeShutdown;
+ private final Future<Collection<Description>> testsBeforeForcedShutdown;
+
+ ComputerWrapper( Computer computer )
{
- return new ConfigurableParallelComputer();
+ this( computer, 0, null, 0, null );
}
- else
+
+ ComputerWrapper( Computer computer,
+ int timeout, Future<Collection<Description>> testsBeforeShutdown,
+ int timeoutForced, Future<Collection<Description>> testsBeforeForcedShutdown )
{
- return new ConfigurableParallelComputer(
- jUnitCoreParameters.isParallelClasses() | jUnitCoreParameters.isParallelBoth(),
- jUnitCoreParameters.isParallelMethod() | jUnitCoreParameters.isParallelBoth(),
- jUnitCoreParameters.getThreadCount(), jUnitCoreParameters.isPerCoreThreadCount() );
+ this.computer = computer;
+ this.timeout = timeout;
+ this.testsBeforeShutdown = testsBeforeShutdown;
+ this.timeoutForced = timeoutForced;
+ this.testsBeforeForcedShutdown = testsBeforeForcedShutdown;
}
- }
+ Computer getComputer()
+ {
+ return computer;
+ }
+
+ String describeElapsedTimeout() throws TestSetFailedException
+ {
+ TreeSet<String> executedTests = new TreeSet<String>();
+ if ( timeout > 0 )
+ {
+ executedTests.addAll( printShutdownHook( testsBeforeShutdown ) );
+ }
+
+ if ( timeoutForced > 0 )
+ {
+ executedTests.addAll( printShutdownHook( testsBeforeForcedShutdown ) );
+ }
+
+ StringBuilder msg = new StringBuilder();
+ if ( !executedTests.isEmpty() )
+ {
+ msg.append( "The test run has finished abruptly after timeout of " );
+ msg.append( Math.min( timeout, timeoutForced ) );
+ msg.append( " seconds.\n" );
+ msg.append( "These tests were executed in prior of the shutdown operation:\n" );
+ for ( String executedTest : executedTests )
+ {
+ msg.append( executedTest ).append( "\n" );
+ }
+ }
+ return msg.toString();
+ }
+
+ static Collection<String> printShutdownHook( Future<Collection<Description>> future )
+ throws TestSetFailedException
+ {
+ if ( !future.isCancelled() && future.isDone() )
+ {
+ try
+ {
+ TreeSet<String> executedTests = new TreeSet<String>();
+ for ( Description executedTest : future.get() )
+ {
+ if ( executedTest != null && executedTest.getDisplayName() != null )
+ {
+ executedTests.add( executedTest.getDisplayName() );
+ }
+ }
+ return executedTests;
+ }
+ catch ( Exception e )
+ {
+ throw new TestSetFailedException( e );
+ }
+ }
+ return Collections.emptySet();
+ }
+ }
}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java
new file mode 100644
index 0000000..3937cd4
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/ParallelComputerFactory.java
@@ -0,0 +1,368 @@
+package org.apache.maven.surefire.junitcore;
+
+/*
+ * 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.
+ */
+
+import org.apache.maven.surefire.junitcore.pc.ParallelComputer;
+import org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder;
+import org.apache.maven.surefire.testset.TestSetFailedException;
+
+/**
+ * An algorithm which configures {@link ParallelComputer} with allocated thread resources by given {@link JUnitCoreParameters}.
+ * The <code>AbstractSurefireMojo</code> has to provide correct combinations of thread-counts and <em>parallel</em>.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ *
+ * @see org.apache.maven.surefire.junitcore.pc.ParallelComputerBuilder
+ */
+final class ParallelComputerFactory
+{
+ private static int availableProcessors = Runtime.getRuntime().availableProcessors();
+
+ static class Concurrency
+ {
+ int suites, classes, methods, capacity;
+ }
+
+ private ParallelComputerFactory()
+ {
+ throw new IllegalStateException("Suppresses calling constructor, ensuring non-instantiability.");
+ }
+
+ /*
+ * For testing purposes.
+ */
+ static void overrideAvailableProcessors( int availableProcessors )
+ {
+ ParallelComputerFactory.availableProcessors = availableProcessors;
+ }
+
+ /*
+ * For testing purposes.
+ */
+ static void setDefaultAvailableProcessors()
+ {
+ ParallelComputerFactory.availableProcessors = Runtime.getRuntime().availableProcessors();
+ }
+
+ static ParallelComputer createParallelComputer( JUnitCoreParameters params ) throws TestSetFailedException
+ {
+ Concurrency concurrency = resolveConcurrency( params );
+ ParallelComputerBuilder builder = new ParallelComputerBuilder();
+
+ if ( params.isParallelSuites() )
+ {
+ resolveSuitesConcurrency( builder, concurrency.suites );
+ }
+
+ if ( params.isParallelClasses() )
+ {
+ resolveClassesConcurrency( builder, concurrency.classes );
+ }
+
+ if ( params.isParallelMethod() )
+ {
+ resolveMethodsConcurrency( builder, concurrency.methods );
+ }
+
+ resolveCapacity( builder, concurrency.capacity );
+ return builder.buildComputer();
+ }
+
+ static Concurrency resolveConcurrency( JUnitCoreParameters params ) throws TestSetFailedException
+ {
+ if ( !params.isAnyParallelitySelected() )
+ {
+ throw new TestSetFailedException( "Unspecified parameter '" + JUnitCoreParameters.PARALLEL_KEY + "'." );
+ }
+
+ if ( !params.isUseUnlimitedThreads() && !hasThreadCount( params ) && !hasThreadCounts( params ) )
+ {
+ throw new TestSetFailedException( "Unspecified thread-count(s). " +
+ "See the parameters " + JUnitCoreParameters.USEUNLIMITEDTHREADS_KEY + ", "
+ + JUnitCoreParameters.THREADCOUNT_KEY + ", " + JUnitCoreParameters.THREADCOUNTSUITES_KEY + ", "
+ + JUnitCoreParameters.THREADCOUNTCLASSES_KEY + ", " + JUnitCoreParameters.THREADCOUNTMETHODS_KEY + ".");
+ }
+
+ if ( params.isUseUnlimitedThreads() )
+ {
+ return concurrencyForUnlimitedThreads( params );
+ }
+ else
+ {
+ if ( hasThreadCount( params ) )
+ {
+ if ( hasThreadCounts( params ) )
+ {
+ return isLeafUnspecified( params ) ?
+ concurrencyFromAllThreadCountsButUnspecifiedLeafCount( params ) :
+ concurrencyFromAllThreadCounts( params );
+ }
+ else
+ {
+ return estimateConcurrency( params );
+ }
+ }
+ else
+ {
+ return concurrencyFromThreadCounts( params );
+ }
+ }
+ }
+
+ private static void resolveSuitesConcurrency( ParallelComputerBuilder builder, int concurrency )
+ {
+ if ( concurrency > 0 )
+ {
+ if ( concurrency == Integer.MAX_VALUE )
+ {
+ builder.parallelSuites();
+ }
+ else
+ {
+ builder.parallelSuites( concurrency );
+ }
+ }
+ }
+
+ private static void resolveClassesConcurrency( ParallelComputerBuilder builder, int concurrency )
+ {
+ if ( concurrency > 0 )
+ {
+ if ( concurrency == Integer.MAX_VALUE )
+ {
+ builder.parallelClasses();
+ }
+ else
+ {
+ builder.parallelClasses( concurrency );
+ }
+ }
+ }
+
+ private static void resolveMethodsConcurrency( ParallelComputerBuilder builder, int concurrency )
+ {
+ if ( concurrency > 0 )
+ {
+ if ( concurrency == Integer.MAX_VALUE )
+ {
+ builder.parallelMethods();
+ }
+ else
+ {
+ builder.parallelMethods( concurrency );
+ }
+ }
+ }
+
+ private static void resolveCapacity( ParallelComputerBuilder builder, int capacity )
+ {
+ if ( capacity > 0 )
+ {
+ builder.useOnePool( capacity );
+ }
+ }
+
+ private static Concurrency concurrencyForUnlimitedThreads( JUnitCoreParameters params )
+ {
+ Concurrency concurrency = new Concurrency();
+ concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
+ concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
+ concurrency.methods = params.isParallelMethod() ? threadCountMethods( params ) : 0;
+ concurrency.capacity = Integer.MAX_VALUE;
+ return concurrency;
+ }
+
+ private static Concurrency estimateConcurrency( JUnitCoreParameters params )
+ {
+ Concurrency concurrency = new Concurrency();
+ concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
+ concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
+ concurrency.methods = params.isParallelMethod() ? params.getThreadCountMethods() : 0;
+ concurrency.capacity = params.getThreadCount();
+
+ // estimate parallel thread counts
+ double ratio = 1d / countParallelEntities( params );
+ int threads = multiplyByCoreCount( params, ratio * concurrency.capacity );
+ concurrency.suites = params.isParallelSuites() ? threads : 0;
+ concurrency.classes = params.isParallelClasses() ? threads : 0;
+ concurrency.methods = params.isParallelMethod() ? threads : 0;
+ if ( countParallelEntities( params ) == 1 )
+ {
+ concurrency.capacity = 0;
+ }
+ else
+ {
+ concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity );
+ adjustLeaf( params, concurrency );
+ }
+ return concurrency;
+ }
+
+ private static Concurrency concurrencyFromAllThreadCountsButUnspecifiedLeafCount( JUnitCoreParameters params )
+ {
+ Concurrency concurrency = new Concurrency();
+ concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
+ concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
+ concurrency.methods = params.isParallelMethod() ? params.getThreadCountMethods() : 0;
+ concurrency.capacity = params.getThreadCount();
+
+ setLeafInfinite( params, concurrency );
+ concurrency.suites = params.isParallelSuites() ? multiplyByCoreCount( params, concurrency.suites ) : 0;
+ concurrency.classes = params.isParallelClasses() ? multiplyByCoreCount( params, concurrency.classes ) : 0;
+ concurrency.methods = params.isParallelMethod() ? multiplyByCoreCount( params, concurrency.methods ) : 0;
+ concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity );
+
+ return concurrency;
+ }
+
+ private static Concurrency concurrencyFromAllThreadCounts( JUnitCoreParameters params )
+ {
+ Concurrency concurrency = new Concurrency();
+ concurrency.suites = params.isParallelSuites() ? params.getThreadCountSuites() : 0;
+ concurrency.classes = params.isParallelClasses() ? params.getThreadCountClasses() : 0;
+ concurrency.methods = params.isParallelMethod() ? params.getThreadCountMethods() : 0;
+ concurrency.capacity = params.getThreadCount();
+ double all = sumThreadCounts( concurrency );
+
+ concurrency.suites = params.isParallelSuites() ?
+ multiplyByCoreCount( params, concurrency.capacity * ( concurrency.suites / all ) ) : 0;
+
+ concurrency.classes = params.isParallelClasses() ?
+ multiplyByCoreCount( params, concurrency.capacity * ( concurrency.classes / all ) ) : 0;
+
+ concurrency.methods = params.isParallelMethod() ?
+ multiplyByCoreCount( params, concurrency.capacity * ( concurrency.methods / all ) ) : 0;
+
+ concurrency.capacity = multiplyByCoreCount( params, concurrency.capacity );
+ adjustPrecisionInLeaf( params, concurrency );
+ return concurrency;
+ }
+
+ private static Concurrency concurrencyFromThreadCounts( JUnitCoreParameters params )
+ {
+ Concurrency concurrency = new Concurrency();
+ concurrency.suites = params.isParallelSuites() ? threadCountSuites( params ) : 0;
+ concurrency.classes = params.isParallelClasses() ? threadCountClasses( params ) : 0;
+ concurrency.methods = params.isParallelMethod() ? threadCountMethods( params ) : 0 ;
+ concurrency.capacity = (int) Math.min( sumThreadCounts( concurrency ), Integer.MAX_VALUE );
+ return concurrency;
+ }
+
+ private static int countParallelEntities( JUnitCoreParameters params )
+ {
+ int count = 0;
+ if ( params.isParallelSuites() ) count++;
+ if ( params.isParallelClasses() ) count++;
+ if ( params.isParallelMethod() ) count++;
+ return count;
+ }
+
+ private static void adjustPrecisionInLeaf( JUnitCoreParameters params, Concurrency concurrency )
+ {
+ if ( params.isParallelMethod() )
+ {
+ concurrency.methods = concurrency.capacity - concurrency.suites - concurrency.classes;
+ }
+ else if ( params.isParallelClasses() )
+ {
+ concurrency.classes = concurrency.capacity - concurrency.suites;
+ }
+ }
+
+ private static void adjustLeaf( JUnitCoreParameters params, Concurrency concurrency )
+ {
+ if ( params.isParallelMethod() )
+ {
+ concurrency.methods = Integer.MAX_VALUE;
+ }
+ else if ( params.isParallelClasses() )
+ {
+ concurrency.classes = Integer.MAX_VALUE;
+ }
+ }
+
+ private static void setLeafInfinite( JUnitCoreParameters params, Concurrency concurrency )
+ {
+ if ( params.isParallelMethod() ) concurrency.methods = Integer.MAX_VALUE;
+ else if ( params.isParallelClasses() ) concurrency.classes = Integer.MAX_VALUE;
+ else if ( params.isParallelSuites() ) concurrency.suites = Integer.MAX_VALUE;
+ }
+
+ private static boolean isLeafUnspecified( JUnitCoreParameters params )
+ {
+ int maskOfParallel = params.isParallelSuites() ? 4: 0;
+ maskOfParallel |= params.isParallelClasses() ? 2 : 0;
+ maskOfParallel |= params.isParallelMethod() ? 1 : 0;
+
+ int maskOfConcurrency = params.getThreadCountSuites() > 0 ? 4 : 0;
+ maskOfConcurrency |= params.getThreadCountClasses() > 0 ? 2 : 0;
+ maskOfConcurrency |= params.getThreadCountMethods() > 0 ? 1 : 0;
+
+ maskOfConcurrency &= maskOfParallel;
+
+ int leaf = Integer.lowestOneBit( maskOfParallel );
+ return maskOfConcurrency == maskOfParallel - leaf;
+ }
+
+ private static double sumThreadCounts( Concurrency concurrency )
+ {
+ double sum = concurrency.suites;
+ sum += concurrency.classes;
+ sum += concurrency.methods;
+ return sum;
+ }
+
+ private static boolean hasThreadCounts( JUnitCoreParameters jUnitCoreParameters )
+ {
+ return jUnitCoreParameters.getThreadCountSuites() > 0 ||
+ jUnitCoreParameters.getThreadCountClasses() > 0 ||
+ jUnitCoreParameters.getThreadCountMethods() > 0;
+ }
+
+ private static boolean hasThreadCount ( JUnitCoreParameters jUnitCoreParameters )
+ {
+ return jUnitCoreParameters.getThreadCount() > 0;
+ }
+
+ private static int threadCountMethods( JUnitCoreParameters jUnitCoreParameters )
+ {
+ return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountMethods() );
+ }
+
+ private static int threadCountClasses( JUnitCoreParameters jUnitCoreParameters )
+ {
+ return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountClasses() );
+ }
+
+ private static int threadCountSuites( JUnitCoreParameters jUnitCoreParameters )
+ {
+ return multiplyByCoreCount( jUnitCoreParameters, jUnitCoreParameters.getThreadCountSuites() );
+ }
+
+ private static int multiplyByCoreCount( JUnitCoreParameters jUnitCoreParameters, double threadsPerCore )
+ {
+ double numberOfThreads =
+ jUnitCoreParameters.isPerCoreThreadCount() ?
+ threadsPerCore * (double) availableProcessors : threadsPerCore;
+
+ return numberOfThreads > 0 ? (int) Math.min( numberOfThreads, Integer.MAX_VALUE ) : Integer.MAX_VALUE;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java
new file mode 100644
index 0000000..fea0701
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/AbstractThreadPoolStrategy.java
@@ -0,0 +1,111 @@
+package org.apache.maven.surefire.junitcore.pc;
+
+/*
+ * 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.
+ */
+
+import java.util.Collection;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * Abstract parallel scheduling strategy in private package.
+ * The remaining abstract methods have to be implemented differently
+ * depending if the thread pool is shared with other strategies or not.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ *
+ * @see SchedulingStrategy
+ * @see SharedThreadPoolStrategy
+ * @see NonSharedThreadPoolStrategy
+ */
+abstract class AbstractThreadPoolStrategy extends SchedulingStrategy {
+ private final ExecutorService threadPool;
+ private final Collection<Future<?>> futureResults;
+ private final AtomicBoolean canSchedule = new AtomicBoolean(true);
+
+ AbstractThreadPoolStrategy(ExecutorService threadPool) {
+ this(threadPool, null);
+ }
+
+ AbstractThreadPoolStrategy(ExecutorService threadPool, Collection<Future<?>> futureResults) {
+ this.threadPool = threadPool;
+ this.futureResults = futureResults;
+ }
+
+ protected final ExecutorService getThreadPool() {
+ return threadPool;
+ }
+
+ protected final Collection<Future<?>> getFutureResults() {
+ return futureResults;
+ }
+
+ protected final void disable() {
+ canSchedule.set(false);
+ }
+
+ @Override
+ public void schedule(Runnable task) {
+ if (canSchedule()) {
+ Future<?> futureResult = threadPool.submit(task);
+ if (futureResults != null) {
+ futureResults.add(futureResult);
+ }
+ }
+ }
+
+ @Override
+ protected boolean stop() {
+ boolean wasRunning = canSchedule.getAndSet(false);
+ if (threadPool.isShutdown()) {
+ wasRunning = false;
+ } else {
+ threadPool.shutdown();
+ }
+ return wasRunning;
+ }
+
+ @Override
+ protected boolean stopNow() {
+ boolean wasRunning = canSchedule.getAndSet(false);
+ if (threadPool.isShutdown()) {
+ wasRunning = false;
+ } else {
+ threadPool.shutdownNow();
+ }
+ return wasRunning;
+ }
+
+ @Override
+ protected void setDefaultShutdownHandler(Scheduler.ShutdownHandler handler) {
+ if (threadPool instanceof ThreadPoolExecutor) {
+ ThreadPoolExecutor pool = (ThreadPoolExecutor) threadPool;
+ handler.setRejectedExecutionHandler(pool.getRejectedExecutionHandler());
+ pool.setRejectedExecutionHandler(handler);
+ }
+ }
+
+ @Override
+ public final boolean canSchedule() {
+ return canSchedule.get();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java
new file mode 100644
index 0000000..1b28309
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/Balancer.java
@@ -0,0 +1,49 @@
+package org.apache.maven.surefire.junitcore.pc;
+
+/*
+ * 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.
+ */
+
+/**
+ * The Balancer controls the maximum of concurrent threads in the current Scheduler(s) and prevents
+ * from own thread resources exhaustion if other group of schedulers share the same pool of threads.
+ * <p>
+ * If a permit is available, {@link #acquirePermit()} simply returns and a new test is scheduled
+ * by {@link Scheduler#schedule(Runnable)} in the current runner. Otherwise waiting for a release.
+ * One permit is released as soon as the child thread has finished.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ */
+public interface Balancer {
+
+ /**
+ * Acquires a permit from this balancer, blocking until one is available.
+ *
+ * @return <code>true</code> if current thread is <em>NOT</em> interrupted
+ * while waiting for a permit.
+ */
+ public boolean acquirePermit();
+
+ /**
+ * Releases a permit, returning it to the balancer.
+ */
+ public void releasePermit();
+
+ public void releaseAllPermits();
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java
new file mode 100644
index 0000000..e7db197
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/BalancerFactory.java
@@ -0,0 +1,68 @@
+package org.apache.maven.surefire.junitcore.pc;
+
+/*
+ * 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.
+ */
+
+/**
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ *
+ * @see Balancer
+ */
+public class BalancerFactory {
+ private BalancerFactory()
+ {
+ }
+
+ /**
+ * Infinite permits.
+ */
+ public static Balancer createInfinitePermitsBalancer()
+ {
+ return balancer( 0, false );
+ }
+
+ /**
+ * Balancer without fairness.
+ * Fairness guarantees the waiting schedulers to wake up in order they acquired a permit.
+ *
+ * @param concurrency number of permits to acquire when maintaining concurrency on tests
+ */
+ public static Balancer createBalancer( int concurrency )
+ {
+ return balancer( concurrency, false );
+ }
+
+ /**
+ * Balancer with fairness.
+ * Fairness guarantees the waiting schedulers to wake up in order they acquired a permit.
+ *
+ * @param concurrency number of permits to acquire when maintaining concurrency on tests
+ */
+ public static Balancer createBalancerWithFairness( int concurrency )
+ {
+ return balancer( concurrency, true );
+ }
+
+ private static Balancer balancer( int concurrency, boolean fairness )
+ {
+ boolean shouldBalance = concurrency > 0 && concurrency < Integer.MAX_VALUE;
+ return shouldBalance ? new ThreadResourcesBalancer( concurrency, fairness ) : new NullBalancer();
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java
new file mode 100644
index 0000000..dc1e5b3
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/InvokerStrategy.java
@@ -0,0 +1,61 @@
+package org.apache.maven.surefire.junitcore.pc;
+
+/*
+ * 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.
+ */
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * The sequentially executing strategy in private package.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ *
+ * @see SchedulingStrategy
+ */
+final class InvokerStrategy extends SchedulingStrategy {
+ private final AtomicBoolean canSchedule = new AtomicBoolean(true);
+
+ @Override
+ public void schedule(Runnable task) {
+ if (canSchedule()) {
+ task.run();
+ }
+ }
+
+ @Override
+ protected boolean stop() {
+ return canSchedule.getAndSet(false);
+ }
+
+ @Override
+ public boolean hasSharedThreadPool() {
+ return false;
+ }
+
+ @Override
+ public boolean canSchedule() {
+ return canSchedule.get();
+ }
+
+ @Override
+ public boolean finished() throws InterruptedException {
+ return stop();
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java
new file mode 100644
index 0000000..b463605
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NonSharedThreadPoolStrategy.java
@@ -0,0 +1,54 @@
+package org.apache.maven.surefire.junitcore.pc;
+
+/*
+ * 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.
+ */
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Parallel strategy for non-shared thread pool in private package.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ *
+ * @see AbstractThreadPoolStrategy
+ */
+final class NonSharedThreadPoolStrategy extends AbstractThreadPoolStrategy {
+ NonSharedThreadPoolStrategy(ExecutorService threadPool) {
+ super(threadPool);
+ }
+
+ @Override
+ public boolean hasSharedThreadPool() {
+ return false;
+ }
+
+ @Override
+ public boolean finished() throws InterruptedException {
+ boolean wasRunning = canSchedule();
+ getThreadPool().shutdown();
+ try {
+ getThreadPool().awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
+ return wasRunning;
+ } finally {
+ disable();
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/49c4a625/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java
new file mode 100644
index 0000000..eec3759
--- /dev/null
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/NullBalancer.java
@@ -0,0 +1,44 @@
+package org.apache.maven.surefire.junitcore.pc;
+
+/*
+ * 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.
+ */
+
+/**
+ * This balancer implements {@link Balancer} and does not do anything -no blocking operation.
+ *
+ * @author Tibor Digana (tibor17)
+ * @since 2.16
+ *
+ * @see Balancer
+ */
+final class NullBalancer implements Balancer
+{
+ public boolean acquirePermit()
+ {
+ return true;
+ }
+
+ public void releasePermit()
+ {
+ }
+
+ public void releaseAllPermits()
+ {
+ }
+}
\ No newline at end of file