From 69a7c1d337b22b8330d780a7fdd2810999ef0087 Mon Sep 17 00:00:00 2001 From: Abdullah <89297042+AzazelSensei@users.noreply.github.com> Date: Thu, 13 Aug 2026 12:24:30 +0300 Subject: [PATCH 1/4] fix: resolve dart coverage package_config in pub workspaces Pub workspaces only write .dart_tool/package_config.json at the workspace root. Pass the package directory as packagePath so the coverage resolver walks up the same way dart does. --- lib/src/cli/test_cli_runner.dart | 12 +++--- test/src/cli/test_cli_runner_test.dart | 53 ++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 7 deletions(-) diff --git a/lib/src/cli/test_cli_runner.dart b/lib/src/cli/test_cli_runner.dart index 3d6c42c06..82522a38a 100644 --- a/lib/src/cli/test_cli_runner.dart +++ b/lib/src/cli/test_cli_runner.dart @@ -198,19 +198,17 @@ class TestCLIRunner { p.join(cwd, 'coverage'), ); - final packagesPath = p.join( - '.dart_tool', - 'package_config.json', - ); + // Resolve package_config.json the way dart does: start at + // the package cwd and walk up. In a pub workspace the file + // lives at the workspace root, not in the member package. final hitmap = await coverage.HitMap.parseFiles( files, - packagePath: packagesPath, + packagePath: cwd, checkIgnoredLines: checkIgnore, ); final resolver = await coverage.Resolver.create( - packagesPath: packagesPath, - packagePath: packagesPath, + packagePath: cwd, ); final output = hitmap.formatLcov( diff --git a/test/src/cli/test_cli_runner_test.dart b/test/src/cli/test_cli_runner_test.dart index 0ad1e1744..3aec9001f 100644 --- a/test/src/cli/test_cli_runner_test.dart +++ b/test/src/cli/test_cli_runner_test.dart @@ -778,6 +778,59 @@ void main() { expect(testRunnerArgs, equals(['--coverage=coverage'])); }); + test( + 'resolves dart coverage package_config from the pub workspace root', + () async { + final workspaceRoot = Directory.systemTemp.createTempSync(); + addTearDown(() => workspaceRoot.deleteSync(recursive: true)); + + final member = Directory( + p.join(workspaceRoot.path, 'packages', 'foo'), + )..createSync(recursive: true); + File(p.join(member.path, 'pubspec.yaml')).createSync(); + Directory(p.join(member.path, 'test')).createSync(); + + // Pub workspaces only write package_config.json at the root. + File(p.join(workspaceRoot.path, '.dart_tool', 'package_config.json')) + ..createSync(recursive: true) + ..writeAsStringSync('{"configVersion":2,"packages":[]}'); + + final lcovFile = File(p.join(member.path, 'coverage', 'lcov.info')); + + final originalCwd = Directory.current; + addTearDown(() => Directory.current = originalCwd); + Directory.current = member; + + await expectLater( + TestCLIRunner.test( + testType: TestRunType.dart, + cwd: member.path, + collectCoverage: true, + stdout: stdoutLogs.add, + stderr: stderrLogs.add, + overrideTestRunner: testRunner( + Stream.fromIterable([ + const DoneTestEvent(success: true, time: 0), + const ExitTestEvent(exitCode: 0, time: 0), + ]), + onStart: () { + expect(lcovFile.existsSync(), isFalse); + lcovFile.createSync(recursive: true); + }, + ), + logger: logger, + ), + completion(equals([ExitCode.success.code])), + ); + expect( + File( + p.join(member.path, '.dart_tool', 'package_config.json'), + ).existsSync(), + isFalse, + ); + }, + ); + test('runs dart tests w/coverage and checkIgnore', () async { final tempDirectory = Directory.systemTemp.createTempSync(); addTearDown(() => tempDirectory.deleteSync(recursive: true)); From f818cf7cd952afcee7df3694f272ec9ee0c59369 Mon Sep 17 00:00:00 2001 From: Abdullah <89297042+AzazelSensei@users.noreply.github.com> Date: Mon, 24 Aug 2026 06:26:39 +0000 Subject: [PATCH 2/4] test: assert workspace coverage resolves package URIs Seed a real package_config entry, source file, and coverage JSON so the workspace test proves package:foo/foo.dart maps to lib/foo.dart. --- test/src/cli/test_cli_runner_test.dart | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/test/src/cli/test_cli_runner_test.dart b/test/src/cli/test_cli_runner_test.dart index 3aec9001f..d4d16353e 100644 --- a/test/src/cli/test_cli_runner_test.dart +++ b/test/src/cli/test_cli_runner_test.dart @@ -789,11 +789,24 @@ void main() { )..createSync(recursive: true); File(p.join(member.path, 'pubspec.yaml')).createSync(); Directory(p.join(member.path, 'test')).createSync(); + File(p.join(member.path, 'lib', 'foo.dart')) + ..createSync(recursive: true) + ..writeAsStringSync('void foo() {}'); // Pub workspaces only write package_config.json at the root. File(p.join(workspaceRoot.path, '.dart_tool', 'package_config.json')) ..createSync(recursive: true) - ..writeAsStringSync('{"configVersion":2,"packages":[]}'); + ..writeAsStringSync( + '{"configVersion":2,"packages":[' + '{"name":"foo","rootUri":"../packages/foo","packageUri":"lib/"}' + ']}', + ); + + File(p.join(member.path, 'coverage', 'coverage.json')) + ..createSync(recursive: true) + ..writeAsStringSync( + '{"coverage":[{"source":"package:foo/foo.dart","hits":[1,1]}]}', + ); final lcovFile = File(p.join(member.path, 'coverage', 'lcov.info')); @@ -813,10 +826,6 @@ void main() { const DoneTestEvent(success: true, time: 0), const ExitTestEvent(exitCode: 0, time: 0), ]), - onStart: () { - expect(lcovFile.existsSync(), isFalse); - lcovFile.createSync(recursive: true); - }, ), logger: logger, ), @@ -828,6 +837,8 @@ void main() { ).existsSync(), isFalse, ); + expect(lcovFile.existsSync(), isTrue); + expect(lcovFile.readAsStringSync(), contains('SF:lib/foo.dart')); }, ); From 95fefb23bcc2f3056c72610cbccebac37153c712 Mon Sep 17 00:00:00 2001 From: Abdullah <89297042+AzazelSensei@users.noreply.github.com> Date: Mon, 24 Aug 2026 12:10:56 +0000 Subject: [PATCH 3/4] fix: resolve dart coverage reportOn against the real package path Windows junctions made package:coverage filter out every lib file, so workspace coverage wrote an empty lcov.info. --- lib/src/cli/test_cli_runner.dart | 33 ++++++++++++++++++-------- test/src/cli/test_cli_runner_test.dart | 8 +++++-- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/lib/src/cli/test_cli_runner.dart b/lib/src/cli/test_cli_runner.dart index 82522a38a..3793377d0 100644 --- a/lib/src/cli/test_cli_runner.dart +++ b/lib/src/cli/test_cli_runner.dart @@ -2,12 +2,13 @@ part of 'cli.dart'; /// Type definition for the [flutterTest]/[dartTest] command /// from 'package:very_good_test_runner`. -typedef VeryGoodTestRunner = Stream Function({ - List? arguments, - String? workingDirectory, - Map? environment, - bool runInShell, -}); +typedef VeryGoodTestRunner = + Stream Function({ + List? arguments, + String? workingDirectory, + Map? environment, + bool runInShell, + }); /// Which test runner to use for running tests. enum TestRunType { @@ -201,20 +202,32 @@ class TestCLIRunner { // Resolve package_config.json the way dart does: start at // the package cwd and walk up. In a pub workspace the file // lives at the workspace root, not in the member package. + // + // Canonicalize cwd first. On Windows the temp/workspace + // directory is often a junction, and package:coverage + // resolves files through that real path. reportOn: ['lib'] + // is canonicalized against the unresolved cwd, so the + // prefix check drops every file and lcov comes out empty. + final resolvedCwd = Directory(cwd).resolveSymbolicLinksSync(); + final resolvedReportOn = [ + for (final path in reportOn ?? ['lib']) + p.isAbsolute(path) ? path : p.join(resolvedCwd, path), + ]; + final hitmap = await coverage.HitMap.parseFiles( files, - packagePath: cwd, + packagePath: resolvedCwd, checkIgnoredLines: checkIgnore, ); final resolver = await coverage.Resolver.create( - packagePath: cwd, + packagePath: resolvedCwd, ); final output = hitmap.formatLcov( resolver, - reportOn: reportOn ?? ['lib'], - basePath: cwd, + reportOn: resolvedReportOn, + basePath: resolvedCwd, ); // Write the lcov output to the file. diff --git a/test/src/cli/test_cli_runner_test.dart b/test/src/cli/test_cli_runner_test.dart index d4d16353e..4bfb6bcbe 100644 --- a/test/src/cli/test_cli_runner_test.dart +++ b/test/src/cli/test_cli_runner_test.dart @@ -838,7 +838,10 @@ void main() { isFalse, ); expect(lcovFile.existsSync(), isTrue); - expect(lcovFile.readAsStringSync(), contains('SF:lib/foo.dart')); + expect( + lcovFile.readAsStringSync(), + contains('SF:${p.join('lib', 'foo.dart')}'), + ); }, ); @@ -1267,7 +1270,8 @@ void main() { TestStartEvent( test: Test( id: 0, - name: 'app/view/app_test.dart CounterCubit emits [1] when increment is called', + name: + 'app/view/app_test.dart CounterCubit emits [1] when increment is called', suiteID: 4, groupIDs: [10, 99], metadata: TestMetadata(skip: false), From 0b671ec00106fe1e8f00b771b3d2647aae753e55 Mon Sep 17 00:00:00 2001 From: Abdullah <89297042+AzazelSensei@users.noreply.github.com> Date: Mon, 24 Aug 2026 13:36:41 +0000 Subject: [PATCH 4/4] style: dart format coverage runner files --- lib/src/cli/test_cli_runner.dart | 13 ++++++------- test/src/cli/test_cli_runner_test.dart | 3 +-- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/lib/src/cli/test_cli_runner.dart b/lib/src/cli/test_cli_runner.dart index 3793377d0..4ab89c72c 100644 --- a/lib/src/cli/test_cli_runner.dart +++ b/lib/src/cli/test_cli_runner.dart @@ -2,13 +2,12 @@ part of 'cli.dart'; /// Type definition for the [flutterTest]/[dartTest] command /// from 'package:very_good_test_runner`. -typedef VeryGoodTestRunner = - Stream Function({ - List? arguments, - String? workingDirectory, - Map? environment, - bool runInShell, - }); +typedef VeryGoodTestRunner = Stream Function({ + List? arguments, + String? workingDirectory, + Map? environment, + bool runInShell, +}); /// Which test runner to use for running tests. enum TestRunType { diff --git a/test/src/cli/test_cli_runner_test.dart b/test/src/cli/test_cli_runner_test.dart index 4bfb6bcbe..8c163bedc 100644 --- a/test/src/cli/test_cli_runner_test.dart +++ b/test/src/cli/test_cli_runner_test.dart @@ -1270,8 +1270,7 @@ void main() { TestStartEvent( test: Test( id: 0, - name: - 'app/view/app_test.dart CounterCubit emits [1] when increment is called', + name: 'app/view/app_test.dart CounterCubit emits [1] when increment is called', suiteID: 4, groupIDs: [10, 99], metadata: TestMetadata(skip: false),