You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2022/03/02 20:25:47 UTC

[maven-surefire] 01/01: [SUREFIRE-1964] Support for method filtering on excludesFile and includesFile

This is an automated email from the ASF dual-hosted git repository.

tibordigana pushed a commit to branch inc-exc-files
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git

commit f2db6ba6e7c108fc16bf73dec316b00853477b4b
Author: Tibor Digaňa <ti...@apache.org>
AuthorDate: Wed Jan 12 23:50:57 2022 +0100

    [SUREFIRE-1964] Support for method filtering on excludesFile and includesFile
---
 .../plugin/surefire/AbstractSurefireMojo.java      | 120 ++++++++------
 .../plugin/surefire/AbstractSurefireMojoTest.java  | 178 +++++++++++++++++++--
 .../maven/plugin/surefire/MojoMocklessTest.java    |   6 +
 .../maven/plugin/surefire/SurefirePlugin.java      |  12 ++
 .../its/AbstractTestMultipleMethodPatterns.java    |   3 +-
 .../maven/surefire/its/jiras/Surefire1964IT.java   |  55 +++++++
 .../test/resources/surefire-1964/exclusions.txt    |   1 +
 .../test/resources/surefire-1964/inclusions.txt    |   1 +
 .../src/test/resources/surefire-1964/pom.xml       |  58 +++++++
 .../src/test/java/pkg/ExcludedTest.java            |  12 ++
 .../src/test/java/pkg/FilterTest.java              |  24 +++
 11 files changed, 412 insertions(+), 58 deletions(-)

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 ed946b6..6c38445 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
@@ -2194,78 +2194,110 @@ public abstract class AbstractSurefireMojo
         }
     }
 
-    private void maybeAppendList( List<String> base, List<String> list )
+    @Nonnull
+    private List<String> getExcludedScanList()
+        throws MojoFailureException
     {
-        if ( list != null )
-        {
-            base.addAll( list );
-        }
+        return getExcludeList( true );
     }
 
-    @Nonnull private List<String> getExcludeList()
+    @Nonnull
+    private List<String> getExcludeList()
+        throws MojoFailureException
+    {
+        return getExcludeList( false );
+    }
+
+    /**
+     * Computes a merge list of test exclusions.
+     * Used only in {@link #getExcludeList()} and {@link #getExcludedScanList()}.
+     * @param asScanList true if dependency or directory scanner
+     * @return list of patterns
+     * @throws MojoFailureException if the excludes breaks a pattern format
+     */
+    @Nonnull
+    private List<String> getExcludeList( boolean asScanList )
         throws MojoFailureException
     {
-        List<String> actualExcludes = null;
+        List<String> excludes;
         if ( isSpecificTestSpecified() )
         {
-            actualExcludes = Collections.emptyList();
+            excludes = Collections.emptyList();
         }
         else
         {
-            if ( getExcludesFile() != null )
+            excludes = new ArrayList<>();
+            if ( asScanList )
             {
-                actualExcludes = readListFromFile( getExcludesFile() );
+                if ( getExcludes() != null )
+                {
+                    excludes.addAll( getExcludes() );
+                }
+                checkMethodFilterInIncludesExcludes( excludes );
             }
 
-            if ( actualExcludes == null )
-            {
-                actualExcludes = getExcludes();
-            }
-            else
+            if ( getExcludesFile() != null )
             {
-                maybeAppendList( actualExcludes, getExcludes() );
+                excludes.addAll( readListFromFile( getExcludesFile() ) );
             }
 
-            checkMethodFilterInIncludesExcludes( actualExcludes );
-
-            if ( actualExcludes == null || actualExcludes.isEmpty() )
+            if ( asScanList && excludes.isEmpty() )
             {
-                actualExcludes = Collections.singletonList( getDefaultExcludes() );
+                excludes = Collections.singletonList( getDefaultExcludes() );
             }
         }
-        return filterNulls( actualExcludes );
+        return filterNulls( excludes );
+    }
+
+    @Nonnull
+    private List<String> getIncludedScanList()
+        throws MojoFailureException
+    {
+        return getIncludeList( true );
     }
 
+    @Nonnull
     private List<String> getIncludeList()
         throws MojoFailureException
     {
-        List<String> includes = null;
+        return getIncludeList( false );
+    }
+
+    /**
+     * Computes a merge list of test inclusions.
+     * Used only in {@link #getIncludeList()} and {@link #getIncludedScanList()}.
+     * @param asScanList true if dependency or directory scanner
+     * @return list of patterns
+     * @throws MojoFailureException if the includes breaks a pattern format
+     */
+    @Nonnull
+    private List<String> getIncludeList( boolean asScanList )
+        throws MojoFailureException
+    {
+        final List<String> includes = new ArrayList<>();
         if ( isSpecificTestSpecified() )
         {
-            includes = new ArrayList<>();
             addAll( includes, split( getTest(), "," ) );
         }
         else
         {
-            if ( getIncludesFile() != null )
+            if ( asScanList )
             {
-                includes = readListFromFile( getIncludesFile() );
+                if ( getIncludes() != null )
+                {
+                    includes.addAll( getIncludes() );
+                }
+                checkMethodFilterInIncludesExcludes( includes );
             }
 
-            if ( includes == null )
-            {
-                includes = getIncludes();
-            }
-            else
+            if ( getIncludesFile() != null )
             {
-                maybeAppendList( includes, getIncludes() );
+                includes.addAll( readListFromFile( getIncludesFile() ) );
             }
 
-            checkMethodFilterInIncludesExcludes( includes );
-
-            if ( includes == null || includes.isEmpty() )
+            if ( asScanList && includes.isEmpty() )
             {
-                includes = asList( getDefaultIncludes() );
+                addAll( includes, getDefaultIncludes() );
             }
         }
 
@@ -2275,16 +2307,12 @@ public abstract class AbstractSurefireMojo
     private void checkMethodFilterInIncludesExcludes( Iterable<String> patterns )
         throws MojoFailureException
     {
-        if ( patterns != null )
+        for ( String pattern : patterns )
         {
-            for ( String pattern : patterns )
+            if ( pattern != null && pattern.contains( "#" ) )
             {
-                if ( pattern != null && pattern.contains( "#" ) )
-                {
-                    throw new MojoFailureException( "Method filter prohibited in "
-                                                        + "includes|excludes|includesFile|excludesFile parameter: "
-                                                        + pattern );
-                }
+                throw new MojoFailureException( "Method filter prohibited in includes|excludes parameter: "
+                    + pattern );
             }
         }
     }
@@ -2294,16 +2322,18 @@ public abstract class AbstractSurefireMojo
     {
         if ( includedExcludedTests == null )
         {
-            includedExcludedTests = new TestListResolver( getIncludeList(), getExcludeList() );
+            includedExcludedTests = new TestListResolver( getIncludedScanList(), getExcludedScanList() );
+            getConsoleLogger().debug( "Resolved included and excluded patterns: " + includedExcludedTests );
         }
         return includedExcludedTests;
     }
 
     public TestListResolver getSpecificTests()
+        throws MojoFailureException
     {
         if ( specificTests == null )
         {
-            specificTests = new TestListResolver( getTest() );
+            specificTests = new TestListResolver( getIncludeList(), getExcludeList() );
         }
         return specificTests;
     }
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
index 4cce707..d8db23f 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/AbstractSurefireMojoTest.java
@@ -30,14 +30,17 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
 
+import org.apache.commons.io.FileUtils;
 import org.apache.maven.artifact.Artifact;
 import org.apache.maven.artifact.DefaultArtifact;
 import org.apache.maven.artifact.handler.ArtifactHandler;
+import org.apache.maven.artifact.handler.DefaultArtifactHandler;
 import org.apache.maven.artifact.repository.ArtifactRepository;
 import org.apache.maven.artifact.resolver.ArtifactResolutionRequest;
 import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
@@ -84,6 +87,7 @@ import org.powermock.core.classloader.annotations.PrepareForTest;
 import org.powermock.modules.junit4.PowerMockRunner;
 
 import static java.io.File.separatorChar;
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static java.nio.file.Files.write;
 import static java.util.Arrays.asList;
 import static java.util.Collections.emptyList;
@@ -95,8 +99,8 @@ import static org.apache.maven.artifact.versioning.VersionRange.createFromVersio
 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_9;
 import static org.apache.maven.surefire.shared.lang3.JavaVersion.JAVA_RECENT;
 import static org.apache.maven.surefire.shared.lang3.SystemUtils.IS_OS_WINDOWS;
-import static org.codehaus.plexus.languages.java.jpms.ModuleNameSource.MODULEDESCRIPTOR;
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.codehaus.plexus.languages.java.jpms.ModuleNameSource.MODULEDESCRIPTOR;
 import static org.junit.Assert.fail;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
@@ -1990,6 +1994,12 @@ public class AbstractSurefireMojoTest
         private int failOnFlakeCount;
         private String[] includeJUnit5Engines;
         private String[] excludeJUnit5Engines;
+        private List<Artifact> projectTestArtifacts;
+        private File includesFile;
+        private File excludesFile;
+        private List<String> includes;
+        private List<String> excludes;
+        private String test;
 
         private JUnitPlatformProviderInfo createJUnitPlatformProviderInfo( Artifact junitPlatformArtifact,
                                                                            TestClassPath testClasspathWrapper )
@@ -1997,6 +2007,17 @@ public class AbstractSurefireMojoTest
             return new JUnitPlatformProviderInfo( junitPlatformArtifact, testClasspathWrapper );
         }
 
+        void setProjectTestArtifacts( List<Artifact> projectTestArtifacts )
+        {
+            this.projectTestArtifacts = projectTestArtifacts;
+        }
+
+        @Override
+        List<Artifact> getProjectTestArtifacts()
+        {
+            return projectTestArtifacts;
+        }
+
         @Override
         protected void logDebugOrCliShowErrors( String s )
         {
@@ -2114,43 +2135,48 @@ public class AbstractSurefireMojoTest
         @Override
         public String getTest()
         {
-            return null;
+            return test;
         }
 
         @Override
         public void setTest( String test )
         {
-
+            this.test = test;
         }
 
         @Override
         public List<String> getIncludes()
         {
-            return null;
+            return includes;
         }
 
         @Override
-        public File getIncludesFile()
+        public void setIncludes( List<String> includes )
         {
-            return null;
+            this.includes = includes;
         }
 
-        @Override
-        public void setIncludes( List<String> includes )
+        void setIncludesFile( File includesFile )
         {
+            this.includesFile = includesFile;
+        }
 
+        @Override
+        public File getIncludesFile()
+        {
+            return includesFile;
         }
 
         @Override
         public List<String> getExcludes()
         {
-            return null;
+            return excludes;
         }
 
         @Override
         public void setExcludes( List<String> excludes )
         {
-
+            this.excludes = excludes;
         }
 
         @Override
@@ -2309,10 +2335,15 @@ public class AbstractSurefireMojoTest
             return null;
         }
 
+        void setExcludesFile( File excludesFile )
+        {
+            this.excludesFile = excludesFile;
+        }
+
         @Override
         public File getExcludesFile()
         {
-            return null;
+            return excludesFile;
         }
 
         @Override
@@ -2483,6 +2514,131 @@ public class AbstractSurefireMojoTest
         }
     }
 
+    @Test
+    public void shouldNotPerformMethodFilteringOnIncludes() throws Exception
+    {
+        Mojo plugin = new Mojo();
+
+        File includesExcludes = File.createTempFile( "surefire", "-includes" );
+        FileUtils.write( includesExcludes, "AnotherTest#method" , UTF_8 );
+        plugin.setIncludesFile( includesExcludes );
+
+        List<String> includes = new LinkedList<>();
+        includes.add( "AnotherTest#method " );
+        plugin.setIncludes( includes );
+
+        VersionRange version = VersionRange.createFromVersion( "1.0" );
+        ArtifactHandler handler = new DefaultArtifactHandler();
+        Artifact testDeps = new DefaultArtifact( "g", "a", version, "compile", "jar", null, handler );
+        File artifactFile = File.createTempFile( "surefire", ".jar" );
+        artifactFile.deleteOnExit();
+        testDeps.setFile( artifactFile );
+        plugin.setProjectTestArtifacts( singletonList( testDeps ) );
+        plugin.setDependenciesToScan( new String[] { "g:a" } );
+
+        try
+        {
+            plugin.scanDependencies();
+            fail( "MojoFailureException expected" );
+        }
+        catch ( MojoFailureException e )
+        {
+            assertThat( e.getLocalizedMessage() )
+                .isEqualTo( "Method filter prohibited in includes|excludes parameter: AnotherTest#method " );
+        }
+    }
+
+    @Test
+    public void shouldFilterTestsOnIncludesFile() throws Exception
+    {
+        Mojo plugin = new Mojo();
+
+        plugin.setLogger( mock( Logger.class ) );
+
+        File includes = File.createTempFile( "surefire", "-includes" );
+        FileUtils.write( includes, "AnotherTest#method" , UTF_8 );
+        plugin.setIncludesFile( includes );
+
+        VersionRange version = VersionRange.createFromVersion( "1.0" );
+        ArtifactHandler handler = new DefaultArtifactHandler();
+        Artifact testDeps = new DefaultArtifact( "g", "a", version, "compile", "test-jar", null, handler );
+        File artifactFile = File.createTempFile( "surefire", "-classes" );
+        String classDir = artifactFile.getCanonicalPath();
+        assertThat( artifactFile.delete() ).isTrue();
+        File classes = new File( classDir );
+        assertThat( classes.mkdir() ).isTrue();
+        testDeps.setFile( classes );
+        assertThat( new File( classes, "AnotherTest.class" ).createNewFile() )
+            .isTrue();
+        plugin.setProjectTestArtifacts( singletonList( testDeps ) );
+        plugin.setDependenciesToScan( new String[] { "g:a" } );
+
+        DefaultScanResult result = plugin.scanDependencies();
+        assertThat ( result.getClasses() ).hasSize( 1 );
+        assertThat ( result.getClasses().iterator().next() ).isEqualTo( "AnotherTest" );
+    }
+
+    @Test
+    public void shouldFilterTestsOnExcludesFile() throws Exception
+    {
+        Mojo plugin = new Mojo();
+
+        plugin.setLogger( mock( Logger.class ) );
+
+        File excludes = File.createTempFile( "surefire", "-excludes" );
+        FileUtils.write( excludes, "AnotherTest" , UTF_8 );
+        plugin.setExcludesFile( excludes );
+
+        VersionRange version = VersionRange.createFromVersion( "1.0" );
+        ArtifactHandler handler = new DefaultArtifactHandler();
+        Artifact testDeps = new DefaultArtifact( "g", "a", version, "compile", "test-jar", null, handler );
+        File artifactFile = File.createTempFile( "surefire", "-classes" );
+        String classDir = artifactFile.getCanonicalPath();
+        assertThat( artifactFile.delete() ).isTrue();
+        File classes = new File( classDir );
+        assertThat( classes.mkdir() ).isTrue();
+        testDeps.setFile( classes );
+        assertThat( new File( classes, "AnotherTest.class" ).createNewFile() )
+            .isTrue();
+        plugin.setProjectTestArtifacts( singletonList( testDeps ) );
+        plugin.setDependenciesToScan( new String[] { "g:a" } );
+
+        DefaultScanResult result = plugin.scanDependencies();
+        assertThat ( result.getClasses() )
+            .isEmpty();
+    }
+
+    @Test
+    public void shouldUseOnlySpecificTests() throws Exception
+    {
+        Mojo plugin = new Mojo();
+
+        plugin.setLogger( mock( Logger.class ) );
+
+        File includes = File.createTempFile( "surefire", "-includes" );
+        FileUtils.write( includes, "AnotherTest" , UTF_8 );
+        plugin.setIncludesFile( includes );
+        plugin.setTest( "DifferentTest" );
+
+        VersionRange version = VersionRange.createFromVersion( "1.0" );
+        ArtifactHandler handler = new DefaultArtifactHandler();
+        Artifact testDeps = new DefaultArtifact( "g", "a", version, "compile", "test-jar", null, handler );
+        File artifactFile = File.createTempFile( "surefire", "-classes" );
+        String classDir = artifactFile.getCanonicalPath();
+        assertThat( artifactFile.delete() ).isTrue();
+        File classes = new File( classDir );
+        assertThat( classes.mkdir() ).isTrue();
+        testDeps.setFile( classes );
+        assertThat( new File( classes, "AnotherTest.class" ).createNewFile() )
+            .isTrue();
+        plugin.setProjectTestArtifacts( singletonList( testDeps ) );
+        plugin.setDependenciesToScan( new String[] { "g:a" } );
+
+        DefaultScanResult result = plugin.scanDependencies();
+        assertThat ( result.getClasses() )
+            .isEmpty();
+    }
+
     private static File mockFile( String absolutePath )
     {
         File f = mock( File.class );
diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
index 1548dfd..6ad0416 100644
--- a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
+++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/MojoMocklessTest.java
@@ -32,6 +32,7 @@ import org.apache.maven.surefire.extensions.ForkNodeFactory;
 import org.apache.maven.surefire.api.suite.RunResult;
 import org.apache.maven.surefire.api.util.DefaultScanResult;
 import org.apache.maven.toolchain.Toolchain;
+import org.codehaus.plexus.logging.Logger;
 import org.junit.Test;
 
 import java.io.File;
@@ -44,6 +45,7 @@ import static java.util.Arrays.asList;
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
 import static org.powermock.reflect.Whitebox.invokeMethod;
 import static org.powermock.reflect.Whitebox.setInternalState;
 
@@ -243,6 +245,7 @@ public class MojoMocklessTest
         List<Artifact> projectTestArtifacts = singletonList( testDeps );
         String[] dependenciesToScan = { "g:a" };
         Mojo mojo = new Mojo( projectTestArtifacts, dependenciesToScan );
+        mojo.setLogger( mock( Logger.class ) );
         DefaultScanResult result = mojo.scanDependencies();
 
         assertThat( result )
@@ -275,6 +278,7 @@ public class MojoMocklessTest
         List<Artifact> projectTestArtifacts = singletonList( testDeps );
         String[] dependenciesToScan = { "g:a" };
         Mojo mojo = new Mojo( projectTestArtifacts, dependenciesToScan );
+        mojo.setLogger( mock( Logger.class ) );
         DefaultScanResult result = mojo.scanDependencies();
 
         assertThat( result )
@@ -306,6 +310,7 @@ public class MojoMocklessTest
         List<Artifact> projectTestArtifacts = singletonList( testDeps );
         String[] dependenciesToScan = { "g:a" };
         Mojo mojo = new Mojo( projectTestArtifacts, dependenciesToScan );
+        mojo.setLogger( mock( Logger.class ) );
         DefaultScanResult result = mojo.scanDependencies();
 
         assertThat( result )
@@ -352,6 +357,7 @@ public class MojoMocklessTest
         List<Artifact> projectTestArtifacts = asList( testDep1, testDep2 );
         String[] dependenciesToScan = { "g:a" };
         Mojo mojo = new Mojo( projectTestArtifacts, dependenciesToScan );
+        mojo.setLogger( mock( Logger.class ) );
         DefaultScanResult result = mojo.scanDependencies();
 
         assertThat( result )
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 240d2a5..2ce8caa 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
@@ -355,6 +355,12 @@ public class SurefirePlugin
      * **{@literal /}NotIncludedByDefault.java
      * %regex[.*Test.*|.*Not.*]
      * </code></pre>
+     * 
+     * Since 3.0.0-M6, method filtering support is provided in includes file. Example:
+     * <pre><code>
+     * foo.bar.SomeTest#test
+     * com.test.Test#testMethod
+     * </code></pre>
      *
      * @since 2.13
      */
@@ -370,6 +376,12 @@ public class SurefirePlugin
      * %regex[.*Test.*|.*Not.*]
      * </code></pre>
      *
+     * Since 3.0.0-M6, method filtering support is provided in excludes file. Example:
+     * <pre><code>
+     * foo.bar.SomeTest#test
+     * com.test.Test#testMethod
+     * </code></pre>
+     *
      * @since 2.13
      */
     @Parameter( property = "surefire.excludesFile" )
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/AbstractTestMultipleMethodPatterns.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/AbstractTestMultipleMethodPatterns.java
index 6c458f5..055c407 100644
--- a/surefire-its/src/test/java/org/apache/maven/surefire/its/AbstractTestMultipleMethodPatterns.java
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/AbstractTestMultipleMethodPatterns.java
@@ -483,7 +483,6 @@ public abstract class AbstractTestMultipleMethodPatterns
         prepare( pattern )
             .failNever()
             .executeTest()
-            .verifyTextInLog( "Method filter prohibited in includes|excludes|includesFile|excludesFile parameter: "
-                                  + pattern );
+            .verifyTextInLog( "Method filter prohibited in includes|excludes parameter: " + pattern );
     }
 }
diff --git a/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1964IT.java b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1964IT.java
new file mode 100644
index 0000000..62f60d2
--- /dev/null
+++ b/surefire-its/src/test/java/org/apache/maven/surefire/its/jiras/Surefire1964IT.java
@@ -0,0 +1,55 @@
+package org.apache.maven.surefire.its.jiras;
+
+/*
+ * 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.its.fixture.SurefireJUnit4IntegrationTestCase;
+import org.junit.Test;
+
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.is;
+
+/**
+ *
+ */
+public class Surefire1964IT extends SurefireJUnit4IntegrationTestCase
+{
+    @Test
+    public void shouldFilterTests() throws Exception
+    {
+        unpack( "surefire-1964" )
+            .executeTest()
+            .assertTestSuiteResults( 1 )
+            .assertThatLogLine( containsString( "executed testXYZ" ), is( 1 ) )
+            .assertThatLogLine( containsString( "executed testABC" ), is( 0 ) )
+            .assertThatLogLine( containsString( "executed dontRun" ), is( 0 ) );
+    }
+
+    @Test
+    public void shouldFilterTestsInPluginProcess() throws Exception
+    {
+        unpack( "surefire-1964" )
+            .forkCount( 0 )
+            .executeTest()
+            .assertTestSuiteResults( 1 )
+            .assertThatLogLine( containsString( "executed testXYZ" ), is( 1 ) )
+            .assertThatLogLine( containsString( "executed testABC" ), is( 0 ) )
+            .assertThatLogLine( containsString( "executed dontRun" ), is( 0 ) );
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1964/exclusions.txt b/surefire-its/src/test/resources/surefire-1964/exclusions.txt
new file mode 100644
index 0000000..5bacad2
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1964/exclusions.txt
@@ -0,0 +1 @@
+FilterTest#testABC
\ No newline at end of file
diff --git a/surefire-its/src/test/resources/surefire-1964/inclusions.txt b/surefire-its/src/test/resources/surefire-1964/inclusions.txt
new file mode 100644
index 0000000..50077e4
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1964/inclusions.txt
@@ -0,0 +1 @@
+FilterTest#test*
\ No newline at end of file
diff --git a/surefire-its/src/test/resources/surefire-1964/pom.xml b/surefire-its/src/test/resources/surefire-1964/pom.xml
new file mode 100644
index 0000000..8f3ba29
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1964/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ 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.
+  -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+
+    <groupId>org.example</groupId>
+    <artifactId>maven-surefire-excludefiles</artifactId>
+    <version>1.0-SNAPSHOT</version>
+
+    <properties>
+        <maven.compiler.source>${java.specification.version}</maven.compiler.source>
+        <maven.compiler.target>${java.specification.version}</maven.compiler.target>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>4.13.2</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <version>${surefire.version}</version>
+                <configuration>
+                    <includesFile>inclusions.txt</includesFile>
+                    <excludesFile>exclusions.txt</excludesFile>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>
diff --git a/surefire-its/src/test/resources/surefire-1964/src/test/java/pkg/ExcludedTest.java b/surefire-its/src/test/resources/surefire-1964/src/test/java/pkg/ExcludedTest.java
new file mode 100644
index 0000000..b871231
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1964/src/test/java/pkg/ExcludedTest.java
@@ -0,0 +1,12 @@
+package pkg;
+
+import org.junit.Test;
+
+public class ExcludedTest
+{
+    @Test
+    public void dontRun()
+    {
+        System.out.println( "executed dontRun" );
+    }
+}
diff --git a/surefire-its/src/test/resources/surefire-1964/src/test/java/pkg/FilterTest.java b/surefire-its/src/test/resources/surefire-1964/src/test/java/pkg/FilterTest.java
new file mode 100644
index 0000000..d93eca9
--- /dev/null
+++ b/surefire-its/src/test/resources/surefire-1964/src/test/java/pkg/FilterTest.java
@@ -0,0 +1,24 @@
+package pkg;
+
+import org.junit.Test;
+
+public class FilterTest
+{
+    @Test
+    public void testABC()
+    {
+        System.out.println( "executed testABC" );
+    }
+
+    @Test
+    public void dontRun()
+    {
+        System.out.println( "executed dontRun" );
+    }
+
+    @Test
+    public void testXYZ()
+    {
+        System.out.println( "executed testXYZ" );
+    }
+}