Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand All @@ -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);
}

/**
Expand Down Expand Up @@ -472,21 +464,6 @@ private static String[] normalizePatterns(final Collection<String> 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.
Expand All @@ -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;
}
Expand All @@ -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));
}
Expand Down Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,42 @@ public void testBraceAlternationOnlyWithExplicitGlob(@TempDir Path directory) th
assertTrue(explicitGlob.matches(mainFile));
assertTrue(explicitGlob.matches(testFile));
}

/**
* Regression test for <a href="https://github.com/apache/maven/pull/12617">#12617</a>.
* 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.
*
* <p>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 <code>"**&sol;test-classes&sol;**"</code>), every file under the base directory would be
* falsely matched because the base directory itself satisfied the pattern.</p>
*/
@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'");
}
}
Loading