From ee991a7dadfadd659a7091d11f26aa560035c5eb Mon Sep 17 00:00:00 2001 From: Kwiatek Szymon Date: Fri, 20 Mar 2026 13:12:52 +0100 Subject: [PATCH 1/6] Fix --exclude flag --- .../lib/src/commands/build_android.dart | 5 +- .../lib/src/commands/build_ios.dart | 5 +- .../lib/src/commands/build_macos.dart | 5 +- .../patrol_cli/lib/src/commands/test.dart | 5 +- packages/patrol_cli/lib/src/test_finder.dart | 71 +++++++++++-------- .../test/general/test_finder_test.dart | 30 ++++++++ 6 files changed, 83 insertions(+), 38 deletions(-) diff --git a/packages/patrol_cli/lib/src/commands/build_android.dart b/packages/patrol_cli/lib/src/commands/build_android.dart index 48c981ba12..98aadb8656 100644 --- a/packages/patrol_cli/lib/src/commands/build_android.dart +++ b/packages/patrol_cli/lib/src/commands/build_android.dart @@ -92,12 +92,13 @@ class BuildAndroidCommand extends PatrolCommand { } final testFinder = _testFinderFactory.create(testDirectory); + final excludes = stringsArg('exclude').toSet(); final target = stringsArg('target'); final targets = target.isNotEmpty - ? testFinder.findTests(target, testFileSuffix) + ? testFinder.findTests(target, testFileSuffix, excludes) : testFinder.findAllTests( - excludes: stringsArg('exclude').toSet(), + excludes: excludes, testFileSuffix: testFileSuffix, ); diff --git a/packages/patrol_cli/lib/src/commands/build_ios.dart b/packages/patrol_cli/lib/src/commands/build_ios.dart index b247e35229..e77bf6b2d2 100644 --- a/packages/patrol_cli/lib/src/commands/build_ios.dart +++ b/packages/patrol_cli/lib/src/commands/build_ios.dart @@ -94,12 +94,13 @@ class BuildIOSCommand extends PatrolCommand { } final testFinder = _testFinderFactory.create(testDirectory); + final excludes = stringsArg('exclude').toSet(); final target = stringsArg('target'); final targets = target.isNotEmpty - ? testFinder.findTests(target, testFileSuffix) + ? testFinder.findTests(target, testFileSuffix, excludes) : testFinder.findAllTests( - excludes: stringsArg('exclude').toSet(), + excludes: excludes, testFileSuffix: testFileSuffix, ); diff --git a/packages/patrol_cli/lib/src/commands/build_macos.dart b/packages/patrol_cli/lib/src/commands/build_macos.dart index 106df8d840..f4336f7b3f 100644 --- a/packages/patrol_cli/lib/src/commands/build_macos.dart +++ b/packages/patrol_cli/lib/src/commands/build_macos.dart @@ -89,12 +89,13 @@ class BuildMacOSCommand extends PatrolCommand { } final testFinder = _testFinderFactory.create(testDirectory); + final excludes = stringsArg('exclude').toSet(); final target = stringsArg('target'); final targets = target.isNotEmpty - ? testFinder.findTests(target, testFileSuffix) + ? testFinder.findTests(target, testFileSuffix, excludes) : testFinder.findAllTests( - excludes: stringsArg('exclude').toSet(), + excludes: excludes, testFileSuffix: testFileSuffix, ); diff --git a/packages/patrol_cli/lib/src/commands/test.dart b/packages/patrol_cli/lib/src/commands/test.dart index 58ecf093d2..b3f81d9ec6 100644 --- a/packages/patrol_cli/lib/src/commands/test.dart +++ b/packages/patrol_cli/lib/src/commands/test.dart @@ -105,12 +105,13 @@ class TestCommand extends PatrolCommand { final testFileSuffix = config.testFileSuffix; final testFinder = _testFinderFactory.create(testDirectory); + final excludes = stringsArg('exclude').toSet(); final target = stringsArg('target'); final targets = target.isNotEmpty - ? testFinder.findTests(target, testFileSuffix) + ? testFinder.findTests(target, testFileSuffix, excludes) : testFinder.findAllTests( - excludes: stringsArg('exclude').toSet(), + excludes: excludes, testFileSuffix: testFileSuffix, ); diff --git a/packages/patrol_cli/lib/src/test_finder.dart b/packages/patrol_cli/lib/src/test_finder.dart index 0daef0b3dc..f174b9b3f2 100644 --- a/packages/patrol_cli/lib/src/test_finder.dart +++ b/packages/patrol_cli/lib/src/test_finder.dart @@ -41,8 +41,10 @@ class TestFinder { List findTests( List targets, [ String testFileSuffix = _kDefaultTestFileSuffix, + Set excludes = const {}, ]) { final testFiles = []; + final absoluteExcludes = _toAbsoluteExcludes(excludes); for (final target in targets) { if (target.endsWith(testFileSuffix)) { @@ -50,10 +52,14 @@ class TestFinder { if (!isFile) { throwToolExit('target file $target does not exist'); } - testFiles.add(_fs.file(target).absolute.path); + final absoluteTargetPath = _fs.file(target).absolute.path; + if (!_isExcluded(absoluteTargetPath, absoluteExcludes)) { + testFiles.add(absoluteTargetPath); + } } else if (_fs.isDirectorySync(target)) { final foundTargets = findAllTests( - directory: _fs.directory(target), + directory: _fs.directory(target).absolute, + excludes: excludes, testFileSuffix: testFileSuffix, ); if (foundTargets.isEmpty) { @@ -86,16 +92,7 @@ class TestFinder { throwToolExit("Directory ${directory.path} doesn't exist"); } - final absoluteExcludes = excludes.map((exclude) { - if (_fs.path.isAbsolute(exclude)) { - return exclude; - } - // Resolve relative to rootDir (not the process CWD). - // _rootDir.path is absolute in production (findRootDirectory returns - // an absolute directory), so avoid calling .absolute which would - // re-resolve against CWD. - return _fs.path.join(_rootDir.path, exclude); - }).toSet(); + final absoluteExcludes = _toAbsoluteExcludes(excludes); return directory .listSync(recursive: true, followLinks: false) @@ -110,28 +107,42 @@ class TestFinder { .where((fileSystemEntity) { final filePath = fileSystemEntity.path; - for (final exclude in absoluteExcludes) { - // Check if the file exactly matches an excluded file - if (filePath == exclude) { - return false; - } - - // Check if the file is inside an excluded directory - // Need to add path separator to avoid matching prefixes - // e.g., "patrol_test/permissions" shouldn't match "patrol_test/permissions_other" - final excludeWithSeparator = exclude.endsWith(_fs.path.separator) - ? exclude - : exclude + _fs.path.separator; - if (filePath.startsWith(excludeWithSeparator)) { - return false; - } - } - - return true; + return !_isExcluded(filePath, absoluteExcludes); }) .map((entity) => entity.absolute.path) .toList(); } + + Set _toAbsoluteExcludes(Set excludes) { + return excludes.map((exclude) { + if (_fs.path.isAbsolute(exclude)) { + return exclude; + } + // Resolve relative to rootDir (not the process CWD). + // _rootDir.path is absolute in production (findRootDirectory returns + // an absolute directory), so avoid calling .absolute which would + // re-resolve against CWD. + return _fs.path.join(_rootDir.path, exclude); + }).toSet(); + } + + bool _isExcluded(String filePath, Set absoluteExcludes) { + for (final exclude in absoluteExcludes) { + // Check if the file exactly matches an excluded file. + if (filePath == exclude) { + return true; + } + + final excludeWithSeparator = exclude.endsWith(_fs.path.separator) + ? exclude + : exclude + _fs.path.separator; + if (filePath.startsWith(excludeWithSeparator)) { + return true; + } + } + + return false; + } } class TestFinderFactory { diff --git a/packages/patrol_cli/test/general/test_finder_test.dart b/packages/patrol_cli/test/general/test_finder_test.dart index 4abbea8986..23c2e7a9a2 100644 --- a/packages/patrol_cli/test/general/test_finder_test.dart +++ b/packages/patrol_cli/test/general/test_finder_test.dart @@ -178,6 +178,36 @@ void _test(Platform platform) { ); }); + test('applies excludes when target is a directory', () { + // given + final included = fs.path.join( + 'patrol_test', + 'permissions', + 'other_test.dart', + ); + final excluded = fs.path.join( + 'patrol_test', + 'permissions', + 'test', + 'excluded_test.dart', + ); + fs.file(included).createSync(recursive: true); + fs.file(excluded).createSync(recursive: true); + + // when + final found = testFinder.findTests( + [fs.path.join('patrol_test', 'permissions')], + '_test.dart', + {fs.path.join('patrol_test', 'permissions', 'test', '')}, + ); + + // then + expect( + found, + equals([fs.path.join(fs.currentDirectory.path, included)]), + ); + }); + test('finds tests when targets are files and directories', () { // given final testRoot = fs.directory('patrol_test')..createSync(); From 6fd9ec511cef0e14b8dd51b550805a76cc2aa7ba Mon Sep 17 00:00:00 2001 From: Kwiatek Szymon Date: Fri, 20 Mar 2026 13:20:31 +0100 Subject: [PATCH 2/6] Format file --- packages/patrol_cli/test/general/test_finder_test.dart | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/packages/patrol_cli/test/general/test_finder_test.dart b/packages/patrol_cli/test/general/test_finder_test.dart index 23c2e7a9a2..2eb18cfd00 100644 --- a/packages/patrol_cli/test/general/test_finder_test.dart +++ b/packages/patrol_cli/test/general/test_finder_test.dart @@ -202,10 +202,7 @@ void _test(Platform platform) { ); // then - expect( - found, - equals([fs.path.join(fs.currentDirectory.path, included)]), - ); + expect(found, equals([fs.path.join(fs.currentDirectory.path, included)])); }); test('finds tests when targets are files and directories', () { From d7475d2f8a779839ab3af2677efdf7f4cf1d49f7 Mon Sep 17 00:00:00 2001 From: Kwiatek Szymon Date: Fri, 20 Mar 2026 16:03:43 +0100 Subject: [PATCH 3/6] Add test for excluding files in test finder --- .../test/general/test_finder_test.dart | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/packages/patrol_cli/test/general/test_finder_test.dart b/packages/patrol_cli/test/general/test_finder_test.dart index 2eb18cfd00..31805fb919 100644 --- a/packages/patrol_cli/test/general/test_finder_test.dart +++ b/packages/patrol_cli/test/general/test_finder_test.dart @@ -205,6 +205,22 @@ void _test(Platform platform) { expect(found, equals([fs.path.join(fs.currentDirectory.path, included)])); }); + test('applies excludes when target is a file', () { + // given + final targetFile = fs.path.join('patrol_test', 'app_test.dart'); + fs.file(targetFile).createSync(recursive: true); + + // when + final found = testFinder.findTests( + [targetFile], + '_test.dart', + {targetFile}, + ); + + // then + expect(found, isEmpty); + }); + test('finds tests when targets are files and directories', () { // given final testRoot = fs.directory('patrol_test')..createSync(); From f4ce5664d61582f3af3b7e6efef3f761557285f7 Mon Sep 17 00:00:00 2001 From: Kwiatek Szymon Date: Mon, 23 Mar 2026 09:47:44 +0100 Subject: [PATCH 4/6] Update CHANGELOG --- packages/patrol_cli/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/patrol_cli/CHANGELOG.md b/packages/patrol_cli/CHANGELOG.md index a34359aa88..0432e1db5b 100644 --- a/packages/patrol_cli/CHANGELOG.md +++ b/packages/patrol_cli/CHANGELOG.md @@ -1,6 +1,7 @@ ## Unreleased - Reflect failed tests in Playwright report. (#2970) +- Fix `--exclude` not working. (#2990) ## 4.2.0 From 9f4513e79e3510157430ef2600b94f5c8c85da16 Mon Sep 17 00:00:00 2001 From: Kwiatek Szymon Date: Mon, 23 Mar 2026 09:47:53 +0100 Subject: [PATCH 5/6] Add documentation for exclude handling in TestFinder --- packages/patrol_cli/lib/src/test_finder.dart | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/packages/patrol_cli/lib/src/test_finder.dart b/packages/patrol_cli/lib/src/test_finder.dart index f174b9b3f2..61ce21c33c 100644 --- a/packages/patrol_cli/lib/src/test_finder.dart +++ b/packages/patrol_cli/lib/src/test_finder.dart @@ -113,19 +113,24 @@ class TestFinder { .toList(); } + /// Converts `excludes` to absolute paths. + /// + /// Pass file paths or directory paths in `excludes`, either absolute or + /// relative to [_rootDir]. Absolute paths are kept as-is, and relative paths + /// are resolved against [_rootDir] (not the process working directory). Set _toAbsoluteExcludes(Set excludes) { return excludes.map((exclude) { if (_fs.path.isAbsolute(exclude)) { return exclude; } - // Resolve relative to rootDir (not the process CWD). - // _rootDir.path is absolute in production (findRootDirectory returns - // an absolute directory), so avoid calling .absolute which would - // re-resolve against CWD. return _fs.path.join(_rootDir.path, exclude); }).toSet(); } + /// Returns `true` when [filePath] should be filtered out by exclusions. + /// + /// A file is excluded if it exactly matches an excluded path, or if it is + /// located under an excluded directory path. bool _isExcluded(String filePath, Set absoluteExcludes) { for (final exclude in absoluteExcludes) { // Check if the file exactly matches an excluded file. From 6a9190fd47c87c09c6f308a16f6a4da7bc558515 Mon Sep 17 00:00:00 2001 From: Kwiatek Szymon Date: Thu, 21 May 2026 16:27:08 +0200 Subject: [PATCH 6/6] Update CHANGELOG.md to include fix for `--exclude` flag not working --- packages/patrol_cli/CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/patrol_cli/CHANGELOG.md b/packages/patrol_cli/CHANGELOG.md index af58aaea89..94a1095cbf 100644 --- a/packages/patrol_cli/CHANGELOG.md +++ b/packages/patrol_cli/CHANGELOG.md @@ -1,3 +1,6 @@ +## Unreleased +- Fix `--exclude` not working. (#2990) + ## 4.4.0 - Fix iOS Simulator test crash on Xcode 26.4+ caused by missing platform frameworks path in xctestrun.