Unverified Commit 895b317e authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

[flutter_tools] use package:vm_service types for coverage collection requests (#80526)

parent c6e9d411
...@@ -11,7 +11,6 @@ import 'package:vm_service/vm_service.dart' as vm_service; ...@@ -11,7 +11,6 @@ import 'package:vm_service/vm_service.dart' as vm_service;
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/utils.dart';
import '../globals_null_migrated.dart' as globals; import '../globals_null_migrated.dart' as globals;
import '../vmservice.dart'; import '../vmservice.dart';
...@@ -211,54 +210,64 @@ Future<Map<String, dynamic>> collect(Uri serviceUri, bool Function(String) libra ...@@ -211,54 +210,64 @@ Future<Map<String, dynamic>> collect(Uri serviceUri, bool Function(String) libra
bool waitPaused = false, bool waitPaused = false,
String debugName, String debugName,
Future<FlutterVmService> Function(Uri) connector = _defaultConnect, Future<FlutterVmService> Function(Uri) connector = _defaultConnect,
@visibleForTesting bool forceSequential = false,
}) async { }) async {
final FlutterVmService vmService = await connector(serviceUri); final FlutterVmService vmService = await connector(serviceUri);
final Map<String, dynamic> result = await _getAllCoverage(vmService.service, libraryPredicate); final Map<String, dynamic> result = await _getAllCoverage(vmService.service, libraryPredicate, forceSequential);
await vmService.dispose(); await vmService.dispose();
return result; return result;
} }
Future<Map<String, dynamic>> _getAllCoverage(vm_service.VmService service, bool Function(String) libraryPredicate) async { Future<Map<String, dynamic>> _getAllCoverage(
vm_service.VmService service,
bool Function(String) libraryPredicate,
bool forceSequential,
) async {
final vm_service.VM vm = await service.getVM(); final vm_service.VM vm = await service.getVM();
final List<Map<String, dynamic>> coverage = <Map<String, dynamic>>[]; final List<Map<String, dynamic>> coverage = <Map<String, dynamic>>[];
for (final vm_service.IsolateRef isolateRef in vm.isolates) { for (final vm_service.IsolateRef isolateRef in vm.isolates) {
Map<String, Object> scriptList; if (isolateRef.isSystemIsolate) {
continue;
}
vm_service.ScriptList scriptList;
try { try {
final vm_service.ScriptList actualScriptList = await service.getScripts(isolateRef.id); scriptList = await service.getScripts(isolateRef.id);
scriptList = actualScriptList.json;
} on vm_service.SentinelException { } on vm_service.SentinelException {
continue; continue;
} }
final List<Future<void>> futures = <Future<void>>[];
final Map<String, Map<String, dynamic>> scripts = <String, Map<String, dynamic>>{}; final List<Future<void>> futures = <Future<void>>[];
final Map<String, Map<String, dynamic>> sourceReports = <String, Map<String, dynamic>>{}; final Map<String, vm_service.Script> scripts = <String, vm_service.Script>{};
final Map<String, vm_service.SourceReport> sourceReports = <String, vm_service.SourceReport>{};
// For each ScriptRef loaded into the VM, load the corresponding Script and // For each ScriptRef loaded into the VM, load the corresponding Script and
// SourceReport object. // SourceReport object.
for (final Map<String, dynamic> script in (scriptList['scripts'] as List<dynamic>).cast<Map<String, dynamic>>()) { for (final vm_service.ScriptRef script in scriptList.scripts) {
if (!libraryPredicate(script['uri'] as String)) { final String libraryUri = script.uri;
if (!libraryPredicate(libraryUri)) {
continue; continue;
} }
final String scriptId = script['id'] as String; final String scriptId = script.id;
futures.add( final Future<void> getSourceReport = service.getSourceReport(
service.getSourceReport( isolateRef.id,
isolateRef.id, <String>['Coverage'],
<String>['Coverage'], scriptId: scriptId,
scriptId: scriptId, forceCompile: true,
forceCompile: true, )
) .then((vm_service.SourceReport report) {
.then((vm_service.SourceReport report) { sourceReports[scriptId] = report;
sourceReports[scriptId] = report.json; });
}) if (forceSequential) {
); await null;
futures.add( }
service final Future<void> getObject = service
.getObject(isolateRef.id, scriptId) .getObject(isolateRef.id, scriptId)
.then((vm_service.Obj script) { .then((vm_service.Obj response) {
scripts[scriptId] = script.json; final vm_service.Script script = response as vm_service.Script;
}) scripts[scriptId] = script;
); });
futures.add(getSourceReport);
futures.add(getObject);
} }
await Future.wait(futures); await Future.wait(futures);
_buildCoverageMap(scripts, sourceReports, coverage); _buildCoverageMap(scripts, sourceReports, coverage);
...@@ -268,27 +277,27 @@ Future<Map<String, dynamic>> _getAllCoverage(vm_service.VmService service, bool ...@@ -268,27 +277,27 @@ Future<Map<String, dynamic>> _getAllCoverage(vm_service.VmService service, bool
// Build a hitmap of Uri -> Line -> Hit Count for each script object. // Build a hitmap of Uri -> Line -> Hit Count for each script object.
void _buildCoverageMap( void _buildCoverageMap(
Map<String, Map<String, dynamic>> scripts, Map<String, vm_service.Script> scripts,
Map<String, Map<String, dynamic>> sourceReports, Map<String, vm_service.SourceReport> sourceReports,
List<Map<String, dynamic>> coverage, List<Map<String, dynamic>> coverage,
) { ) {
final Map<String, Map<int, int>> hitMaps = <String, Map<int, int>>{}; final Map<String, Map<int, int>> hitMaps = <String, Map<int, int>>{};
for (final String scriptId in scripts.keys) { for (final String scriptId in scripts.keys) {
final Map<String, dynamic> sourceReport = sourceReports[scriptId]; final vm_service.SourceReport sourceReport = sourceReports[scriptId];
for (final Map<String, dynamic> range in (sourceReport['ranges'] as List<dynamic>).cast<Map<String, dynamic>>()) { for (final vm_service.SourceReportRange range in sourceReport.ranges) {
final Map<String, dynamic> coverage = castStringKeyedMap(range['coverage']); final vm_service.SourceReportCoverage coverage = range.coverage;
// Coverage reports may sometimes be null for a Script. // Coverage reports may sometimes be null for a Script.
if (coverage == null) { if (coverage == null) {
continue; continue;
} }
final Map<String, dynamic> scriptRef = castStringKeyedMap(sourceReport['scripts'][range['scriptIndex']]); final vm_service.ScriptRef scriptRef = sourceReport.scripts[range.scriptIndex];
final String uri = scriptRef['uri'] as String; final String uri = scriptRef.uri;
hitMaps[uri] ??= <int, int>{}; hitMaps[uri] ??= <int, int>{};
final Map<int, int> hitMap = hitMaps[uri]; final Map<int, int> hitMap = hitMaps[uri];
final List<int> hits = (coverage['hits'] as List<dynamic>).cast<int>(); final List<int> hits = coverage.hits;
final List<int> misses = (coverage['misses'] as List<dynamic>).cast<int>(); final List<int> misses = coverage.misses;
final List<dynamic> tokenPositions = scripts[scriptRef['id']]['tokenPosTable'] as List<dynamic>; final List<dynamic> tokenPositions = scripts[scriptRef.id].tokenPosTable;
// The token positions can be null if the script has no lines that may be covered. // The token positions can be null if the script has no lines that may be covered.
if (tokenPositions == null) { if (tokenPositions == null) {
continue; continue;
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
// @dart = 2.8 // @dart = 2.8
import 'package:flutter_tools/src/test/coverage_collector.dart'; import 'package:flutter_tools/src/test/coverage_collector.dart';
import 'package:vm_service/vm_service.dart' as vm_service; import 'package:vm_service/vm_service.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/fake_vm_services.dart'; import '../src/fake_vm_services.dart';
...@@ -16,9 +16,9 @@ void main() { ...@@ -16,9 +16,9 @@ void main() {
requests: <VmServiceExpectation>[ requests: <VmServiceExpectation>[
FakeVmServiceRequest( FakeVmServiceRequest(
method: 'getVM', method: 'getVM',
jsonResponse: (vm_service.VM.parse(<String, Object>{}) jsonResponse: (VM.parse(<String, Object>{})
..isolates = <vm_service.IsolateRef>[ ..isolates = <IsolateRef>[
vm_service.IsolateRef.parse(<String, Object>{ IsolateRef.parse(<String, Object>{
'id': '1' 'id': '1'
}), }),
] ]
...@@ -47,4 +47,95 @@ void main() { ...@@ -47,4 +47,95 @@ void main() {
expect(result, <String, Object>{'type': 'CodeCoverage', 'coverage': <Object>[]}); expect(result, <String, Object>{'type': 'CodeCoverage', 'coverage': <Object>[]});
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}); });
testWithoutContext('Coverage collector processes coverage and script data', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[
FakeVmServiceRequest(
method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{})
..isolates = <IsolateRef>[
IsolateRef.parse(<String, Object>{
'id': '1'
}),
]
).toJson(),
),
FakeVmServiceRequest(
method: 'getScripts',
args: <String, Object>{
'isolateId': '1',
},
jsonResponse: ScriptList(scripts: <ScriptRef>[
ScriptRef(uri: 'foo.dart', id: '1'),
]).toJson(),
),
FakeVmServiceRequest(
method: 'getSourceReport',
args: <String, Object>{
'isolateId': '1',
'reports': <Object>['Coverage'],
'scriptId': '1',
'forceCompile': true,
},
jsonResponse: SourceReport(
ranges: <SourceReportRange>[
SourceReportRange(
scriptIndex: 0,
startPos: 0,
endPos: 0,
compiled: true,
coverage: SourceReportCoverage(
hits: <int>[],
misses: <int>[],
),
),
],
scripts: <ScriptRef>[
ScriptRef(
uri: 'foo.dart',
id: '1',
),
],
).toJson(),
),
FakeVmServiceRequest(
method: 'getObject',
args: <String, Object>{
'isolateId': '1',
'objectId': '1',
},
jsonResponse: Script(
uri: 'foo.dart',
id: '1',
library: LibraryRef(name: '', id: '1111', uri: 'foo.dart'),
tokenPosTable: <List<int>>[],
).toJson(),
),
],
);
final Map<String, Object> result = await collect(
null,
(String predicate) => true,
connector: (Uri uri) async {
return fakeVmServiceHost.vmService;
},
);
expect(result, <String, Object>{'type': 'CodeCoverage', 'coverage': <Object>[
<String, Object>{
'source': 'foo.dart',
'script': <String, Object>{
'type': '@Script',
'fixedId': true,
'id': 'libraries/1/scripts/foo.dart',
'uri': 'foo.dart',
'_kind': 'library',
},
'hits': <Object>[],
},
]});
expect(fakeVmServiceHost.hasRemainingExpectations, false);
});
} }
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