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) {