[SUREFIRE] completed commit 889caca

Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/866a535b
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/866a535b
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/866a535b

Branch: refs/heads/master
Commit: 866a535b66229e9bab3f09cbd42732d70ca9504f
Parents: 889caca
Author: Tibor17 <tibo...@lycos.com>
Authored: Tue Mar 24 02:10:22 2015 +0100
Committer: Tibor17 <tibo...@lycos.com>
Committed: Tue Mar 24 02:10:22 2015 +0100

----------------------------------------------------------------------
 .../plugin/failsafe/IntegrationTestMojo.java    |  23 +-
 .../plugin/surefire/AbstractSurefireMojo.java   | 177 +++---
 .../surefire/SurefireExecutionParameters.java   |   3 +-
 .../surefire/booterclient/BooterSerializer.java |   7 +-
 .../plugin/surefire/util/DependencyScanner.java | 107 +---
 .../plugin/surefire/util/DirectoryScanner.java  |  52 +-
 .../maven/plugin/surefire/util/FileScanner.java | 126 ++++
 .../maven/plugin/surefire/util/ScannerUtil.java |  47 --
 .../surefire/util/SpecificFileFilter.java       |   1 +
 ...erDeserializerProviderConfigurationTest.java |  10 +-
 .../surefire/util/DependenciesScannerTest.java  |   3 +-
 .../surefire/util/DirectoryScannerTest.java     |   6 +-
 .../maven/plugin/surefire/SurefirePlugin.java   |  23 +-
 .../apt/examples/inclusion-exclusion.apt.vm     |  16 +
 .../src/site/apt/examples/single-test.apt.vm    |  26 +-
 .../providerapi/ProviderParameters.java         |   1 +
 .../surefire/testset/GenericTestPattern.java    |  54 ++
 .../testset/IncludedExcludedPatterns.java       |  27 +
 .../maven/surefire/testset/ResolvedTest.java    | 229 ++++---
 .../maven/surefire/testset/TestFilter.java      |  31 +
 .../surefire/testset/TestListResolver.java      | 376 +++++++++---
 .../surefire/testset/ResolvedTestTest.java      |  49 ++
 .../surefire/testset/TestListResolverTest.java  | 302 ++++++++++
 .../maven/surefire/booter/ForkedBooter.java     |   2 +-
 .../maven/surefire/booter/ProviderFactory.java  |   6 -
 surefire-integration-tests/pom.xml              |   4 +-
 .../its/AbstractTestMultipleMethodPatterns.java | 466 +++++++++++++++
 .../its/TestMultipleMethodPatternsIT.java       | 160 +----
 .../its/TestMultipleMethodPatternsTestNGIT.java |  64 ++
 .../surefire/its/fixture/Configuration.java     |  29 +
 .../maven/surefire/its/fixture/Settings.java    |  72 +++
 .../SurefireJUnit4IntegrationTestCase.java      |   8 +-
 .../surefire/its/fixture/TestFramework.java     |  29 +
 .../jiras/Surefire847AdditionalFailureIT.java   |   2 +-
 .../resources/junit44-multiple-methods/pom.xml  |  14 +-
 .../junit48-multiple-method-patterns/pom.xml    | 156 ++++-
 .../resources/junit48-multiple-methods/pom.xml  |  10 +-
 .../testng-multiple-method-patterns/pom.xml     | 116 ++++
 .../test/java/jiras/surefire745/BasicTest.java  |  47 ++
 .../test/java/jiras/surefire745/TestFive.java   |  44 ++
 .../test/java/jiras/surefire745/TestFour.java   |  44 ++
 .../test/java/jiras/surefire745/TestThree.java  |  46 ++
 .../test/java/jiras/surefire745/TestTwo.java    |  38 ++
 .../surefire/common/junit48/MethodFilter.java   |   9 +-
 .../surefire/common/junit48/RequestedTest.java  |   9 +-
 .../common/junit48/FilterFactoryTest.java       | 589 +++++++++++++------
 .../surefire/common/junit48/tests/ATest.java    |  23 +
 .../surefire/common/junit48/tests/a/ATest.java  |  23 +
 .../surefire/common/junit48/tests/pt/PT.java    |  86 +++
 .../maven/surefire/junit4/JUnit4Provider.java   |   6 +-
 .../surefire/junitcore/JUnitCoreProvider.java   |  33 +-
 .../surefire/testng/utils/MethodSelector.java   |   5 +-
 .../testng/TestNGDirectoryTestSuite.java        |  14 +-
 .../maven/surefire/testng/TestNGExecutor.java   |  10 +-
 .../maven/surefire/testng/TestNGProvider.java   |  23 +-
 55 files changed, 3052 insertions(+), 831 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
----------------------------------------------------------------------
diff --git 
a/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
 
b/maven-failsafe-plugin/src/main/java/org/apache/maven/plugin/failsafe/IntegrationTestMojo.java
index 0900e81..b9daaa6 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
@@ -36,7 +36,6 @@ import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.shared.utils.ReaderFactory;
 import org.apache.maven.shared.utils.StringUtils;
 import org.apache.maven.surefire.suite.RunResult;
-import org.apache.maven.surefire.testset.TestListResolver;
 
 import static org.apache.maven.shared.utils.io.IOUtil.close;
 
@@ -78,8 +77,16 @@ public class IntegrationTestMojo
      * This parameter overrides the <code>includes/excludes</code> parameters, 
and the TestNG <code>suiteXmlFiles</code>
      * parameter.
      * <p/>
-     * since 2.7.3 You can execute a limited number of methods in the test 
with adding #myMethod or #my*ethod. E.g. type
+     * Since 2.7.3 You can execute a limited number of methods in the test 
with adding #myMethod or #my*ethod. E.g. type
      * "-Dit.test=MyTest#myMethod" <b>supported for junit 4.x and testNg</b>
+     * <br/>
+     * Since 2.19 a complex syntax is supported in one parameter (JUnit 4, 
JUnit 4.7+, TestNG):<br/>
+     * "-Dit.test=???IT, !Unstable*, pkg&#47;**&#47;Ci*leIT.java, 
*IT#test*One+testTwo?????, #fast*+slowTest"<br/>
+     * "-Dit.test=Basic*, !%regex[.*.Unstable.*], 
!%regex[.*.MyIT.class#one.*|two.*], %regex[#fast.*|slow.*]"<br/>
+     * <br/>
+     * The Parameterized JUnit runner <em>describes</em> test methods using an 
index in brackets, so the non-regex
+     * method pattern would become: <em>#testMethod[*]</em>.
+     * <br/>
      */
     @Parameter( property = "it.test" )
     private String test;
@@ -177,6 +184,8 @@ public class IntegrationTestMojo
      * <p/>
      * Each include item may also contain a comma-separated sublist of items, 
which will be treated as multiple
      * &nbsp;&lt;include> entries.<br/>
+     * Since 2.19 a complex syntax is supported in one parameter (JUnit 4, 
JUnit 4.7+, TestNG):<br/>
+     * &nbsp;&lt;include>%regex[.*[Cat|Dog].*], !%regex[pkg.*Slow.*.class], 
pkg&#47;**&#47;*Fast*.java, Basic????, !Unstable*&lt;/include><br/>
      * <p/>
      * This parameter is ignored if the TestNG <code>suiteXmlFiles</code> 
parameter is specified.<br/>
      * <br/>
@@ -261,8 +270,6 @@ public class IntegrationTestMojo
     @Parameter( property = "failsafe.runOrder", defaultValue = "filesystem" )
     protected String runOrder;
 
-    private TestListResolver testListResolver;
-
     protected int getRerunFailingTestsCount()
     {
         return rerunFailingTestsCount;
@@ -420,13 +427,9 @@ public class IntegrationTestMojo
         this.reportsDirectory = reportsDirectory;
     }
 
-    public TestListResolver getTest()
+    public String getTest()
     {
-        if ( testListResolver == null && test != null )
-        {
-            testListResolver = new TestListResolver( test );
-        }
-        return testListResolver;
+        return test;
     }
 
     public void setTest( String test )

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/AbstractSurefireMojo.java
index dfca11a..fecf74d 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
@@ -82,6 +82,7 @@ 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.TestListResolver;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.DefaultScanResult;
@@ -210,7 +211,7 @@ public abstract class AbstractSurefireMojo
 
     /**
      * A file containing include patterns. Blank lines, or lines starting with 
# are ignored. If {@code includes} are
-     * also specified these patterns are appended.
+     * also specified, these patterns are appended.
      */
     @Parameter
     protected File includesFile;
@@ -226,6 +227,8 @@ public abstract class AbstractSurefireMojo
      * <p/>
      * Each exclude item may also contain a comma-separated sublist of items, 
which will be treated as multiple
      * &nbsp;&lt;exclude> entries.<br/>
+     * Since 2.19 a complex syntax is supported in one parameter (JUnit 4, 
JUnit 4.7+, TestNG):<br/>
+     * <exclude>%regex[pkg.*Slow.*.class], Unstable*</exclude><br/>
      * <br/>
      * <em>Notice that</em> these values are relative to the directory 
containing generated test classes of the project
      * being tested. This directory is declared by the parameter 
<code>testClassesDirectory</code> which defaults
@@ -237,7 +240,7 @@ public abstract class AbstractSurefireMojo
 
     /**
      * A file containing exclude patterns. Blank lines, or lines starting with 
# are ignored. If {@code excludes} are
-     * also specified these patterns are appended.
+     * also specified, these patterns are appended.
      */
     @Parameter
     protected File excludesFile;
@@ -686,14 +689,16 @@ public abstract class AbstractSurefireMojo
      */
     public static final String FORK_NUMBER_PLACEHOLDER = 
"${surefire.forkNumber}";
 
-    private static final String JAVA_EXTENSION = ".java";
-
     protected abstract String getPluginName();
 
     protected abstract int getRerunFailingTestsCount();
 
     private SurefireDependencyResolver dependencyResolver;
 
+    private TestListResolver specificTests;
+
+    private TestListResolver includedExcludedTests;
+
     public void execute()
         throws MojoExecutionException, MojoFailureException
     {
@@ -702,10 +707,10 @@ public abstract class AbstractSurefireMojo
 
         if ( verifyParameters() && !hasExecutedBefore() )
         {
-            DefaultScanResult scan = scanForTestClasses();//!
+            DefaultScanResult scan = scanForTestClasses();
             if ( !isValidSuiteXmlFileConfig() && scan.isEmpty() )
             {
-                if ( getEffectiveFailIfNoTests() )//! isSpecificTestSpecified
+                if ( getEffectiveFailIfNoTests() )
                 {
                     throw new MojoFailureException(
                         "No tests were executed!  (Set -DfailIfNoTests=false 
to ignore this error.)" );
@@ -714,7 +719,7 @@ public abstract class AbstractSurefireMojo
                 return;
             }
             logReportsDirectory();
-            executeAfterPreconditionsChecked( scan );//! getTest providers
+            executeAfterPreconditionsChecked( scan );
         }
     }
 
@@ -725,17 +730,20 @@ public abstract class AbstractSurefireMojo
         toolchain = getToolchain();
     }
 
-    private DefaultScanResult scanForTestClasses()//! execute()
+    private DefaultScanResult scanForTestClasses()
+        throws MojoFailureException
     {
-        DefaultScanResult scan = scanDirectories();//! 1
-        DefaultScanResult scanDeps = scanDependencies();//!
+        DefaultScanResult scan = scanDirectories();
+        DefaultScanResult scanDeps = scanDependencies();
         return scan.append( scanDeps );
     }
 
-    private DefaultScanResult scanDirectories()//!
+    private DefaultScanResult scanDirectories()
+        throws MojoFailureException
     {
-        DirectoryScanner scanner =
-            new DirectoryScanner( getTestClassesDirectory(), getIncludeList(), 
getExcludeList(), getSpecificTests() );//!
+        DirectoryScanner scanner = new DirectoryScanner( 
getTestClassesDirectory(),
+                                                         
getIncludedAndExcludedTests(),
+                                                         getSpecificTests() );
         return scanner.scan();
     }
 
@@ -749,10 +757,14 @@ public abstract class AbstractSurefireMojo
         {
             try
             {
-                // noinspection unchecked
-                return new DependencyScanner( DependencyScanner.filter( 
project.getTestArtifacts(),
-                                                                        
Arrays.asList( getDependenciesToScan() ) ),
-                                              getIncludeList(), 
getExcludeList(), getSpecificTests() ).scan();//!
+                // @TODO noinspection unchecked, check MavenProject 3.x for 
Generics in surefire:3.0
+                @SuppressWarnings( "unchecked" )
+                List<File> dependenciesToScan =
+                    DependencyScanner.filter( project.getTestArtifacts(), 
Arrays.asList( getDependenciesToScan() ) );
+                DependencyScanner scanner = new DependencyScanner( 
dependenciesToScan,
+                                                                   
getIncludedAndExcludedTests(),
+                                                                   
getSpecificTests() );
+                return scanner.scan();
             }
             catch ( Exception e )
             {
@@ -817,7 +829,7 @@ public abstract class AbstractSurefireMojo
         {
             try
             {
-                current = current.aggregate( executeProvider( provider, 
scanResult ) );//! getTest
+                current = current.aggregate( executeProvider( provider, 
scanResult ) );
             }
             catch ( SurefireBooterForkException e )
             {
@@ -966,7 +978,7 @@ public abstract class AbstractSurefireMojo
             createCopyAndReplaceForkNumPlaceholder( effectiveProperties, 1 
).copyToSystemProperties();
 
             InPluginVMSurefireStarter surefireStarter =
-                createInprocessStarter( provider, classLoaderConfiguration, 
runOrderParameters );//! getTest
+                createInprocessStarter( provider, classLoaderConfiguration, 
runOrderParameters );
             return surefireStarter.runSuitesInProcess( scanResult );
         }
         else
@@ -981,7 +993,7 @@ public abstract class AbstractSurefireMojo
             try
             {
                 ForkStarter forkStarter =
-                    createForkStarter( provider, forkConfiguration, 
classLoaderConfiguration, runOrderParameters,//! getTest
+                    createForkStarter( provider, forkConfiguration, 
classLoaderConfiguration, runOrderParameters,
                                        getLog() );
                 return forkStarter.run( effectiveProperties, scanResult );
             }
@@ -1371,7 +1383,7 @@ public abstract class AbstractSurefireMojo
 
     private boolean getEffectiveFailIfNoTests()
     {
-        if ( isSpecificTestSpecified() )//!
+        if ( isSpecificTestSpecified() )
         {
             if ( getFailIfNoSpecifiedTests() != null )
             {
@@ -1404,9 +1416,8 @@ public abstract class AbstractSurefireMojo
         TestArtifactInfo testNg =
             isTestNg ? new TestArtifactInfo( testNgArtifact.getVersion(), 
testNgArtifact.getClassifier() ) : null;
         List<File> testXml = getSuiteXmlFiles() != null ? Arrays.asList( 
getSuiteXmlFiles() ) : null;
-        TestRequest testSuiteDefinition =
-            new TestRequest( testXml, getTestSourceDirectory(), 
getTest().onlyMethodFilters(),
-                             getRerunFailingTestsCount() );//!
+        TestRequest testSuiteDefinition = new TestRequest( testXml, 
getTestSourceDirectory(), getSpecificTests(),
+                                                           
getRerunFailingTestsCount() );
 
         final boolean actualFailIfNoTests;
 
@@ -1420,9 +1431,9 @@ public abstract class AbstractSurefireMojo
         }
         else
         {
-            if ( isSpecificTestSpecified() )//!
+            if ( isSpecificTestSpecified() )
             {
-                actualFailIfNoTests = getEffectiveFailIfNoTests();//! 
isSpecificTestSpecified
+                actualFailIfNoTests = getEffectiveFailIfNoTests();
                 setFailIfNoTests( actualFailIfNoTests );
             }
             else
@@ -1430,9 +1441,15 @@ public abstract class AbstractSurefireMojo
                 actualFailIfNoTests = getFailIfNoTests() != null && 
getFailIfNoTests();
             }
 
-            List<String> actualIncludes = getIncludeList();//!
-            List<String> actualExcludes = getExcludeList();//!
-            List<String> specificTests = getSpecificTests();//!
+            // @todo remove these three params and use 
DirectoryScannerParameters to pass into DirectoryScanner only
+            // @todo or remove it in next major version :: 3.0
+            // @todo remove deprecated methods in ProviderParameters => 
included|excluded|specificTests not needed here
+
+            List<String> actualIncludes = getIncludeList(); // 
Collections.emptyList(); behaves same
+            List<String> actualExcludes = getExcludeList(); // 
Collections.emptyList(); behaves same
+            // Collections.emptyList(); behaves same
+            List<String> specificTests = new ArrayList<String>( 
getSpecificTests().getTestSpecificClasses() );
+
             directoryScannerParameters =
                 new DirectoryScannerParameters( getTestClassesDirectory(), 
actualIncludes, actualExcludes,
                                                 specificTests, 
actualFailIfNoTests, getRunOrder() );
@@ -1451,7 +1468,6 @@ public abstract class AbstractSurefireMojo
             + configurationHash;
     }
 
-
     StartupConfiguration createStartupConfiguration( ProviderInfo provider,
                                                      ClassLoaderConfiguration 
classLoaderConfiguration )
         throws MojoExecutionException, MojoFailureException
@@ -1466,7 +1482,6 @@ public abstract class AbstractSurefireMojo
             {
                 providerClasspath = provider.getProviderClasspath();
                 ClasspathCache.setCachedClasspath( providerName, 
providerClasspath );
-
             }
             Artifact surefireArtifact = getCommonArtifact();
             Classpath inprocClassPath = providerClasspath.
@@ -1523,7 +1538,7 @@ public abstract class AbstractSurefireMojo
 
     private boolean isSpecificTestSpecified()
     {
-        return getTest() != null;//!
+        return getTest() != null;
     }
 
     private boolean isValidSuiteXmlFileConfig()
@@ -1571,9 +1586,10 @@ public abstract class AbstractSurefireMojo
 
     @SuppressWarnings( "checkstyle:modifierorder" )
     private @Nonnull List<String> getExcludeList()
+        throws MojoFailureException
     {
         List<String> actualExcludes = null;
-        if ( isSpecificTestSpecified() )//!
+        if ( isSpecificTestSpecified() )
         {
             // Check to see if we are running a single test. The raw parameter 
will
             // come through if it has not been set.
@@ -1583,21 +1599,23 @@ public abstract class AbstractSurefireMojo
         }
         else
         {
-            if ( getExcludesFile() != null )//!
+            if ( getExcludesFile() != null )
             {
-                actualExcludes = readListFromFile( getExcludesFile() );//!
+                actualExcludes = readListFromFile( getExcludesFile() );
             }
 
             // If we have excludesFile, and we have excludes, then append 
excludes to excludesFile content
             if ( actualExcludes == null )
             {
-                actualExcludes = getExcludes();//!
+                actualExcludes = getExcludes();
             }
             else
             {
-                maybeAppendList( actualExcludes, getExcludes() );//!
+                maybeAppendList( actualExcludes, getExcludes() );
             }
 
+            checkMethodFilterInIncludesExcludes( actualExcludes );
+
             // defaults here, qdox doesn't like the end javadoc value
             // Have to wrap in an ArrayList as surefire expects an ArrayList 
instead of a List for some reason
             if ( actualExcludes == null || actualExcludes.isEmpty() )
@@ -1609,40 +1627,79 @@ public abstract class AbstractSurefireMojo
     }
 
     private List<String> getIncludeList()
+        throws MojoFailureException
     {
         List<String> includes = null;
-        if ( isSpecificTestSpecified() && !isMultipleExecutionBlocksDetected() 
)//!
+        if ( isSpecificTestSpecified() && !isMultipleExecutionBlocksDetected() 
)
         {
-            includes = getSpecificTests();//!
+            includes = Arrays.asList( getTest() );
         }
         else
         {
-            if ( getIncludesFile() != null )//!
+            if ( getIncludesFile() != null )
             {
-                includes = readListFromFile( getIncludesFile() );//!
+                includes = readListFromFile( getIncludesFile() );
             }
 
             // If we have includesFile, and we have includes, then append 
includes to includesFile content
             if ( includes == null )
             {
-                includes = getIncludes();//!
+                includes = getIncludes();
             }
             else
             {
-                maybeAppendList( includes, getIncludes() );//!
+                maybeAppendList( includes, getIncludes() );
             }
+
+            checkMethodFilterInIncludesExcludes( includes );
         }
 
         // defaults here, qdox doesn't like the end javadoc value
         // Have to wrap in an ArrayList as surefire expects an ArrayList 
instead of a List for some reason
         if ( includes == null || includes.isEmpty() )
         {
-            includes = Arrays.asList( getDefaultIncludes() );//!
+            includes = Arrays.asList( getDefaultIncludes() );
         }
 
         return filterNulls( includes );
     }
 
+    private void checkMethodFilterInIncludesExcludes( Iterable<String> 
patterns )
+        throws MojoFailureException
+    {
+        if ( patterns != null )
+        {
+            for ( String pattern : patterns )
+            {
+                if ( pattern != null && pattern.contains( "#" ) )
+                {
+                    throw new MojoFailureException( "Method filter prohibited 
in "
+                                                        + 
"includes|excludes|includesFile|excludesFile parameter: "
+                                                        + pattern );
+                }
+            }
+        }
+    }
+
+    private TestListResolver getIncludedAndExcludedTests()
+        throws MojoFailureException
+    {
+        if ( includedExcludedTests == null )
+        {
+            includedExcludedTests = new TestListResolver( getIncludeList(), 
getExcludeList() );
+        }
+        return includedExcludedTests;
+    }
+
+    public TestListResolver getSpecificTests()
+    {
+        if ( specificTests == null )
+        {
+            specificTests = new TestListResolver( getTest() );
+        }
+        return specificTests;
+    }
+
     @SuppressWarnings( "checkstyle:modifierorder" )
     private @Nonnull List<String> filterNulls( @Nonnull List<String> toFilter )
     {
@@ -1676,28 +1733,6 @@ public abstract class AbstractSurefireMojo
         return false;
     }
 
-    private List<String> getSpecificTests()
-    {
-        if ( isSpecificTestSpecified() )//!
-        {
-            List<String> specificTests = new ArrayList<String>();
-            for ( String testRegex : getTest().getTestSpecificClasses() )//!
-            {
-                if ( testRegex.endsWith( JAVA_EXTENSION ) )
-                {
-                    testRegex = testRegex.substring( 0, testRegex.length() - 
JAVA_EXTENSION.length() );
-                }
-                testRegex = testRegex.replace( '.', '/' );
-                specificTests.add( "**/" + testRegex + JAVA_EXTENSION );
-            }
-            return specificTests;
-        }
-        else
-        {
-            return Collections.emptyList();
-        }
-    }
-
     private Artifact getTestNgArtifact()
         throws MojoExecutionException
     {
@@ -1747,7 +1782,7 @@ public abstract class AbstractSurefireMojo
         StartupConfiguration startupConfiguration = 
createStartupConfiguration( provider, classLoaderConfiguration );
         String configChecksum = getConfigChecksum();
         StartupReportConfiguration startupReportConfiguration = 
getStartupReportConfiguration( configChecksum );
-        ProviderConfiguration providerConfiguration = 
createProviderConfiguration( runOrderParameters );//! getTest
+        ProviderConfiguration providerConfiguration = 
createProviderConfiguration( runOrderParameters );
         return new ForkStarter( providerConfiguration, startupConfiguration, 
forkConfiguration,
                                 getForkedProcessTimeoutInSeconds(), 
startupReportConfiguration, log );
     }
@@ -1760,7 +1795,7 @@ public abstract class AbstractSurefireMojo
         StartupConfiguration startupConfiguration = 
createStartupConfiguration( provider, classLoaderConfiguration );
         String configChecksum = getConfigChecksum();
         StartupReportConfiguration startupReportConfiguration = 
getStartupReportConfiguration( configChecksum );
-        ProviderConfiguration providerConfiguration = 
createProviderConfiguration( runOrderParameters );//! getTest
+        ProviderConfiguration providerConfiguration = 
createProviderConfiguration( runOrderParameters );
         return new InPluginVMSurefireStarter( startupConfiguration, 
providerConfiguration, startupReportConfiguration );
 
     }
@@ -1917,9 +1952,9 @@ public abstract class AbstractSurefireMojo
         checksum.add( getAdditionalClasspathElements() );
         checksum.add( getReportsDirectory() );
         checksum.add( getTestSourceDirectory() );
-        checksum.add( isSpecificTestSpecified() ? 
getTest().getPluginParameterTest() : null );//!
-        checksum.add( getIncludes() );//!
-        checksum.add( getExcludes() );//!
+        checksum.add( getTest() );
+        checksum.add( getIncludes() );
+        checksum.add( getExcludes() );
         checksum.add( getLocalRepository() );
         checksum.add( getSystemProperties() );
         checksum.add( getSystemPropertyVariables() );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/SurefireExecutionParameters.java
index 566c91f..48e3fd9 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
@@ -22,7 +22,6 @@ package org.apache.maven.plugin.surefire;
 import java.io.File;
 import java.util.List;
 import org.apache.maven.artifact.repository.ArtifactRepository;
-import org.apache.maven.surefire.testset.TestListResolver;
 
 /**
  * This interface contains all the common parameters that have different 
implementations in Surefire vs IntegrationTest
@@ -64,7 +63,7 @@ public interface SurefireExecutionParameters
 
     void setTestSourceDirectory( File testSourceDirectory );
 
-    TestListResolver getTest();
+    String getTest();
 
     void setTest( String test );
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/BooterSerializer.java
----------------------------------------------------------------------
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 bfdce4e..c8d1b3a 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
@@ -34,6 +34,7 @@ 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.TestListResolver;
 import org.apache.maven.surefire.testset.TestRequest;
 import org.apache.maven.surefire.util.RunOrder;
 
@@ -93,8 +94,10 @@ class BooterSerializer
         {
             properties.setProperty( BooterConstants.SOURCE_DIRECTORY, 
testSuiteDefinition.getTestSourceDirectory() );
             properties.addList( testSuiteDefinition.getSuiteXmlFiles(), 
BooterConstants.TEST_SUITE_XML_FILES );
-            properties.setNullableProperty( BooterConstants.REQUESTEDTEST,
-                                            
testSuiteDefinition.getTestListResolver().getPluginParameterTest() );
+            TestListResolver methodFilter = 
testSuiteDefinition.getTestListResolver();
+            String requestedTest =
+                methodFilter == null || methodFilter.isEmpty() ? null : 
methodFilter.getPluginParameterTest();
+            properties.setNullableProperty( BooterConstants.REQUESTEDTEST, 
requestedTest );
             properties.setNullableProperty( 
BooterConstants.RERUN_FAILING_TESTS_COUNT,
                                             String.valueOf( 
testSuiteDefinition.getRerunFailingTestsCount() ) );
         }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
index 356640e..1fb14f5 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DependencyScanner.java
@@ -20,25 +20,23 @@ package org.apache.maven.plugin.surefire.util;
  */
 
 import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.convertJarFileResourceToJavaClassName;
-import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.convertSlashToSystemFileSeparator;
-import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.processIncludesExcludes;
 
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Enumeration;
+import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.plugin.MojoExecutionException;
-import org.apache.maven.shared.utils.io.MatchPatterns;
+import org.apache.maven.surefire.testset.TestFilter;
+import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.util.DefaultScanResult;
 
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
 /**
  * Scans dependencies looking for tests.
  *
@@ -46,77 +44,64 @@ import javax.annotation.Nullable;
  */
 public class DependencyScanner
 {
-
     private final List<File> dependenciesToScan;
 
-    protected final List<String> includes;
+    private final TestListResolver includedAndExcludedTests;
 
-    @SuppressWarnings( "checkstyle:modifierorder" )
-    protected final @Nonnull List<String> excludes;
+    private final TestListResolver specificTests;
 
-    protected final List<String> specificTests;
-
-    public DependencyScanner( List<File> dependenciesToScan, List<String> 
includes, @Nonnull List<String> excludes,
-                              List<String> specificTests )
+    public DependencyScanner( List<File> dependenciesToScan,
+                              TestListResolver includedAndExcludedTests, 
TestListResolver specificTests )
     {
         this.dependenciesToScan = dependenciesToScan;
-        this.includes = includes;
-        this.excludes = excludes;
+        this.includedAndExcludedTests = includedAndExcludedTests;
         this.specificTests = specificTests;
     }
 
     public DefaultScanResult scan()
         throws MojoExecutionException
     {
-        Matcher matcher = new Matcher( includes, excludes, specificTests );
-        List<String> found = new ArrayList<String>();
+        Set<String> classes = new LinkedHashSet<String>();
         for ( File artifact : dependenciesToScan )
         {
             try
             {
-                found.addAll( scanArtifact( artifact, matcher ) );
+                scanArtifact( artifact, includedAndExcludedTests.and( 
specificTests ), classes );
             }
             catch ( IOException e )
             {
                 throw new MojoExecutionException( "Could not scan dependency " 
+ artifact.toString(), e );
             }
         }
-        return new DefaultScanResult( found );
+        return new DefaultScanResult( new ArrayList<String>( classes ) );
     }
 
-    private List<String> scanArtifact( File artifact, Matcher matcher )
+    private static void scanArtifact( File artifact, TestFilter<String, 
String> filter, Set<String> classes )
         throws IOException
     {
-        List<String> found = new ArrayList<String>();
-
-        if ( artifact != null )
+        if ( artifact != null && artifact.isFile() )
         {
-            if ( artifact.isFile() )
+            JarFile jar = null;
+            try
             {
-                JarFile jar = null;
-                try
+                jar = new JarFile( artifact );
+                for ( Enumeration<JarEntry> entries = jar.entries(); 
entries.hasMoreElements(); )
                 {
-                    jar = new JarFile( artifact );
-                    Enumeration<JarEntry> entries = jar.entries();
-                    while ( entries.hasMoreElements() )
+                    JarEntry entry = entries.nextElement();
+                    if ( filter.shouldRun( entry.getName(), null ) )
                     {
-                        JarEntry entry = entries.nextElement();
-                        if ( matcher.shouldInclude( entry.getName() ) )
-                        {
-                            found.add( convertJarFileResourceToJavaClassName( 
entry.getName() ) );
-                        }
+                        classes.add( convertJarFileResourceToJavaClassName( 
entry.getName() ) );
                     }
                 }
-                finally
+            }
+            finally
+            {
+                if ( jar != null )
                 {
-                    if ( jar != null )
-                    {
-                        jar.close();
-                    }
+                    jar.close();
                 }
             }
         }
-        return found;
     }
 
     public static List<File> filter( List<Artifact> artifacts, List<String> 
groupArtifactIds )
@@ -145,44 +130,4 @@ public class DependencyScanner
         }
         return matches;
     }
-
-    private class Matcher
-    {
-
-        private MatchPatterns includes;
-
-        private MatchPatterns excludes;
-
-        private SpecificFileFilter specificTestFilter;
-
-        public Matcher( @Nullable List<String> includes, @Nonnull List<String> 
excludes,
-                        @Nullable List<String> specificTests )
-        {
-            String[] specific = specificTests == null ? new String[0] : 
processIncludesExcludes( specificTests );
-            specificTestFilter = new SpecificFileFilter( specific );
-
-            if ( includes != null && !includes.isEmpty() )
-            {
-                this.includes = MatchPatterns.from( processIncludesExcludes( 
includes ) );
-            }
-            else
-            {
-                this.includes = MatchPatterns.from( "**" );
-            }
-            this.excludes = MatchPatterns.from( processIncludesExcludes( 
excludes ) );
-        }
-
-        public boolean shouldInclude( String name )
-        {
-            if ( !name.endsWith( ".class" ) )
-            {
-                return false;
-            }
-            name = convertSlashToSystemFileSeparator( name );
-            boolean isIncluded = includes.matches( name, false );
-            boolean isExcluded = excludes.matches( name, false );
-
-            return isIncluded && !isExcluded && specificTestFilter.accept( 
name );
-        }
-    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DirectoryScanner.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DirectoryScanner.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DirectoryScanner.java
index 01fcc51..8b4c434 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DirectoryScanner.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/DirectoryScanner.java
@@ -19,15 +19,11 @@ package org.apache.maven.plugin.surefire.util;
  * under the License.
  */
 
-import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.convertSlashToSystemFileSeparator;
-import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.convertToJavaClassName;
-import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.processIncludesExcludes;
-import static org.apache.maven.plugin.surefire.util.ScannerUtil.stripBaseDir;
-
 import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.util.DefaultScanResult;
 
 /**
@@ -40,54 +36,24 @@ public class DirectoryScanner
 {
     private final File basedir;
 
-    private final List<String> includes;
-
-    private final List<String> excludes;
+    private final TestListResolver includedAndExcludedTests;
 
-    private final List<String> specificTests;
+    private final TestListResolver specificTests;
 
-    public DirectoryScanner( File basedir, List<String> includes, List<String> 
excludes, List<String> specificTests )
+    public DirectoryScanner( File basedir, TestListResolver 
includedAndExcludedTests, TestListResolver specificTests )
     {
         this.basedir = basedir;
-        this.includes = includes;
-        this.excludes = excludes;
+        this.includedAndExcludedTests = includedAndExcludedTests;
         this.specificTests = specificTests;
     }
 
     public DefaultScanResult scan()
     {
-        String[] specific = specificTests == null ? new String[0] : 
processIncludesExcludes( specificTests );
-        SpecificFileFilter specificTestFilter = new SpecificFileFilter( 
specific );
-
+        FileScanner scanner = new FileScanner( basedir, "class" );
         List<String> result = new ArrayList<String>();
-        if ( basedir.exists() )
-        {
-            org.apache.maven.shared.utils.io.DirectoryScanner scanner =
-                new org.apache.maven.shared.utils.io.DirectoryScanner();
-
-            scanner.setBasedir( basedir );
-
-            if ( includes != null )
-            {
-                scanner.setIncludes( processIncludesExcludes( includes ) );
-            }
-
-            if ( excludes != null )
-            {
-                scanner.setExcludes( processIncludesExcludes( excludes ) );
-            }
-
-            scanner.scan();
-            for ( String test : scanner.getIncludedFiles() )
-            {
-                if ( specificTestFilter.accept(
-                         convertSlashToSystemFileSeparator(
-                              stripBaseDir( basedir.getAbsolutePath(), test ) 
) ) )
-                {
-                    result.add( convertToJavaClassName( test ) );
-                }
-            }
-        }
+        TestListResolver includedExcludedClasses = 
includedAndExcludedTests.createClassFilters();
+        TestListResolver specificClasses = specificTests.createClassFilters();
+        scanner.scanTo( result, includedExcludedClasses.and( specificClasses ) 
);
         return new DefaultScanResult( result );
     }
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/FileScanner.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/FileScanner.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/FileScanner.java
new file mode 100644
index 0000000..02a51ab
--- /dev/null
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/FileScanner.java
@@ -0,0 +1,126 @@
+package org.apache.maven.plugin.surefire.util;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.testset.TestFilter;
+
+import java.io.File;
+import java.util.Collection;
+
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+
+final class FileScanner
+{
+    private static final String CLASS_FILE_EXTENSION = ".class";
+
+    private final File basedir;
+
+    private final String ext;
+
+    FileScanner( File basedir, String ext )
+    {
+        this.basedir = basedir;
+        ext = isBlank( ext ) ? null : ext.trim();
+        this.ext = ext == null || ext.startsWith( "." ) ? ext : "." + ext;
+    }
+
+    void scanTo( Collection<String> scannedJavaClassNames, TestFilter<String, 
String> filter )
+    {
+        scan( scannedJavaClassNames, filter, basedir );
+    }
+
+    private void scan( Collection<String> scannedJavaClassNames,
+                       TestFilter<String, String> filter, File basedir, 
String... subDirectories )
+    {
+        File[] filesAndDirs = basedir.listFiles();
+        if ( filesAndDirs != null )
+        {
+            final String pAckage = toJavaPackage( subDirectories );
+            final String path = toPath( subDirectories );
+            final String ext = this.ext;
+            final boolean hasExtension = ext != null;
+            final int extLength = hasExtension ? ext.length() : 0;
+            for ( File fileOrDir : filesAndDirs )
+            {
+                String name = fileOrDir.getName();
+                if ( name.length() != 0 )
+                {
+                    if ( fileOrDir.isFile() )
+                    {
+                        final int clsLength = name.length() - extLength;
+                        if ( clsLength > 0
+                            && ( !hasExtension || name.regionMatches( true, 
clsLength, ext, 0, extLength ) ) )
+                        {
+                            String simpleClassName = hasExtension ? 
name.substring( 0, clsLength ) : name;
+                            if ( filter.shouldRun( toFile( path, 
simpleClassName ), null ) )
+                            {
+                                String fullyQualifiedClassName =
+                                    pAckage.length() == 0 ? simpleClassName : 
pAckage + '.' + simpleClassName;
+                                scannedJavaClassNames.add( 
fullyQualifiedClassName );
+                            }
+                        }
+                    }
+                    else if ( fileOrDir.isDirectory() )
+                    {
+                        String[] paths = new String[subDirectories.length + 1];
+                        System.arraycopy( subDirectories, 0, paths, 0, 
subDirectories.length );
+                        paths[subDirectories.length] = name;
+                        scan( scannedJavaClassNames, filter, fileOrDir, paths 
);
+                    }
+                }
+            }
+        }
+    }
+
+    private static String toJavaPackage( String... subDirectories )
+    {
+        StringBuilder pkg = new StringBuilder();
+        for ( int i = 0; i < subDirectories.length; i++ )
+        {
+            if ( i > 0 && i < subDirectories.length )
+            {
+                pkg.append( '.' );
+            }
+            pkg.append( subDirectories[i] );
+        }
+        return pkg.toString();
+    }
+
+    private static String toPath( String... subDirectories )
+    {
+        StringBuilder pkg = new StringBuilder();
+        for ( int i = 0; i < subDirectories.length; i++ )
+        {
+            if ( i > 0 && i < subDirectories.length )
+            {
+                pkg.append( '/' );
+            }
+            pkg.append( subDirectories[i] );
+        }
+        return pkg.toString();
+    }
+
+    private static String toFile( String path, String fileNameWithoutExtension 
)
+    {
+        String pathWithoutExtension =
+            path.length() == 0 ? fileNameWithoutExtension : path + '/' + 
fileNameWithoutExtension;
+        return pathWithoutExtension + CLASS_FILE_EXTENSION;
+    }
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
index e3ff45d..8452a7a 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/ScannerUtil.java
@@ -19,12 +19,7 @@ package org.apache.maven.plugin.surefire.util;
  * under the License.
  */
 
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-
 import org.apache.commons.lang3.StringUtils;
-
 import javax.annotation.Nonnull;
 
 final class ScannerUtil
@@ -36,19 +31,9 @@ final class ScannerUtil
 
     private static final String FS = System.getProperty( "file.separator" );
 
-    private static final String JAVA_SOURCE_FILE_EXTENSION = ".java";
-
-    private static final String JAVA_CLASS_FILE_EXTENSION = ".class";
-
     private static final boolean IS_NON_UNIX_FS = ( !FS.equals( "/" ) );
 
     @SuppressWarnings( "checkstyle:modifierorder" )
-    public static @Nonnull String convertToJavaClassName( @Nonnull String test 
)
-    {
-        return StringUtils.removeEnd( test, ".class" ).replace( FS, "." );
-    }
-
-    @SuppressWarnings( "checkstyle:modifierorder" )
     public static @Nonnull String convertJarFileResourceToJavaClassName( 
@Nonnull String test )
     {
         return StringUtils.removeEnd( test, ".class" ).replace( "/", "." );
@@ -59,36 +44,4 @@ final class ScannerUtil
     {
         return ( IS_NON_UNIX_FS ? path.replace( "/", FS ) : path );
     }
-
-    @SuppressWarnings( "checkstyle:modifierorder" )
-    public static @Nonnull String stripBaseDir( String basedir, String test )
-    {
-        return StringUtils.removeStart( test, basedir );
-    }
-
-    @SuppressWarnings( "checkstyle:modifierorder" )
-    public static @Nonnull String[] processIncludesExcludes( @Nonnull 
List<String> list )
-    {
-        List<String> newList = new ArrayList<String>();
-        for ( Object aList : list )
-        {
-            String include = (String) aList;
-            String[] includes = include.split( "," );
-            Collections.addAll( newList, includes );
-        }
-
-        String[] incs = new String[newList.size()];
-
-        for ( int i = 0; i < incs.length; i++ )
-        {
-            String inc = newList.get( i );
-            if ( inc.endsWith( JAVA_SOURCE_FILE_EXTENSION ) )
-            {
-                inc = StringUtils.removeEnd( inc, JAVA_SOURCE_FILE_EXTENSION ) 
+ JAVA_CLASS_FILE_EXTENSION;
-            }
-            incs[i] = convertSlashToSystemFileSeparator( inc );
-
-        }
-        return incs;
-    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/SpecificFileFilter.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/SpecificFileFilter.java
 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/SpecificFileFilter.java
index f168d32..912f274 100644
--- 
a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/SpecificFileFilter.java
+++ 
b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/util/SpecificFileFilter.java
@@ -32,6 +32,7 @@ import static 
org.apache.maven.plugin.surefire.util.ScannerUtil.convertSlashToSy
  * filters file names by a given collection of class name patterns
  *
  */
+@Deprecated
 public class SpecificFileFilter
 {
 

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/BooterDeserializerProviderConfigurationTest.java
----------------------------------------------------------------------
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 94d89df..1713476 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
@@ -133,12 +133,12 @@ public class BooterDeserializerProviderConfigurationTest
         Assert.assertNotNull( resolver );
         Assert.assertFalse( resolver.isEmpty() );
         Assert.assertEquals( aUserRequestedTest + "#" + 
aUserRequestedTestMethod, resolver.getPluginParameterTest() );
-        Assert.assertFalse( resolver.getIncludedFilters().isEmpty() );
-        Assert.assertTrue( resolver.getExcludedFilters().isEmpty() );
-        Assert.assertEquals( 1, resolver.getIncludedFilters().size() );
-        ResolvedTest filter = resolver.getIncludedFilters().iterator().next();
+        Assert.assertFalse( resolver.getIncludedPatterns().isEmpty() );
+        Assert.assertTrue( resolver.getExcludedPatterns().isEmpty() );
+        Assert.assertEquals( 1, resolver.getIncludedPatterns().size() );
+        ResolvedTest filter = resolver.getIncludedPatterns().iterator().next();
         Assert.assertNotNull( filter );
-        Assert.assertEquals( aUserRequestedTest + ".class", 
filter.getTestClassPattern() );
+        Assert.assertEquals( "**/" + aUserRequestedTest, 
filter.getTestClassPattern() );
         Assert.assertEquals( aUserRequestedTestMethod, 
filter.getTestMethodPattern() );
         Assert.assertEquals( rerunFailingTestsCount, 
testSuiteDefinition.getRerunFailingTestsCount() );
     }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DependenciesScannerTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DependenciesScannerTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DependenciesScannerTest.java
index c897e1e..3fc0976 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DependenciesScannerTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DependenciesScannerTest.java
@@ -36,6 +36,7 @@ import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.versioning.VersionRange;
 import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.ScanResult;
 
@@ -65,7 +66,7 @@ public class DependenciesScannerTest
 
         DependencyScanner scanner = new DependencyScanner(
                 DependencyScanner.filter(Arrays.asList(artifact), 
scanDependencies),
-                include, exclude, new ArrayList<String>() );
+                new TestListResolver( include, exclude ), new 
TestListResolver( "" ) );
 
         ScanResult classNames = scanner.scan();
         assertNotNull( classNames );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DirectoryScannerTest.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DirectoryScannerTest.java
 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DirectoryScannerTest.java
index bf3d383..4c41854 100644
--- 
a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DirectoryScannerTest.java
+++ 
b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/util/DirectoryScannerTest.java
@@ -25,6 +25,8 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Properties;
+
+import org.apache.maven.surefire.testset.TestListResolver;
 import org.apache.maven.surefire.testset.TestSetFailedException;
 import org.apache.maven.surefire.util.ScanResult;
 
@@ -37,7 +39,7 @@ public class DirectoryScannerTest
     extends TestCase
 {
     public void testLocateTestClasses()
-        throws IOException, TestSetFailedException
+        throws Exception
     {
         // use target as people can configure ide to compile in an other place 
than maven
         File baseDir = new File( new File( "target/test-classes" 
).getCanonicalPath() );
@@ -46,7 +48,7 @@ public class DirectoryScannerTest
         List<String> exclude = new ArrayList<String>();
 
         DirectoryScanner surefireDirectoryScanner =
-            new DirectoryScanner( baseDir, include, exclude, new 
ArrayList<String>() );
+            new DirectoryScanner( baseDir, new TestListResolver( include, 
exclude ), new TestListResolver( "" ) );
 
         ScanResult classNames = surefireDirectoryScanner.scan();
         assertNotNull( classNames );

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
----------------------------------------------------------------------
diff --git 
a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
 
b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefirePlugin.java
index 988cddc..8d45d61 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
@@ -28,7 +28,6 @@ import org.apache.maven.plugins.annotations.Mojo;
 import org.apache.maven.plugins.annotations.Parameter;
 import org.apache.maven.plugins.annotations.ResolutionScope;
 import org.apache.maven.surefire.suite.RunResult;
-import org.apache.maven.surefire.testset.TestListResolver;
 
 /**
  * Run tests using Surefire.
@@ -65,7 +64,15 @@ public class SurefirePlugin
      * parameter.
      * <p/>
      * Since 2.7.3, you can execute a limited number of methods in the test by 
adding #myMethod or #my*ethod. For
-     * example, "-Dtest=MyTest#myMethod". This is supported for junit 4.x and 
testNg.
+     * example, "-Dtest=MyTest#myMethod". This is supported for junit 4.x and 
testNg.<br/>
+     * <br/>
+     * Since 2.19 a complex syntax is supported in one parameter (JUnit 4, 
JUnit 4.7+, TestNG):<br/>
+     * "-Dtest=???Test, !Unstable*, pkg&#47;**&#47;Ci*leTest.java, 
*Test#test*One+testTwo?????, #fast*+slowTest"<br/>
+     * "-Dtest=Basic*, !%regex[.*.Unstable.*], 
!%regex[.*.MyTest.class#one.*|two.*], %regex[#fast.*|slow.*]"<br/>
+     * <br/>
+     * The Parameterized JUnit runner <em>describes</em> test methods using an 
index in brackets, so the non-regex
+     * method pattern would become: <em>#testMethod[*]</em>.
+     * <br/>
      */
     @Parameter( property = "test" )
     private String test;
@@ -158,6 +165,8 @@ public class SurefirePlugin
      * <p/>
      * Each include item may also contain a comma-separated sublist of items, 
which will be treated as multiple
      * &nbsp;&lt;include> entries.<br/>
+     * Since 2.19 a complex syntax is supported in one parameter (JUnit 4, 
JUnit 4.7+, TestNG):<br/>
+     * &nbsp;&lt;include>%regex[.*[Cat|Dog].*], !%regex[pkg.*Slow.*.class], 
pkg&#47;**&#47;*Fast*.java, Basic????, !Unstable*&lt;/include><br/>
      * <p/>
      * This parameter is ignored if the TestNG <code>suiteXmlFiles</code> 
parameter is specified.<br/>
      * <br/>
@@ -236,8 +245,6 @@ public class SurefirePlugin
     @Parameter( property = "surefire.runOrder", defaultValue = "filesystem" )
     protected String runOrder;
 
-    private TestListResolver testListResolver;
-
     protected int getRerunFailingTestsCount()
     {
         return rerunFailingTestsCount;
@@ -372,13 +379,9 @@ public class SurefirePlugin
         this.reportsDirectory = reportsDirectory;
     }
 
-    public TestListResolver getTest()
+    public String getTest()
     {
-        if ( testListResolver == null && test != null )
-        {
-            testListResolver = new TestListResolver( test );
-        }
-        return testListResolver;
+        return test;
     }
 
     public boolean isUseSystemClassLoader()

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-plugin/src/site/apt/examples/inclusion-exclusion.apt.vm
----------------------------------------------------------------------
diff --git 
a/maven-surefire-plugin/src/site/apt/examples/inclusion-exclusion.apt.vm 
b/maven-surefire-plugin/src/site/apt/examples/inclusion-exclusion.apt.vm
index 6992294..515fa18 100644
--- a/maven-surefire-plugin/src/site/apt/examples/inclusion-exclusion.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/inclusion-exclusion.apt.vm
@@ -162,3 +162,19 @@ Inclusions and Exclusions of Tests
 
   Note the syntax <<<%regex[expr]>>>, where <<<expr>>> is the actual 
expression and the rest is just wrapping. Also
   note that regex matches are done over <<<*.class>>> files and not 
<<<*.java>>> files.
+
+* Multiple Formats in One
+
+  As of ${thisPlugin} Plugin 2.19, a complex syntax is supported in one 
parameter (JUnit 4, JUnit 4.7+, TestNG):
++---+
+  [...]
+          <include>%regex[.*[Cat|Dog].*], !%regex[pkg.*Slow.*.class], 
pkg/**/*Fast*.java, Basic????, !Unstable*</include>
+  [...]
+          <exclude>%regex[pkg.*Slow.*.class], Unstable*</exclude>
+  [...]
++---+
+  This syntax can be used in parameters: test, includes, excludes, 
includesFile, excludesFile.
+  Exclamation mark (!) excludes tests. The syntax in excludes and excludesFile 
should not use (!). The character (?)
+  within non-regex pattern replaces one character in file name or path. The 
file extensions are not mandatory in
+  non-regex patterns, and packages with slash can be used. The regex validates 
fully qualified class file.
+  The regex supports '.class' file extension only. Note the regex comments, 
marked by (#) character, are unsupported.

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/maven-surefire-plugin/src/site/apt/examples/single-test.apt.vm
----------------------------------------------------------------------
diff --git a/maven-surefire-plugin/src/site/apt/examples/single-test.apt.vm 
b/maven-surefire-plugin/src/site/apt/examples/single-test.apt.vm
index d069ab7..2ee087e 100644
--- a/maven-surefire-plugin/src/site/apt/examples/single-test.apt.vm
+++ b/maven-surefire-plugin/src/site/apt/examples/single-test.apt.vm
@@ -103,7 +103,7 @@ mvn -Dit.test=ITCircle#test* verify
 +---+
 #{end}
 
-  As of Surefire 2.12.1, you can select multiple methods (JUnit 4.x only at 
this time; patches welcome!):
+  Since of ${thisPlugin} Plugin 2.19 you can select multiple methods (JUnit 4, 
JUnit 4.7+ and TestNG):
 
 #{if}(${project.artifactId}=="maven-surefire-plugin")
 +---+
@@ -114,3 +114,27 @@ mvn -Dtest=TestCircle#testOne+testTwo test
 mvn -Dit.test=ITCircle#testOne+testTwo verify
 +---+
 #{end}
+
+  Note this feature was available in JUnit 4 provider only since of 
${thisPlugin} Plugin 2.12.1.
+
+
+  As of ${thisPlugin} Plugin 2.19 multiple formats are supported in one 
pattern (JUnit 4, JUnit 4.7+, TestNG):
+
+#{if}(${project.artifactId}=="maven-surefire-plugin")
++---+
+mvn "-Dtest=???Test, !Unstable*, pkg/**/Ci*leTest.java, 
*Test#test*One+testTwo?????, #fast*+slowTest" test
+mvn "-Dtest=Basic*, !%regex[.*.Unstable.*], 
!%regex[.*.MyTest.class#one.*|two.*], %regex[#fast.*|slow.*]"
++---+
+#{else}
++---+
+mvn "-Dit.test=???IT, !Unstable*, pkg/**/Ci*leIT.java, 
*IT#test*One+testTwo?????, #fast*+slowTest" verify
+mvn "-Dit.test=Basic*, !%regex[.*.Unstable.*], 
!%regex[.*.MyIT.class#one.*|two.*], %regex[#fast.*|slow.*]" verify
++---+
+#{end}
+
+  The exclamation mark (!) excludes tests. The character (?) within non-regex 
pattern replaces one character in file
+  name or path. The file extensions are not mandatory in non-regex patterns, 
and packages with slash can be used.
+  The regex validates fully qualified class file, and validates test methods 
separately after (#) however class
+  is optional. The regex supports '.class' file extension only. Note the regex 
comments, marked by (#)
+  character, are unsupported. The Parameterized JUnit runner describes test 
methods using an index in brackets, so the
+  non-regex method pattern would become: <<<#testMethod[*]>>>.

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/surefire-api/src/main/java/org/apache/maven/surefire/providerapi/ProviderParameters.java
----------------------------------------------------------------------
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 06048e9..cc9e91c 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
@@ -89,6 +89,7 @@ public interface ProviderParameters
      * @return The parameters
      * @deprecated Use scanresult instead, as of version 2.12.2. Will be 
removed in next major version.
      */
+    @Deprecated
     DirectoryScannerParameters getDirectoryScannerParameters();
 
     /**

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/surefire-api/src/main/java/org/apache/maven/surefire/testset/GenericTestPattern.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/testset/GenericTestPattern.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/GenericTestPattern.java
new file mode 100644
index 0000000..585351c
--- /dev/null
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/GenericTestPattern.java
@@ -0,0 +1,54 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.Set;
+
+/**
+ * Resolves string test patterns in object oriented patterns {@code P}.
+ *
+ * @param <T>    itself
+ * @param <P>    resolved atomic test, object oriented - not necessary to be a 
string
+ * @param <C>    test class, or null if not mandatory
+ * @param <M>    test method, or null if not mandatory
+ */
+public interface GenericTestPattern<T extends GenericTestPattern, P, C, M>
+    extends TestFilter<C, M>
+{
+    boolean hasIncludedMethodPatterns();
+
+    boolean hasExcludedMethodPatterns();
+
+    boolean hasMethodPatterns();
+
+    T createMethodFilters();
+
+    T createClassFilters();
+
+    boolean isEmpty();
+
+    String getPluginParameterTest();
+
+    Set<P> getIncludedPatterns();
+
+    Set<P> getExcludedPatterns();
+
+    Set<String> getTestSpecificClasses();
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/surefire-api/src/main/java/org/apache/maven/surefire/testset/IncludedExcludedPatterns.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/testset/IncludedExcludedPatterns.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/IncludedExcludedPatterns.java
new file mode 100644
index 0000000..bc5c5d4
--- /dev/null
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/IncludedExcludedPatterns.java
@@ -0,0 +1,27 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.
+ */
+
+final class IncludedExcludedPatterns
+{
+    boolean hasExcludedMethodPatterns;
+
+    boolean hasIncludedMethodPatterns;
+}

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/surefire-api/src/main/java/org/apache/maven/surefire/testset/ResolvedTest.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/testset/ResolvedTest.java
 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/ResolvedTest.java
index 79f4174..635010e 100644
--- 
a/surefire-api/src/main/java/org/apache/maven/surefire/testset/ResolvedTest.java
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/ResolvedTest.java
@@ -19,47 +19,91 @@ package org.apache.maven.surefire.testset;
  * under the License.
  */
 
+import org.apache.maven.shared.utils.StringUtils;
+import org.apache.maven.shared.utils.io.MatchPatterns;
 import org.apache.maven.shared.utils.io.SelectorUtils;
 
-import java.util.Map;
+import java.io.File;
 
 /**
  * Single pattern test filter resolved from multi pattern filter 
-Dtest=MyTest#test,AnotherTest#otherTest.
  */
 public final class ResolvedTest
 {
+    /**
+     * Type of patterns in ResolvedTest constructor.
+     */
+    public static enum Type
+    {
+        CLASS, METHOD
+    }
+
     private static final String CLASS_FILE_EXTENSION = ".class";
 
     private static final String JAVA_FILE_EXTENSION = ".java";
 
+    private static final String WILDCARD_PATH_PREFIX = "**/";
+
+    private static final String WILDCARD_FILENAME_POSTFIX = ".*";
+
     private final String classPattern;
 
     private final String methodPattern;
 
-    private final Map<Class<?>, String> classConversion;
-
     private final boolean isRegexTestClassPattern;
 
     private final boolean isRegexTestMethodPattern;
 
+    private final String description;
+
     /**
      * '*' means zero or more characters<br>
      * '?' means one and only one character
-     * The %ant[] expression is substituted by %regex[].
+     * The pattern %regex[] prefix and suffix does not appear. The regex 
<code>pattern</code> is always
+     * unwrapped by the caller.
      *
      * @param classPattern     test class file pattern
      * @param methodPattern    test method
-     * @throws IllegalArgumentException if regex not finished with ']'
+     * @param isRegex          {@code true} if regex
+     */
+    public ResolvedTest( String classPattern, String methodPattern, boolean 
isRegex )
+    {
+        classPattern = tryBlank( classPattern );
+        methodPattern = tryBlank( methodPattern );
+        description = description( classPattern, methodPattern, isRegex );
+
+        if ( isRegex && classPattern != null )
+        {
+            classPattern = wrapRegex( classPattern );
+        }
+
+        if ( isRegex && methodPattern != null )
+        {
+            methodPattern = wrapRegex( methodPattern );
+        }
+
+        this.classPattern = reformatClassPattern( classPattern, isRegex );
+        this.methodPattern = methodPattern;
+        isRegexTestClassPattern = isRegex;
+        isRegexTestMethodPattern = isRegex;
+    }
+
+    /**
+     * The regex <code>pattern</code> is always unwrapped.
      */
-    ResolvedTest( String classPattern, String methodPattern, Map<Class<?>, 
String> classConversion )
+    public ResolvedTest( Type type, String pattern, boolean isRegex )
     {
-        this.classPattern = reformatPattern( classPattern, true );
-        this.methodPattern = reformatPattern( methodPattern, false );
-        this.classConversion = classConversion;
-        isRegexTestClassPattern =
-            this.classPattern != null && this.classPattern.startsWith( 
SelectorUtils.REGEX_HANDLER_PREFIX );
-        isRegexTestMethodPattern =
-            this.methodPattern != null && this.methodPattern.startsWith( 
SelectorUtils.REGEX_HANDLER_PREFIX );
+        pattern = tryBlank( pattern );
+        final boolean isClass = type == Type.CLASS;
+        description = description( isClass ? pattern : null, !isClass ? 
pattern : null, isRegex );
+        if ( isRegex && pattern != null )
+        {
+            pattern = wrapRegex( pattern );
+        }
+        classPattern = isClass ? reformatClassPattern( pattern, isRegex ) : 
null;
+        methodPattern = !isClass ? pattern : null;
+        isRegexTestClassPattern = isRegex && isClass;
+        isRegexTestMethodPattern = isRegex && !isClass;
     }
 
     /**
@@ -74,6 +118,11 @@ public final class ResolvedTest
         return classPattern;
     }
 
+    public boolean hasTestClassPattern()
+    {
+        return classPattern != null;
+    }
+
     /**
      * Test method, e.g. "realTestMethod".<br/>
      * Other examples: test* or testSomethin? or %regex[testOne|testTwo] or 
%ant[testOne|testTwo]<br/>
@@ -86,6 +135,11 @@ public final class ResolvedTest
         return methodPattern;
     }
 
+    public boolean hasTestMethodPattern()
+    {
+        return methodPattern != null;
+    }
+
     public boolean isRegexTestClassPattern()
     {
         return isRegexTestClassPattern;
@@ -101,42 +155,34 @@ public final class ResolvedTest
         return classPattern == null && methodPattern == null;
     }
 
-    public boolean shouldRun( Class<?> realTestClass, String methodName )
+    public boolean shouldRun( String testClassFile, String methodName )
     {
-        if ( methodPattern != null )
+        if ( isEmpty() )
+        {
+            return true;
+        }
+
+        boolean matchedMethodPattern = false;
+
+        if ( methodPattern != null && methodName != null )
         {
-            if ( methodName != null && !SelectorUtils.matchPath( 
methodPattern, methodName ) )
+            if ( SelectorUtils.matchPath( methodPattern, methodName ) )
+            {
+                matchedMethodPattern = true;
+            }
+            else
             {
                 return false;
             }
         }
 
-        if ( classPattern == null )
+        if ( classPattern != null )
         {
-            return methodName != null;
+            return isRegexTestClassPattern ? matchClassRegexPatter( 
testClassFile ) : matchClassPatter( testClassFile );
         }
         else
         {
-            String classFile = classFile( realTestClass );
-            if ( isRegexTestClassPattern() )
-            {
-                String pattern = classPattern.indexOf( '$' ) == -1 ? 
classPattern : classPattern.replace( "$", "\\$" );
-                return SelectorUtils.matchPath( pattern, classFile );
-            }
-            else
-            {
-                if ( SelectorUtils.matchPath( classPattern, classFile ) )
-                {
-                    // match class pattern with package
-                    return true;
-                }
-                else
-                {
-                    int indexOfSimpleName = classFile.lastIndexOf( '/' );
-                    return indexOfSimpleName != -1
-                        && SelectorUtils.matchPath( classPattern, 
classFile.substring( 1 + indexOfSimpleName ) );
-                }
-            }
+            return matchedMethodPattern;
         }
     }
 
@@ -156,7 +202,6 @@ public final class ResolvedTest
 
         return ( classPattern == null ? that.classPattern == null : 
classPattern.equals( that.classPattern ) )
             && ( methodPattern == null ? that.methodPattern == null : 
methodPattern.equals( that.methodPattern ) );
-
     }
 
     @Override
@@ -170,27 +215,59 @@ public final class ResolvedTest
     @Override
     public String toString()
     {
-        return "ResolvedTest{classPattern='" + classPattern + "', 
methodPattern='" + methodPattern + "'}";
+        return isEmpty() ? null : description;
+    }
+
+    private static String description( String clazz, String method, boolean 
isRegex )
+    {
+        String description;
+        if ( clazz == null && method == null )
+        {
+            description = null;
+        }
+        else if ( clazz == null )
+        {
+            description = "#" + method;
+        }
+        else if ( method == null )
+        {
+            description = clazz;
+        }
+        else
+        {
+            description = clazz + "#" + method;
+        }
+        return isRegex && description != null ? wrapRegex( description ) : 
description;
     }
 
-    private String classFile( Class<?> realTestClass )
+    private boolean matchClassPatter( String testClassFile )
     {
-        String classFile = classConversion.get( realTestClass );
-        if ( classFile == null )
+        //@todo We have to use File.separator only because the MatchPatterns 
is using it internally - cannot override.
+        String classPattern = this.classPattern;
+        if ( File.separatorChar != '/' )
         {
-            classFile = realTestClass.getName().replace( '.', '/' ) + 
CLASS_FILE_EXTENSION;
-            classConversion.put( realTestClass, classFile );
+            testClassFile = testClassFile.replace( '/', File.separatorChar );
+            classPattern = classPattern.replace( '/', File.separatorChar );
+        }
+
+        if ( classPattern.endsWith( WILDCARD_FILENAME_POSTFIX ) || 
classPattern.endsWith( CLASS_FILE_EXTENSION ) )
+        {
+            return MatchPatterns.from( classPattern ).matches( testClassFile, 
true );
+        }
+        else
+        {
+            String[] classPatterns = { classPattern + CLASS_FILE_EXTENSION, 
classPattern };
+            return MatchPatterns.from( classPatterns ).matches( testClassFile, 
true );
         }
-        return classFile;
     }
 
-    /**
-     * {@link Class#getSimpleName()} does not return this format with nested 
classes FirstClass$NestedTest.
-     */
-    private static String simpleClassFileName( String classFile )
+    private boolean matchClassRegexPatter( String testClassFile )
     {
-        int indexOfSimpleName = classFile.lastIndexOf( '/' );
-        return indexOfSimpleName == -1 ? classFile : classFile.substring( 1 + 
indexOfSimpleName );
+        if ( File.separatorChar != '/' )
+        {
+            testClassFile = testClassFile.replace( '/', File.separatorChar );
+        }
+        return MatchPatterns.from( classPattern ).matches( testClassFile, true 
);
     }
 
     private static String tryBlank( String s )
@@ -202,60 +279,42 @@ public final class ResolvedTest
         else
         {
             s = s.trim();
-            return s.length() == 0 ? null : s;
+            return StringUtils.isEmpty( s ) ? null : s;
         }
     }
 
-    private static String reformatPattern( String s, boolean isTestClass )
+    private static String reformatClassPattern( String s, boolean isRegex )
     {
-        s = tryBlank( s );
-        if ( s == null )
+        if ( s != null && !isRegex )
         {
-            return null;
-        }
-        else if ( s.startsWith( SelectorUtils.REGEX_HANDLER_PREFIX ) )
-        {
-            if ( !s.endsWith( SelectorUtils.PATTERN_HANDLER_SUFFIX ) )
-            {
-                throw new IllegalArgumentException( s + " enclosed regex does 
not finish with ']'" );
-            }
-        }
-        else if ( s.startsWith( SelectorUtils.ANT_HANDLER_PREFIX ) )
-        {
-            if ( s.endsWith( SelectorUtils.PATTERN_HANDLER_SUFFIX ) )
-            {
-                s = SelectorUtils.REGEX_HANDLER_PREFIX + s.substring( 
SelectorUtils.ANT_HANDLER_PREFIX.length() );
-            }
-            else
+            s = convertToPath( s );
+            if ( s != null && !s.startsWith( WILDCARD_PATH_PREFIX ) )
             {
-                throw new IllegalArgumentException( s + " enclosed regex does 
not finish with ']'" );
+                s = WILDCARD_PATH_PREFIX + s;
             }
         }
-        else if ( isTestClass )
-        {
-            s = convertToPath( s );
-        }
         return s;
     }
 
     private static String convertToPath( String className )
     {
-        if ( className == null || className.trim().length() == 0 )
+        if ( StringUtils.isBlank( className ) )
         {
-            return className;
+            return null;
         }
         else
         {
             if ( className.endsWith( JAVA_FILE_EXTENSION ) )
             {
                 className = className.substring( 0, className.length() - 
JAVA_FILE_EXTENSION.length() );
+                className += CLASS_FILE_EXTENSION;
             }
-            else if ( className.endsWith( CLASS_FILE_EXTENSION ) )
-            {
-                className = className.substring( 0, className.length() - 
CLASS_FILE_EXTENSION.length() );
-            }
-            className = className.replace( '.', '/' );
-            return className + CLASS_FILE_EXTENSION;
+            return className;
         }
     }
+
+    static String wrapRegex( String unwrapped )
+    {
+        return SelectorUtils.REGEX_HANDLER_PREFIX + unwrapped + 
SelectorUtils.PATTERN_HANDLER_SUFFIX;
+    }
 }

http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/866a535b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestFilter.java
----------------------------------------------------------------------
diff --git 
a/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestFilter.java 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestFilter.java
new file mode 100644
index 0000000..d0155cb
--- /dev/null
+++ 
b/surefire-api/src/main/java/org/apache/maven/surefire/testset/TestFilter.java
@@ -0,0 +1,31 @@
+package org.apache.maven.surefire.testset;
+
+/*
+ * 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.
+ */
+
+/**
+ * Generic interface of test filter.
+ *
+ * @param <C>    test class, or null if not mandatory
+ * @param <M>    test method, or null if not mandatory
+ */
+public interface TestFilter<C, M>
+{
+    boolean shouldRun( C testClass, M methodName );
+}

Reply via email to