diff --git a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java index 0f3d1a3c1259..bc90337c64ec 100644 --- a/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java +++ b/impl/maven-impl/src/main/java/org/apache/maven/impl/PathSelector.java @@ -213,13 +213,6 @@ final class PathSelector implements PathMatcher { */ private final Path baseDirectory; - /** - * Whether paths must be relativized before being given to a matcher. If {@code true}, then every paths - * will be made relative to {@link #baseDirectory} for allowing patterns like {@code "foo/bar/*.java"} - * to work. As a slight optimization, we can skip this step if all patterns start with {@code "**"}. - */ - private final boolean needRelativize; - /** * Creates a new selector from the given includes and excludes. * @@ -240,7 +233,6 @@ private PathSelector( FileSystem fileSystem = baseDirectory.getFileSystem(); this.includes = matchers(fileSystem, includePatterns); this.excludes = matchers(fileSystem, excludePatterns); - needRelativize = needRelativize(includePatterns) || needRelativize(excludePatterns); } /** @@ -472,21 +464,6 @@ private static String[] normalizePatterns(final Collection patterns, fin return normalized.toArray(String[]::new); } - /** - * Returns {@code true} if at least one pattern requires path being relativized before to be matched. - * - * @param patterns include or exclude patterns - * @return whether at least one pattern require relativization - */ - private static boolean needRelativize(String[] patterns) { - for (String pattern : patterns) { - if (!pattern.startsWith(DEFAULT_SYNTAX + WILDCARD_FOR_ANY_PREFIX)) { - return true; - } - } - return false; - } - /** * Creates the path matchers for the given patterns. * The syntax (usually {@value #DEFAULT_SYNTAX}) must be specified for each pattern. @@ -504,16 +481,8 @@ private static PathMatcher[] matchers(final FileSystem fs, final String[] patter */ @SuppressWarnings("checkstyle:MissingSwitchDefault") private PathMatcher simplify() { - if (excludes.length == 0) { - switch (includes.length) { - case 0: - return INCLUDES_ALL; - case 1: - if (needRelativize) { - break; - } - return includes[0]; - } + if (excludes.length == 0 && includes.length == 0) { + return INCLUDES_ALL; } return this; } @@ -527,9 +496,7 @@ private PathMatcher simplify() { */ @Override public boolean matches(Path path) { - if (needRelativize) { - path = baseDirectory.relativize(path); - } + path = baseDirectory.relativize(path); return (includes.length == 0 || isMatched(path, includes)) && (excludes.length == 0 || !isMatched(path, excludes)); } @@ -648,9 +615,7 @@ public boolean matches(Path directory) { if (baseDirectory.equals(directory)) { return true; } - if (needRelativize) { - directory = baseDirectory.relativize(directory); - } + directory = baseDirectory.relativize(directory); if (isMatched(directory, dirExcludes)) { return false; } diff --git a/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java b/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java index f541ee5e40ab..9e0f2e5078b9 100644 --- a/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java +++ b/impl/maven-impl/src/test/java/org/apache/maven/impl/PathSelectorTest.java @@ -159,4 +159,42 @@ public void testBraceAlternationOnlyWithExplicitGlob(@TempDir Path directory) th assertTrue(explicitGlob.matches(mainFile)); assertTrue(explicitGlob.matches(testFile)); } + + /** + * Regression test for #12617. + * Verifies that when the base directory path itself contains a segment matching a {@code **} pattern, + * files that are not actually under a matching child directory are not falsely included. + * + *

The removed optimization skipped path relativization when all patterns started with {@code "**"}, + * which caused the full (non-relativized) path to be matched against the pattern. If the base directory + * happened to contain the matching segment (e.g. base = {@code "something/target/test-classes"} with + * pattern "**/test-classes/**"), every file under the base directory would be + * falsely matched because the base directory itself satisfied the pattern.

+ */ + @Test + public void testNoFalsePositiveWhenBaseDirectoryMatchesPattern(@TempDir Path tempDir) throws IOException { + // Base directory path contains "test-classes" as a segment — this is the trigger for the false positive + Path baseDir = Files.createDirectories(tempDir.resolve("something/target/test-classes")); + + // A child directory that also happens to be named "test-classes" + Path testClassesChild = Files.createDirectories(baseDir.resolve("test-classes")); + // Another child directory with a different name + Path otherChild = Files.createDirectories(baseDir.resolve("other")); + + Path fileInTestClasses = Files.createFile(testClassesChild.resolve("MyTest.class")); + Path fileInOther = Files.createFile(otherChild.resolve("Other.class")); + + // Include pattern: anything under a "test-classes" directory + PathMatcher matcher = PathSelector.of(baseDir, List.of("**/test-classes/**"), null, false); + + // Should match: file is inside the test-classes/ child directory (relative: test-classes/MyTest.class) + assertTrue(matcher.matches(fileInTestClasses), "File inside test-classes/ child directory should be matched"); + + // Should NOT match: file is inside other/, not test-classes/. + // Before the fix, the non-relativized path "something/target/test-classes/other/Other.class" + // would satisfy "**/test-classes/**" because "test-classes" appears in the base directory path. + assertFalse( + matcher.matches(fileInOther), + "File in other/ should not be matched just because the base directory path contains 'test-classes'"); + } }