This is an automated email from the ASF dual-hosted git repository.
olamy pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
The following commit(s) were added to refs/heads/master by this push:
new 99212a2c6 Allow method filters in excludes (#3561)
99212a2c6 is described below
commit 99212a2c62d8e0a1bb56b8a867bed12d0b714055
Author: Olivier Lamy <[email protected]>
AuthorDate: Fri Oct 30 19:51:03 2026 +1000
Allow method filters in excludes (#3561)
---
.../maven/plugin/failsafe/IntegrationTestMojo.java | 15 +++++-
.../plugin/surefire/AbstractSurefireMojo.java | 32 +++++++++---
.../plugin/surefire/AbstractSurefireMojoTest.java | 59 +++++++++++++++++++++-
.../apache/maven/plugin/surefire/SurefireMojo.java | 15 +++++-
.../markdown/examples/inclusion-exclusion.md.vm | 19 +++++++
.../its/AbstractTestMultipleMethodPatterns.java | 5 +-
.../surefire/its/IncludesExcludesJunit5IT.java | 8 +++
.../resources/includes-excludes-junit-5/pom.xml | 19 +++++++
8 files changed, 160 insertions(+), 12 deletions(-)
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 c6df87d78..925eb28ec 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
@@ -100,7 +100,7 @@ public class IntegrationTestMojo extends
AbstractSurefireMojo {
* <code>**{@literal /}${it.test}.java</code>, so you can just type {@code
-Dit.test=MyIT} to run
* a single test file called "foo/MyIT.java". The test patterns prefixed
with a <em>!</em> will be excluded.
* <br>
- * This parameter overrides the parameter {@code includes} and {@code
excludes}.
+ * This parameter overrides {@code includes}, {@code excludes}, {@code
includesFile} and {@code excludesFile}.
* <br>
* Since 2.7.3 You can execute a limited number of methods in the test
with adding <i>#myMethod</i> or
* <i>#my*ethod</i>. E.g. type {@code -Dit.test=MyIT#myMethod}
<b>supported for junit 4.x and TestNg.</b>
@@ -248,6 +248,9 @@ public class IntegrationTestMojo extends
AbstractSurefireMojo {
* {@literal <include>}%regex[.*[Cat|Dog].*], !%regex[pkg.*Slow.*.class],
pkg{@literal /}**{@literal /}*Fast*.java{@literal </include>}
* </code></pre>
* <br>
+ * Method filters like <i>pkg{@literal /}SomeIT#testMethod</i> are not
allowed here, use {@code includesFile}
+ * or {@code test} instead.
+ * <br>
* <br>
* <b>Notice that</b> 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} which defaults
@@ -275,6 +278,12 @@ public class IntegrationTestMojo extends
AbstractSurefireMojo {
* {@literal <exclude>}%regex[pkg.*Slow.*.class], Unstable*{@literal
</exclude>}
* </code></pre>
* <br>
+ * Since 3.6.2, you can also exclude test methods, with the same syntax as
the {@code test} parameter. The rest
+ * of the class still runs:
+ * <pre><code>
+ * {@literal <exclude>}pkg{@literal /}SomeIT#testMethod, **{@literal
/}*IT#testSlow*{@literal </exclude>}
+ * </code></pre>
+ * <br>
* <b>Notice that</b> 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} which defaults
* to the POM property <code>${project.build.testOutputDirectory}</code>,
typically
@@ -410,6 +419,8 @@ public class IntegrationTestMojo extends
AbstractSurefireMojo {
* <pre><code>
* pkg.SomeIT#testMethod
* </code></pre>
+ * <br>
+ * This file is ignored when the {@code test} parameter is set.
*
* @since 2.13
*/
@@ -430,6 +441,8 @@ public class IntegrationTestMojo extends
AbstractSurefireMojo {
* <pre><code>
* pkg.SomeIT#testMethod
* </code></pre>
+ * <br>
+ * This file is ignored when the {@code test} parameter is set.
*
* @since 2.13
*/
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 ac51631ea..2c13eff1f 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
@@ -2290,11 +2290,13 @@ private List<String> getExcludeList(boolean asScanList)
throws MojoFailureExcept
excludes = Collections.emptyList();
} else {
excludes = new ArrayList<>();
- if (asScanList) {
- if (getExcludes() != null) {
+ if (getExcludes() != null) {
+ if (asScanList) {
excludes.addAll(getExcludes());
+ } else {
+ // excluded classes are already dropped by the scan, only
method excludes matter here
+ excludes.addAll(getMethodFilters(getExcludes()));
}
- checkMethodFilterInIncludesExcludes(excludes);
}
if (getExcludesFile() != null) {
@@ -2336,7 +2338,7 @@ private List<String> getIncludeList(boolean asScanList)
throws MojoFailureExcept
if (getIncludes() != null) {
includes.addAll(getIncludes());
}
- checkMethodFilterInIncludesExcludes(includes);
+ checkMethodFilterInIncludes(includes);
}
if (getIncludesFile() != null) {
@@ -2351,12 +2353,30 @@ private List<String> getIncludeList(boolean asScanList)
throws MojoFailureExcept
return filterNulls(includes);
}
- private void checkMethodFilterInIncludesExcludes(Iterable<String>
patterns) throws MojoFailureException {
+ private void checkMethodFilterInIncludes(Iterable<String> patterns) throws
MojoFailureException {
for (String pattern : patterns) {
if (pattern != null && pattern.contains("#")) {
- throw new MojoFailureException("Method filter prohibited in
includes|excludes parameter: " + pattern);
+ throw new MojoFailureException("Method filter prohibited in
includes parameter: " + pattern);
+ }
+ }
+ }
+
+ /**
+ * Keeps only the patterns naming a method, like {@code
pkg/SomeTest#testMethod}.
+ */
+ @Nonnull
+ private static List<String> getMethodFilters(List<String> patterns) {
+ List<String> methodFilters = new ArrayList<>();
+ for (String pattern : patterns) {
+ if (pattern != null) {
+ for (String item : split(pattern, ",")) {
+ if (item.contains("#")) {
+ methodFilters.add(item.trim());
+ }
+ }
}
}
+ return methodFilters;
}
private TestListResolver getIncludedAndExcludedTests() throws
MojoFailureException {
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 5c4c32971..a5d0c3cf9 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
@@ -56,6 +56,7 @@
import org.apache.maven.project.MavenProject;
import org.apache.maven.surefire.api.suite.RunResult;
import org.apache.maven.surefire.api.testset.RunOrderParameters;
+import org.apache.maven.surefire.api.testset.TestListResolver;
import org.apache.maven.surefire.api.util.DefaultScanResult;
import org.apache.maven.surefire.api.util.RunOrder;
import org.apache.maven.surefire.api.util.SureFireFileManager;
@@ -2459,8 +2460,7 @@ public void shouldNotPerformMethodFilteringOnIncludes()
throws Exception {
plugin.setDependenciesToScan(new String[] {"g:a"});
Exception e = assertThrows(Exception.class, plugin::scanDependencies);
- assertThat(e.getMessage())
- .isEqualTo("Method filter prohibited in includes|excludes
parameter: AnotherTest#method ");
+ assertThat(e.getMessage()).isEqualTo("Method filter prohibited in
includes parameter: AnotherTest#method ");
}
@Test
@@ -2543,6 +2543,61 @@ public void shouldFilterTestsOnExcludes() throws
Exception {
assertThat(result.getClasses()).isEmpty();
}
+ @Test
+ public void shouldFilterTestMethodsOnExcludes() throws Exception {
+ Mojo plugin = new Mojo();
+
+ plugin.setLogger(mock(Logger.class));
+
+ plugin.setExcludes(singletonList("AnotherTest#method"));
+
+ VersionRange version = VersionRange.createFromVersion("1.0");
+ ArtifactHandler handler = new DefaultArtifactHandler();
+ Artifact testDeps = new DefaultArtifact("g", "a", version, "compile",
"test-jar", null, handler);
+ File artifactFile = SureFireFileManager.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"});
+
+ // the class is kept, only its method is skipped
+ DefaultScanResult result = plugin.scanDependencies();
+ assertThat(result.getClasses()).containsExactly("AnotherTest");
+
+ TestListResolver specificTests = plugin.getSpecificTests();
+ assertThat(specificTests.shouldRun("AnotherTest.class",
"method")).isFalse();
+ assertThat(specificTests.shouldRun("AnotherTest.class",
"otherMethod")).isTrue();
+ }
+
+ @Test
+ public void shouldPassOnlyMethodExcludesToMethodFilter() throws Exception {
+ Mojo plugin = new Mojo();
+
+ plugin.setExcludes(asList("**/SlowTest.java", "pkg/AnotherTest#method,
**/UnstableTest.java", null));
+
+ TestListResolver specificTests = plugin.getSpecificTests();
+ assertThat(specificTests.getIncludedPatterns()).isEmpty();
+ assertThat(specificTests.getExcludedPatterns()).hasSize(1);
+ assertThat(specificTests.shouldRun("pkg/AnotherTest.class",
"method")).isFalse();
+ assertThat(specificTests.shouldRun("pkg/SlowTest.class",
"method")).isTrue();
+ }
+
+ @Test
+ public void shouldIgnoreMethodExcludesWhenTestIsSet() throws Exception {
+ Mojo plugin = new Mojo();
+
+ plugin.setExcludes(singletonList("AnotherTest#method"));
+ plugin.setTest("AnotherTest");
+
+ TestListResolver specificTests = plugin.getSpecificTests();
+ assertThat(specificTests.getExcludedPatterns()).isEmpty();
+ assertThat(specificTests.shouldRun("AnotherTest.class",
"method")).isTrue();
+ }
+
@Test
public void shouldUseOnlySpecificTests() throws Exception {
Mojo plugin = new Mojo();
diff --git
a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefireMojo.java
b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefireMojo.java
index 78d4fcb36..6d50f7f05 100644
---
a/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefireMojo.java
+++
b/maven-surefire-plugin/src/main/java/org/apache/maven/plugin/surefire/SurefireMojo.java
@@ -72,7 +72,7 @@ public class SurefireMojo extends AbstractSurefireMojo
implements SurefireReport
* <code>**{@literal /}${test}.java</code>, so you can just type {@code
-Dtest=MyTest} to run a single test called
* "foo/MyTest.java". The test patterns prefixed with a <em>!</em> will be
excluded.
* <br>
- * This parameter overrides the parameter {@code includes}, {@code
excludes}.
+ * This parameter overrides {@code includes}, {@code excludes}, {@code
includesFile} and {@code excludesFile}.
* <br>
* Since 2.7.3, you can execute a limited number of methods in the test by
adding <i>#myMethod</i> or
* <i>#my*ethod</i>. For example, {@code -Dtest=MyTest#myMethod}. This is
supported for junit 4.x and TestNg.<br>
@@ -202,6 +202,9 @@ public class SurefireMojo extends AbstractSurefireMojo
implements SurefireReport
* {@literal <include>}%regex[.*[Cat|Dog].*], !%regex[pkg.*Slow.*.class],
pkg{@literal /}**{@literal /}*Fast*.java{@literal </include>}
* </code></pre>
* <br>
+ * Method filters like <i>pkg{@literal /}SomeTest#testMethod</i> are not
allowed here, use {@code includesFile}
+ * or {@code test} instead.
+ * <br>
* <br>
* <b>Notice that</b> 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} which defaults
@@ -229,6 +232,12 @@ public class SurefireMojo extends AbstractSurefireMojo
implements SurefireReport
* {@literal <exclude>}%regex[pkg.*Slow.*.class], Unstable*{@literal
</exclude>}
* </code></pre>
* <br>
+ * Since 3.6.2, you can also exclude test methods, with the same syntax as
the {@code test} parameter. The rest
+ * of the class still runs:
+ * <pre><code>
+ * {@literal <exclude>}pkg{@literal /}SomeTest#testMethod, **{@literal
/}*Test#testSlow*{@literal </exclude>}
+ * </code></pre>
+ * <br>
* <b>Notice that</b> 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} which defaults
* to the POM property <code>${project.build.testOutputDirectory}</code>,
typically
@@ -373,6 +382,8 @@ public class SurefireMojo extends AbstractSurefireMojo
implements SurefireReport
* <pre><code>
* pkg.SomeTest#testMethod
* </code></pre>
+ * <br>
+ * This file is ignored when the {@code test} parameter is set.
*
* @since 2.13
*/
@@ -392,6 +403,8 @@ public class SurefireMojo extends AbstractSurefireMojo
implements SurefireReport
* <pre><code>
* pkg.SomeTest#testMethod
* </code></pre>
+ * <br>
+ * This file is ignored when the {@code test} parameter is set.
*
* @since 2.13
*/
diff --git
a/maven-surefire-plugin/src/site/markdown/examples/inclusion-exclusion.md.vm
b/maven-surefire-plugin/src/site/markdown/examples/inclusion-exclusion.md.vm
index 8007b11a1..cb7e2fd42 100644
--- a/maven-surefire-plugin/src/site/markdown/examples/inclusion-exclusion.md.vm
+++ b/maven-surefire-plugin/src/site/markdown/examples/inclusion-exclusion.md.vm
@@ -159,6 +159,25 @@ As of ${thisPlugin} Plugin 2.19.1, the syntax with fully
qualified class names o
The character (?) replaces single character and (*) represents zero or more
characters. Multiple formats can be additionally combined. This syntax can be
used in parameters: `test`, `includes`, `excludes`, `includesFile`,
`excludesFile`.
+Excluding test methods
+----------------------
+
+As of ${thisPlugin} Plugin 3.6.2, you can also exclude test methods, with the
same syntax as the `test` parameter (see [Running a Single
Test](single-test.html)):
+
+```xml
+ [...]
+#{if}(${project.artifactId}=="maven-surefire-plugin")
+ <exclude>pkg/SomeTest#testMethod, **/*Test#testSlow*</exclude>
+#{else}
+ <exclude>pkg/SomeIT#testMethod, **/*IT#testSlow*</exclude>
+#{end}
+ [...]
+```
+
+Only the matching methods are skipped, the rest of the class still runs.
`includesFile` and `excludesFile` have accepted methods since 3.0.0-M6, but
`includes` does not and fails the build if you try.
+
+When `test` is set, `includes`, `excludes`, `includesFile` and `excludesFile`
are all ignored.
+
Tests from dependencies
-----------------------
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 0a8b9382b..c6c5208ed 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
@@ -397,7 +397,8 @@ public void shouldNotRunExcludedMethods() {
@Test
public void shouldNotRunExcludedClassesAndMethods() {
- assumeTrue(getSettings().getConfiguration() == TEST);
+ // in includes/excludes mode the ! patterns go to <excludes>
+ assumeTrue(getSettings().getConfiguration() == TEST ||
getSettings().getConfiguration() == INCLUDES_EXCLUDES);
prepare("!#*Fail*, !TestFour#testSuccessTwo")
.executeTest()
.verifyErrorFree(11)
@@ -414,6 +415,6 @@ public void negativeTest() {
prepare(pattern)
.failNever()
.executeTest()
- .verifyTextInLog("Method filter prohibited in
includes|excludes parameter: " + pattern);
+ .verifyTextInLog("Method filter prohibited in includes
parameter: " + pattern);
}
}
diff --git
a/surefire-its/src/test/java/org/apache/maven/surefire/its/IncludesExcludesJunit5IT.java
b/surefire-its/src/test/java/org/apache/maven/surefire/its/IncludesExcludesJunit5IT.java
index 837da130b..7e2be9de5 100644
---
a/surefire-its/src/test/java/org/apache/maven/surefire/its/IncludesExcludesJunit5IT.java
+++
b/surefire-its/src/test/java/org/apache/maven/surefire/its/IncludesExcludesJunit5IT.java
@@ -51,6 +51,14 @@ public void testPathBasedIncludesExcludes() {
testWithProfile("path");
}
+ /**
+ * Excludes the failing method of DontRunTest instead of the whole class.
+ */
+ @Test
+ public void testMethodExcludes() {
+ testWithProfile("method");
+ }
+
private void testWithProfile(String profile) {
final OutputValidator outputValidator =
unpack().activateProfile(profile).executeTest().verifyErrorFree(2);
diff --git a/surefire-its/src/test/resources/includes-excludes-junit-5/pom.xml
b/surefire-its/src/test/resources/includes-excludes-junit-5/pom.xml
index a20ed25ef..9d9a1c995 100644
--- a/surefire-its/src/test/resources/includes-excludes-junit-5/pom.xml
+++ b/surefire-its/src/test/resources/includes-excludes-junit-5/pom.xml
@@ -110,6 +110,25 @@ under the License.
</plugin>
</plugins>
</build>
+ </profile>
+ <profile>
+ <id>method</id>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>**/DontRunTest#testRun</exclude>
+ </excludes>
+ <includes>
+ <include>**/NotIncludedByDefault.java</include>
+ <include>**/*Test.java</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</profile>
<profile>
<id>withXmlFile</id>