Unverified Commit 803ef6a4 authored by jensjoha's avatar jensjoha Committed by GitHub

Improve coverage speed by using new caching option for package:coverage (#107395)

Speedup coverage test runs by using (new) coverage handle with caching.

On a `flutter test --coverage` run on `packages/flutter` the runtime goes from ~828 seconds to ~617 seconds, or a ~25% reduction in time spent (testing without coverage takes ~236 seconds so the overhead of `--coverage` in this case goes from ~592 seconds to ~381 seconds, or a ~35% reduction).
parent c251856c
......@@ -116,9 +116,11 @@ Future<void> run(List<String> args) async {
// setting libraryNames to null.
final Set<String> libraryNames = coverageDirectory != null ? null :
<String>{FlutterProject.current().manifest.appName};
final String packagesPath = globals.fs.path.normalize(globals.fs.path.absolute(argResults[_kOptionPackages] as String));
collector = CoverageCollector(
packagesPath: globals.fs.path.normalize(globals.fs.path.absolute(argResults[_kOptionPackages] as String)),
libraryNames: libraryNames);
packagesPath: packagesPath,
libraryNames: libraryNames,
resolver: await CoverageCollector.getResolver(packagesPath));
if (!argResults.options.contains(_kOptionTestDirectory)) {
throwToolExit('Use of --coverage requires setting --test-directory');
}
......
......@@ -374,7 +374,8 @@ class TestCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts {
collector = CoverageCollector(
verbose: !machine,
libraryNames: <String>{projectName},
packagesPath: buildInfo.packagesPath
packagesPath: buildInfo.packagesPath,
resolver: await CoverageCollector.getResolver(buildInfo.packagesPath)
);
}
......
......@@ -19,7 +19,7 @@ import 'watcher.dart';
/// A class that collects code coverage data during test runs.
class CoverageCollector extends TestWatcher {
CoverageCollector({this.libraryNames, this.verbose = true, required this.packagesPath});
CoverageCollector({this.libraryNames, this.verbose = true, required this.packagesPath, this.resolver});
/// True when log messages should be emitted.
final bool verbose;
......@@ -35,6 +35,19 @@ class CoverageCollector extends TestWatcher {
/// will be accepted.
Set<String>? libraryNames;
final coverage.Resolver? resolver;
final Map<String, List<List<int>>> _ignoredLinesInFilesCache = <String, List<List<int>>>{};
static Future<coverage.Resolver> getResolver(String? packagesPath) async {
try {
return await coverage.Resolver.create(packagesPath: packagesPath);
} on FileSystemException {
// When given a bad packages path (as for instance done in some tests)
// just ignore it and return one without a packages path.
return coverage.Resolver.create();
}
}
@override
Future<void> handleFinishedTest(TestDevice testDevice) async {
_logMessage('Starting coverage collection');
......@@ -104,7 +117,7 @@ class CoverageCollector extends TestWatcher {
/// has been run to completion so that all coverage data has been recorded.
///
/// The returned [Future] completes when the coverage is collected.
Future<void> collectCoverage(TestDevice testDevice) async {
Future<void> collectCoverage(TestDevice testDevice, {@visibleForTesting Future<FlutterVmService> Function(Uri?)? connector}) async {
assert(testDevice != null);
Map<String, dynamic>? data;
......@@ -119,7 +132,7 @@ class CoverageCollector extends TestWatcher {
final Future<void> collectionComplete = testDevice.observatoryUri
.then((Uri? observatoryUri) {
_logMessage('collecting coverage data from $testDevice at $observatoryUri...');
return collect(observatoryUri, libraryNames)
return collect(observatoryUri, libraryNames, connector: connector ?? _defaultConnect)
.then<void>((Map<String, dynamic> result) {
if (result == null) {
throw Exception('Failed to collect coverage.');
......@@ -133,11 +146,12 @@ class CoverageCollector extends TestWatcher {
assert(data != null);
_logMessage('Merging coverage data...');
_addHitmap(await coverage.HitMap.parseJson(
final Map<String, coverage.HitMap> hitmap = coverage.HitMap.parseJsonSync(
data!['coverage'] as List<Map<String, dynamic>>,
packagePath: packageDirectory,
checkIgnoredLines: true,
));
resolver: resolver ?? await CoverageCollector.getResolver(packageDirectory),
ignoredLinesInFilesCache: _ignoredLinesInFilesCache);
_addHitmap(hitmap);
_logMessage('Done merging coverage data into global coverage map.');
}
......@@ -154,13 +168,13 @@ class CoverageCollector extends TestWatcher {
return null;
}
if (formatter == null) {
resolver ??= await coverage.Resolver.create(packagesPath: packagesPath);
final coverage.Resolver usedResolver = resolver ?? this.resolver ?? await CoverageCollector.getResolver(packagesPath);
final String packagePath = globals.fs.currentDirectory.path;
final List<String> reportOn = coverageDirectory == null
? <String>[globals.fs.path.join(packagePath, 'lib')]
: <String>[coverageDirectory.path];
formatter = (Map<String, coverage.HitMap> hitmap) => hitmap
.formatLcov(resolver!, reportOn: reportOn, basePath: packagePath);
.formatLcov(usedResolver, reportOn: reportOn, basePath: packagePath);
}
final String result = formatter(_globalHitmap!);
_globalHitmap = null;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment