Migrate flutter_tools to use package:coverage (#111681)
diff --git a/packages/flutter_tools/lib/src/test/coverage_collector.dart b/packages/flutter_tools/lib/src/test/coverage_collector.dart
index c0c448d..0380557 100644
--- a/packages/flutter_tools/lib/src/test/coverage_collector.dart
+++ b/packages/flutter_tools/lib/src/test/coverage_collector.dart
@@ -2,11 +2,8 @@
// 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' as coverage;
import 'package:meta/meta.dart';
-import 'package:vm_service/vm_service.dart' as vm_service;
import '../base/file_system.dart';
import '../base/io.dart';
@@ -99,13 +96,11 @@
///
/// The returned [Future] completes when the coverage is collected.
Future<void> collectCoverageIsolate(Uri observatoryUri) async {
- assert(observatoryUri != null);
_logMessage('collecting coverage data from $observatoryUri...');
final Map<String, dynamic> data = await collect(observatoryUri, libraryNames);
if (data == null) {
throw Exception('Failed to collect coverage.');
}
- assert(data != null);
_logMessage('($observatoryUri): collected coverage data; merging...');
_addHitmap(await coverage.HitMap.parseJson(
@@ -122,12 +117,12 @@
/// 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, {@visibleForTesting Future<FlutterVmService> Function(Uri?)? connector}) async {
- assert(testDevice != null);
-
+ Future<void> collectCoverage(TestDevice testDevice, {
+ @visibleForTesting FlutterVmService? serviceOverride,
+ }) async {
final Stopwatch? totalTestTimeRecorderStopwatch = testTimeRecorder?.start(TestTimePhases.CoverageTotal);
- Map<String, dynamic>? data;
+ late Map<String, dynamic> data;
final Stopwatch? collectTestTimeRecorderStopwatch = testTimeRecorder?.start(TestTimePhases.CoverageCollect);
@@ -141,7 +136,7 @@
final Future<void> collectionComplete = testDevice.observatoryUri
.then((Uri? observatoryUri) {
_logMessage('collecting coverage data from $testDevice at $observatoryUri...');
- return collect(observatoryUri, libraryNames, connector: connector ?? _defaultConnect)
+ return collect(observatoryUri!, libraryNames, serviceOverride: serviceOverride)
.then<void>((Map<String, dynamic> result) {
if (result == null) {
throw Exception('Failed to collect coverage.');
@@ -152,13 +147,14 @@
});
await Future.any<void>(<Future<void>>[ processComplete, collectionComplete ]);
- assert(data != null);
+
testTimeRecorder?.stop(TestTimePhases.CoverageCollect, collectTestTimeRecorderStopwatch!);
_logMessage('Merging coverage data...');
final Stopwatch? parseTestTimeRecorderStopwatch = testTimeRecorder?.start(TestTimePhases.CoverageParseJson);
- final Map<String, coverage.HitMap> hitmap = coverage.HitMap.parseJsonSync(
- data!['coverage'] as List<Map<String, dynamic>>,
+
+ final Map<String, coverage.HitMap> hitmap = coverage.HitMap.parseJsonSync(
+ data['coverage'] as List<Map<String, dynamic>>,
checkIgnoredLines: true,
resolver: resolver ?? await CoverageCollector.getResolver(packageDirectory),
ignoredLinesInFilesCache: _ignoredLinesInFilesCache);
@@ -253,158 +249,11 @@
Future<void> handleTestTimedOut(TestDevice testDevice) async { }
}
-Future<FlutterVmService> _defaultConnect(Uri? serviceUri) {
- return connectToVmService(
- serviceUri!, compression: CompressionOptions.compressionOff, logger: globals.logger,);
-}
-
-Future<Map<String, dynamic>> collect(Uri? serviceUri, Set<String>? libraryNames, {
+Future<Map<String, dynamic>> collect(Uri serviceUri, Set<String>? libraryNames, {
bool waitPaused = false,
String? debugName,
- Future<FlutterVmService> Function(Uri?) connector = _defaultConnect,
@visibleForTesting bool forceSequential = false,
-}) async {
- final FlutterVmService vmService = await connector(serviceUri);
- final Map<String, dynamic> result = await _getAllCoverage(vmService.service, libraryNames, forceSequential);
- await vmService.dispose();
- return result;
-}
-
-Future<Map<String, dynamic>> _getAllCoverage(
- vm_service.VmService service,
- Set<String>? libraryNames,
- bool forceSequential,
-) async {
- final vm_service.Version version = await service.getVersion();
- final bool libraryFilters = (version.major == 3 && version.minor! >= 57) || version.major! > 3;
- final vm_service.VM vm = await service.getVM();
- final List<Map<String, dynamic>> coverage = <Map<String, dynamic>>[];
- bool libraryPredicate(String? libraryName) {
- if (libraryNames == null) {
- return true;
- }
- final Uri uri = Uri.parse(libraryName!);
- if (uri.scheme != 'package') {
- return false;
- }
- final String scope = uri.path.split('/').first;
- return libraryNames.contains(scope);
- }
- for (final vm_service.IsolateRef isolateRef in vm.isolates!) {
- if (isolateRef.isSystemIsolate!) {
- continue;
- }
- if (libraryFilters) {
- final vm_service.SourceReport sourceReport = await service.getSourceReport(
- isolateRef.id!,
- <String>['Coverage'],
- forceCompile: true,
- reportLines: true,
- libraryFilters: libraryNames == null ? null : List<String>.from(
- libraryNames.map((String name) => 'package:$name/')),
- );
- _buildCoverageMap(
- <String, vm_service.Script>{},
- <vm_service.SourceReport>[sourceReport],
- coverage,
- );
- } else {
- vm_service.ScriptList scriptList;
- try {
- scriptList = await service.getScripts(isolateRef.id!);
- } on vm_service.SentinelException {
- continue;
- }
-
- final List<Future<void>> futures = <Future<void>>[];
- final Map<String, vm_service.Script> scripts = <String, vm_service.Script>{};
- final List<vm_service.SourceReport> sourceReports = <vm_service.SourceReport>[];
-
- // For each ScriptRef loaded into the VM, load the corresponding Script
- // and SourceReport object.
- for (final vm_service.ScriptRef script in scriptList.scripts!) {
- final String? libraryUri = script.uri;
- if (!libraryPredicate(libraryUri)) {
- continue;
- }
- final String? scriptId = script.id;
- final Future<void> getSourceReport = service.getSourceReport(
- isolateRef.id!,
- <String>['Coverage'],
- scriptId: scriptId,
- forceCompile: true,
- reportLines: true,
- )
- .then((vm_service.SourceReport report) {
- sourceReports.add(report);
- });
- if (forceSequential) {
- await null;
- }
- futures.add(getSourceReport);
- }
- await Future.wait(futures);
- _buildCoverageMap(scripts, sourceReports, coverage);
- }
- }
- return <String, dynamic>{'type': 'CodeCoverage', 'coverage': coverage};
-}
-
-// Build a hitmap of Uri -> Line -> Hit Count for each script object.
-void _buildCoverageMap(
- Map<String, vm_service.Script> scripts,
- List<vm_service.SourceReport> sourceReports,
- List<Map<String, dynamic>> coverage,
-) {
- final Map<String?, Map<int, int>> hitMaps = <String?, Map<int, int>>{};
- for (final vm_service.SourceReport sourceReport in sourceReports) {
- for (final vm_service.SourceReportRange range in sourceReport.ranges!) {
- final vm_service.SourceReportCoverage? coverage = range.coverage;
- // Coverage reports may sometimes be null for a Script.
- if (coverage == null) {
- continue;
- }
- final vm_service.ScriptRef scriptRef = sourceReport.scripts![range.scriptIndex!];
- final String? uri = scriptRef.uri;
-
- hitMaps[uri] ??= <int, int>{};
- final Map<int, int>? hitMap = hitMaps[uri];
- final List<int>? hits = coverage.hits;
- final List<int>? misses = coverage.misses;
- if (hits != null) {
- for (final int line in hits) {
- final int current = hitMap![line] ?? 0;
- hitMap[line] = current + 1;
- }
- }
- if (misses != null) {
- for (final int line in misses) {
- hitMap![line] ??= 0;
- }
- }
- }
- }
- hitMaps.forEach((String? uri, Map<int, int> hitMap) {
- coverage.add(_toScriptCoverageJson(uri!, hitMap));
- });
-}
-
-// Returns a JSON hit map backward-compatible with pre-1.16.0 SDKs.
-Map<String, dynamic> _toScriptCoverageJson(String scriptUri, Map<int, int> hitMap) {
- final Map<String, dynamic> json = <String, dynamic>{};
- final List<int> hits = <int>[];
- hitMap.forEach((int line, int hitCount) {
- hits.add(line);
- hits.add(hitCount);
- });
- json['source'] = scriptUri;
- json['script'] = <String, dynamic>{
- 'type': '@Script',
- 'fixedId': true,
- 'id': 'libraries/1/scripts/${Uri.encodeComponent(scriptUri)}',
- 'uri': scriptUri,
- '_kind': 'library',
- };
- json['hits'] = hits;
- return json;
+ @visibleForTesting FlutterVmService? serviceOverride,
+}) {
+ return coverage.collect(serviceUri, false, false, false, libraryNames, serviceOverrideForTesting: serviceOverride?.service);
}
diff --git a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart
index 0f473c3..9f330d7 100644
--- a/packages/flutter_tools/test/general.shard/coverage_collector_test.dart
+++ b/packages/flutter_tools/test/general.shard/coverage_collector_test.dart
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+@Timeout.factor(10)
+
import 'dart:convert' show jsonEncode;
import 'dart:io' show Directory, File;
@@ -21,10 +23,6 @@
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
- method: 'getVersion',
- jsonResponse: Version(major: 3, minor: 51).toJson(),
- ),
- FakeVmServiceRequest(
method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{})!
..isolates = <IsolateRef>[
@@ -34,6 +32,10 @@
]
).toJson(),
),
+ FakeVmServiceRequest(
+ method: 'getVersion',
+ jsonResponse: Version(major: 3, minor: 51).toJson(),
+ ),
const FakeVmServiceRequest(
method: 'getScripts',
args: <String, Object>{
@@ -47,11 +49,9 @@
);
final Map<String, Object?> result = await collect(
- null,
+ Uri(),
<String>{'foo'},
- connector: (Uri? uri) async {
- return fakeVmServiceHost.vmService;
- },
+ serviceOverride: fakeVmServiceHost.vmService,
);
expect(result, <String, Object>{'type': 'CodeCoverage', 'coverage': <Object>[]});
@@ -62,10 +62,6 @@
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
- method: 'getVersion',
- jsonResponse: Version(major: 3, minor: 51).toJson(),
- ),
- FakeVmServiceRequest(
method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{})!
..isolates = <IsolateRef>[
@@ -76,6 +72,10 @@
).toJson(),
),
FakeVmServiceRequest(
+ method: 'getVersion',
+ jsonResponse: Version(major: 3, minor: 51).toJson(),
+ ),
+ FakeVmServiceRequest(
method: 'getScripts',
args: <String, Object>{
'isolateId': '1',
@@ -119,11 +119,9 @@
);
final Map<String, Object?> result = await collect(
- null,
+ Uri(),
<String>{'foo'},
- connector: (Uri? uri) async {
- return fakeVmServiceHost.vmService;
- },
+ serviceOverride: fakeVmServiceHost.vmService,
);
expect(result, <String, Object>{
@@ -149,11 +147,9 @@
final FakeVmServiceHost fakeVmServiceHost = createFakeVmServiceHostWithFooAndBar();
final Map<String, Object?> result = await collect(
+ Uri(),
null,
- null,
- connector: (Uri? uri) async {
- return fakeVmServiceHost.vmService;
- },
+ serviceOverride: fakeVmServiceHost.vmService,
);
expect(result, <String, Object>{
@@ -190,10 +186,6 @@
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
- method: 'getVersion',
- jsonResponse: Version(major: 3, minor: 57).toJson(),
- ),
- FakeVmServiceRequest(
method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{})!
..isolates = <IsolateRef>[
@@ -204,6 +196,10 @@
).toJson(),
),
FakeVmServiceRequest(
+ method: 'getVersion',
+ jsonResponse: Version(major: 3, minor: 57).toJson(),
+ ),
+ FakeVmServiceRequest(
method: 'getSourceReport',
args: <String, Object>{
'isolateId': '1',
@@ -237,11 +233,9 @@
);
final Map<String, Object?> result = await collect(
- null,
+ Uri(),
<String>{'foo'},
- connector: (Uri? uri) async {
- return fakeVmServiceHost.vmService;
- },
+ serviceOverride: fakeVmServiceHost.vmService,
);
expect(result, <String, Object>{
@@ -267,10 +261,6 @@
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
- method: 'getVersion',
- jsonResponse: Version(major: 3, minor: 57).toJson(),
- ),
- FakeVmServiceRequest(
method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{})!
..isolates = <IsolateRef>[
@@ -281,6 +271,10 @@
).toJson(),
),
FakeVmServiceRequest(
+ method: 'getVersion',
+ jsonResponse: Version(major: 3, minor: 57).toJson(),
+ ),
+ FakeVmServiceRequest(
method: 'getSourceReport',
args: <String, Object>{
'isolateId': '1',
@@ -313,11 +307,9 @@
);
final Map<String, Object?> result = await collect(
+ Uri(),
null,
- null,
- connector: (Uri? uri) async {
- return fakeVmServiceHost.vmService;
- },
+ serviceOverride: fakeVmServiceHost.vmService,
);
expect(result, <String, Object>{
@@ -356,9 +348,10 @@
packagesPath: packagesPath,
resolver: await CoverageCollector.getResolver(packagesPath)
);
- await collector.collectCoverage(TestTestDevice(), connector: (Uri? uri) async {
- return createFakeVmServiceHostWithFooAndBar().vmService;
- });
+ await collector.collectCoverage(
+ TestTestDevice(),
+ serviceOverride: createFakeVmServiceHostWithFooAndBar(libraryFilters: <String>['package:foo/', 'package:bar/']).vmService,
+ );
Future<void> getHitMapAndVerify() async {
final Map<String, HitMap> gottenHitmap = <String, HitMap>{};
@@ -389,9 +382,10 @@
// Collecting again gets us the same data even though the foo file has been deleted.
// This means that the fact that line 2 was ignored has been cached.
fooFile.deleteSync();
- await collector.collectCoverage(TestTestDevice(), connector: (Uri? uri) async {
- return createFakeVmServiceHostWithFooAndBar().vmService;
- });
+ await collector.collectCoverage(
+ TestTestDevice(),
+ serviceOverride: createFakeVmServiceHostWithFooAndBar(libraryFilters: <String>['package:foo/', 'package:bar/']).vmService,
+ );
await getHitMapAndVerify();
} finally {
tempDir?.deleteSync(recursive: true);
@@ -418,9 +412,10 @@
resolver: await CoverageCollector.getResolver(packagesPath),
testTimeRecorder: testTimeRecorder
);
- await collector.collectCoverage(TestTestDevice(), connector: (Uri? uri) async {
- return createFakeVmServiceHostWithFooAndBar().vmService;
- });
+ await collector.collectCoverage(
+ TestTestDevice(),
+ serviceOverride: createFakeVmServiceHostWithFooAndBar(libraryFilters: <String>['package:foo/', 'package:bar/']).vmService,
+ );
// Expect one message for each phase.
final List<String> logPhaseMessages = testTimeRecorder.getPrintAsListForTesting().where((String m) => m.startsWith('Runtime for phase ')).toList();
@@ -454,14 +449,12 @@
return file;
}
-FakeVmServiceHost createFakeVmServiceHostWithFooAndBar() {
+FakeVmServiceHost createFakeVmServiceHostWithFooAndBar({
+ List<String>? libraryFilters,
+ }) {
return FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
- method: 'getVersion',
- jsonResponse: Version(major: 3, minor: 51).toJson(),
- ),
- FakeVmServiceRequest(
method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{})!
..isolates = <IsolateRef>[
@@ -472,23 +465,17 @@
).toJson(),
),
FakeVmServiceRequest(
- method: 'getScripts',
- args: <String, Object>{
- 'isolateId': '1',
- },
- jsonResponse: ScriptList(scripts: <ScriptRef>[
- ScriptRef(uri: 'package:foo/foo.dart', id: '1'),
- ScriptRef(uri: 'package:bar/bar.dart', id: '2'),
- ]).toJson(),
+ method: 'getVersion',
+ jsonResponse: Version(major: 3, minor: 61).toJson(),
),
FakeVmServiceRequest(
method: 'getSourceReport',
args: <String, Object>{
'isolateId': '1',
'reports': <Object>['Coverage'],
- 'scriptId': '1',
'forceCompile': true,
'reportLines': true,
+ if (libraryFilters != null) 'libraryFilters': libraryFilters,
},
jsonResponse: SourceReport(
ranges: <SourceReportRange>[
@@ -502,28 +489,8 @@
misses: <int>[2],
),
),
- ],
- scripts: <ScriptRef>[
- ScriptRef(
- uri: 'package:foo/foo.dart',
- id: '1',
- ),
- ],
- ).toJson(),
- ),
- FakeVmServiceRequest(
- method: 'getSourceReport',
- args: <String, Object>{
- 'isolateId': '1',
- 'reports': <Object>['Coverage'],
- 'scriptId': '2',
- 'forceCompile': true,
- 'reportLines': true,
- },
- jsonResponse: SourceReport(
- ranges: <SourceReportRange>[
SourceReportRange(
- scriptIndex: 0,
+ scriptIndex: 1,
startPos: 0,
endPos: 0,
compiled: true,
@@ -535,6 +502,10 @@
],
scripts: <ScriptRef>[
ScriptRef(
+ uri: 'package:foo/foo.dart',
+ id: '1',
+ ),
+ ScriptRef(
uri: 'package:bar/bar.dart',
id: '2',
),
@@ -553,7 +524,7 @@
Future<void> kill() => Future<void>.value();
@override
- Future<Uri?> get observatoryUri => Future<Uri?>.value();
+ Future<Uri?> get observatoryUri => Future<Uri?>.value(Uri());
@override
Future<StreamChannel<String>> start(String entrypointPath) {