Unverified Commit 9afbab84 authored by Liam Appelbe's avatar Liam Appelbe Committed by GitHub

Use report_lines flag in flutter coverage (#90142)

* Use the report lines flag

* Handle major version bumps in the version check

* Fix existing tests and add a new test
parent a389dbd7
...@@ -223,6 +223,8 @@ Future<Map<String, dynamic>> _getAllCoverage( ...@@ -223,6 +223,8 @@ Future<Map<String, dynamic>> _getAllCoverage(
bool Function(String) libraryPredicate, bool Function(String) libraryPredicate,
bool forceSequential, bool forceSequential,
) async { ) async {
final vm_service.Version version = await service.getVersion();
final bool reportLines = (version.major == 3 && version.minor >= 51) || version.major > 3;
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) {
...@@ -253,6 +255,7 @@ Future<Map<String, dynamic>> _getAllCoverage( ...@@ -253,6 +255,7 @@ Future<Map<String, dynamic>> _getAllCoverage(
<String>['Coverage'], <String>['Coverage'],
scriptId: scriptId, scriptId: scriptId,
forceCompile: true, forceCompile: true,
reportLines: reportLines ? true : null,
) )
.then((vm_service.SourceReport report) { .then((vm_service.SourceReport report) {
sourceReports[scriptId] = report; sourceReports[scriptId] = report;
...@@ -260,17 +263,20 @@ Future<Map<String, dynamic>> _getAllCoverage( ...@@ -260,17 +263,20 @@ Future<Map<String, dynamic>> _getAllCoverage(
if (forceSequential) { if (forceSequential) {
await null; await null;
} }
futures.add(getSourceReport);
if (reportLines) {
continue;
}
final Future<void> getObject = service final Future<void> getObject = service
.getObject(isolateRef.id, scriptId) .getObject(isolateRef.id, scriptId)
.then((vm_service.Obj response) { .then((vm_service.Obj response) {
final vm_service.Script script = response as vm_service.Script; final vm_service.Script script = response as vm_service.Script;
scripts[scriptId] = script; scripts[scriptId] = script;
}); });
futures.add(getSourceReport);
futures.add(getObject); futures.add(getObject);
} }
await Future.wait(futures); await Future.wait(futures);
_buildCoverageMap(scripts, sourceReports, coverage); _buildCoverageMap(scripts, sourceReports, coverage, reportLines);
} }
return <String, dynamic>{'type': 'CodeCoverage', 'coverage': coverage}; return <String, dynamic>{'type': 'CodeCoverage', 'coverage': coverage};
} }
...@@ -280,9 +286,10 @@ void _buildCoverageMap( ...@@ -280,9 +286,10 @@ void _buildCoverageMap(
Map<String, vm_service.Script> scripts, Map<String, vm_service.Script> scripts,
Map<String, vm_service.SourceReport> sourceReports, Map<String, vm_service.SourceReport> sourceReports,
List<Map<String, dynamic>> coverage, List<Map<String, dynamic>> coverage,
bool reportLines,
) { ) {
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 sourceReports.keys) {
final vm_service.SourceReport sourceReport = sourceReports[scriptId]; final vm_service.SourceReport sourceReport = sourceReports[scriptId];
for (final vm_service.SourceReportRange range in sourceReport.ranges) { for (final vm_service.SourceReportRange range in sourceReport.ranges) {
final vm_service.SourceReportCoverage coverage = range.coverage; final vm_service.SourceReportCoverage coverage = range.coverage;
...@@ -297,21 +304,24 @@ void _buildCoverageMap( ...@@ -297,21 +304,24 @@ void _buildCoverageMap(
final Map<int, int> hitMap = hitMaps[uri]; final Map<int, int> hitMap = hitMaps[uri];
final List<int> hits = coverage.hits; final List<int> hits = coverage.hits;
final List<int> misses = coverage.misses; final List<int> misses = coverage.misses;
final List<dynamic> tokenPositions = scripts[scriptRef.id].tokenPosTable; 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
if (tokenPositions == null) { // covered. It will also be null if reportLines is true.
if (tokenPositions == null && !reportLines) {
continue; continue;
} }
if (hits != null) { if (hits != null) {
for (final int hit in hits) { for (final int hit in hits) {
final int line = _lineAndColumn(hit, tokenPositions)[0]; final int line =
reportLines ? hit : _lineAndColumn(hit, tokenPositions)[0];
final int current = hitMap[line] ?? 0; final int current = hitMap[line] ?? 0;
hitMap[line] = current + 1; hitMap[line] = current + 1;
} }
} }
if (misses != null) { if (misses != null) {
for (final int miss in misses) { for (final int miss in misses) {
final int line = _lineAndColumn(miss, tokenPositions)[0]; final int line =
reportLines ? miss : _lineAndColumn(miss, tokenPositions)[0];
hitMap[line] ??= 0; hitMap[line] ??= 0;
} }
} }
......
...@@ -14,6 +14,10 @@ void main() { ...@@ -14,6 +14,10 @@ void main() {
testWithoutContext('Coverage collector Can handle coverage SentinelException', () async { testWithoutContext('Coverage collector Can handle coverage SentinelException', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[ requests: <VmServiceExpectation>[
FakeVmServiceRequest(
method: 'getVersion',
jsonResponse: Version(major: 3, minor: 50).toJson(),
),
FakeVmServiceRequest( FakeVmServiceRequest(
method: 'getVM', method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{}) jsonResponse: (VM.parse(<String, Object>{})
...@@ -51,6 +55,10 @@ void main() { ...@@ -51,6 +55,10 @@ void main() {
testWithoutContext('Coverage collector processes coverage and script data', () async { testWithoutContext('Coverage collector processes coverage and script data', () async {
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
requests: <VmServiceExpectation>[ requests: <VmServiceExpectation>[
FakeVmServiceRequest(
method: 'getVersion',
jsonResponse: Version(major: 3, minor: 50).toJson(),
),
FakeVmServiceRequest( FakeVmServiceRequest(
method: 'getVM', method: 'getVM',
jsonResponse: (VM.parse(<String, Object>{}) jsonResponse: (VM.parse(<String, Object>{})
...@@ -138,4 +146,87 @@ void main() { ...@@ -138,4 +146,87 @@ void main() {
]}); ]});
expect(fakeVmServiceHost.hasRemainingExpectations, false); expect(fakeVmServiceHost.hasRemainingExpectations, false);
}); });
testWithoutContext('Coverage collector skips loading Script objects when reportLines is available', () async {
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>[
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,
'reportLines': true,
},
jsonResponse: SourceReport(
ranges: <SourceReportRange>[
SourceReportRange(
scriptIndex: 0,
startPos: 0,
endPos: 0,
compiled: true,
coverage: SourceReportCoverage(
hits: <int>[1, 3],
misses: <int>[2],
),
),
],
scripts: <ScriptRef>[
ScriptRef(
uri: 'foo.dart',
id: '1',
),
],
).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>[1, 1, 3, 1, 2, 0],
},
]});
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