From 9ea25d3970112f195d0bc8bb610908199623bfa3 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Mon, 7 Jul 2025 12:31:19 +1000 Subject: [PATCH 01/19] Filter coverage report to the current package --- pkgs/test_core/lib/src/runner/vm/platform.dart | 4 ++-- pkgs/test_core/lib/src/util/package_config.dart | 5 +++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkgs/test_core/lib/src/runner/vm/platform.dart b/pkgs/test_core/lib/src/runner/vm/platform.dart index f2586abfb..3ba4f6d5e 100644 --- a/pkgs/test_core/lib/src/runner/vm/platform.dart +++ b/pkgs/test_core/lib/src/runner/vm/platform.dart @@ -346,8 +346,8 @@ stderr: ${processResult.stderr}'''); Future> _gatherCoverage(Environment environment) async { final isolateId = Uri.parse(environment.observatoryUrl!.fragment) .queryParameters['isolateId']; - return await collect(environment.observatoryUrl!, false, false, false, {}, - isolateIds: {isolateId!}); + return await collect(environment.observatoryUrl!, false, false, false, + {await currentPackageName}, isolateIds: {isolateId!}); } Uri _wsUriFor(Uri observatoryUrl) => diff --git a/pkgs/test_core/lib/src/util/package_config.dart b/pkgs/test_core/lib/src/util/package_config.dart index c33dda628..1ed52ae58 100644 --- a/pkgs/test_core/lib/src/util/package_config.dart +++ b/pkgs/test_core/lib/src/util/package_config.dart @@ -37,3 +37,8 @@ Future absoluteUri(String path) async { return absoluteUri; } } + +/// Returns the name of the current package. +final Future currentPackageName = () async { + return (await currentPackageConfig).packageOf(await packageConfigUri)!.name; +}(); From 9b3ef12a50fc45c5fa537d01ac110173d82b5f44 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Mon, 7 Jul 2025 13:14:20 +1000 Subject: [PATCH 02/19] Flags and plumbing --- .../lib/src/runner/configuration.dart | 49 +++++++++++++++++-- .../lib/src/runner/configuration/args.dart | 24 ++++++++- .../test_core/lib/src/runner/vm/platform.dart | 16 ++++-- 3 files changed, 80 insertions(+), 9 deletions(-) diff --git a/pkgs/test_core/lib/src/runner/configuration.dart b/pkgs/test_core/lib/src/runner/configuration.dart index 8ba4aa810..e6b5420fb 100644 --- a/pkgs/test_core/lib/src/runner/configuration.dart +++ b/pkgs/test_core/lib/src/runner/configuration.dart @@ -58,11 +58,24 @@ class Configuration { final bool? _pauseAfterLoad; /// Whether to run browsers in their respective debug modes - bool get debug => pauseAfterLoad || (_debug ?? false) || coverage != null; + bool get debug => + pauseAfterLoad || + (_debug ?? false) || + _coverage != null || + coverageLcov != null; final bool? _debug; - /// The output folder for coverage gathering - final String? coverage; + /// The output folder for coverage gathering. + String? get coverage => + _coverage ?? (coverageLcov != null ? p.dirname(coverageLcov!) : null); + final String? _coverage; + + /// The lcov file to output coverage to. + final String? coverageLcov; + + /// Whether to collect branch coverage info. + bool get branchCoverage => _branchCoverage ?? false; + final bool? _branchCoverage; /// The path to the file from which to load more configuration information. /// @@ -257,6 +270,8 @@ class Configuration { required String? reporter, required Map? fileReporters, required String? coverage, + required String? coverageLcov, + required bool? branchCoverage, required int? concurrency, required int? shardIndex, required int? totalShards, @@ -309,6 +324,8 @@ class Configuration { reporter: reporter, fileReporters: fileReporters, coverage: coverage, + coverageLcov: coverageLcov, + branchCoverage: branchCoverage, concurrency: concurrency, shardIndex: shardIndex, totalShards: totalShards, @@ -366,6 +383,8 @@ class Configuration { String? reporter, Map? fileReporters, String? coverage, + String? coverageLcov, + bool? branchCoverage, int? concurrency, int? shardIndex, int? totalShards, @@ -417,6 +436,8 @@ class Configuration { reporter: reporter, fileReporters: fileReporters, coverage: coverage, + coverageLcov: coverageLcov, + branchCoverage: branchCoverage, concurrency: concurrency, shardIndex: shardIndex, totalShards: totalShards, @@ -485,6 +506,8 @@ class Configuration { reporter: null, fileReporters: null, coverage: null, + coverageLcov: null, + branchCoverage: null, concurrency: null, shardIndex: null, totalShards: null, @@ -550,6 +573,8 @@ class Configuration { reporter: null, fileReporters: null, coverage: null, + coverageLcov: null, + branchCoverage: null, concurrency: null, shardIndex: null, totalShards: null, @@ -617,6 +642,8 @@ class Configuration { color: null, configurationPath: null, coverage: null, + coverageLcov: null, + branchCoverage: null, shardIndex: null, totalShards: null, testSelections: null, @@ -678,6 +705,8 @@ class Configuration { reporter: null, fileReporters: null, coverage: null, + coverageLcov: null, + branchCoverage: null, concurrency: null, shardIndex: null, totalShards: null, @@ -739,7 +768,9 @@ class Configuration { required String? configurationPath, required String? reporter, required Map? fileReporters, - required this.coverage, + required String? coverage, + required this.coverageLcov, + required bool? branchCoverage, required int? concurrency, required this.shardIndex, required this.totalShards, @@ -766,6 +797,8 @@ class Configuration { _configurationPath = configurationPath, _reporter = reporter, fileReporters = fileReporters ?? {}, + _coverage = coverage, + _branchCoverage = branchCoverage, _concurrency = concurrency, _testSelections = testSelections == null || testSelections.isEmpty ? null @@ -823,6 +856,8 @@ class Configuration { reporter: null, fileReporters: null, coverage: null, + coverageLcov: null, + branchCoverage: null, concurrency: null, shardIndex: null, totalShards: null, @@ -921,6 +956,8 @@ class Configuration { reporter: other._reporter ?? _reporter, fileReporters: mergeMaps(fileReporters, other.fileReporters), coverage: other.coverage ?? coverage, + coverageLcov: other.coverageLcov ?? coverageLcov, + branchCoverage: other.branchCoverage ?? branchCoverage, concurrency: other._concurrency ?? _concurrency, shardIndex: other.shardIndex ?? shardIndex, totalShards: other.totalShards ?? totalShards, @@ -969,6 +1006,8 @@ class Configuration { String? reporter, Map? fileReporters, String? coverage, + String? coverageLcov, + bool? branchCoverage, int? concurrency, int? shardIndex, int? totalShards, @@ -1016,6 +1055,8 @@ class Configuration { reporter: reporter ?? _reporter, fileReporters: fileReporters ?? this.fileReporters, coverage: coverage ?? this.coverage, + coverageLcov: coverageLcov ?? this.coverageLcov, + branchCoverage: branchCoverage ?? this.branchCoverage, concurrency: concurrency ?? _concurrency, shardIndex: shardIndex ?? this.shardIndex, totalShards: totalShards ?? this.totalShards, diff --git a/pkgs/test_core/lib/src/runner/configuration/args.dart b/pkgs/test_core/lib/src/runner/configuration/args.dart index e981bedc3..d0792d2b1 100644 --- a/pkgs/test_core/lib/src/runner/configuration/args.dart +++ b/pkgs/test_core/lib/src/runner/configuration/args.dart @@ -113,6 +113,14 @@ final ArgParser _parser = (() { help: 'Gather coverage and output it to the specified directory.\n' 'Implies --debug.', valueHelp: 'directory'); + parser.addOption('coverage-lcov', + help: 'Gather coverage and output an lcov report to the specified file.\n' + 'Implies --debug.', + valueHelp: 'file'); + parser.addFlag('branch-coverage', + help: 'Include branch coverage information in the coverage report.\n' + 'Must be paired with --coverage or --coverage-lcov.', + negatable: false); parser.addFlag('chain-stack-traces', help: 'Use chained stack traces to provide greater exception details\n' 'especially for asynchronous code. It may be useful to disable\n' @@ -314,6 +322,18 @@ class _Parser { 'open an issue at https://github.com/dart-lang/test/issues/new.'); } + final coverageDir = _ifParsed('coverage'); + final coverageLcov = _ifParsed('coverage-lcov'); + final branchCoverage = _ifParsed('branch-coverage') ?? false; + if (coverageDir == null && coverageLcov == null && branchCoverage) { + throw ArgumentError( + 'If you set --branch-coverage you must set either --coverage or ' + '--coverage-lcov'); + } + if (coverageLcov != null && coverageDir != null) { + throw ArgumentError('Cannot set both --coverage and --coverage-lcov'); + } + return Configuration( help: _ifParsed('help'), version: _ifParsed('version'), @@ -328,7 +348,9 @@ class _Parser { precompiledPath: _ifParsed('precompiled'), reporter: reporter, fileReporters: _parseFileReporterOption(), - coverage: _ifParsed('coverage'), + coverage: coverageDir, + coverageLcov: coverageLcov, + branchCoverage: branchCoverage, concurrency: _parseOption('concurrency', int.parse), shardIndex: shardIndex, totalShards: totalShards, diff --git a/pkgs/test_core/lib/src/runner/vm/platform.dart b/pkgs/test_core/lib/src/runner/vm/platform.dart index 3ba4f6d5e..d5fdea255 100644 --- a/pkgs/test_core/lib/src/runner/vm/platform.dart +++ b/pkgs/test_core/lib/src/runner/vm/platform.dart @@ -137,7 +137,7 @@ class VMPlatform extends PlatformPlugin { var controller = deserializeSuite( path, platform, suiteConfig, environment, channel.cast(), message, - gatherCoverage: () => _gatherCoverage(environment!)); + gatherCoverage: () => _gatherCoverage(environment!, _config)); if (isolateRef != null) { await client!.streamListen('Debug'); @@ -343,11 +343,19 @@ stderr: ${processResult.stderr}'''); } } -Future> _gatherCoverage(Environment environment) async { +Future> _gatherCoverage( + Environment environment, Configuration config) async { final isolateId = Uri.parse(environment.observatoryUrl!.fragment) .queryParameters['isolateId']; - return await collect(environment.observatoryUrl!, false, false, false, - {await currentPackageName}, isolateIds: {isolateId!}); + return await collect( + environment.observatoryUrl!, + false, + false, + false, + {await currentPackageName}, + isolateIds: {isolateId!}, + branchCoverage: config.branchCoverage, + ); } Uri _wsUriFor(Uri observatoryUrl) => From 5696bbf5cf889e2d1a8f9b2a6cb05819b54d1029 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 13:07:19 +1000 Subject: [PATCH 03/19] Output lcov --- pkgs/test/pubspec.yaml | 2 +- pkgs/test_core/lib/src/runner.dart | 1 + .../lib/src/runner/configuration.dart | 13 +++---- .../lib/src/runner/configuration/args.dart | 6 +-- pkgs/test_core/lib/src/runner/coverage.dart | 35 +++++++++++++---- .../lib/src/runner/coverage_stub.dart | 11 +++++- pkgs/test_core/lib/src/runner/engine.dart | 38 +++++++++++++++---- .../test_core/lib/src/runner/vm/platform.dart | 2 +- .../lib/src/util/package_config.dart | 6 +-- pkgs/test_core/pubspec.yaml | 2 +- 10 files changed, 82 insertions(+), 34 deletions(-) diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml index a62ae0e2f..1441152f0 100644 --- a/pkgs/test/pubspec.yaml +++ b/pkgs/test/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: async: ^2.5.0 boolean_selector: ^2.1.0 collection: ^1.15.0 - coverage: ^1.0.1 + coverage: ^1.15.0 http_multi_server: ^3.0.0 io: ^1.0.0 js: '>=0.6.4 <0.8.0' diff --git a/pkgs/test_core/lib/src/runner.dart b/pkgs/test_core/lib/src/runner.dart index af8e9c96d..51aa1d139 100644 --- a/pkgs/test_core/lib/src/runner.dart +++ b/pkgs/test_core/lib/src/runner.dart @@ -77,6 +77,7 @@ class Runner { var engine = Engine( concurrency: config.concurrency, coverage: config.coverage, + coverageLcov: config.coverageLcov, testRandomizeOrderingSeed: config.testRandomizeOrderingSeed, stopOnFirstFailure: config.stopOnFirstFailure, ); diff --git a/pkgs/test_core/lib/src/runner/configuration.dart b/pkgs/test_core/lib/src/runner/configuration.dart index e6b5420fb..ebc302f9b 100644 --- a/pkgs/test_core/lib/src/runner/configuration.dart +++ b/pkgs/test_core/lib/src/runner/configuration.dart @@ -61,14 +61,12 @@ class Configuration { bool get debug => pauseAfterLoad || (_debug ?? false) || - _coverage != null || + coverage != null || coverageLcov != null; final bool? _debug; /// The output folder for coverage gathering. - String? get coverage => - _coverage ?? (coverageLcov != null ? p.dirname(coverageLcov!) : null); - final String? _coverage; + final String? coverage; /// The lcov file to output coverage to. final String? coverageLcov; @@ -768,7 +766,7 @@ class Configuration { required String? configurationPath, required String? reporter, required Map? fileReporters, - required String? coverage, + required this.coverage, required this.coverageLcov, required bool? branchCoverage, required int? concurrency, @@ -797,7 +795,6 @@ class Configuration { _configurationPath = configurationPath, _reporter = reporter, fileReporters = fileReporters ?? {}, - _coverage = coverage, _branchCoverage = branchCoverage, _concurrency = concurrency, _testSelections = testSelections == null || testSelections.isEmpty @@ -957,7 +954,7 @@ class Configuration { fileReporters: mergeMaps(fileReporters, other.fileReporters), coverage: other.coverage ?? coverage, coverageLcov: other.coverageLcov ?? coverageLcov, - branchCoverage: other.branchCoverage ?? branchCoverage, + branchCoverage: other._branchCoverage ?? _branchCoverage, concurrency: other._concurrency ?? _concurrency, shardIndex: other.shardIndex ?? shardIndex, totalShards: other.totalShards ?? totalShards, @@ -1056,7 +1053,7 @@ class Configuration { fileReporters: fileReporters ?? this.fileReporters, coverage: coverage ?? this.coverage, coverageLcov: coverageLcov ?? this.coverageLcov, - branchCoverage: branchCoverage ?? this.branchCoverage, + branchCoverage: branchCoverage ?? _branchCoverage, concurrency: concurrency ?? _concurrency, shardIndex: shardIndex ?? this.shardIndex, totalShards: totalShards ?? this.totalShards, diff --git a/pkgs/test_core/lib/src/runner/configuration/args.dart b/pkgs/test_core/lib/src/runner/configuration/args.dart index d0792d2b1..b94d37687 100644 --- a/pkgs/test_core/lib/src/runner/configuration/args.dart +++ b/pkgs/test_core/lib/src/runner/configuration/args.dart @@ -322,9 +322,9 @@ class _Parser { 'open an issue at https://github.com/dart-lang/test/issues/new.'); } - final coverageDir = _ifParsed('coverage'); - final coverageLcov = _ifParsed('coverage-lcov'); - final branchCoverage = _ifParsed('branch-coverage') ?? false; + final coverageDir = _ifParsed('coverage'); + final coverageLcov = _ifParsed('coverage-lcov'); + final branchCoverage = _ifParsed('branch-coverage') ?? false; if (coverageDir == null && coverageLcov == null && branchCoverage) { throw ArgumentError( 'If you set --branch-coverage you must set either --coverage or ' diff --git a/pkgs/test_core/lib/src/runner/coverage.dart b/pkgs/test_core/lib/src/runner/coverage.dart index 841bdd72f..0f67efdc8 100644 --- a/pkgs/test_core/lib/src/runner/coverage.dart +++ b/pkgs/test_core/lib/src/runner/coverage.dart @@ -5,20 +5,39 @@ import 'dart:convert'; import 'dart:io'; +import 'package:coverage/coverage.dart'; import 'package:path/path.dart' as p; +import '../util/package_config.dart'; import 'live_suite_controller.dart'; /// Collects coverage and outputs to the [coveragePath] path. -Future writeCoverage( - String coveragePath, LiveSuiteController controller) async { - var suite = controller.liveSuite.suite; - var coverage = await controller.liveSuite.suite.gatherCoverage(); - final outfile = File(p.join(coveragePath, - '${suite.path}.${suite.platform.runtime.name.toLowerCase()}.json')) - ..createSync(recursive: true); +Future> writeCoverage( + String? coveragePath, LiveSuiteController controller) async { + final suite = controller.liveSuite.suite; + final coverage = await controller.liveSuite.suite.gatherCoverage(); + if (coveragePath != null) { + final outfile = File(p.join(coveragePath, + '${suite.path}.${suite.platform.runtime.name.toLowerCase()}.json')) + ..createSync(recursive: true); + final out = outfile.openWrite(); + out.write(json.encode(coverage)); + await out.flush(); + await out.close(); + } + return HitMap.parseJson(coverage['coverage'] as List>); +} + +Future writeCoverageLcov( + String coverageLcov, Map allCoverageData) async { + final resolver = await Resolver.create( + packagePath: (await currentPackage).root.toFilePath()); + final filteredCoverageData = allCoverageData + .filterIgnored(ignoredLinesInFilesCache: {}, resolver: resolver); + final lcovData = filteredCoverageData.formatLcov(resolver); + final outfile = File(coverageLcov)..createSync(recursive: true); final out = outfile.openWrite(); - out.write(json.encode(coverage)); + out.write(lcovData); await out.flush(); await out.close(); } diff --git a/pkgs/test_core/lib/src/runner/coverage_stub.dart b/pkgs/test_core/lib/src/runner/coverage_stub.dart index 64f69c75e..05360d5dd 100644 --- a/pkgs/test_core/lib/src/runner/coverage_stub.dart +++ b/pkgs/test_core/lib/src/runner/coverage_stub.dart @@ -2,9 +2,16 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +import 'package:coverage/coverage.dart'; + import 'live_suite_controller.dart'; -Future writeCoverage( - String coveragePath, LiveSuiteController controller) => +Future> writeCoverage( + String? coveragePath, LiveSuiteController controller) => + throw UnsupportedError( + 'Coverage is only supported through the test runner.'); + +Future writeCoverageLcov( + String coverageLcov, Map allCoverageData) => throw UnsupportedError( 'Coverage is only supported through the test runner.'); diff --git a/pkgs/test_core/lib/src/runner/engine.dart b/pkgs/test_core/lib/src/runner/engine.dart index 4404d1c5c..40a01f68d 100644 --- a/pkgs/test_core/lib/src/runner/engine.dart +++ b/pkgs/test_core/lib/src/runner/engine.dart @@ -7,6 +7,7 @@ import 'dart:math'; import 'package:async/async.dart' hide Result; import 'package:collection/collection.dart'; +import 'package:coverage/coverage.dart'; import 'package:pool/pool.dart'; import 'package:test_api/src/backend/group.dart'; // ignore: implementation_imports import 'package:test_api/src/backend/invoker.dart'; // ignore: implementation_imports @@ -61,7 +62,13 @@ class Engine { bool? _closedBeforeDone; /// The coverage output directory. - String? _coverage; + final String? _coverage; + + /// The coverage output lcov file. + final String? _coverageLcov; + + /// The merged coverage data from all tests. + final Map _allCoverageData = {}; /// The seed used to generate randomness for test case shuffling. /// @@ -92,8 +99,7 @@ class Engine { /// This will be `null` if [close] was called before all the tests finished /// running. Future get success async { - await Future.wait([_group.future, _runPool.done], eagerError: true); - if (_closedBeforeDone!) return null; + if (!await _done) return null; return liveTests.every((liveTest) => liveTest.state.result.isPassing && liveTest.state.status == Status.complete); @@ -211,11 +217,13 @@ class Engine { Engine({ int? concurrency, String? coverage, + String? coverageLcov, this.testRandomizeOrderingSeed, bool stopOnFirstFailure = false, }) : _runPool = Pool(concurrency ?? 1), _stopOnFirstFailure = stopOnFirstFailure, - _coverage = coverage { + _coverage = coverage, + _coverageLcov = coverageLcov { _group.future.then((_) { _onTestStartedGroup.close(); _onSuiteStartedController.close(); @@ -233,10 +241,14 @@ class Engine { /// [concurrency] controls how many suites are run at once. If [runSkipped] is /// `true`, skipped tests will be run as though they weren't skipped. factory Engine.withSuites(List suites, - {int? concurrency, String? coverage, bool stopOnFirstFailure = false}) { + {int? concurrency, + String? coverage, + String? coverageLcov, + bool stopOnFirstFailure = false}) { var engine = Engine( concurrency: concurrency, coverage: coverage, + coverageLcov: coverageLcov, stopOnFirstFailure: stopOnFirstFailure, ); for (var suite in suites) { @@ -282,17 +294,24 @@ class Engine { if (_closed) return; await _runGroup(controller, controller.liveSuite.suite.group, []); controller.noMoreLiveTests(); - if (_coverage != null) await writeCoverage(_coverage!, controller); + if (_coverage != null || _coverageLcov != null) { + _allCoverageData + .merge(await writeCoverage(_coverage, controller)); + } } finally { resource.allowRelease(() => controller?.close()); } }()); }) - ..onDone(() { + ..onDone(() async { _subscriptions.remove(subscription); _onSuiteAddedController.close(); _group.close(); _runPool.close(); + + if (_coverageLcov != null && await _done) { + await writeCoverageLcov(_coverageLcov, _allCoverageData); + } }); _subscriptions.add(subscription); @@ -551,4 +570,9 @@ class Engine { futures.add(_runPool.close()); await Future.wait(futures, eagerError: true); } + + Future get _done async { + await Future.wait([_group.future, _runPool.done], eagerError: true); + return !_closedBeforeDone!; + } } diff --git a/pkgs/test_core/lib/src/runner/vm/platform.dart b/pkgs/test_core/lib/src/runner/vm/platform.dart index d5fdea255..9e5b0e3a1 100644 --- a/pkgs/test_core/lib/src/runner/vm/platform.dart +++ b/pkgs/test_core/lib/src/runner/vm/platform.dart @@ -352,7 +352,7 @@ Future> _gatherCoverage( false, false, false, - {await currentPackageName}, + {(await currentPackage).name}, isolateIds: {isolateId!}, branchCoverage: config.branchCoverage, ); diff --git a/pkgs/test_core/lib/src/util/package_config.dart b/pkgs/test_core/lib/src/util/package_config.dart index 1ed52ae58..53ad07cc0 100644 --- a/pkgs/test_core/lib/src/util/package_config.dart +++ b/pkgs/test_core/lib/src/util/package_config.dart @@ -38,7 +38,7 @@ Future absoluteUri(String path) async { } } -/// Returns the name of the current package. -final Future currentPackageName = () async { - return (await currentPackageConfig).packageOf(await packageConfigUri)!.name; +/// Returns the current package. +final Future currentPackage = () async { + return (await currentPackageConfig).packageOf(await packageConfigUri)!; }(); diff --git a/pkgs/test_core/pubspec.yaml b/pkgs/test_core/pubspec.yaml index 78cc8ea24..7ca9cc448 100644 --- a/pkgs/test_core/pubspec.yaml +++ b/pkgs/test_core/pubspec.yaml @@ -14,7 +14,7 @@ dependencies: async: ^2.5.0 boolean_selector: ^2.1.0 collection: ^1.15.0 - coverage: ^1.0.0 + coverage: ^1.15.0 frontend_server_client: '>=3.2.0 <5.0.0' glob: ^2.0.0 io: ^1.0.0 From 58465002320e309bef14064b46b5c5ff9a434cc9 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 13:33:55 +1000 Subject: [PATCH 04/19] pubspec and changelog --- pkgs/test/pubspec.yaml | 2 +- pkgs/test_core/CHANGELOG.md | 4 ++++ pkgs/test_core/pubspec.yaml | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml index 1441152f0..5269f8101 100644 --- a/pkgs/test/pubspec.yaml +++ b/pkgs/test/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: # Use an exact version until the test_api and test_core package are stable. test_api: 0.7.6 - test_core: 0.6.11 + test_core: 0.6.12 typed_data: ^1.3.0 web_socket_channel: '>=2.0.0 <4.0.0' diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md index 8029c3fe2..2dcdeeb50 100644 --- a/pkgs/test_core/CHANGELOG.md +++ b/pkgs/test_core/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.6.12 + +* Add `--coverage-lcov` and `--branch-cov` options to `dart test`. + ## 0.6.11 * Graduate native assets from experiment to preview. diff --git a/pkgs/test_core/pubspec.yaml b/pkgs/test_core/pubspec.yaml index 7ca9cc448..4782b1d02 100644 --- a/pkgs/test_core/pubspec.yaml +++ b/pkgs/test_core/pubspec.yaml @@ -1,5 +1,5 @@ name: test_core -version: 0.6.11 +version: 0.6.12 description: A basic library for writing tests and running them on the VM. repository: https://github.com/dart-lang/test/tree/master/pkgs/test_core issue_tracker: https://github.com/dart-lang/test/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Atest From 1441c86df485f74e7f775443f63796e808e98ce3 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 13:37:25 +1000 Subject: [PATCH 05/19] readme --- pkgs/test/README.md | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/pkgs/test/README.md b/pkgs/test/README.md index 36103d9c1..b65ea4761 100644 --- a/pkgs/test/README.md +++ b/pkgs/test/README.md @@ -258,35 +258,27 @@ The available options for the `--reporter` flag are: ### Collecting Code Coverage To collect code coverage, you can run tests with the `--coverage ` -argument. The directory specified can be an absolute or relative path. +argument or the `--coverage-lcov ` argument. The directory or file +specified can be an absolute or relative path. The `--coverage` option outputs a +json formatted report per test suite, whereas the `--coverage-lcov` option +merges all the test coverage into a single LCOV file. If a directory does not exist at the path specified, a directory will be created. If a directory does exist, files may be overwritten with the latest coverage data, if they conflict. -This option will enable code coverage collection on a suite-by-suite basis, -and the resulting coverage files will be outputted in the directory specified. -The files can then be formatted using the `package:coverage` -`format_coverage` executable. - Coverage gathering is currently only implemented for tests run on the Dart VM or Chrome. Here's an example of how to run tests and format the collected coverage to LCOV: ```shell -## Run Dart tests and output them at directory `./coverage`: -dart run test --coverage=./coverage - -## Activate package `coverage` (if needed): -dart pub global activate coverage - -## Format collected coverage to LCOV (only for directory "lib") -dart pub global run coverage:format_coverage --packages=.dart_tool/package_config.json --report-on=lib --lcov -o ./coverage/lcov.info -i ./coverage +## Run Dart tests and output coverage info to `./coverage/lcov.info`: +dart run test --coverage-lcov=./coverage/lcov.info -## Generate LCOV report: +## Generate a human readable report: genhtml -o ./coverage/report ./coverage/lcov.info -## Open the HTML coverage report: +## Open the coverage report: open ./coverage/report/index.html ``` From adff492f4d534616d8d94e9a8a899032a42831fd Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 13:39:35 +1000 Subject: [PATCH 06/19] readme --- pkgs/test/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/test/README.md b/pkgs/test/README.md index b65ea4761..ffee9e9eb 100644 --- a/pkgs/test/README.md +++ b/pkgs/test/README.md @@ -258,10 +258,10 @@ The available options for the `--reporter` flag are: ### Collecting Code Coverage To collect code coverage, you can run tests with the `--coverage ` -argument or the `--coverage-lcov ` argument. The directory or file -specified can be an absolute or relative path. The `--coverage` option outputs a -json formatted report per test suite, whereas the `--coverage-lcov` option -merges all the test coverage into a single LCOV file. +argument or the `--coverage-lcov ` argument. The `--coverage` option +outputs a json formatted report per test suite, whereas the `--coverage-lcov` +option merges all the test coverage into a single LCOV file. +The directory or file specified can be an absolute or relative path. If a directory does not exist at the path specified, a directory will be created. If a directory does exist, files may be overwritten with the latest coverage data, if they conflict. From 94d008ce095e4882349db02a648e8063e8b0e6ec Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 13:41:44 +1000 Subject: [PATCH 07/19] more pubspec and changelog --- pkgs/test/CHANGELOG.md | 4 ++++ pkgs/test/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md index 606d0d3ba..5cb90a593 100644 --- a/pkgs/test/CHANGELOG.md +++ b/pkgs/test/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.27.0 + +* Add `--coverage-lcov` and `--branch-cov` options to `dart test`. + ## 1.26.2 * Graduate native assets from experiment to preview. diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml index 5269f8101..9c3ddf657 100644 --- a/pkgs/test/pubspec.yaml +++ b/pkgs/test/pubspec.yaml @@ -1,5 +1,5 @@ name: test -version: 1.26.2 +version: 1.27.0 description: >- A full featured library for writing and running Dart tests across platforms. repository: https://github.com/dart-lang/test/tree/master/pkgs/test From 6cb7cb1c148fcf6dc4d3f2251209ff9431b7943a Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 13:52:06 +1000 Subject: [PATCH 08/19] fix analysis --- pkgs/test/test/utils.dart | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkgs/test/test/utils.dart b/pkgs/test/test/utils.dart index 23d5b7e14..25c367ed6 100644 --- a/pkgs/test/test/utils.dart +++ b/pkgs/test/test/utils.dart @@ -121,6 +121,7 @@ Engine declareEngine( void Function() body, { bool runSkipped = false, String? coverage, + String? coverageLcov, bool stopOnFirstFailure = false, }) { var declarer = Declarer()..declare(body); @@ -133,6 +134,7 @@ Engine declareEngine( suitePlatform) ], coverage: coverage, + coverageLcov: coverageLcov, stopOnFirstFailure: stopOnFirstFailure, ); } @@ -199,6 +201,8 @@ Configuration configuration( String? reporter, Map? fileReporters, String? coverage, + String? coverageLcov, + bool? branchCoverage, int? concurrency, int? shardIndex, int? totalShards, @@ -249,6 +253,8 @@ Configuration configuration( reporter: reporter, fileReporters: fileReporters, coverage: coverage, + coverageLcov: coverageLcov, + branchCoverage: branchCoverage, concurrency: concurrency, shardIndex: shardIndex, totalShards: totalShards, From 54bab039bda6e2cd9ee09cbf19ad9eceb9def800 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 9 Jul 2025 14:56:35 +1000 Subject: [PATCH 09/19] Fix some tests --- pkgs/test/test/runner/runner_test.dart | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/test/test/runner/runner_test.dart b/pkgs/test/test/runner/runner_test.dart index 379e2a7f1..b08c8f38d 100644 --- a/pkgs/test/test/runner/runner_test.dart +++ b/pkgs/test/test/runner/runner_test.dart @@ -92,6 +92,10 @@ $_runtimeCompilers --debug Run the VM and Chrome tests in debug mode. --coverage= Gather coverage and output it to the specified directory. Implies --debug. + --coverage-lcov= Gather coverage and output an lcov report to the specified file. + Implies --debug. + --branch-coverage Include branch coverage information in the coverage report. + Must be paired with --coverage or --coverage-lcov. --[no-]chain-stack-traces Use chained stack traces to provide greater exception details especially for asynchronous code. It may be useful to disable to provide improved test performance but at the cost of From 37da3c8b618a89f31d675baeebdf50903ada5c70 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Thu, 10 Jul 2025 14:29:05 +1000 Subject: [PATCH 10/19] WIP test --- pkgs/test/test/io.dart | 7 +-- pkgs/test/test/runner/coverage_test.dart | 64 ++++++++++++++++++------ 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/pkgs/test/test/io.dart b/pkgs/test/test/io.dart index b13f8bdda..66994d4fc 100644 --- a/pkgs/test/test/io.dart +++ b/pkgs/test/test/io.dart @@ -21,10 +21,6 @@ final Future packageDir = return dir; }); -/// The path to the `pub` executable in the current Dart SDK. -final _pubPath = p.absolute(p.join(p.dirname(Platform.resolvedExecutable), - Platform.isWindows ? 'pub.bat' : 'pub')); - /// The platform-specific message emitted when a nonexistent file is loaded. final String noSuchFileMessage = Platform.isWindows ? 'The system cannot find the file specified.' @@ -151,7 +147,8 @@ Future runDart(Iterable args, /// Runs Pub. Future runPub(Iterable args, {Map? environment}) { - return TestProcess.start(_pubPath, args, + return TestProcess.start( + p.absolute(Platform.resolvedExecutable), ['pub', ...args], workingDirectory: d.sandbox, environment: environment, description: 'pub ${args.first}'); diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart index a49124ba8..f19f78c7b 100644 --- a/pkgs/test/test/runner/coverage_test.dart +++ b/pkgs/test/test/runner/coverage_test.dart @@ -20,6 +20,7 @@ void main() { group('with the --coverage flag,', () { late Directory coverageDirectory; + late d.DirectoryDescriptor packageDirectory; Future validateCoverage(TestProcess test, String coveragePath) async { expect(test.stdout, emitsThrough(contains('+1: All tests passed!'))); @@ -32,18 +33,44 @@ void main() { } setUp(() async { - await d.file('test.dart', ''' - import 'package:test/test.dart'; - - void main() { - test("test 1", () { - expect(true, isTrue); - }); - } - ''').create(); - coverageDirectory = await Directory.systemTemp.createTemp('test_coverage'); + + packageDirectory = d.dir(d.sandbox, [ + d.dir('lib', [ + d.file('calculate.dart', ''' + int calculate(int x) { + if (x % 2 == 0) { + return x * 2; + } else { + return x * 3; + } + } + '''), + ]), + d.dir('test', [ + d.file('test.dart', ''' + // import 'package:fake_package/calculate.dart'; + import '../lib/calculate.dart'; + import 'package:test/test.dart'; + + void main() { + test('test 1', () { + expect(calculate(6), 12); + }); + } + ''') + ]), + d.file('pubspec.yaml', ''' +name: fake_package +version: 1.0.0 +environment: + sdk: ^3.5.0 +dev_dependencies: + test: ^1.26.2 + '''), + ]); + await packageDirectory.create(); }); tearDown(() async { @@ -51,14 +78,23 @@ void main() { }); test('gathers coverage for VM tests', () async { - var test = - await runTest(['--coverage', coverageDirectory.path, 'test.dart']); + await runPub(['get']); + print(packageDirectory.describe()); + var test = await runTest( + ['--coverage', coverageDirectory.path, 'test/test.dart'], + packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); await validateCoverage(test, 'test.dart.vm.json'); }); test('gathers coverage for Chrome tests', () async { - var test = await runTest( - ['--coverage', coverageDirectory.path, 'test.dart', '-p', 'chrome']); + await runDart(['pub', 'get']); + var test = await runTest([ + '--coverage', + coverageDirectory.path, + 'test/test.dart', + '-p', + 'chrome' + ]); await validateCoverage(test, 'test.dart.chrome.json'); }); From 143be0d7b4cf979fb2b00c5814a33833659154c1 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Thu, 10 Jul 2025 15:36:26 +1000 Subject: [PATCH 11/19] Fix test --- pkgs/test/test/runner/coverage_test.dart | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart index f19f78c7b..369b5a00b 100644 --- a/pkgs/test/test/runner/coverage_test.dart +++ b/pkgs/test/test/runner/coverage_test.dart @@ -50,8 +50,7 @@ void main() { ]), d.dir('test', [ d.file('test.dart', ''' - // import 'package:fake_package/calculate.dart'; - import '../lib/calculate.dart'; + import 'package:fake_package/calculate.dart'; import 'package:test/test.dart'; void main() { @@ -78,12 +77,11 @@ dev_dependencies: }); test('gathers coverage for VM tests', () async { - await runPub(['get']); - print(packageDirectory.describe()); + await (await runPub(['get'])).shouldExit(0); var test = await runTest( ['--coverage', coverageDirectory.path, 'test/test.dart'], packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); - await validateCoverage(test, 'test.dart.vm.json'); + await validateCoverage(test, 'test/test.dart.vm.json'); }); test('gathers coverage for Chrome tests', () async { From d9f017c79753ec6e5467e798c82ca52472808b12 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Thu, 10 Jul 2025 16:07:39 +1000 Subject: [PATCH 12/19] Test new flags --- pkgs/test/test/runner/coverage_test.dart | 57 +++++++++++++++++++++--- 1 file changed, 52 insertions(+), 5 deletions(-) diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart index 369b5a00b..1a2adf43c 100644 --- a/pkgs/test/test/runner/coverage_test.dart +++ b/pkgs/test/test/runner/coverage_test.dart @@ -8,6 +8,7 @@ library; import 'dart:convert'; import 'dart:io'; +import 'package:coverage/coverage.dart'; import 'package:path/path.dart' as p; import 'package:test/test.dart'; import 'package:test_descriptor/test_descriptor.dart' as d; @@ -22,14 +23,21 @@ void main() { late Directory coverageDirectory; late d.DirectoryDescriptor packageDirectory; - Future validateCoverage(TestProcess test, String coveragePath) async { + Future validateTest(TestProcess test) async { expect(test.stdout, emitsThrough(contains('+1: All tests passed!'))); await test.shouldExit(0); + } + + Future> validateCoverage( + TestProcess test, String coveragePath) async { + await validateTest(test); final coverageFile = File(p.join(coverageDirectory.path, coveragePath)); final coverage = await coverageFile.readAsString(); - final jsonCoverage = json.decode(coverage); - expect(jsonCoverage['coverage'], isNotEmpty); + final jsonCoverage = json.decode(coverage)['coverage'] as List; + expect(jsonCoverage, isNotEmpty); + + return HitMap.parseJson(jsonCoverage.cast>()); } setUp(() async { @@ -81,7 +89,46 @@ dev_dependencies: var test = await runTest( ['--coverage', coverageDirectory.path, 'test/test.dart'], packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); - await validateCoverage(test, 'test/test.dart.vm.json'); + final coverage = await validateCoverage(test, 'test/test.dart.vm.json'); + final hitmap = coverage['package:fake_package/calculate.dart']!; + expect(hitmap.lineHits, {1: 1, 2: 2, 3: 1, 5: 0}); + expect(hitmap.funcHits, isNull); + expect(hitmap.branchHits, isNull); + }); + + test('gathers branch coverage for VM tests', () async { + await (await runPub(['get'])).shouldExit(0); + var test = await runTest([ + '--coverage', + coverageDirectory.path, + '--branch-coverage', + 'test/test.dart' + ], vmArgs: [ + '--branch-coverage' + ], packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); + final coverage = await validateCoverage(test, 'test/test.dart.vm.json'); + final hitmap = coverage['package:fake_package/calculate.dart']!; + expect(hitmap.lineHits, {1: 1, 2: 2, 3: 1, 5: 0}); + expect(hitmap.funcHits, isNull); + expect(hitmap.branchHits, {1: 1, 2: 1, 4: 0}); + }); + + test('gathers lcov coverage for VM tests', () async { + await (await runPub(['get'])).shouldExit(0); + final lcovFile = p.join(coverageDirectory.path, 'lcov.info'); + var test = await runTest(['--coverage-lcov', lcovFile, 'test/test.dart'], + packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); + await validateTest(test); + expect(File(lcovFile).readAsStringSync(), ''' +SF:${d.sandbox}/lib/calculate.dart +DA:1,1 +DA:2,2 +DA:3,1 +DA:5,0 +LF:4 +LH:3 +end_of_record +'''); }); test('gathers coverage for Chrome tests', () async { @@ -93,7 +140,7 @@ dev_dependencies: '-p', 'chrome' ]); - await validateCoverage(test, 'test.dart.chrome.json'); + await validateCoverage(test, 'test/test.dart.chrome.json'); }); test( From bb1c18c78af51a33cc472054265e44ed008aa0f6 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Fri, 11 Jul 2025 14:06:11 +1000 Subject: [PATCH 13/19] Fix chrome test --- pkgs/test/test/runner/coverage_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart index 1a2adf43c..723dbab52 100644 --- a/pkgs/test/test/runner/coverage_test.dart +++ b/pkgs/test/test/runner/coverage_test.dart @@ -132,14 +132,14 @@ end_of_record }); test('gathers coverage for Chrome tests', () async { - await runDart(['pub', 'get']); + await (await runPub(['get'])).shouldExit(0); var test = await runTest([ '--coverage', coverageDirectory.path, 'test/test.dart', '-p', 'chrome' - ]); + ], packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); await validateCoverage(test, 'test/test.dart.chrome.json'); }); From 84e35b583d082076552d1c77088d9504455151b6 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Fri, 11 Jul 2025 14:12:41 +1000 Subject: [PATCH 14/19] typo --- pkgs/test/CHANGELOG.md | 2 +- pkgs/test_core/CHANGELOG.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md index 5cb90a593..f1654aa3b 100644 --- a/pkgs/test/CHANGELOG.md +++ b/pkgs/test/CHANGELOG.md @@ -1,6 +1,6 @@ ## 1.27.0 -* Add `--coverage-lcov` and `--branch-cov` options to `dart test`. +* Add `--coverage-lcov` and `--branch-coverage` options to `dart test`. ## 1.26.2 diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md index 2dcdeeb50..5e42ad96f 100644 --- a/pkgs/test_core/CHANGELOG.md +++ b/pkgs/test_core/CHANGELOG.md @@ -1,6 +1,6 @@ ## 0.6.12 -* Add `--coverage-lcov` and `--branch-cov` options to `dart test`. +* Add `--coverage-lcov` and `--branch-coverage` options to `dart test`. ## 0.6.11 From 908186e569fe3458d54845d5ce6b7ae33fb2a446 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Fri, 11 Jul 2025 14:30:55 +1000 Subject: [PATCH 15/19] Fix windows test --- pkgs/test/test/runner/coverage_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart index 723dbab52..ed0c2e600 100644 --- a/pkgs/test/test/runner/coverage_test.dart +++ b/pkgs/test/test/runner/coverage_test.dart @@ -120,7 +120,7 @@ dev_dependencies: packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); await validateTest(test); expect(File(lcovFile).readAsStringSync(), ''' -SF:${d.sandbox}/lib/calculate.dart +SF:${p.join(d.sandbox, 'lib', 'calculate.dart')} DA:1,1 DA:2,2 DA:3,1 From 537ff4ede50928687b84f2809b1f21ef4192885b Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 23 Jul 2025 14:03:41 +1000 Subject: [PATCH 16/19] Fix merge cruft --- pkgs/test/pubspec.yaml | 2 +- pkgs/test_core/CHANGELOG.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml index 6d8839086..2d49d9773 100644 --- a/pkgs/test/pubspec.yaml +++ b/pkgs/test/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: # Use an exact version until the test_api and test_core package are stable. test_api: 0.7.7 - test_core: 0.6.12 + test_core: 0.6.13 typed_data: ^1.3.0 web_socket_channel: '>=2.0.0 <4.0.0' diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md index 1afeaa79e..f39d03deb 100644 --- a/pkgs/test_core/CHANGELOG.md +++ b/pkgs/test_core/CHANGELOG.md @@ -1,8 +1,8 @@ -## 0.6.12 +## 0.6.13 * Add `--coverage-lcov` and `--branch-coverage` options to `dart test`. -## 0.6.13 +## 0.6.12 * Expand pub constraint to allow the latest `analyzer`. From ee1c8c7d8ee1d3d34c883a4b073a4e16725983c0 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Wed, 23 Jul 2025 14:25:55 +1000 Subject: [PATCH 17/19] Fix version error --- pkgs/test_core/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/test_core/pubspec.yaml b/pkgs/test_core/pubspec.yaml index 76ec6b952..8ef950f9d 100644 --- a/pkgs/test_core/pubspec.yaml +++ b/pkgs/test_core/pubspec.yaml @@ -1,5 +1,5 @@ name: test_core -version: 0.6.12 +version: 0.6.13 description: A basic library for writing tests and running them on the VM. repository: https://github.com/dart-lang/test/tree/master/pkgs/test_core issue_tracker: https://github.com/dart-lang/test/issues?q=is%3Aissue+is%3Aopen+label%3Apackage%3Atest From 7859f5a2b347701ae4d849f66cb666696f0a8216 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Mon, 4 Aug 2025 12:37:59 +1000 Subject: [PATCH 18/19] fix merge cruft --- pkgs/test/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/test/pubspec.yaml b/pkgs/test/pubspec.yaml index 827138aff..7c167d764 100644 --- a/pkgs/test/pubspec.yaml +++ b/pkgs/test/pubspec.yaml @@ -36,6 +36,7 @@ dependencies: stream_channel: ^2.1.0 # Use an exact version until the test_api and test_core package are stable. + test_api: 0.7.8-wip test_core: 0.6.13-wip typed_data: ^1.3.0 From b15d4c76b399593180f36fd0197aea91f083b9a4 Mon Sep 17 00:00:00 2001 From: Liam Appelbe Date: Thu, 7 Aug 2025 12:48:44 +1000 Subject: [PATCH 19/19] Rename --coverage-lcov to --coverage-path --- pkgs/test/CHANGELOG.md | 2 +- pkgs/test/README.md | 17 +++++++---------- pkgs/test/test/runner/coverage_test.dart | 2 +- pkgs/test/test/runner/runner_test.dart | 4 ++-- pkgs/test_core/CHANGELOG.md | 2 +- .../lib/src/runner/configuration/args.dart | 10 +++++----- 6 files changed, 17 insertions(+), 20 deletions(-) diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md index 4b7fc942b..2893eb21f 100644 --- a/pkgs/test/CHANGELOG.md +++ b/pkgs/test/CHANGELOG.md @@ -2,7 +2,7 @@ * Restrict to latest version of analyzer package. * Require Dart 3.7 -* Add `--coverage-lcov` and `--branch-coverage` options to `dart test`. +* Add `--coverage-path` and `--branch-coverage` options to `dart test`. ## 1.26.3 diff --git a/pkgs/test/README.md b/pkgs/test/README.md index ffee9e9eb..ccca4504a 100644 --- a/pkgs/test/README.md +++ b/pkgs/test/README.md @@ -257,23 +257,20 @@ The available options for the `--reporter` flag are: ### Collecting Code Coverage -To collect code coverage, you can run tests with the `--coverage ` -argument or the `--coverage-lcov ` argument. The `--coverage` option -outputs a json formatted report per test suite, whereas the `--coverage-lcov` -option merges all the test coverage into a single LCOV file. -The directory or file specified can be an absolute or relative path. -If a directory does not exist at the path specified, a directory will be -created. If a directory does exist, files may be overwritten with the latest -coverage data, if they conflict. +To collect code coverage, you can run tests with the `--coverage-path ` +argument, which outputs a LCOV report to the given file path. The file +specified can be an absolute or relative path. If a directory does not exist at +the path specified, a directory will be created. If a directory does exist, +files may be overwritten with the latest coverage data, if they conflict. Coverage gathering is currently only implemented for tests run on the Dart VM or Chrome. -Here's an example of how to run tests and format the collected coverage to LCOV: +Here's an example of how to run tests and collect coverage: ```shell ## Run Dart tests and output coverage info to `./coverage/lcov.info`: -dart run test --coverage-lcov=./coverage/lcov.info +dart run test --coverage-path=./coverage/lcov.info ## Generate a human readable report: genhtml -o ./coverage/report ./coverage/lcov.info diff --git a/pkgs/test/test/runner/coverage_test.dart b/pkgs/test/test/runner/coverage_test.dart index b1b2a7785..62dce565f 100644 --- a/pkgs/test/test/runner/coverage_test.dart +++ b/pkgs/test/test/runner/coverage_test.dart @@ -124,7 +124,7 @@ dev_dependencies: await (await runPub(['get'])).shouldExit(0); final lcovFile = p.join(coverageDirectory.path, 'lcov.info'); var test = await runTest([ - '--coverage-lcov', + '--coverage-path', lcovFile, 'test/test.dart', ], packageConfig: p.join(d.sandbox, '.dart_tool/package_config.json')); diff --git a/pkgs/test/test/runner/runner_test.dart b/pkgs/test/test/runner/runner_test.dart index 3937d0fb6..fdb4c5738 100644 --- a/pkgs/test/test/runner/runner_test.dart +++ b/pkgs/test/test/runner/runner_test.dart @@ -92,10 +92,10 @@ $_runtimeCompilers --debug Run the VM and Chrome tests in debug mode. --coverage= Gather coverage and output it to the specified directory. Implies --debug. - --coverage-lcov= Gather coverage and output an lcov report to the specified file. + --coverage-path= Gather coverage and output an lcov report to the specified file. Implies --debug. --branch-coverage Include branch coverage information in the coverage report. - Must be paired with --coverage or --coverage-lcov. + Must be paired with --coverage or --coverage-path. --[no-]chain-stack-traces Use chained stack traces to provide greater exception details especially for asynchronous code. It may be useful to disable to provide improved test performance but at the cost of diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md index f2d37007a..74d7fb713 100644 --- a/pkgs/test_core/CHANGELOG.md +++ b/pkgs/test_core/CHANGELOG.md @@ -2,7 +2,7 @@ * Restrict to latest version of analyzer package. * Require Dart 3.7 -* Add `--coverage-lcov` and `--branch-coverage` options to `dart test`. +* Add `--coverage-path` and `--branch-coverage` options to `dart test`. ## 0.6.12 diff --git a/pkgs/test_core/lib/src/runner/configuration/args.dart b/pkgs/test_core/lib/src/runner/configuration/args.dart index a25448aeb..e248bf244 100644 --- a/pkgs/test_core/lib/src/runner/configuration/args.dart +++ b/pkgs/test_core/lib/src/runner/configuration/args.dart @@ -167,7 +167,7 @@ final ArgParser _parser = valueHelp: 'directory', ); parser.addOption( - 'coverage-lcov', + 'coverage-path', help: 'Gather coverage and output an lcov report to the specified file.\n' 'Implies --debug.', @@ -177,7 +177,7 @@ final ArgParser _parser = 'branch-coverage', help: 'Include branch coverage information in the coverage report.\n' - 'Must be paired with --coverage or --coverage-lcov.', + 'Must be paired with --coverage or --coverage-path.', negatable: false, ); parser.addFlag( @@ -433,16 +433,16 @@ class _Parser { } final coverageDir = _ifParsed('coverage'); - final coverageLcov = _ifParsed('coverage-lcov'); + final coverageLcov = _ifParsed('coverage-path'); final branchCoverage = _ifParsed('branch-coverage') ?? false; if (coverageDir == null && coverageLcov == null && branchCoverage) { throw ArgumentError( 'If you set --branch-coverage you must set either --coverage or ' - '--coverage-lcov', + '--coverage-path', ); } if (coverageLcov != null && coverageDir != null) { - throw ArgumentError('Cannot set both --coverage and --coverage-lcov'); + throw ArgumentError('Cannot set both --coverage and --coverage-path'); } return Configuration(