From f4461104f472371838040af60e36b595b9e1b4b2 Mon Sep 17 00:00:00 2001
From: Janmejay Singh <singh.janmejay@gmail.com>
Date: Sat, 9 Apr 2011 18:20:59 +0530
Subject: [PATCH 1/2] Adds test-list-preprocessor support, which loads an optional user specified algorithm(class) from any dependency and uses it to pre-process the list of tests to be run.

---
 .../maven/plugin/failsafe/IntegrationTestMojo.java |   23 +++++-
 .../plugin/surefire/AbstractSurefireMojo.java      |   39 +++++---
 .../surefire/SurefireDependencyResolver.java       |   12 ++-
 .../surefire/SurefireExecutionParameters.java      |    3 +
 .../surefire/booterclient/BooterSerializer.java    |    1 +
 ...ooterDeserializerProviderConfigurationTest.java |   19 ++--
 ...BooterDeserializerStartupConfigurationTest.java |    5 +-
 .../maven/plugin/surefire/SurefirePlugin.java      |   22 +++++
 surefire-api/pom.xml                               |    4 +
 .../maven/surefire/booter/BaseProviderFactory.java |    7 +-
 .../maven/surefire/booter/SurefireReflector.java   |    6 +-
 .../surefire/providerapi/ProviderParameters.java   |    7 ++
 .../testset/DirectoryScannerParameters.java        |   25 +++++-
 .../surefire/testset/TestPreprocessorConfig.java   |  100 ++++++++++++++++++++
 .../surefire/util/DefaultDirectoryScanner.java     |   19 ++--
 .../maven/surefire/util/TestListPreprocessor.java  |    7 ++
 .../util/SurefireDirectoryScannerTest.java         |    3 +-
 .../maven/surefire/booter/BooterConstants.java     |   49 +++++-----
 .../maven/surefire/booter/BooterDeserializer.java  |    6 +-
 .../surefire/booter/ProviderConfiguration.java     |    9 +-
 .../surefire/booter/SurefireReflectorTest.java     |   14 ++-
 .../booter/TestPreprocessorConfigTest.java         |   81 ++++++++++++++++
 .../surefire/testng/TestNGDirectoryTestSuite.java  |    8 +-
 .../maven/surefire/testng/TestNGProvider.java      |    5 +-
 24 files changed, 394 insertions(+), 80 deletions(-)
 create mode 100644 surefire-api/src/main/java/org/apache/maven/surefire/testset/TestPreprocessorConfig.java
 create mode 100644 surefire-api/src/main/java/org/apache/maven/surefire/util/TestListPreprocessor.java
 create mode 100644 surefire-booter/src/test/java/org/apache/maven/surefire/booter/TestPreprocessorConfigTest.java

diff --git a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index 8a04e2f..997c5ba 100644
--- a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
+++ b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
@@ -632,11 +632,24 @@ public class IntegrationTestMojo
     private String runOrder;
 
     /**
+     * Defines test-preprocessor that the list of tests are passed to before execution. Custom implementations can change the list of test-classes to be run.
+     * <p/>
+     * Given expression must be of the format "<fully qualified class name>[<org>:<artifact-name>:<version>]".
+     * <p/>
+     * The class passed in must have a default constructor(zero arity constructor).
+     *
+     * Value "none" disables preprocessor.
+     *
+     * @parameter default-value="none"
+     * @since 2.8.2
+     */
+    private String testPreprocessor;
+
+    /**
      * @component
      */
     private ToolchainManager toolchainManager;
 
-
     public void executeAfterPreconditionsChecked()
         throws MojoExecutionException, MojoFailureException
     {
@@ -1377,6 +1390,14 @@ public class IntegrationTestMojo
         return runOrder;
     }
 
+    public void setTestPreprocessor(String preprocessor) {
+        testPreprocessor = preprocessor;
+    }
+
+    public String getTestPreprocessor() {
+        return testPreprocessor;
+    }
+
     public void setRunOrder( String runOrder )
     {
         this.runOrder = runOrder;
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index df525f9..1e0f2bc 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
@@ -48,11 +48,7 @@ import org.apache.maven.plugin.surefire.booterclient.ChecksumCalculator;
 import org.apache.maven.plugin.surefire.booterclient.ForkConfiguration;
 import org.apache.maven.plugin.surefire.booterclient.ForkStarter;
 import org.apache.maven.shared.artifact.filter.PatternIncludesArtifactFilter;
-import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
-import org.apache.maven.surefire.booter.Classpath;
-import org.apache.maven.surefire.booter.ClasspathConfiguration;
-import org.apache.maven.surefire.booter.ProviderConfiguration;
-import org.apache.maven.surefire.booter.StartupConfiguration;
+import org.apache.maven.surefire.booter.*;
 import org.apache.maven.surefire.report.BriefConsoleReporter;
 import org.apache.maven.surefire.report.BriefFileReporter;
 import org.apache.maven.surefire.report.ConsoleReporter;
@@ -63,6 +59,7 @@ import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.report.XMLReporter;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.NestedRuntimeException;
 import org.apache.maven.surefire.util.Relocator;
@@ -90,7 +87,7 @@ public abstract class AbstractSurefireMojo
 
     protected abstract String getPluginName();
 
-    private SurefireDependencyResolver dependencyResolver;
+    protected SurefireDependencyResolver dependencyResolver;
 
     public void execute()
         throws MojoExecutionException, MojoFailureException
@@ -317,7 +314,7 @@ public abstract class AbstractSurefireMojo
             List excludes = getExcludeList();
             directoryScannerParameters = new DirectoryScannerParameters( getTestClassesDirectory(), includes, excludes,
                                                                          Boolean.valueOf( failIfNoTests ),
-                                                                         getRunOrder() );
+                                                                         getRunOrder(), testPreprocessor());
         }
 
         Properties providerProperties = getProperties();
@@ -328,7 +325,7 @@ public abstract class AbstractSurefireMojo
 
         ProviderConfiguration providerConfiguration1 =
             new ProviderConfiguration( directoryScannerParameters, failIfNoTests, reporterConfiguration, testNg,
-                                       testSuiteDefinition, providerProperties, null );
+                                       testSuiteDefinition, providerProperties, null);
 
         Toolchain tc = getToolchain();
 
@@ -352,6 +349,10 @@ public abstract class AbstractSurefireMojo
         return providerConfiguration1;
     }
 
+    private TestPreprocessorConfig testPreprocessor() {
+        return TestPreprocessorConfig.parse(getTestPreprocessor());
+    }
+
     protected StartupConfiguration createStartupConfiguration( ForkConfiguration forkConfiguration,
                                                                ProviderInfo provider,
                                                                ClassLoaderConfiguration classLoaderConfiguration )
@@ -362,14 +363,24 @@ public abstract class AbstractSurefireMojo
         {
             provider.addProviderProperties();
             String providerName = provider.getProviderName();
-            final Classpath providerClasspath = provider.getProviderClasspath();
+
+            Classpath preprocessorClasspath =
+                    new Classpath(testPreprocessor().resolveClasspath(new TestPreprocessorConfig.ClasspathResolver()
+            {
+                public List getClasspath(String org, String artifactName, String version)
+                        throws ArtifactResolutionException, ArtifactNotFoundException
+                {
+                    return dependencyResolver.getArtifactClasspath(org, artifactName, version, null);
+                }
+            }));
+
+            final Classpath providerClasspath = Classpath.join(provider.getProviderClasspath(), preprocessorClasspath);
             final Classpath testClasspath = generateTestClasspath();
 
-            logClasspath( testClasspath, "test classpath" );
-            logClasspath( testClasspath, "provider classpath" );
-            final ClasspathConfiguration classpathConfiguration =
-                new ClasspathConfiguration( testClasspath, providerClasspath, isEnableAssertions(),
-                                            isChildDelegation() );
+            logClasspath(  testClasspath, "test classpath" );
+            logClasspath(  testClasspath, "provider classpath" );
+             final ClasspathConfiguration classpathConfiguration =new ClasspathConfiguration( testClasspath,
+                     providerClasspath, isEnableAssertions(), isChildDelegation());
 
             return new StartupConfiguration( providerName, classpathConfiguration, classLoaderConfiguration,
                                              forkConfiguration.isForking(), false, isRedirectTestOutputToFile() );
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
index 4d092cf..306f180 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireDependencyResolver.java
@@ -154,8 +154,14 @@ public class SurefireDependencyResolver
     public Classpath getProviderClasspath( String provider, String version, Artifact filteredArtifact )
         throws ArtifactNotFoundException, ArtifactResolutionException
     {
-        Artifact providerArtifact = artifactFactory.createDependencyArtifact( "org.apache.maven.surefire", provider,
-                                                                              VersionRange.createFromVersion( version ),
+        return new Classpath(getArtifactClasspath("org.apache.maven.surefire", provider, version, filteredArtifact));
+    }
+
+    public List getArtifactClasspath(String org, String provider, String version, Artifact filteredArtifact)
+            throws ArtifactResolutionException, ArtifactNotFoundException
+    {
+        Artifact providerArtifact = artifactFactory.createDependencyArtifact(org, provider,
+                                                                              VersionRange.createFromVersion(version),
                                                                               "jar", null, Artifact.SCOPE_TEST );
         ArtifactResolutionResult result = resolveArtifact( filteredArtifact, providerArtifact );
         List files = new ArrayList();
@@ -170,7 +176,7 @@ public class SurefireDependencyResolver
 
             files.add( artifact.getFile().getAbsolutePath() );
         }
-        return new Classpath( files );
+        return files;
     }
 
     public Classpath addProviderToClasspath( Map pluginArtifactMap, Artifact surefireArtifact )
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
index 6d57f21..45ad85c 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
@@ -275,5 +275,8 @@ public interface SurefireExecutionParameters
     void setRunOrder( String runOrder );
 
     String getRunOrder();
+    
+    void setTestPreprocessor(String preprocessor);
 
+    String getTestPreprocessor();
 }
diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
index 84c5507..60ed041 100644
--- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
+++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
@@ -106,6 +106,7 @@ public class BooterSerializer
             properties.setProperty( BooterConstants.TEST_CLASSES_DIRECTORY,
                                     directoryScannerParameters.getTestClassesDirectory() );
             properties.setProperty( BooterConstants.RUN_ORDER, directoryScannerParameters.getRunOrder() );
+            properties.setProperty( BooterConstants.TEST_PREPROCESSOR, directoryScannerParameters.getTestPreprocessorConfigDeclaration());
         }
 
         ReporterConfiguration reporterConfiguration = booterConfiguration.getReporterConfiguration();
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
index 2f16434..ee8c4d9 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
@@ -19,15 +19,11 @@ package org.apache.maven.plugin.surefire.booterclient;
  * under the License.
  */
 
-import org.apache.maven.surefire.booter.BooterDeserializer;
-import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
-import org.apache.maven.surefire.booter.ClasspathConfiguration;
-import org.apache.maven.surefire.booter.ProviderConfiguration;
-import org.apache.maven.surefire.booter.StartupConfiguration;
-import org.apache.maven.surefire.booter.SystemPropertyManager;
+import org.apache.maven.surefire.booter.*;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 
 import java.io.File;
@@ -55,6 +51,7 @@ public class BooterDeserializerProviderConfigurationTest
     private final String aUserRequestedTest = "aUserRequestedTest";
     
     private final String aUserRequestedTestMethod = "aUserRequestedTestMethod";
+    private TestPreprocessorConfig preprocessorConfig = TestPreprocessorConfig.parse("com.foo.BarClass[com.foo:bar-artifact:1.2.3]");
 
     public static ClassLoaderConfiguration getForkConfiguration()
         throws IOException
@@ -85,7 +82,13 @@ public class BooterDeserializerProviderConfigurationTest
         Assert.assertEquals( includes.get( 1 ), read.getIncludes().get( 1 ) );
         Assert.assertEquals( excludes.get( 0 ), read.getExcludes().get( 0 ) );
         Assert.assertEquals( excludes.get( 1 ), read.getExcludes().get( 1 ) );
+        DirectoryScannerParameters dirScannerParams = read.getDirScannerParams();
 
+        Assert.assertEquals(includes.get(0), dirScannerParams.getIncludes().get(0));
+        Assert.assertEquals( includes.get( 1 ), dirScannerParams.getIncludes().get( 1 ) );
+        Assert.assertEquals( excludes.get( 0 ), dirScannerParams.getExcludes().get( 0 ) );
+        Assert.assertEquals( excludes.get( 1 ), dirScannerParams.getExcludes().get( 1 ) );
+        Assert.assertEquals(preprocessorConfig, dirScannerParams.getTestPreprocessorConfig());
     }
 
     public void testReporterConfiguration()
@@ -176,7 +179,7 @@ public class BooterDeserializerProviderConfigurationTest
         excludes.add( "xx1" );
         excludes.add( "xx2" );
 
-        return new DirectoryScannerParameters( aDir, includes, excludes, Boolean.TRUE, null );
+        return new DirectoryScannerParameters( aDir, includes, excludes, Boolean.TRUE, null, preprocessorConfig);
     }
 
     private ProviderConfiguration saveAndReload( ProviderConfiguration booterConfiguration,
@@ -205,7 +208,7 @@ public class BooterDeserializerProviderConfigurationTest
             new TestRequest( getSuiteXmlFileStrings(), getTestSourceDirectory(), aUserRequestedTest, aUserRequestedTestMethod );
         return new ProviderConfiguration( directoryScannerParameters, true, reporterConfiguration,
                                           new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(),
-                                          aTest );
+                                          aTest);
     }
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
index 5abb288..7fad169 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerStartupConfigurationTest.java
@@ -30,6 +30,7 @@ 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.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 
 import java.io.File;
@@ -144,7 +145,7 @@ public class BooterDeserializerStartupConfigurationTest
 
         File cwd = new File( "." );
         DirectoryScannerParameters directoryScannerParameters =
-            new DirectoryScannerParameters( cwd, new ArrayList(), new ArrayList(), Boolean.TRUE, "hourly" );
+            new DirectoryScannerParameters( cwd, new ArrayList(), new ArrayList(), Boolean.TRUE, "hourly", new TestPreprocessorConfig("foo.bar.Baz", "foo.bar", "baz-jar", "1.2.3"));
         ReporterConfiguration reporterConfiguration =
             new ReporterConfiguration( new ArrayList(), cwd, Boolean.TRUE, null );
         String aUserRequestedTest = "aUserRequestedTest";
@@ -154,7 +155,7 @@ public class BooterDeserializerStartupConfigurationTest
                              aUserRequestedTestMethod );
         return new ProviderConfiguration( directoryScannerParameters, true, reporterConfiguration,
                                           new TestArtifactInfo( "5.0", "ABC" ), testSuiteDefinition, new Properties(),
-                                          aTest );
+                                          aTest);
     }
 
     private StartupConfiguration getTestStartupConfiguration( ClassLoaderConfiguration classLoaderConfiguration )
diff --git a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index d5b9e4c..91d05be 100644
--- a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
+++ b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
@@ -594,6 +594,20 @@ public class SurefirePlugin
     private String runOrder;
 
     /**
+     * Defines test-preprocessor that the list of tests are passed to before execution. Custom implementations can change the list of test-classes to be run.
+     * <p/>
+     * Given expression must be of the format "<fully qualified class name>[<org>:<artifact-name>:<version>]".
+     * <p/>
+     * The class passed in must have a default constructor(zero arity constructor).
+     *
+     * Value "none" disables preprocessor.
+     *
+     * @parameter default-value="none"
+     * @since 2.8.2
+     */
+    private String testPreprocessor;
+
+    /**
      * @component
      */
     private ToolchainManager toolchainManager;
@@ -1291,6 +1305,14 @@ public class SurefirePlugin
         return runOrder;
     }
 
+    public void setTestPreprocessor(String testPreprocessor) {
+        this.testPreprocessor = testPreprocessor;
+    }
+
+    public String getTestPreprocessor() {
+        return testPreprocessor;
+    }
+
     public void setRunOrder( String runOrder )
     {
         this.runOrder = runOrder;
diff --git a/surefire-api/pom.xml b/surefire-api/pom.xml
index 64812c4..f97d874 100644
--- a/surefire-api/pom.xml
+++ b/surefire-api/pom.xml
@@ -35,6 +35,10 @@
       <groupId>org.codehaus.plexus</groupId>
       <artifactId>plexus-utils</artifactId>
     </dependency>
+    <dependency>
+      <groupId>org.apache.maven</groupId>
+      <artifactId>maven-artifact</artifactId>
+    </dependency>
   </dependencies>
   <build>
     <plugins>
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
index 4025281..e6f8c3c 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BaseProviderFactory.java
@@ -62,7 +62,8 @@ public class BaseProviderFactory
         return new DefaultDirectoryScanner( directoryScannerParameters.getTestClassesDirectory(),
                                             directoryScannerParameters.getIncludes(),
                                             directoryScannerParameters.getExcludes(),
-                                            directoryScannerParameters.getRunOrder());
+                                            directoryScannerParameters.getRunOrder(),
+                                            directoryScannerParameters.getTestListPreprocessor(surefireClassLoader));
     }
 
     public ReporterFactory getReporterFactory()
@@ -117,6 +118,10 @@ public class BaseProviderFactory
         return testClassLoader;
     }
 
+    public ClassLoader getSurefireClassLoader() {
+        return surefireClassLoader;
+    }
+
     public void setProviderProperties( Properties providerProperties )
     {
         this.providerProperties = providerProperties;
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
index 1794943..9422da0 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/SurefireReflector.java
@@ -24,6 +24,7 @@ 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.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.ReflectionUtils;
 import org.apache.maven.surefire.util.SurefireReflectionException;
@@ -159,14 +160,15 @@ public class SurefireReflector
         {
             return null;
         }
-        Class[] arguments = { File.class, List.class, List.class, Boolean.class, String.class };
+        Class[] arguments = { File.class, List.class, List.class, Boolean.class, String.class, String.class};
         Constructor constructor = ReflectionUtils.getConstructor( this.directoryScannerParameters, arguments );
         return ReflectionUtils.newInstance( constructor,
                                             new Object[]{ directoryScannerParameters.getTestClassesDirectory(),
                                                 directoryScannerParameters.getIncludes(),
                                                 directoryScannerParameters.getExcludes(),
                                                 directoryScannerParameters.isFailIfNoTests(),
-                                                directoryScannerParameters.getRunOrder() } );
+                                                directoryScannerParameters.getRunOrder(),
+                                                directoryScannerParameters.getTestPreprocessorConfigDeclaration() });
     }
 
     Object createTestArtifactInfo( TestArtifactInfo testArtifactInfo )
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
index faf5141..2c9d168 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
@@ -85,6 +85,13 @@ public interface ProviderParameters
     ClassLoader getTestClassLoader();
 
     /**
+     * The class loader for plugin classes
+     *
+     * @return the classloader
+     */
+    ClassLoader getSurefireClassLoader();
+
+    /**
      * The per-provider specific properties that may come all the way from the plugin's properties setting.
      *
      * @return the provider specific properties
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/testset/DirectoryScannerParameters.java b/surefire-api/src/main/java/org/apache/maven/surefire/testset/DirectoryScannerParameters.java
index dd1da3f..580f02b 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/testset/DirectoryScannerParameters.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/testset/DirectoryScannerParameters.java
@@ -19,6 +19,8 @@ package org.apache.maven.surefire.testset;
  * under the License.
  */
 
+import org.apache.maven.surefire.util.TestListPreprocessor;
+
 import java.io.File;
 import java.util.List;
 
@@ -37,13 +39,22 @@ public class DirectoryScannerParameters
 
     private final String runOrder;
 
-    public DirectoryScannerParameters( File testClassesDirectory, List includes, List excludes, Boolean failIfNoTests, String runOrder )
+    private final TestPreprocessorConfig testPreprocessorConfig;
+
+    public DirectoryScannerParameters(File testClassesDirectory, List includes, List excludes, Boolean failIfNoTests, String runOrder, String testPreprocessorConfigDeclaration)
+    {
+        this(testClassesDirectory, includes, excludes, failIfNoTests, runOrder, TestPreprocessorConfig.parse(testPreprocessorConfigDeclaration));
+    }
+
+
+    public DirectoryScannerParameters(File testClassesDirectory, List includes, List excludes, Boolean failIfNoTests, String runOrder, TestPreprocessorConfig testPreprocessorConfig)
     {
         this.testClassesDirectory = testClassesDirectory;
         this.includes = includes;
         this.excludes = excludes;
         this.failIfNoTests = failIfNoTests;
         this.runOrder = runOrder;
+        this.testPreprocessorConfig = testPreprocessorConfig;
     }
 
     /**
@@ -86,4 +97,16 @@ public class DirectoryScannerParameters
     {
         return runOrder;
     }
+
+    public TestPreprocessorConfig getTestPreprocessorConfig() {
+        return testPreprocessorConfig;
+    }
+
+    public String getTestPreprocessorConfigDeclaration() {
+        return getTestPreprocessorConfig().toString();
+    }
+
+    public TestListPreprocessor getTestListPreprocessor(ClassLoader classLoader) {
+        return getTestPreprocessorConfig().getPreprocessor(classLoader);
+    }
 }
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestPreprocessorConfig.java b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestPreprocessorConfig.java
new file mode 100644
index 0000000..f51c6e8
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestPreprocessorConfig.java
@@ -0,0 +1,100 @@
+package org.apache.maven.surefire.testset;
+
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.surefire.util.ReflectionUtils;
+import org.apache.maven.surefire.util.TestListPreprocessor;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class TestPreprocessorConfig {
+    private static final Pattern DECLARATION_PATTERN = Pattern.compile("^(.+?)\\[(.+?):(.+?):(.+?)\\]$");
+    public static final String NONE = "none";
+
+    private final String className;
+    private final String org;
+    private final String artifactName;
+    private final String version;
+
+    public static final class NoOpTestListPreprocessor implements TestListPreprocessor {
+        public List preprocessTestClasses(List testClasses) {
+            return testClasses;
+        }
+    }
+
+    public static interface ClasspathResolver {
+        List getClasspath(String org, String artifactName, String version) throws ArtifactResolutionException, ArtifactNotFoundException;
+    }
+
+    public static final TestPreprocessorConfig DEFAULT_TEST_PREPROCESSOR_CONFIG = new TestPreprocessorConfig(null, null, null, null) {
+        public TestListPreprocessor getPreprocessor(ClassLoader loader) {
+            return new NoOpTestListPreprocessor();
+        }
+
+        public List resolveClasspath(ClasspathResolver resolver) {
+            return new ArrayList();
+        }
+
+        public String toString() {
+            return "none";
+        }
+    };
+    public static final String PROPERTY_NAME = "testPreprocessor";
+
+    public TestPreprocessorConfig(String className, String org, String artifactName, String version) {
+        this.className = className;
+        this.org = org;
+        this.artifactName = artifactName;
+        this.version = version;
+    }
+
+    public static TestPreprocessorConfig parse(String declaration) {
+        if (NONE.equals(declaration)) {
+            return DEFAULT_TEST_PREPROCESSOR_CONFIG;
+        }
+        Matcher matcher = DECLARATION_PATTERN.matcher(declaration);
+        if (matcher.matches()) {
+            return new TestPreprocessorConfig(matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4));
+        } else {
+            throw new RuntimeException("Bad value '" + declaration + "' given for configuration parameter '" + PROPERTY_NAME + "'.");
+        }
+    }
+
+
+    public String toString() {
+        return className + "[" + org + ":" + artifactName + ":" + version + "]";
+    }
+
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        TestPreprocessorConfig that = (TestPreprocessorConfig) o;
+
+        if (artifactName != null ? !artifactName.equals(that.artifactName) : that.artifactName != null) return false;
+        if (className != null ? !className.equals(that.className) : that.className != null) return false;
+        if (org != null ? !org.equals(that.org) : that.org != null) return false;
+        if (version != null ? !version.equals(that.version) : that.version != null) return false;
+
+        return true;
+    }
+
+    public int hashCode() {
+        int result = className != null ? className.hashCode() : 0;
+        result = 31 * result + (org != null ? org.hashCode() : 0);
+        result = 31 * result + (artifactName != null ? artifactName.hashCode() : 0);
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        return result;
+    }
+
+    public TestListPreprocessor getPreprocessor(ClassLoader loader) {
+        return (TestListPreprocessor) ReflectionUtils.instantiate(loader, className);
+    }
+
+    public List resolveClasspath(ClasspathResolver resolver) throws ArtifactResolutionException, ArtifactNotFoundException {
+        return resolver.getClasspath(org, artifactName, version);
+    }
+}
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/DefaultDirectoryScanner.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/DefaultDirectoryScanner.java
index d0214f9..d0d3166 100644
--- a/surefire-api/src/main/java/org/apache/maven/surefire/util/DefaultDirectoryScanner.java
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/DefaultDirectoryScanner.java
@@ -19,12 +19,10 @@ package org.apache.maven.surefire.util;
  * under the License.
  */
 
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
+
 import java.io.File;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.List;
+import java.util.*;
 
 /**
  * Scans directories looking for tests.
@@ -56,13 +54,15 @@ public class DefaultDirectoryScanner
 
     private final String runOrder;
 
+    private final TestListPreprocessor testListPreprocessor;
 
-    public DefaultDirectoryScanner( File basedir, List includes, List excludes, String runOrder )
+    public DefaultDirectoryScanner(File basedir, List includes, List excludes, String runOrder, TestListPreprocessor testListPreprocessor)
     {
         this.basedir = basedir;
         this.includes = includes;
         this.excludes = excludes;
         this.runOrder = runOrder;
+        this.testListPreprocessor = testListPreprocessor;
         this.sortOrder = getSortOrderComparator( runOrder );
     }
 
@@ -88,13 +88,14 @@ public class DefaultDirectoryScanner
         }
         if ( "random".equals( runOrder ) )
         {
-            Collections.shuffle( result );
-        }
+            Collections.shuffle(result);
+        } 
         else if ( sortOrder != null )
         {
             Collections.sort( result, sortOrder );
         }
-        return new TestsToRun( result );
+
+        return new TestsToRun(testListPreprocessor.preprocessTestClasses(result));
     }
 
     private static Class loadClass( ClassLoader classLoader, String className )
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/util/TestListPreprocessor.java b/surefire-api/src/main/java/org/apache/maven/surefire/util/TestListPreprocessor.java
new file mode 100644
index 0000000..e92d69f
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/util/TestListPreprocessor.java
@@ -0,0 +1,7 @@
+package org.apache.maven.surefire.util;
+
+import java.util.List;
+
+public interface TestListPreprocessor {
+    List preprocessTestClasses(List testClasses);
+}
diff --git a/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java b/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java
index 0fda2b8..afa3c23 100644
--- a/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java
+++ b/surefire-api/src/test/java/org/apache/maven/surefire/util/SurefireDirectoryScannerTest.java
@@ -20,6 +20,7 @@ package org.apache.maven.surefire.util;
  */
 
 import junit.framework.TestCase;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 
 import java.io.File;
@@ -44,7 +45,7 @@ public class SurefireDirectoryScannerTest
         List exclude = new ArrayList();
 
         DefaultDirectoryScanner surefireDirectoryScanner = new DefaultDirectoryScanner( baseDir, include, exclude,
-                                                                                        "filesystem" );
+                                                                                        "filesystem", new TestPreprocessorConfig.NoOpTestListPreprocessor());
         String[] classNames = surefireDirectoryScanner.collectTests();
         assertNotNull( classNames );
         System.out.println("classNames " + Arrays.asList( classNames ));
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
index 5e277cf..44b8ab9 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterConstants.java
@@ -20,6 +20,8 @@ package org.apache.maven.surefire.booter;
  */
 
 
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
+
 /**
  * Constants used by the serializer/deserializer
  *
@@ -27,27 +29,28 @@ package org.apache.maven.surefire.booter;
  */
 public interface BooterConstants
 {
-    String INCLUDES_PROPERTY_PREFIX = "includes";
-    String EXCLUDES_PROPERTY_PREFIX = "excludes";
-    String DIRSCANNER_PROPERTY_PREFIX = "dirscanner.";
-    String DIRSCANNER_OPTIONS = "directoryScannerOptions";
-    String REPORT_PROPERTY_PREFIX = "report.";
-    String PARAMS_SUFIX = ".params";
-    String TYPES_SUFIX = ".types";
-    String USESYSTEMCLASSLOADER = "useSystemClassLoader";
-    String USEMANIFESTONLYJAR = "useManifestOnlyJar";
-    String FAILIFNOTESTS = "failIfNoTests";
-    String ISTRIMSTACKTRACE = "isTrimStackTrace";
-    String REPORTSDIRECTORY = "reportsDirectory";
-    String FORKTIMEOUT = "forkTimeout";
-    String TESTARTIFACT_VERSION = "testFwJarVersion";
-    String TESTARTIFACT_CLASSIFIER = "testFwJarClassifier";
-    String REQUESTEDTEST = "requestedTest";
-    String REQUESTEDTESTMETHOD = "requestedTestMethod";
-    String SOURCE_DIRECTORY = "testSuiteDefinitionTestSourceDirectory";
-    String TEST_CLASSES_DIRECTORY = "testClassesDirectory";
-    String RUN_ORDER = "runOrder";
-    String TEST_SUITE_XML_FILES = "testSuiteXmlFiles";
-    String PROVIDER_CONFIGURATION = "providerConfiguration";
-    String FORKTESTSET = "forkTestSet";
+    static final String INCLUDES_PROPERTY_PREFIX = "includes";
+    static final String EXCLUDES_PROPERTY_PREFIX = "excludes";
+    static final String DIRSCANNER_PROPERTY_PREFIX = "dirscanner.";
+    static final String DIRSCANNER_OPTIONS = "directoryScannerOptions";
+    static final String REPORT_PROPERTY_PREFIX = "report.";
+    static final String PARAMS_SUFIX = ".params";
+    static final String TYPES_SUFIX = ".types";
+    static final String USESYSTEMCLASSLOADER = "useSystemClassLoader";
+    static final String USEMANIFESTONLYJAR = "useManifestOnlyJar";
+    static final String FAILIFNOTESTS = "failIfNoTests";
+    static final String ISTRIMSTACKTRACE = "isTrimStackTrace";
+    static final String REPORTSDIRECTORY = "reportsDirectory";
+    static final String FORKTIMEOUT = "forkTimeout";
+    static final String TESTARTIFACT_VERSION = "testFwJarVersion";
+    static final String TESTARTIFACT_CLASSIFIER = "testFwJarClassifier";
+    static final String REQUESTEDTEST = "requestedTest";
+    static final String REQUESTEDTESTMETHOD = "requestedTestMethod";
+    static final String SOURCE_DIRECTORY = "testSuiteDefinitionTestSourceDirectory";
+    static final String TEST_CLASSES_DIRECTORY = "testClassesDirectory";
+    static final String RUN_ORDER = "runOrder";
+    static final String TEST_PREPROCESSOR = TestPreprocessorConfig.PROPERTY_NAME;
+    static final String TEST_SUITE_XML_FILES = "testSuiteXmlFiles";
+    static final String PROVIDER_CONFIGURATION = "providerConfiguration";
+    static final String FORKTESTSET = "forkTestSet";
 }
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
index 888b871..2cb3e6f 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/BooterDeserializer.java
@@ -22,6 +22,7 @@ package org.apache.maven.surefire.booter;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 
 import java.io.File;
@@ -76,10 +77,11 @@ public class BooterDeserializer
         final List testSuiteXmlFiles = properties.getStringList( TEST_SUITE_XML_FILES );
         final File testClassesDirectory = properties.getFileProperty( TEST_CLASSES_DIRECTORY );
         final String runOrder = properties.getProperty( RUN_ORDER );
+        final String testPreprocessor = properties.getProperty( TEST_PREPROCESSOR );
 
         DirectoryScannerParameters dirScannerParams =
             new DirectoryScannerParameters( testClassesDirectory, includesList, excludesList,
-                                            properties.getBooleanObjectProperty( FAILIFNOTESTS ), runOrder );
+                                            properties.getBooleanObjectProperty( FAILIFNOTESTS ), runOrder, TestPreprocessorConfig.parse(testPreprocessor));
 
         TestArtifactInfo testNg = new TestArtifactInfo( testNgVersion, testArtifactClassifier );
         TestRequest testSuiteDefinition = new TestRequest( testSuiteXmlFiles, sourceDirectory, requestedTest, requestedTestMethod );
@@ -90,7 +92,7 @@ public class BooterDeserializer
 
         return new ProviderConfiguration( dirScannerParams, properties.getBooleanProperty( FAILIFNOTESTS ),
                                           reporterConfiguration, testNg, testSuiteDefinition,
-                                          properties.getProperties(), testForFork );
+                                          properties.getProperties(), testForFork);
     }
 
     public StartupConfiguration getProviderConfiguration()
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
index 62842d1..ae1e13c 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ProviderConfiguration.java
@@ -22,6 +22,7 @@ package org.apache.maven.surefire.booter;
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 
 import java.io.File;
@@ -63,9 +64,9 @@ public class ProviderConfiguration
 
     private final Object forkTestSet;
 
-    public ProviderConfiguration( DirectoryScannerParameters directoryScannerParameters, boolean failIfNoTests,
-                                  ReporterConfiguration reporterConfiguration, TestArtifactInfo testArtifact,
-                                  TestRequest testSuiteDefinition, Properties providerProperties, Object forkTestSet )
+    public ProviderConfiguration(DirectoryScannerParameters directoryScannerParameters, boolean failIfNoTests,
+                                 ReporterConfiguration reporterConfiguration, TestArtifactInfo testArtifact,
+                                 TestRequest testSuiteDefinition, Properties providerProperties, Object forkTestSet)
     {
         this.providerProperties = providerProperties;
         this.reporterConfiguration = reporterConfiguration;
@@ -155,6 +156,4 @@ public class ProviderConfiguration
             TESTS_FAILED_EXIT_CODE == returnCode;
 
     }
-
-
 }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
index d4d0c19..5241bf9 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/SurefireReflectorTest.java
@@ -23,6 +23,7 @@ nse agreements.  See the NOTICE file
 import org.apache.maven.surefire.report.ReporterConfiguration;
 import org.apache.maven.surefire.testset.DirectoryScannerParameters;
 import org.apache.maven.surefire.testset.TestArtifactInfo;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
 import org.apache.maven.surefire.testset.TestRequest;
 
 import java.io.File;
@@ -45,13 +46,18 @@ public class SurefireReflectorTest
         throws Exception
     {
         SurefireReflector surefireReflector = getReflector();
-        Object foo = getFoo();
+        Foo foo = getFoo();
 
         DirectoryScannerParameters directoryScannerParameters =
-            new DirectoryScannerParameters( new File( "ABC" ), new ArrayList(), new ArrayList(), Boolean.FALSE,
-                                            "hourly" );
+            new DirectoryScannerParameters( new File( "ABC" ), Arrays.asList(new String[]{"a", "b"}), Arrays.asList(new String[] {"c", "d"}), Boolean.FALSE,
+                                            "hourly", new TestPreprocessorConfig("foo.bar.Baz", "foo.bar", "baz-jar", "3.4.5"));
         surefireReflector.setDirectoryScannerParameters( foo, directoryScannerParameters );
         assertTrue( isCalled( foo ).booleanValue() );
+        assertEquals( Arrays.asList(new String[] {"a", "b"}), foo.directoryScannerParameters.getIncludes() );
+        assertEquals( Arrays.asList(new String[] {"c", "d"}), foo.directoryScannerParameters.getExcludes() );
+        assertEquals( new File("ABC"), foo.directoryScannerParameters.getTestClassesDirectory() );
+        assertEquals( "hourly", foo.directoryScannerParameters.getRunOrder() );
+        assertEquals( "foo.bar.Baz[foo.bar:baz-jar:3.4.5]", foo.directoryScannerParameters.getTestPreprocessorConfigDeclaration() );
 
     }
 
@@ -120,7 +126,7 @@ public class SurefireReflectorTest
         return new SurefireReflector( this.getClass().getClassLoader() );
     }
 
-    public Object getFoo()
+    public Foo getFoo()
     { // Todo: Setup a different classloader so we can really test crossing
         return new Foo();
     }
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/TestPreprocessorConfigTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/TestPreprocessorConfigTest.java
new file mode 100644
index 0000000..af90f78
--- /dev/null
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/TestPreprocessorConfigTest.java
@@ -0,0 +1,81 @@
+package org.apache.maven.surefire.booter;
+
+import junit.framework.TestCase;
+import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
+import org.apache.maven.artifact.resolver.ArtifactResolutionException;
+import org.apache.maven.surefire.testset.TestPreprocessorConfig;
+import org.apache.maven.surefire.util.TestListPreprocessor;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.List;
+
+public class TestPreprocessorConfigTest extends TestCase {
+
+    public void testParsesConfiguration() throws Exception {
+        TestPreprocessorConfig cfg = TestPreprocessorConfig.parse("foo.bar.BazClass[org.foo:baz-jar:2.3.4]");
+        assertEquals(new TestPreprocessorConfig("foo.bar.BazClass", "org.foo", "baz-jar", "2.3.4"), cfg);
+    }
+
+    public void testDumpsStringRepresentation() throws Exception {
+        TestPreprocessorConfig cfg = new TestPreprocessorConfig("foo.bar.BazClass", "org.foo", "baz-jar", "2.3.4");
+        assertEquals("foo.bar.BazClass[org.foo:baz-jar:2.3.4]", cfg.toString());
+    }
+
+    public void testReturnNoOpPreprocessorConfigurationForNonePreprocessor() throws Exception {
+        TestPreprocessorConfig cfg = TestPreprocessorConfig.parse("none");
+        assertEquals(TestPreprocessorConfig.DEFAULT_TEST_PREPROCESSOR_CONFIG, cfg);
+    }
+
+    public void testFailsWhenCantParse() throws Exception {
+        try {
+            TestPreprocessorConfig.parse("some-arbitrary-text");
+            fail("Should have failed as text given was not parsable");
+        } catch (RuntimeException e) {
+            assertEquals("Bad value 'some-arbitrary-text' given for configuration parameter 'testPreprocessor'.", e.getMessage());
+        }
+    }
+
+    public void testLoadsPreprocessorClassUsingGivenClassloader() {
+        final String[] givenName = new String[1];
+        TestPreprocessorConfig cfg = new TestPreprocessorConfig(EvenOnlyPreprocessor.class.getCanonicalName(), "foo", "bar", "1.2.3");
+        cfg.getPreprocessor(new URLClassLoader(new URL[0], getClass().getClassLoader()) {
+            public Class loadClass(String name) throws ClassNotFoundException {
+                givenName[0] = name;
+                return EvenOnlyPreprocessor.class;
+            }
+        });
+        assertEquals(EvenOnlyPreprocessor.class.getCanonicalName(), givenName[0]);
+    }
+
+    public void testResolvesClasspathUsingDetailsGiven() throws ArtifactResolutionException, ArtifactNotFoundException {
+        final String[] resolverArgs = new String[3];
+        final List classpathElements = new ArrayList();
+        TestPreprocessorConfig cfg = new TestPreprocessorConfig("foo.bar.Baz", "groupId", "artifactId", "1.2.3");
+        List classpathElementsReturned = cfg.resolveClasspath(new TestPreprocessorConfig.ClasspathResolver() {
+            public List getClasspath(String org, String artifactName, String version) throws ArtifactResolutionException, ArtifactNotFoundException {
+                resolverArgs[0] = org;
+                resolverArgs[1] = artifactName;
+                resolverArgs[2] = version;
+                return classpathElements;
+            }
+        });
+        assertEquals("groupId", resolverArgs[0]);
+        assertEquals("artifactId", resolverArgs[1]);
+        assertEquals("1.2.3", resolverArgs[2]);
+        assertSame(classpathElements, classpathElementsReturned);
+    }
+
+    public static final class EvenOnlyPreprocessor implements TestListPreprocessor {
+        public List preprocessTestClasses(List testClasses) {
+            ArrayList list = new ArrayList();
+            for (int i = 0; i < testClasses.size(); i++) {
+                if (i % 2 == 0) {
+                    list.add(testClasses.get(i));
+                }
+            }
+            return list;
+        }
+    }
+}
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
index b00489c..6810ba2 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGDirectoryTestSuite.java
@@ -31,6 +31,7 @@ import org.apache.maven.surefire.report.SimpleReportEntry;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.DefaultDirectoryScanner;
 import org.apache.maven.surefire.util.DirectoryScanner;
+import org.apache.maven.surefire.util.TestListPreprocessor;
 import org.apache.maven.surefire.util.TestsToRun;
 
 import java.io.File;
@@ -67,11 +68,12 @@ public class TestNGDirectoryTestSuite
     
     private String testMethodPattern;
 
-    public TestNGDirectoryTestSuite( File basedir, ArrayList includes, ArrayList excludes, String testSourceDirectory,
-                                     String artifactVersion, Properties confOptions, File reportsDirectory, String testMethodPattern )
+    public TestNGDirectoryTestSuite(File basedir, ArrayList includes, ArrayList excludes, String testSourceDirectory,
+                                    String artifactVersion, Properties confOptions, File reportsDirectory,
+                                    String testMethodPattern, final TestListPreprocessor testListPreprocessor)
     {
 
-        this.surefireDirectoryScanner = new DefaultDirectoryScanner( basedir, includes, excludes, "filesystem" );
+        this.surefireDirectoryScanner = new DefaultDirectoryScanner( basedir, includes, excludes, "filesystem", testListPreprocessor);
 
         this.options = confOptions;
 
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index b451648..451b3fb 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -54,6 +54,8 @@ public class TestNGProvider
 
     private final ClassLoader testClassLoader;
 
+    private ClassLoader surefireClassLoader;
+
     private final DirectoryScannerParameters directoryScannerParameters;
 
     private final DirectoryScanner directoryScanner;
@@ -77,6 +79,7 @@ public class TestNGProvider
         testArtifactInfo = booterParameters.getTestArtifactInfo();
         reporterConfiguration = booterParameters.getReporterConfiguration();
         this.directoryScanner = booterParameters.getDirectoryScanner();
+        this.surefireClassLoader = booterParameters.getSurefireClassLoader();
     }
 
     public Boolean isRunnable()
@@ -132,7 +135,7 @@ public class TestNGProvider
                                              testRequest.getTestSourceDirectory().toString(),
                                              testArtifactInfo.getVersion(), providerProperties,
                                              reporterConfiguration.getReportsDirectory(),
-                                             testRequest.getRequestedTestMethod() );
+                                             testRequest.getRequestedTestMethod(), directoryScannerParameters.getTestListPreprocessor(surefireClassLoader));
     }
 
     private TestNGXmlTestSuite getXmlSuite()
-- 
1.7.3.4

