You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by kr...@apache.org on 2011/11/22 22:43:28 UTC

svn commit: r1205186 [1/2] - in /maven/surefire/trunk: ./ maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/ maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/ maven-surefire-common/src/main/java/org/apache/maven/p...

Author: krosenvold
Date: Tue Nov 22 21:43:19 2011
New Revision: 1205186

URL: http://svn.apache.org/viewvc?rev=1205186&view=rev
Log:
[SUREFIRE-795] Added support for balancing tests across threads

Added:
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java   (with props)
    maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
      - copied, changed from r1204953, maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
    maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/
    maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/PrioritizedTest.java
      - copied, changed from r1204953, maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/Priority.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionScheduler.java   (with props)
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/booter/RunOrderParametersAware.java   (contents, props changed)
      - copied, changed from r1204953, maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/testset/RunOrderParameters.java   (with props)
    maven/surefire/trunk/surefire-api/src/test/java/org/apache/maven/plugin/
    maven/surefire/trunk/surefire-api/src/test/java/org/apache/maven/plugin/surefire/
    maven/surefire/trunk/surefire-api/src/test/java/org/apache/maven/plugin/surefire/runorder/
    maven/surefire/trunk/surefire-api/src/test/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionSchedulerTest.java   (with props)
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/pom.xml   (with props)
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/java/
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/java/runorder/
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/java/runorder/parallel/
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/java/runorder/parallel/Test1.java   (with props)
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/java/runorder/parallel/Test2.java   (with props)
    maven/surefire/trunk/surefire-integration-tests/src/test/resources/runorder-parallel/src/test/java/runorder/parallel/Test3.java   (with props)
Removed:
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java
Modified:
    maven/surefire/trunk/.gitignore
    maven/surefire/trunk/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
    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/CommonReflector.java
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/ForkStarter.java
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterFactory.java
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/booter/StartupReportConfiguration.java
    maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/TestSetRunListener.java
    maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
    maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
    maven/surefire/trunk/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/testset/DirectoryScannerParameters.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/util/DefaultRunOrderCalculator.java
    maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/surefire/util/RunOrder.java
    maven/surefire/trunk/surefire-api/src/test/java/org/apache/maven/surefire/util/RunOrderCalculatorTest.java
    maven/surefire/trunk/surefire-api/src/test/java/org/apache/maven/surefire/util/RunOrderTest.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
    maven/surefire/trunk/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderFactory.java
    maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/Foo.java
    maven/surefire/trunk/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
    maven/surefire/trunk/surefire-integration-tests/pom.xml
    maven/surefire/trunk/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/TestMethod.java
    maven/surefire/trunk/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
    maven/surefire/trunk/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java

Modified: maven/surefire/trunk/.gitignore
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/.gitignore?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/.gitignore (original)
+++ maven/surefire/trunk/.gitignore Tue Nov 22 21:43:19 2011
@@ -9,4 +9,4 @@ build
 .project
 .settings
 .idea
-
+.surefire-*

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=1205186&r1=1205185&r2=1205186&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 Tue Nov 22 21:43:19 2011
@@ -29,7 +29,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-
 import org.apache.maven.artifact.factory.ArtifactFactory;
 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
 import org.apache.maven.artifact.repository.ArtifactRepository;
@@ -629,11 +628,21 @@ public class IntegrationTestMojo
 
     /**
      * Defines the order the tests will be run in. Supported values are "alphabetical", "reversealphabetical", "random",
-     * "hourly" (alphabetical on even hours, reverse alphabetical on odd hours) and "filesystem".
+     * "hourly" (alphabetical on even hours, reverse alphabetical on odd hours), "failedfirst", "balanced" and "filesystem".
      * <p/>
      * <p/>
      * Odd/Even for hourly is determined at the time the of scanning the classpath, meaning it could change during a
      * multi-module build.
+     * <p/>
+     * Failed first will run tests that failed on previous run first, as well as new tests.
+     * <p/>
+     * Balanced is only relevant with parallel=classes, and will try to optimize the run-order of the tests to
+     * make all tests complete at the same time, reducing the overall execution time.
+     * <p/>
+     * Note that the statistics are stored in a file named .surefire-XXXXXXXXX beside pom.xml, and should not
+     * be checked into version control. The "XXXXX" is the SHA1 checksum of the entire surefire configuration,
+     * so different configurations will have different statistics files, meaning if you change any config
+     * settings you will re-run once before new statistics data can be established.
      *
      * @parameter default-value="filesystem"
      * @since 2.7
@@ -722,7 +731,7 @@ public class IntegrationTestMojo
 
     protected String[] getDefaultIncludes()
     {
-        return new String[]{"**/IT*.java", "**/*IT.java", "**/*ITCase.java"};
+        return new String[]{ "**/IT*.java", "**/*IT.java", "**/*ITCase.java" };
     }
 
     public boolean isSkipTests()

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=1205186&r1=1205185&r2=1205186&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 Tue Nov 22 21:43:19 2011
@@ -61,6 +61,7 @@ import org.apache.maven.surefire.booter.
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.suite.RunResult;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import org.apache.maven.surefire.testset.RunOrderParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.NestedRuntimeException;
@@ -151,9 +152,9 @@ public abstract class AbstractSurefireMo
         {
             final Artifact junitDepArtifact = getJunitDepArtifact();
             ProviderList wellKnownProviders = new ProviderList(
-                new ProviderInfo[]{new TestNgProviderInfo( getTestNgArtifact() ),
+                new ProviderInfo[]{ new TestNgProviderInfo( getTestNgArtifact() ),
                     new JUnitCoreProviderInfo( getJunitArtifact(), junitDepArtifact ),
-                    new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ), new JUnit3ProviderInfo()},
+                    new JUnit4ProviderInfo( getJunitArtifact(), junitDepArtifact ), new JUnit3ProviderInfo() },
                 new DynamicProviderInfo( null ) );
 
             return wellKnownProviders.resolve( getLog() );
@@ -331,7 +332,20 @@ public abstract class AbstractSurefireMo
         return ForkConfiguration.FORK_NEVER.equals( getForkMode() );
     }
 
-    protected ProviderConfiguration createProviderConfiguration()
+    private java.util.List getRunOrders()
+    {
+        String runOrderString = getRunOrder();
+        RunOrder[] runOrder = runOrderString == null ? RunOrder.DEFAULT : RunOrder.valueOfMulti( runOrderString );
+        return Arrays.asList( runOrder );
+    }
+
+    private boolean requiresRunHistory()
+    {
+        final List runOrders = getRunOrders();
+        return runOrders.contains( RunOrder.BALANCED ) || runOrders.contains( RunOrder.FAILEDFIRST );
+    }
+
+    protected ProviderConfiguration createProviderConfiguration( String configurationHash )
         throws MojoExecutionException, MojoFailureException
     {
         ReporterConfiguration reporterConfiguration =
@@ -377,7 +391,7 @@ public abstract class AbstractSurefireMo
             List excludes = getExcludeList();
             directoryScannerParameters = new DirectoryScannerParameters( getTestClassesDirectory(), includes, excludes,
                                                                          Boolean.valueOf( failIfNoTests ),
-                                                                         getRunOrderObject() );
+                                                                         getRunOrder() );
         }
 
         Properties providerProperties = getProperties();
@@ -386,10 +400,26 @@ public abstract class AbstractSurefireMo
             providerProperties = new Properties();
         }
 
-        return new ProviderConfiguration( directoryScannerParameters, failIfNoTests, reporterConfiguration, testNg,
-                                   testSuiteDefinition, providerProperties, null );
+        RunOrderParameters runOrderParameters =
+            new RunOrderParameters( getRunOrder(), getStatisticsFileName( configurationHash ) );
+
+        return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, failIfNoTests,
+                                          reporterConfiguration, testNg, testSuiteDefinition, providerProperties,
+                                          null );
+    }
+
+    public File getStatisticsFile( String configurationHash )
+    {
+        return new File( getStatisticsFileName( configurationHash ) );
     }
 
+    public String getStatisticsFileName( String configurationHash )
+    {
+        return getReportsDirectory().getParentFile().getParentFile() + File.separator + ".surefire-"
+            + configurationHash;
+    }
+
+
     StartupConfiguration createStartupConfiguration( ForkConfiguration forkConfiguration, ProviderInfo provider,
                                                      ClassLoaderConfiguration classLoaderConfiguration )
         throws MojoExecutionException, MojoFailureException
@@ -435,11 +465,12 @@ public abstract class AbstractSurefireMo
         return (Artifact) getPluginArtifactMap().get( "org.apache.maven.surefire:maven-surefire-common" );
     }
 
-    private StartupReportConfiguration getStartupReportConfiguration()
+    private StartupReportConfiguration getStartupReportConfiguration( String configChecksum )
     {
         return new StartupReportConfiguration( isUseFile(), isPrintSummary(), getReportFormat(),
                                                isRedirectTestOutputToFile(), isDisableXmlReport(),
-                                               getReportsDirectory(), isTrimStackTrace(), getReportNameSuffix() );
+                                               getReportsDirectory(), isTrimStackTrace(), getReportNameSuffix(),
+                                               configChecksum, requiresRunHistory() );
     }
 
     void logClasspath( Classpath classpath, String descriptor )
@@ -490,7 +521,7 @@ public abstract class AbstractSurefireMo
             // Have to wrap in an ArrayList as surefire expects an ArrayList instead of a List for some reason
             if ( excludes == null || excludes.size() == 0 )
             {
-                excludes = new ArrayList( Arrays.asList( new String[]{"**/*$*"} ) );
+                excludes = new ArrayList( Arrays.asList( new String[]{ "**/*$*" } ) );
             }
         }
         return excludes;
@@ -548,8 +579,8 @@ public abstract class AbstractSurefireMo
             if ( !range.containsVersion( new DefaultArtifactVersion( artifact.getVersion() ) ) )
             {
                 throw new MojoFailureException(
-                    "TestNG support requires version 4.7 or above. You have declared version " +
-                        artifact.getVersion() );
+                    "TestNG support requires version 4.7 or above. You have declared version "
+                        + artifact.getVersion() );
             }
         }
         return artifact;
@@ -572,20 +603,23 @@ public abstract class AbstractSurefireMo
     {
         StartupConfiguration startupConfiguration =
             createStartupConfiguration( forkConfiguration, provider, classLoaderConfiguration );
-        StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration();
-        ProviderConfiguration providerConfiguration = createProviderConfiguration();
+        String configChecksum = getConfigChecksum();
+        StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum );
+        ProviderConfiguration providerConfiguration = createProviderConfiguration( configChecksum );
         return new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
                                 getForkedProcessTimeoutInSeconds(), startupReportConfiguration );
     }
 
-    protected InPluginVMSurefireStarter createInprocessStarter( ProviderInfo provider, ForkConfiguration forkConfiguration,
-                                                      ClassLoaderConfiguration classLoaderConfiguration )
+    protected InPluginVMSurefireStarter createInprocessStarter( ProviderInfo provider,
+                                                                ForkConfiguration forkConfiguration,
+                                                                ClassLoaderConfiguration classLoaderConfiguration )
         throws MojoExecutionException, MojoFailureException
     {
         StartupConfiguration startupConfiguration =
             createStartupConfiguration( forkConfiguration, provider, classLoaderConfiguration );
-        StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration();
-        ProviderConfiguration providerConfiguration = createProviderConfiguration();
+        String configChecksum = getConfigChecksum();
+        StartupReportConfiguration startupReportConfiguration = getStartupReportConfiguration( configChecksum );
+        ProviderConfiguration providerConfiguration = createProviderConfiguration( configChecksum );
         return new InPluginVMSurefireStarter( startupConfiguration, providerConfiguration, startupReportConfiguration );
 
     }
@@ -655,8 +689,7 @@ public abstract class AbstractSurefireMo
 
             fork.setDebugLine( getDebugForkedProcess() );
 
-
-            if ( (getJvm() == null || "".equals( getJvm() )))
+            if ( ( getJvm() == null || "".equals( getJvm() ) ) )
             {
                 // use the same JVM as the one used to run Maven (the "java.home" one)
                 setJvm( System.getProperty( "java.home" ) + File.separator + "bin" + File.separator + "java" );
@@ -802,7 +835,8 @@ public abstract class AbstractSurefireMo
 
     protected ClassLoaderConfiguration getClassLoaderConfiguration( ForkConfiguration fork )
     {
-        return fork.isForking() ? new ClassLoaderConfiguration( isUseSystemClassLoader(), isUseManifestOnlyJar() )
+        return fork.isForking()
+            ? new ClassLoaderConfiguration( isUseSystemClassLoader(), isUseManifestOnlyJar() )
             : new ClassLoaderConfiguration( false, false );
     }
 
@@ -1078,8 +1112,8 @@ public abstract class AbstractSurefireMo
         }
         catch ( Exception e )
         {
-            String msg = "Build uses Maven 2.0.x, cannot propagate system properties" +
-                " from command line to tests (cf. SUREFIRE-121)";
+            String msg = "Build uses Maven 2.0.x, cannot propagate system properties"
+                + " from command line to tests (cf. SUREFIRE-121)";
             if ( getLog().isDebugEnabled() )
             {
                 getLog().warn( msg, e );
@@ -1137,11 +1171,6 @@ public abstract class AbstractSurefireMo
         }
     }
 
-    private RunOrder getRunOrderObject() {
-        RunOrder runOrder = RunOrder.valueOf( getRunOrder() );
-        return runOrder == null ? RunOrder.FILESYSTEM : runOrder;
-    }
-
     class TestNgProviderInfo
         implements ProviderInfo
     {

Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/CommonReflector.java Tue Nov 22 21:43:19 2011
@@ -51,12 +51,11 @@ public class CommonReflector
 
     public Object createReportingReporterFactory( StartupReportConfiguration startupReportConfiguration )
     {
-        Class[] args =
-            new Class[]{ this.startupReportConfiguration };
+        Class[] args = new Class[]{ this.startupReportConfiguration };
         Object src = createStartupReportConfiguration( startupReportConfiguration );
         Object[] params = new Object[]{ src };
-        return ReflectionUtils.instantiateObject( FileReporterFactory.class.getName(), args,
-                                                  params, surefireClassLoader );
+        return ReflectionUtils.instantiateObject( FileReporterFactory.class.getName(), args, params,
+                                                  surefireClassLoader );
     }
 
 
@@ -65,16 +64,17 @@ public class CommonReflector
         Constructor constructor = ReflectionUtils.getConstructor( this.startupReportConfiguration,
                                                                   new Class[]{ boolean.class, boolean.class,
                                                                       String.class, boolean.class, boolean.class,
-                                                                      File.class, boolean.class, String.class } );
+                                                                      File.class, boolean.class, String.class,
+                                                                      String.class, boolean.class } );
         //noinspection BooleanConstructorCall
         final Object[] params =
             { new Boolean( reporterConfiguration.isUseFile() ), new Boolean( reporterConfiguration.isPrintSummary() ),
                 reporterConfiguration.getReportFormat(),
                 new Boolean( reporterConfiguration.isRedirectTestOutputToFile() ),
-                new Boolean( reporterConfiguration.isDisableXmlReport() ),
-                reporterConfiguration.getReportsDirectory(),
-                new Boolean( reporterConfiguration.isTrimStackTrace()),
-                reporterConfiguration.getReportNameSuffix()};
+                new Boolean( reporterConfiguration.isDisableXmlReport() ), reporterConfiguration.getReportsDirectory(),
+                new Boolean( reporterConfiguration.isTrimStackTrace() ), reporterConfiguration.getReportNameSuffix(),
+                reporterConfiguration.getConfigurationHash(),
+                Boolean.valueOf( reporterConfiguration.isRequiresRunHistory() ) };
         return ReflectionUtils.newInstance( constructor, params );
     }
 

Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java Tue Nov 22 21:43:19 2011
@@ -30,8 +30,10 @@ import org.apache.maven.surefire.booter.
 import org.apache.maven.surefire.booter.SystemPropertyManager;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import org.apache.maven.surefire.testset.RunOrderParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
+import org.apache.maven.surefire.util.RunOrder;
 
 /**
  * Knows how to serialize and deserialize the booter configuration.
@@ -94,7 +96,13 @@ class BooterSerializer
             properties.addList( directoryScannerParameters.getExcludes(), BooterConstants.EXCLUDES_PROPERTY_PREFIX );
             properties.setProperty( BooterConstants.TEST_CLASSES_DIRECTORY,
                                     directoryScannerParameters.getTestClassesDirectory() );
-            properties.setProperty( BooterConstants.RUN_ORDER, directoryScannerParameters.getRunOrder().name() );
+        }
+
+        final RunOrderParameters runOrderParameters = booterConfiguration.getRunOrderParameters();
+        if ( runOrderParameters != null )
+        {
+            properties.setProperty( BooterConstants.RUN_ORDER, RunOrder.asString( runOrderParameters.getRunOrder() ) );
+            properties.setProperty( BooterConstants.RUN_STATISTICS_FILE, runOrderParameters.getRunStatisticsFile() );
         }
 
         ReporterConfiguration reporterConfiguration = booterConfiguration.getReporterConfiguration();

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=1205186&r1=1205185&r2=1205186&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 Tue Nov 22 21:43:19 2011
@@ -37,7 +37,6 @@ import org.apache.maven.surefire.booter.
 import org.apache.maven.surefire.booter.SurefireExecutionException;
 import org.apache.maven.surefire.booter.SystemPropertyManager;
 import org.apache.maven.surefire.providerapi.SurefireProvider;
-import org.apache.maven.surefire.report.ReporterFactory;
 import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.suite.RunResult;
 import org.codehaus.plexus.util.cli.CommandLineException;
@@ -93,13 +92,17 @@ public class ForkStarter
         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(), fileReporterFactory );
+                result = fork( null, providerConfiguration.getProviderProperties(), forkClient, globalRunStatistics );
             }
             else if ( ForkConfiguration.FORK_ALWAYS.equals( requestedForkMode ) )
             {
-                result = runSuitesForkPerTestSet( fileReporterFactory, providerConfiguration.getProviderProperties() );
+                result = runSuitesForkPerTestSet( providerConfiguration.getProviderProperties(), forkClient,
+                                                  globalRunStatistics );
             }
             else
             {
@@ -113,7 +116,8 @@ public class ForkStarter
         return result;
     }
 
-    private RunResult runSuitesForkPerTestSet( FileReporterFactory fileReporterFactory, Properties properties )
+    private RunResult runSuitesForkPerTestSet( Properties properties, ForkClient forkClient,
+                                               RunStatistics globalRunStatistics )
         throws SurefireBooterForkException
     {
         RunResult globalResult = new RunResult( 0, 0, 0, 0 );
@@ -123,14 +127,15 @@ public class ForkStarter
         while ( suites.hasNext() )
         {
             Object testSet = suites.next();
-            RunResult runResult = fork( testSet, properties, fileReporterFactory );
+            RunResult runResult = fork( testSet, properties, forkClient, globalRunStatistics );
             globalResult = globalResult.aggregate( runResult );
         }
 
         return globalResult;
     }
 
-    private RunResult fork( Object testSet, Properties properties, ReporterFactory testSetReporterFactory )
+    private RunResult fork( Object testSet, Properties properties, ForkClient forkClient,
+                            RunStatistics globalRunStatistics )
         throws SurefireBooterForkException
     {
         File surefireProperties;
@@ -172,9 +177,7 @@ public class ForkStarter
             cli.createArg().setFile( systemProperties );
         }
 
-        ForkClient out =
-            new ForkClient( testSetReporterFactory, startupReportConfiguration.getTestVmSystemProperties() );
-        ThreadedStreamConsumer threadedStreamConsumer2 = new ThreadedStreamConsumer( out );
+        ThreadedStreamConsumer threadedStreamConsumer2 = new ThreadedStreamConsumer( forkClient );
 
         if ( forkConfiguration.isDebug() )
         {
@@ -189,13 +192,12 @@ public class ForkStarter
             final int result =
                 CommandLineUtils.executeCommandLine( cli, threadedStreamConsumer2, threadedStreamConsumer2, timeout );
 
-            if (result != RunResult.SUCCESS){
-                throw new SurefireBooterForkException("Error occured in starting fork, check output in log");
+            if ( result != RunResult.SUCCESS )
+            {
+                throw new SurefireBooterForkException( "Error occured in starting fork, check output in log" );
             }
             threadedStreamConsumer2.close();
-            out.close();
-
-            final RunStatistics globalRunStatistics = testSetReporterFactory.getGlobalRunStatistics();
+            forkClient.close();
 
             runResult = globalRunStatistics.getRunResult();
         }
@@ -218,7 +220,8 @@ public class ForkStarter
         {
             final ClasspathConfiguration classpathConfiguration = startupConfiguration.getClasspathConfiguration();
             ClassLoader testsClassLoader = classpathConfiguration.createTestClassLoader( false );
-            ClassLoader surefireClassLoader = classpathConfiguration.createInprocSurefireClassLoader( testsClassLoader );
+            ClassLoader surefireClassLoader =
+                classpathConfiguration.createInprocSurefireClassLoader( testsClassLoader );
 
             CommonReflector commonReflector = new CommonReflector( surefireClassLoader );
             Object reporterFactory = commonReflector.createReportingReporterFactory( startupReportConfiguration );
@@ -226,7 +229,7 @@ public class ForkStarter
             final ProviderFactory providerFactory =
                 new ProviderFactory( startupConfiguration, providerConfiguration, surefireClassLoader, testsClassLoader,
                                      reporterFactory );
-            SurefireProvider surefireProvider = providerFactory.createProvider(false);
+            SurefireProvider surefireProvider = providerFactory.createProvider( false );
             return surefireProvider.getSuites();
         }
         catch ( SurefireExecutionException e )
@@ -236,5 +239,4 @@ public class ForkStarter
     }
 
 
-
 }

Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterFactory.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterFactory.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterFactory.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/report/FileReporterFactory.java Tue Nov 22 21:43:19 2011
@@ -19,12 +19,12 @@ package org.apache.maven.plugin.surefire
  * under the License.
  */
 
+import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
+import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 import org.apache.maven.surefire.booter.StartupReportConfiguration;
-import org.apache.maven.surefire.report.AbstractConsoleReporter;
-import org.apache.maven.surefire.report.AbstractFileReporter;
 import org.apache.maven.surefire.report.ConsoleLogger;
 import org.apache.maven.surefire.report.DefaultDirectConsoleReporter;
 import org.apache.maven.surefire.report.MulticastingReporter;
@@ -34,7 +34,6 @@ import org.apache.maven.surefire.report.
 import org.apache.maven.surefire.report.RunListener;
 import org.apache.maven.surefire.report.RunStatistics;
 import org.apache.maven.surefire.report.TestSetRunListener;
-import org.apache.maven.surefire.report.XMLReporter;
 import org.apache.maven.surefire.suite.RunResult;
 
 /**
@@ -56,11 +55,14 @@ public class FileReporterFactory
 
     private final StartupReportConfiguration reportConfiguration;
 
+    private final StatisticsReporter statisticsReporter;
+
     public FileReporterFactory( StartupReportConfiguration reportConfiguration )
     {
         this.reportConfiguration = reportConfiguration;
         this.reporterConfiguration = getReporterConfiguration();
         multicastingReporter = new MulticastingReporter( instantiateReports() );
+        this.statisticsReporter = reportConfiguration.instantiateStatisticsReporter();
         runStarting();
     }
 
@@ -73,38 +75,23 @@ public class FileReporterFactory
 
     public RunListener createReporter()
     {
-        return new TestSetRunListener( instantiateConsoleReporter(), instantiateFileReporter(),
-                                       instantiateXmlReporter(), instantiateConsoleOutputFileReporter(), globalStats );
-    }
-
-    private AbstractConsoleReporter instantiateConsoleReporter()
-    {
-        return reportConfiguration.instantiateConsoleReporter();
-    }
-
-    private AbstractFileReporter instantiateFileReporter()
-    {
-        return reportConfiguration.instantiateFileReporter();
-    }
-
-    private XMLReporter instantiateXmlReporter()
-    {
-        return reportConfiguration.instantiateXmlReporter();
-    }
-
-    private Reporter instantiateConsoleOutputFileReporter()
-    {
-        return reportConfiguration.instantiateConsoleOutputFileReporterName(
-            reporterConfiguration.getOriginalSystemOut() );
+        final PrintStream sout = reporterConfiguration.getOriginalSystemOut();
+        return new TestSetRunListener( reportConfiguration.instantiateConsoleReporter(),
+                                       reportConfiguration.instantiateFileReporter(),
+                                       reportConfiguration.instantiateXmlReporter(),
+                                       reportConfiguration.instantiateConsoleOutputFileReporter( sout ),
+                                       statisticsReporter, globalStats );
     }
 
     private List instantiateReports()
     {
+        final PrintStream sout = reporterConfiguration.getOriginalSystemOut();
         List result = new ArrayList();
-        addIfNotNull( result, instantiateConsoleReporter() );
-        addIfNotNull( result, instantiateFileReporter() );
-        addIfNotNull( result, instantiateXmlReporter() );
-        addIfNotNull( result, instantiateConsoleOutputFileReporter() );
+        addIfNotNull( result, reportConfiguration.instantiateConsoleReporter() );
+        addIfNotNull( result, reportConfiguration.instantiateFileReporter() );
+        addIfNotNull( result, reportConfiguration.instantiateXmlReporter() );
+        addIfNotNull( result, reportConfiguration.instantiateConsoleOutputFileReporter( sout ) );
+        addIfNotNull( result, statisticsReporter );
         return result;
     }
 

Added: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java?rev=1205186&view=auto
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java (added)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java Tue Nov 22 21:43:19 2011
@@ -0,0 +1,98 @@
+package org.apache.maven.plugin.surefire.runorder;
+
+/*
+ * 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.io.File;
+import java.io.FileNotFoundException;
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.Reporter;
+import org.apache.maven.surefire.util.NestedRuntimeException;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class StatisticsReporter
+    implements Reporter
+{
+    private final RunEntryStatisticsMap existing;
+
+    private final RunEntryStatisticsMap newResults;
+
+    private final File dataFile;
+
+    public StatisticsReporter( File dataFile )
+    {
+        this.dataFile = dataFile;
+        this.existing = RunEntryStatisticsMap.fromFile( this.dataFile );
+        this.newResults = new RunEntryStatisticsMap();
+    }
+
+    public void testSetStarting( ReportEntry report )
+    {
+    }
+
+    public void testSetCompleted( ReportEntry report )
+    {
+        try
+        {
+            newResults.serialize( dataFile );
+        }
+        catch ( FileNotFoundException e )
+        {
+            throw new NestedRuntimeException( e );
+        }
+    }
+
+    public void testStarting( ReportEntry report )
+    {
+    }
+
+    public void testSucceeded( ReportEntry report )
+    {
+        newResults.add( existing.createNextGeneration( report ) );
+    }
+
+    public void testSkipped( ReportEntry report )
+    {
+        newResults.add( existing.createNextGeneration( report ) );
+    }
+
+    public void testError( ReportEntry report, String stdOut, String stdErr )
+    {
+        newResults.add( existing.createNextGenerationFailure( report ) );
+    }
+
+    public void testFailed( ReportEntry report, String stdOut, String stdErr )
+    {
+        newResults.add( existing.createNextGenerationFailure( report ) );
+    }
+
+    public void writeMessage( String message )
+    {
+    }
+
+    public void writeMessage( byte[] b, int off, int len )
+    {
+    }
+
+    public void reset()
+    {
+    }
+}

Propchange: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/runorder/StatisticsReporter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/booter/StartupReportConfiguration.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/booter/StartupReportConfiguration.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/booter/StartupReportConfiguration.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/booter/StartupReportConfiguration.java Tue Nov 22 21:43:19 2011
@@ -24,6 +24,7 @@ import java.io.PrintStream;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Properties;
+import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 import org.apache.maven.surefire.report.AbstractConsoleReporter;
 import org.apache.maven.surefire.report.AbstractFileReporter;
 import org.apache.maven.surefire.report.BriefConsoleReporter;
@@ -39,7 +40,6 @@ import org.apache.maven.surefire.report.
 /**
  * All the parameters used to construct reporters
  * <p/>
- * TODO: Move out of API module
  *
  * @author Kristian Rosenvold
  */
@@ -53,6 +53,10 @@ public class StartupReportConfiguration
 
     private final String reportNameSuffix;
 
+    private final String configurationHash;
+
+    private final boolean requiresRunHistory;
+
     private final boolean redirectTestOutputToFile;
 
     private final boolean disableXmlReport;
@@ -61,7 +65,7 @@ public class StartupReportConfiguration
 
     private final boolean trimStackTrace;
 
-    private final Properties testVmSystemProperties = new Properties(  );
+    private final Properties testVmSystemProperties = new Properties();
 
     public static final String BRIEF_REPORT_FORMAT = "brief";
 
@@ -69,16 +73,8 @@ public class StartupReportConfiguration
 
     public StartupReportConfiguration( boolean useFile, boolean printSummary, String reportFormat,
                                        boolean redirectTestOutputToFile, boolean disableXmlReport,
-                                       File reportsDirectory, boolean trimStackTrace )
-    {
-        this( useFile, printSummary, reportFormat, redirectTestOutputToFile, 
-                disableXmlReport, reportsDirectory, trimStackTrace, null );
-    }
-
-    public StartupReportConfiguration( boolean useFile, boolean printSummary, String reportFormat,
-                                       boolean redirectTestOutputToFile, boolean disableXmlReport,
-                                       File reportsDirectory, boolean trimStackTrace,
-                                       String reportNameSuffix )
+                                       File reportsDirectory, boolean trimStackTrace, String reportNameSuffix,
+                                       String configurationHash, boolean requiresRunHistory )
     {
         this.useFile = useFile;
         this.printSummary = printSummary;
@@ -88,18 +84,22 @@ public class StartupReportConfiguration
         this.reportsDirectory = reportsDirectory;
         this.trimStackTrace = trimStackTrace;
         this.reportNameSuffix = reportNameSuffix;
+        this.configurationHash = configurationHash;
+        this.requiresRunHistory = requiresRunHistory;
     }
 
     public static StartupReportConfiguration defaultValue()
     {
         File target = new File( "./target" );
-        return new StartupReportConfiguration( true, true, "PLAIN", false, false, target, false );
+        return new StartupReportConfiguration( true, true, "PLAIN", false, false, target, false, null, "TESTHASH",
+                                               false );
     }
 
     public static StartupReportConfiguration defaultNoXml()
     {
         File target = new File( "./target" );
-        return new StartupReportConfiguration( true, true, "PLAIN", false, true, target, false );
+        return new StartupReportConfiguration( true, true, "PLAIN", false, true, target, false, null, "TESTHASHxXML",
+                                               false );
     }
 
     public boolean isUseFile()
@@ -150,7 +150,7 @@ public class StartupReportConfiguration
     {
         if ( !isDisableXmlReport() )
         {
-            return new XMLReporter(trimStackTrace, reportsDirectory, reportNameSuffix);
+            return new XMLReporter( trimStackTrace, reportsDirectory, reportNameSuffix );
         }
         return null;
     }
@@ -177,11 +177,11 @@ public class StartupReportConfiguration
         {
             if ( BRIEF_REPORT_FORMAT.equals( getReportFormat() ) )
             {
-                return new BriefFileReporter(trimStackTrace, reportsDirectory, getReportNameSuffix());
+                return new BriefFileReporter( trimStackTrace, reportsDirectory, getReportNameSuffix() );
             }
             else if ( PLAIN_REPORT_FORMAT.equals( getReportFormat() ) )
             {
-                return new FileReporter(trimStackTrace, reportsDirectory, getReportNameSuffix());
+                return new FileReporter( trimStackTrace, reportsDirectory, getReportNameSuffix() );
             }
         }
         return null;
@@ -214,15 +214,15 @@ public class StartupReportConfiguration
     {
         if ( isUseFile() )
         {
-            return isPrintSummary() ? new ConsoleReporter(trimStackTrace) : null;
+            return isPrintSummary() ? new ConsoleReporter( trimStackTrace ) : null;
         }
         else if ( isRedirectTestOutputToFile() || BRIEF_REPORT_FORMAT.equals( getReportFormat() ) )
         {
-            return new BriefConsoleReporter(trimStackTrace);
+            return new BriefConsoleReporter( trimStackTrace );
         }
         else if ( PLAIN_REPORT_FORMAT.equals( getReportFormat() ) )
         {
-            return new DetailedConsoleReporter(trimStackTrace );
+            return new DetailedConsoleReporter( trimStackTrace );
         }
         return null;
     }
@@ -239,16 +239,31 @@ public class StartupReportConfiguration
         }
     }
 
-    public Reporter instantiateConsoleOutputFileReporterName(PrintStream originalSystemOut)
+    public Reporter instantiateConsoleOutputFileReporter( PrintStream originalSystemOut )
     {
         if ( isRedirectTestOutputToFile() )
         {
-            return new ConsoleOutputFileReporter(reportsDirectory, getReportNameSuffix());
+            return new ConsoleOutputFileReporter( reportsDirectory, getReportNameSuffix() );
         }
         else
         {
-            return new ConsoleOutputDirectReporter(originalSystemOut);
+            return new ConsoleOutputDirectReporter( originalSystemOut );
+        }
+    }
+
+    public StatisticsReporter instantiateStatisticsReporter()
+    {
+        if ( requiresRunHistory )
+        {
+            final File target = getStatisticsFile();
+            return new StatisticsReporter( target );
         }
+        return null;
+    }
+
+    public File getStatisticsFile()
+    {
+        return new File( reportsDirectory.getParentFile().getParentFile(), ".surefire-" + this.configurationHash );
     }
 
 
@@ -286,4 +301,14 @@ public class StartupReportConfiguration
     {
         return trimStackTrace;
     }
+
+    public String getConfigurationHash()
+    {
+        return configurationHash;
+    }
+
+    public boolean isRequiresRunHistory()
+    {
+        return requiresRunHistory;
+    }
 }

Modified: maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/TestSetRunListener.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/TestSetRunListener.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/TestSetRunListener.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/surefire/report/TestSetRunListener.java Tue Nov 22 21:43:19 2011
@@ -23,6 +23,7 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import org.apache.maven.plugin.surefire.runorder.StatisticsReporter;
 import org.apache.maven.surefire.util.internal.ByteBuffer;
 
 /**
@@ -45,7 +46,8 @@ public class TestSetRunListener
 
 
     public TestSetRunListener( AbstractConsoleReporter consoleReporter, AbstractFileReporter fileReporter,
-                               XMLReporter xmlReporter, Reporter reporter, RunStatistics globalStats )
+                               XMLReporter xmlReporter, Reporter reporter, StatisticsReporter statisticsReporter,
+                               RunStatistics globalStats )
     {
 
         ArrayList reportes = new ArrayList();
@@ -65,6 +67,10 @@ public class TestSetRunListener
         {
             reportes.add( reporter );
         }
+        if ( statisticsReporter != null )
+        {
+            reportes.add( statisticsReporter );
+        }
         multicastingReporter = new MulticastingReporter( reportes );
         this.testSetStatistics = new TestSetStatistics();
         this.globalStatistics = globalStats;
@@ -77,7 +83,7 @@ public class TestSetRunListener
 
     public void writeMessage( String message )
     {
-        info(  message );
+        info( message );
     }
 
     public void writeMessage( byte[] b, int off, int len )

Modified: maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java Tue Nov 22 21:43:19 2011
@@ -35,6 +35,7 @@ import org.apache.maven.surefire.booter.
 import org.apache.maven.surefire.booter.TypeEncodedValue;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import org.apache.maven.surefire.testset.RunOrderParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.RunOrder;
@@ -160,7 +161,8 @@ public class BooterDeserializerProviderC
         excludes.add( "xx1" );
         excludes.add( "xx2" );
 
-        return new DirectoryScannerParameters( aDir, includes, excludes, Boolean.TRUE, RunOrder.FILESYSTEM );
+        return new DirectoryScannerParameters( aDir, includes, excludes, Boolean.TRUE,
+                                               RunOrder.asString( RunOrder.DEFAULT ) );
     }
 
     private ProviderConfiguration saveAndReload( ProviderConfiguration booterConfiguration,
@@ -187,7 +189,8 @@ public class BooterDeserializerProviderC
         TestRequest testSuiteDefinition =
             new TestRequest( getSuiteXmlFileStrings(), getTestSourceDirectory(), aUserRequestedTest,
                              aUserRequestedTestMethod );
-        return new ProviderConfiguration( directoryScannerParameters, true, reporterConfiguration,
+        RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null );
+        return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration,
                                           new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(),
                                           aTestTyped );
     }

Modified: maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java?rev=1205186&r1=1205185&r2=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java Tue Nov 22 21:43:19 2011
@@ -35,8 +35,10 @@ import org.apache.maven.surefire.booter.
 import org.apache.maven.surefire.booter.SystemPropertyManager;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
+import org.apache.maven.surefire.testset.RunOrderParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
 import org.apache.maven.surefire.testset.TestRequest;
+import org.apache.maven.surefire.util.RunOrder;
 
 import junit.framework.TestCase;
 
@@ -100,7 +102,7 @@ public class BooterDeserializerStartupCo
     {
         Classpath testClassPath = new Classpath( Arrays.asList( new String[]{ "CP1", "CP2" } ) );
         Classpath providerClasspath = new Classpath( Arrays.asList( new String[]{ "SP1", "SP2" } ) );
-        return new ClasspathConfiguration( testClassPath, providerClasspath, new Classpath(  ), true, true );
+        return new ClasspathConfiguration( testClassPath, providerClasspath, new Classpath(), true, true );
     }
 
     public static ClassLoaderConfiguration getSystemClassLoaderConfiguration()
@@ -147,7 +149,9 @@ public class BooterDeserializerStartupCo
         TestRequest testSuiteDefinition =
             new TestRequest( Arrays.asList( getSuiteXmlFileStrings() ), getTestSourceDirectory(), aUserRequestedTest,
                              aUserRequestedTestMethod );
-        return new ProviderConfiguration( directoryScannerParameters, true, reporterConfiguration,
+
+        RunOrderParameters runOrderParameters = new RunOrderParameters( RunOrder.DEFAULT, null );
+        return new ProviderConfiguration( directoryScannerParameters, runOrderParameters, true, reporterConfiguration,
                                           new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(),
                                           BooterDeserializerProviderConfigurationTest.aTestTyped );
     }

Copied: maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java (from r1204953, maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java)
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java?p2=maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java&p1=maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java&r1=1204953&r2=1205186&rev=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java (original)
+++ maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java Tue Nov 22 21:43:19 2011
@@ -21,8 +21,8 @@ package org.apache.maven.plugin.surefire
 
 import org.apache.maven.plugin.surefire.report.FileReporterFactory;
 import org.apache.maven.surefire.booter.StartupReportConfiguration;
-import org.apache.maven.surefire.report.DefaultConsoleReporter;
 import org.apache.maven.surefire.report.ConsoleLogger;
+import org.apache.maven.surefire.report.DefaultConsoleReporter;
 import org.apache.maven.surefire.report.RunListener;
 
 /**

Added: maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java?rev=1205186&view=auto
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java (added)
+++ maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java Tue Nov 22 21:43:19 2011
@@ -0,0 +1,109 @@
+package org.apache.maven.plugin.surefire.runorder;
+
+/*
+ * 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.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import java.util.Arrays;
+import java.util.List;
+import org.apache.maven.surefire.report.ReportEntry;
+import org.apache.maven.surefire.report.SimpleReportEntry;
+
+import junit.framework.TestCase;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class RunEntryStatisticsMapTest
+    extends TestCase
+{
+    public void testPrioritizedClassRuntime()
+        throws IOException
+    {
+        final RunEntryStatisticsMap runEntryStatisticsMap = RunEntryStatisticsMap.fromReader( getStatisticsFile() );
+        final List list = Arrays.asList( new Class[]{ A.class, B.class, C.class } );
+        final List prioritizedTestsClassRunTime = runEntryStatisticsMap.getPrioritizedTestsClassRunTime( list, 2 );
+        assertEquals( C.class, prioritizedTestsClassRunTime.get( 0 ) );
+        assertEquals( B.class, prioritizedTestsClassRunTime.get( 1 ) );
+        assertEquals( A.class, prioritizedTestsClassRunTime.get( 2 ) );
+    }
+
+    public void testPrioritizedFailureFirst()
+        throws IOException
+    {
+        final RunEntryStatisticsMap runEntryStatisticsMap = RunEntryStatisticsMap.fromReader( getStatisticsFile() );
+        final List list = Arrays.asList( new Class[]{ A.class, B.class, C.class } );
+        final List prioritizedTestsClassRunTime = runEntryStatisticsMap.getPrioritizedTestsByFailureFirst( list);
+        assertEquals( A.class, prioritizedTestsClassRunTime.get( 0 ) );
+        assertEquals( C.class, prioritizedTestsClassRunTime.get( 1 ) );
+        assertEquals( B.class, prioritizedTestsClassRunTime.get( 2 ) );
+    }
+
+    private StringReader getStatisticsFile()
+    {
+        String content = "0,17,testA(org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$A)\n" +
+            "2,42,testB(org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$B)\n" +
+            "1,100,testC(org.apache.maven.plugin.surefire.runorder.RunEntryStatisticsMapTest$C)\n";
+        return new StringReader( content );
+    }
+
+    public void testSerialize()
+        throws Exception
+    {
+        File data = File.createTempFile( "surefire-unit", "test" );
+        RunEntryStatisticsMap existingEntries = RunEntryStatisticsMap.fromFile( data );
+        RunEntryStatisticsMap newResults = new RunEntryStatisticsMap();
+
+        ReportEntry reportEntry1 = new SimpleReportEntry( "abc", "method1", new Integer( 42 ) );
+        ReportEntry reportEntry2 = new SimpleReportEntry( "abc", "willFail", new Integer( 17 ) );
+        ReportEntry reportEntry3 = new SimpleReportEntry( "abc", "method3", new Integer( 100 ) );
+
+        newResults.add( existingEntries.createNextGeneration( reportEntry1 ) );
+        newResults.add( existingEntries.createNextGeneration( reportEntry2 ) );
+        newResults.add( existingEntries.createNextGeneration( reportEntry3 ) );
+
+        newResults.serialize( data );
+
+        RunEntryStatisticsMap nextRun = RunEntryStatisticsMap.fromFile( data );
+        newResults = new RunEntryStatisticsMap();
+
+        newResults.add( existingEntries.createNextGeneration( reportEntry1 ) );
+        newResults.add( existingEntries.createNextGenerationFailure( reportEntry2 ) );
+        newResults.add( existingEntries.createNextGeneration( reportEntry3 ) );
+
+        newResults.serialize( data );
+    }
+
+
+
+        class A
+        {
+        }
+
+        class B
+        {
+        }
+
+        class C
+        {
+        }
+
+}

Propchange: maven/surefire/trunk/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMapTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

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=1205186&r1=1205185&r2=1205186&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 Tue Nov 22 21:43:19 2011
@@ -24,7 +24,6 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
-
 import org.apache.maven.artifact.factory.ArtifactFactory;
 import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
 import org.apache.maven.artifact.repository.ArtifactRepository;
@@ -591,11 +590,21 @@ public class SurefirePlugin
 
     /**
      * Defines the order the tests will be run in. Supported values are "alphabetical", "reversealphabetical", "random",
-     * "hourly" (alphabetical on even hours, reverse alphabetical on odd hours) and "filesystem".
+     * "hourly" (alphabetical on even hours, reverse alphabetical on odd hours), "failedfirst", "balanced" and "filesystem".
      * <p/>
      * <p/>
      * Odd/Even for hourly is determined at the time the of scanning the classpath, meaning it could change during a
      * multi-module build.
+     * <p/>
+     * Failed first will run tests that failed on previous run first, as well as new tests.
+     * <p/>
+     * Balanced is only relevant with parallel=classes, and will try to optimize the run-order of the tests to
+     * make all tests complete at the same time, reducing the overall execution time.
+     * <p/>
+     * Note that the statistics are stored in a file named .surefire-XXXXXXXXX beside pom.xml, and should not
+     * be checked into version control. The "XXXXX" is the SHA1 checksum of the entire surefire configuration,
+     * so different configurations will have different statistics files, meaning if you change any config
+     * settings you will re-run once before new statistics data can be established.
      *
      * @parameter default-value="filesystem"
      * @since 2.7
@@ -653,7 +662,7 @@ public class SurefirePlugin
 
     protected String[] getDefaultIncludes()
     {
-        return new String[]{"**/Test*.java", "**/*Test.java", "**/*TestCase.java"};
+        return new String[]{ "**/Test*.java", "**/*Test.java", "**/*TestCase.java" };
     }
 
     // now for the implementation of the field accessors

Copied: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/PrioritizedTest.java (from r1204953, maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java)
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/PrioritizedTest.java?p2=maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/PrioritizedTest.java&p1=maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java&r1=1204953&r2=1205186&rev=1205186&view=diff
==============================================================================
--- maven/surefire/trunk/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/TestSetMockReporterFactory.java (original)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/PrioritizedTest.java Tue Nov 22 21:43:19 2011
@@ -1,4 +1,4 @@
-package org.apache.maven.plugin.surefire.booterclient;
+package org.apache.maven.plugin.surefire.runorder;
 
 /*
  * Licensed to the Apache Software Foundation (ASF) under one
@@ -19,30 +19,33 @@ package org.apache.maven.plugin.surefire
  * under the License.
  */
 
-import org.apache.maven.plugin.surefire.report.FileReporterFactory;
-import org.apache.maven.surefire.booter.StartupReportConfiguration;
-import org.apache.maven.surefire.report.DefaultConsoleReporter;
-import org.apache.maven.surefire.report.ConsoleLogger;
-import org.apache.maven.surefire.report.RunListener;
-
 /**
  * @author Kristian Rosenvold
  */
-public class TestSetMockReporterFactory
-    extends FileReporterFactory
+public class PrioritizedTest
 {
-    public TestSetMockReporterFactory()
+    private final Class clazz;
+    private final Priority priority;
+
+    public PrioritizedTest( Class clazz, Priority pri )
+    {
+        this.clazz = clazz;
+        this.priority = pri;
+    }
+
+    public int getPriority()
     {
-        super( StartupReportConfiguration.defaultValue() );
+        return priority.getPriority();
     }
 
-    public ConsoleLogger createConsoleLogger()
+
+    public int getTotalRuntime()
     {
-        return new DefaultConsoleReporter( System.out );
+        return priority.getTotalRuntime();
     }
 
-    public RunListener createReporter()
+    public Class getClazz()
     {
-        return new MockReporter();
+        return clazz;
     }
 }

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/Priority.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/Priority.java?rev=1205186&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/Priority.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/Priority.java Tue Nov 22 21:43:19 2011
@@ -0,0 +1,74 @@
+package org.apache.maven.plugin.surefire.runorder;
+
+/*
+ * 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 Kristian Rosenvold
+ */
+public class Priority
+{
+    private final String className;
+    int priority;
+    int totalRuntime = 0;
+    int minSuccessRate = Integer.MAX_VALUE;
+
+    public Priority( String className )
+    {
+        this.className = className;
+    }
+
+    public static Priority lowest(String className){
+        Priority priority1 = new Priority( className );
+        priority1.setPriority( Integer.MAX_VALUE );
+        priority1.minSuccessRate = 0;
+        return priority1;
+    }
+
+    public void addItem( RunEntryStatistics itemStat )
+    {
+        totalRuntime += itemStat.getRunTime();
+        minSuccessRate = Math.min(  minSuccessRate, itemStat.getSuccessfulBuilds());
+    }
+
+    public int getTotalRuntime()
+    {
+        return totalRuntime;
+    }
+
+    public int getMinSuccessRate()
+    {
+        return minSuccessRate;
+    }
+
+    public String getClassName()
+    {
+        return className;
+    }
+
+    public int getPriority()
+    {
+        return priority;
+    }
+
+    public void setPriority( int priority )
+    {
+        this.priority = priority;
+    }
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/Priority.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java?rev=1205186&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java Tue Nov 22 21:43:19 2011
@@ -0,0 +1,99 @@
+package org.apache.maven.plugin.surefire.runorder;
+/*
+ * 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.StringTokenizer;
+import org.apache.maven.surefire.report.ReportEntry;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class RunEntryStatistics
+{
+    private final int runTime;
+
+    private final int successfulBuilds;
+
+    private final String testName;
+
+    private RunEntryStatistics( int runTime, int successfulBuilds, String testName )
+    {
+        this.runTime = runTime;
+        this.successfulBuilds = successfulBuilds;
+        this.testName = testName;
+    }
+
+    public static RunEntryStatistics fromReportEntry( ReportEntry previous )
+    {
+        final Integer elapsed = previous.getElapsed();
+        return new RunEntryStatistics( elapsed != null ? elapsed.intValue() : 0, 0, previous.getName() );
+    }
+
+    public static RunEntryStatistics fromValues( int runTime, int successfulBuilds, Class clazz, String testName )
+    {
+        return new RunEntryStatistics( runTime, successfulBuilds, testName + "(" + clazz.getName() + ")" );
+    }
+
+    public RunEntryStatistics nextGeneration( int runTime )
+    {
+        return new RunEntryStatistics( runTime, this.successfulBuilds + 1, this.testName );
+    }
+
+    public RunEntryStatistics nextGenerationFailure( int runTime )
+    {
+        return new RunEntryStatistics( runTime, 0, this.testName );
+    }
+
+    public String getTestName()
+    {
+        return testName;
+    }
+
+    public int getRunTime()
+    {
+        return runTime;
+    }
+
+
+    public int getSuccessfulBuilds()
+    {
+        return successfulBuilds;
+    }
+
+    public static RunEntryStatistics fromString( String line )
+    {
+        StringTokenizer tok = new StringTokenizer( line, "," );
+        int successfulBuilds = Integer.parseInt( tok.nextToken() );
+        int runTime = Integer.parseInt( tok.nextToken() );
+        String className = tok.nextToken();
+        return new RunEntryStatistics( runTime, successfulBuilds, className );
+    }
+
+    public String getAsString()
+    {
+        StringBuffer stringBuffer = new StringBuffer();
+        stringBuffer.append( successfulBuilds );
+        stringBuffer.append( "," );
+        stringBuffer.append( runTime );
+        stringBuffer.append( "," );
+        stringBuffer.append( testName );
+        return stringBuffer.toString();
+    }
+
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatistics.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java?rev=1205186&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java Tue Nov 22 21:43:19 2011
@@ -0,0 +1,289 @@
+package org.apache.maven.plugin.surefire.runorder;
+
+/*
+ * 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.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.maven.surefire.report.ReportEntry;
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class RunEntryStatisticsMap
+{
+    private final Map runEntryStatistics;
+
+    public RunEntryStatisticsMap( Map runEntryStatistics )
+    {
+        this.runEntryStatistics = Collections.synchronizedMap( runEntryStatistics );
+    }
+
+    public RunEntryStatisticsMap()
+    {
+        this( new HashMap() );
+    }
+
+    public static RunEntryStatisticsMap fromFile( File file )
+    {
+        if ( file.exists() )
+        {
+            try
+            {
+                FileReader fileReader = new FileReader( file );
+                return fromReader( fileReader );
+            }
+            catch ( FileNotFoundException e )
+            {
+                throw new RuntimeException( e );
+            }
+            catch ( IOException e1 )
+            {
+                throw new RuntimeException( e1 );
+            }
+
+        }
+        return new RunEntryStatisticsMap();
+    }
+
+    static RunEntryStatisticsMap fromReader( Reader fileReader )
+        throws IOException
+    {
+        Map result = new HashMap();
+        BufferedReader bufferedReader = new BufferedReader( fileReader );
+        String line = bufferedReader.readLine();
+        while ( line != null )
+        {
+            if ( !line.startsWith( "#" ) )
+            {
+                final RunEntryStatistics stats = RunEntryStatistics.fromString( line );
+                result.put( stats.getTestName(), stats );
+            }
+            line = bufferedReader.readLine();
+        }
+        return new RunEntryStatisticsMap( result );
+    }
+
+    public void serialize( File file )
+        throws FileNotFoundException
+    {
+        FileOutputStream fos = new FileOutputStream( file );
+        PrintWriter printWriter = new PrintWriter( fos );
+        List items = new ArrayList( runEntryStatistics.values() );
+        Collections.sort( items, new RunCountComparator() );
+        RunEntryStatistics item;
+        for ( Iterator iter = items.iterator(); iter.hasNext(); )
+        {
+            item = (RunEntryStatistics) iter.next();
+            printWriter.println( item.getAsString() );
+        }
+        printWriter.close();
+    }
+
+
+    public RunEntryStatistics findOrCreate( ReportEntry reportEntry )
+    {
+        final RunEntryStatistics item = (RunEntryStatistics) runEntryStatistics.get( reportEntry.getName() );
+        return item != null ? item : RunEntryStatistics.fromReportEntry( reportEntry );
+    }
+
+    public RunEntryStatistics createNextGeneration( ReportEntry reportEntry )
+    {
+        final RunEntryStatistics newItem = findOrCreate( reportEntry );
+        final Integer elapsed = reportEntry.getElapsed();
+        return newItem.nextGeneration( elapsed != null ? elapsed.intValue() : 0 );
+    }
+
+    public RunEntryStatistics createNextGenerationFailure( ReportEntry reportEntry )
+    {
+        final RunEntryStatistics newItem = findOrCreate( reportEntry );
+        final Integer elapsed = reportEntry.getElapsed();
+        return newItem.nextGenerationFailure( elapsed != null ? elapsed.intValue() : 0 );
+    }
+
+    public void add( RunEntryStatistics item )
+    {
+        runEntryStatistics.put( item.getTestName(), item );
+    }
+
+    class RunCountComparator
+        implements Comparator
+    {
+        public int compare( Object o, Object o1 )
+        {
+            RunEntryStatistics re = (RunEntryStatistics) o;
+            RunEntryStatistics re1 = (RunEntryStatistics) o1;
+            int runtime = re.getSuccessfulBuilds() - re1.getSuccessfulBuilds();
+            if ( runtime == 0 )
+            {
+                return re.getRunTime() - re1.getRunTime();
+            }
+            return runtime;
+        }
+    }
+
+    public List getPrioritizedTestsClassRunTime( List testsToRun, int threadCount )
+    {
+        final List prioritizedTests = getPrioritizedTests( testsToRun, new TestRuntimeComparator() );
+        ThreadedExecutionScheduler threadedExecutionScheduler = new ThreadedExecutionScheduler( threadCount );
+        for ( Iterator prioritizedTest = prioritizedTests.iterator(); prioritizedTest.hasNext(); )
+        {
+            threadedExecutionScheduler.addTest( (PrioritizedTest) prioritizedTest.next() );
+        }
+
+        return threadedExecutionScheduler.getResult();
+
+    }
+
+    public List getPrioritizedTestsByFailureFirst( List testsToRun )
+    {
+        final List prioritizedTests = getPrioritizedTests( testsToRun, new LeastFailureComparator() );
+        return transformToClasses( prioritizedTests );
+    }
+
+
+    private List getPrioritizedTests( List testsToRun, Comparator priorityComparator )
+    {
+        Map classPriorities = getPriorities( priorityComparator );
+
+        List tests = new ArrayList();
+        for ( Iterator iter = testsToRun.iterator(); iter.hasNext(); )
+        {
+            Class clazz = (Class) iter.next();
+            Priority pri = (Priority) classPriorities.get( clazz.getName() );
+            if ( pri == null )
+            {
+                pri = Priority.lowest( clazz.getName() );
+            }
+            PrioritizedTest prioritizedTest = new PrioritizedTest( clazz, pri );
+            tests.add( prioritizedTest );
+        }
+        Collections.sort( tests, new PrioritizedTestComparator() );
+        return tests;
+
+    }
+
+    private List transformToClasses( List tests )
+    {
+        List result = new ArrayList();
+        for ( int i = 0; i < tests.size(); i++ )
+        {
+            result.add( ( (PrioritizedTest) tests.get( i ) ).getClazz() );
+        }
+        return result;
+    }
+
+    public Map getPriorities( Comparator priorityComparator )
+    {
+        Map priorities = new HashMap();
+        for ( Iterator iter = runEntryStatistics.keySet().iterator(); iter.hasNext(); )
+        {
+            String testNames = (String) iter.next();
+            String clazzName = extractClassName( testNames );
+            Priority priority = (Priority) priorities.get( clazzName );
+            if ( priority == null )
+            {
+                priority = new Priority( clazzName );
+                priorities.put( clazzName, priority );
+            }
+
+            RunEntryStatistics itemStat = (RunEntryStatistics) runEntryStatistics.get( testNames );
+            priority.addItem( itemStat );
+        }
+
+        List items = new ArrayList( priorities.values() );
+        Collections.sort( items, priorityComparator );
+        Map result = new HashMap();
+        int i = 0;
+        for ( Iterator iter = items.iterator(); iter.hasNext(); )
+        {
+            Priority pri = (Priority) iter.next();
+            pri.setPriority( i++ );
+            result.put( pri.getClassName(), pri );
+        }
+        return result;
+    }
+
+    class PrioritizedTestComparator
+        implements Comparator
+    {
+        public int compare( Object o, Object o1 )
+        {
+            PrioritizedTest re = (PrioritizedTest) o;
+            PrioritizedTest re1 = (PrioritizedTest) o1;
+            return re.getPriority() - re1.getPriority();
+        }
+    }
+
+    class TestRuntimeComparator
+        implements Comparator
+    {
+        public int compare( Object o, Object o1 )
+        {
+            Priority re = (Priority) o;
+            Priority re1 = (Priority) o1;
+            return re1.getTotalRuntime() - re.getTotalRuntime();
+        }
+    }
+
+    class LeastFailureComparator
+        implements Comparator
+    {
+        public int compare( Object o, Object o1 )
+        {
+            Priority re = (Priority) o;
+            Priority re1 = (Priority) o1;
+            return re.getMinSuccessRate() - re1.getMinSuccessRate();
+        }
+    }
+
+
+    private static final Pattern PARENS = Pattern.compile( "^" + "[^\\(\\)]+" //non-parens
+                                                               + "\\((" // then an open-paren (start matching a group)
+                                                               + "[^\\\\(\\\\)]+" //non-parens
+                                                               + ")\\)" + "$" ); // then a close-paren (end group match)
+
+    String extractClassName( String displayName )
+    {
+        Matcher m = PARENS.matcher( displayName );
+        if ( !m.find() )
+        {
+            return displayName;
+        }
+        return m.group( 1 );
+    }
+
+
+}

Propchange: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/RunEntryStatisticsMap.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionScheduler.java
URL: http://svn.apache.org/viewvc/maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionScheduler.java?rev=1205186&view=auto
==============================================================================
--- maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionScheduler.java (added)
+++ maven/surefire/trunk/surefire-api/src/main/java/org/apache/maven/plugin/surefire/runorder/ThreadedExecutionScheduler.java Tue Nov 22 21:43:19 2011
@@ -0,0 +1,90 @@
+package org.apache.maven.plugin.surefire.runorder;
+/*
+ * 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.ArrayList;
+import java.util.List;
+
+
+/**
+ * @author Kristian Rosenvold
+ */
+public class ThreadedExecutionScheduler
+{
+    private final int numThreads;
+
+    private final int runTime[];
+
+    private final List[] lists;
+
+    public ThreadedExecutionScheduler( int numThreads )
+    {
+        this.numThreads = numThreads;
+        runTime = new int[numThreads];
+        lists = new List[numThreads];
+        for ( int i = 0; i < numThreads; i++ )
+        {
+            lists[i] = new ArrayList();
+        }
+    }
+
+    public void addTest( PrioritizedTest prioritizedTest )
+    {
+        final int leastBusySlot = findLeastBusySlot();
+        runTime[leastBusySlot] += prioritizedTest.getTotalRuntime();
+        lists[leastBusySlot].add( prioritizedTest.getClazz() );
+    }
+
+    public List getResult()
+    {
+        List result = new ArrayList();
+        int index = 0;
+        boolean added = false;
+        do
+        {
+            added = false;
+            for ( int i = 0; i < numThreads; i++ )
+            {
+                if ( lists[i].size() > index )
+                {
+                    result.add( lists[i].get( index ) );
+                    added = true;
+                }
+            }
+            index++;
+        }
+        while ( added );
+        return result;
+    }
+
+    private int findLeastBusySlot()
+    {
+        int leastBusy = 0;
+        int minRuntime = runTime[0];
+        for ( int i = 1; i < numThreads; i++ )
+        {
+            if ( runTime[i] < minRuntime )
+            {
+                leastBusy = i;
+                minRuntime = runTime[i];
+            }
+        }
+        return leastBusy;
+    }
+}