From c3b3a3f673ff104a6488f927f0c9a697209c1b22 Mon Sep 17 00:00:00 2001 From: Tibor Digana Date: Wed, 13 Aug 2014 03:20:47 +0200 Subject: [PATCH] Exceptional parallel execution on @NotThreadSafe type. --- ...fork-options-and-parallel-execution.apt.vm | 6 +++ .../junitcore/pc/ParallelComputerBuilder.java | 54 ++++++++++++------- 2 files changed, 40 insertions(+), 20 deletions(-) diff --git a/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm b/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm index 02b22ed936..6c2fda4784 100644 --- a/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm +++ b/maven-surefire-plugin/src/site/apt/examples/fork-options-and-parallel-execution.apt.vm @@ -93,6 +93,12 @@ Fork Options and Parallel Test Execution The surefire is always trying to reuse threads, optimize the thread-counts, and prefers thread fairness. + + Since of Surefire 2.18, you can apply the JSR-305 annotation + <<<@javax.annotation.concurrent.NotThreadSafe>>> on the type of a test class + or suite in order to execute them in single Thread instance. + Just use the dependency Artifact com.google.code.findbugs:jsr305:2.0.1. + This way you can deselect parallel execution in some tests. <> with the <<>> option is: the concurrency happens within the same JVM process. That is efficient in terms of diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java index 04813ad1f4..8163b85e07 100644 --- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java +++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/pc/ParallelComputerBuilder.java @@ -32,12 +32,15 @@ import org.junit.runners.model.InitializationError; import org.junit.runners.model.RunnerBuilder; +import javax.annotation.concurrent.NotThreadSafe; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -225,7 +228,7 @@ final class PC final Collection nestedClasses = new LinkedHashSet(); - final Collection unscheduledRunners = new LinkedHashSet(); + final Collection notParallelRunners = new LinkedHashSet(); int poolCapacity; @@ -298,7 +301,7 @@ protected Runner getRunner( RunnerBuilder builder, Class testClass ) } else { - unscheduledRunners.add( runner ); + notParallelRunners.add( runner ); } return runner; } @@ -336,9 +339,7 @@ private WrappedRunners wrapRunners( Collection runners ) } } - Suite wrapper = runs.isEmpty() ? null : new Suite( null, runs ) - { - }; + Suite wrapper = runs.isEmpty() ? null : createSuite( runs ); return new WrappedRunners( wrapper, childrenCounter ); } @@ -358,7 +359,7 @@ private ExecutorService createPool( int poolSize ) private Scheduler createMaster( ExecutorService pool, int poolSize ) { - if ( !areSuitesAndClassesParallel() || poolSize <= 1 ) + if ( !(areSuitesAndClassesParallel() || !notParallelRunners.isEmpty() ) || poolSize <= 1 ) { return new Scheduler( null, new InvokerStrategy() ); } @@ -368,7 +369,7 @@ else if ( pool != null && poolSize == Integer.MAX_VALUE ) } else { - return new Scheduler( null, SchedulingStrategies.createParallelStrategy( 2 ) ); + return new Scheduler( null, SchedulingStrategies.createParallelStrategy( 3 ) ); } } @@ -463,24 +464,17 @@ private Runner setSchedulers( ParentRunner suiteSuites, ParentRunner suiteClasse } // resulting runner for Computer#getSuite() scheduled by master scheduler - ParentRunner all = createFinalRunner( suiteSuites, suiteClasses ); + ParentRunner all = createFinalRunner( removeNullRunners( + Arrays.asList( suiteSuites, suiteClasses, createSuite( notParallelRunners ) ) + ) ); all.setScheduler( master ); return all; } - private ParentRunner createFinalRunner( Runner... runners ) + private ParentRunner createFinalRunner( List runners ) throws InitializationError { - ArrayList all = new ArrayList( unscheduledRunners ); - for ( Runner runner : runners ) - { - if ( runner != null ) - { - all.add( runner ); - } - } - - return new Suite( null, all ) + return new Suite( null, runners ) { @Override public void run( RunNotifier notifier ) @@ -556,7 +550,12 @@ else if ( poolSize == 0 ) private boolean canSchedule( Runner runner ) { - return !( runner instanceof ErrorReportingRunner ) && runner instanceof ParentRunner; + return isThreadSafe( runner ) && !( runner instanceof ErrorReportingRunner ) && runner instanceof ParentRunner; + } + + private boolean isThreadSafe( Runner runner ) + { + return runner.getDescription().getAnnotation( NotThreadSafe.class ) == null; } private class SuiteFilter @@ -594,4 +593,19 @@ public String describe() } } } + + private static Suite createSuite( Collection runners ) + throws InitializationError + { + return new Suite( null, removeNullRunners( runners ) ) + { + }; + } + + private static List removeNullRunners( Collection runners ) + { + List onlyRunners = new ArrayList( runners ); + onlyRunners.removeAll( Collections.singleton( null ) ); + return onlyRunners; + } }