Merge pull request #350 from dart-lang/report_lines
Use the report lines flag
diff --git a/CHANGELOG.md b/CHANGELOG.md
index aaf7b28..4c6b7bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,8 @@
optional bool flag controlling whether function coverage is collected.
* Ensure `createHitmap` returns a sorted hitmap. This fixes a potential issue with
ignore line annotations.
+* Use the `reportLines` flag in `vm_service`'s `getSourceReport` RPC. This
+ typically halves the number of RPCs that the coverage collector needs to run.
## 1.0.3 - 2021-05-25
diff --git a/lib/src/collect.dart b/lib/src/collect.dart
index 7c481b0..841a7c5 100644
--- a/lib/src/collect.dart
+++ b/lib/src/collect.dart
@@ -97,6 +97,10 @@
scopedOutput ??= <String>{};
final vm = await service.getVM();
final allCoverage = <Map<String, dynamic>>[];
+ final version = await service.getVersion();
+ final reportLines =
+ (version.major == 3 && version.minor != null && version.minor! >= 51) ||
+ (version.major != null && version.major! > 3);
for (var isolateRef in vm.isolates!) {
if (isolateIds != null && !isolateIds.contains(isolateRef.id)) continue;
@@ -110,9 +114,11 @@
if (!scopedOutput.contains(scope)) continue;
final scriptReport = await service.getSourceReport(
isolateRef.id!, <String>[SourceReportKind.kCoverage],
- forceCompile: true, scriptId: script.id);
- final coverage = await _getCoverageJson(
- service, isolateRef, scriptReport, includeDart, functionCoverage);
+ forceCompile: true,
+ scriptId: script.id,
+ reportLines: reportLines ? true : null);
+ final coverage = await _getCoverageJson(service, isolateRef,
+ scriptReport, includeDart, functionCoverage, reportLines);
allCoverage.addAll(coverage);
}
} else {
@@ -120,9 +126,10 @@
isolateRef.id!,
<String>[SourceReportKind.kCoverage],
forceCompile: true,
+ reportLines: reportLines ? true : null,
);
- final coverage = await _getCoverageJson(
- service, isolateRef, isolateReport, includeDart, functionCoverage);
+ final coverage = await _getCoverageJson(service, isolateRef,
+ isolateReport, includeDart, functionCoverage, reportLines);
allCoverage.addAll(coverage);
}
}
@@ -220,10 +227,12 @@
IsolateRef isolateRef,
SourceReport report,
bool includeDart,
- bool functionCoverage) async {
+ bool functionCoverage,
+ bool reportLines) async {
final hitMaps = <Uri, HitMap>{};
final scripts = <ScriptRef, Script>{};
final libraries = <LibraryRef>{};
+ final needScripts = functionCoverage || !reportLines;
for (var range in report.ranges!) {
final scriptRef = report.scripts![range.scriptIndex!];
final scriptUri = Uri.parse(report.scripts![range.scriptIndex!].uri!);
@@ -234,18 +243,22 @@
// Skip scripts from dart:.
if (!includeDart && scriptUri.scheme == 'dart') continue;
- if (!scripts.containsKey(scriptRef)) {
- scripts[scriptRef] =
- await service.getObject(isolateRef.id!, scriptRef.id!) as Script;
- }
- final script = scripts[scriptRef];
- if (script == null) continue;
-
// Look up the hit maps for this script (shared across isolates).
final hits = hitMaps.putIfAbsent(scriptUri, () => HitMap());
+ Script? script;
+ if (needScripts) {
+ if (!scripts.containsKey(scriptRef)) {
+ scripts[scriptRef] =
+ await service.getObject(isolateRef.id!, scriptRef.id!) as Script;
+ }
+ script = scripts[scriptRef];
+
+ if (script == null) continue;
+ }
+
// If the script's library isn't loaded, load it then look up all its funcs.
- final libRef = script.library;
+ final libRef = script?.library;
if (functionCoverage && libRef != null && !libraries.contains(libRef)) {
hits.funcHits ??= <int, int>{};
hits.funcNames ??= <int, String>{};
@@ -254,7 +267,7 @@
await service.getObject(isolateRef.id!, libRef.id!) as Library;
if (library.functions != null) {
for (var funcRef in library.functions!) {
- await _processFunction(service, isolateRef, script, funcRef, hits);
+ await _processFunction(service, isolateRef, script!, funcRef, hits);
}
}
if (library.classes != null) {
@@ -264,7 +277,7 @@
if (clazz.functions != null) {
for (var funcRef in clazz.functions!) {
await _processFunction(
- service, isolateRef, script, funcRef, hits);
+ service, isolateRef, script!, funcRef, hits);
}
}
}
@@ -276,10 +289,10 @@
if (coverage == null) continue;
- for (final tokenPos in coverage.hits!) {
- final line = _getLineFromTokenPos(script, tokenPos);
+ for (final pos in coverage.hits!) {
+ final line = reportLines ? pos : _getLineFromTokenPos(script!, pos);
if (line == null) {
- print('tokenPos $tokenPos has no line mapping for script $scriptUri');
+ print('tokenPos $pos has no line mapping for script $scriptUri');
continue;
}
_incrementCountForKey(hits.lineHits, line);
@@ -287,10 +300,10 @@
_incrementCountForKey(hits.funcHits!, line);
}
}
- for (final tokenPos in coverage.misses!) {
- final line = _getLineFromTokenPos(script, tokenPos);
+ for (final pos in coverage.misses!) {
+ final line = reportLines ? pos : _getLineFromTokenPos(script!, pos);
if (line == null) {
- print('tokenPos $tokenPos has no line mapping for script $scriptUri');
+ print('tokenPos $pos has no line mapping for script $scriptUri');
continue;
}
hits.lineHits.putIfAbsent(line, () => 0);
diff --git a/pubspec.yaml b/pubspec.yaml
index c5ae05d..2b89721 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -13,7 +13,7 @@
path: ^1.8.0
source_maps: ^0.10.10
stack_trace: ^1.10.0
- vm_service: ">=6.1.0 <8.0.0"
+ vm_service: ">=7.3.0 <8.0.0"
dev_dependencies:
pedantic: ^1.10.0
test: ^1.16.0
diff --git a/test/collect_coverage_api_test.dart b/test/collect_coverage_api_test.dart
index 5f9d1a3..3f82581 100644
--- a/test/collect_coverage_api_test.dart
+++ b/test/collect_coverage_api_test.dart
@@ -34,7 +34,7 @@
});
for (var sampleCoverageData in sources[_sampleAppFileUri]!) {
- expect(sampleCoverageData['hits'], isNotNull);
+ expect(sampleCoverageData['hits'], isNotEmpty);
}
for (var sampleCoverageData in sources[_isolateLibFileUri]!) {
@@ -83,10 +83,38 @@
_expectHitCount(hits, 11, 1);
_expectHitCount(hits, 28, 1);
});
+
+ test('collect_coverage_api with function coverage', () async {
+ final json = await _collectCoverage(functionCoverage: true);
+ expect(json.keys, unorderedEquals(<String>['type', 'coverage']));
+ expect(json, containsPair('type', 'CodeCoverage'));
+
+ final coverage = json['coverage'] as List;
+ expect(coverage, isNotEmpty);
+
+ final sources = coverage.cast<Map>().fold(<String, List<Map>>{},
+ (Map<String, List<Map>> map, value) {
+ final sourceUri = value['source'] as String;
+ map.putIfAbsent(sourceUri, () => <Map>[]).add(value);
+ return map;
+ });
+
+ for (var sampleCoverageData in sources[_sampleAppFileUri]!) {
+ expect(sampleCoverageData['funcNames'], isNotEmpty);
+ expect(sampleCoverageData['funcHits'], isNotEmpty);
+ }
+
+ for (var sampleCoverageData in sources[_isolateLibFileUri]!) {
+ expect(sampleCoverageData['funcNames'], isNotEmpty);
+ expect(sampleCoverageData['funcHits'], isNotEmpty);
+ }
+ });
}
Future<Map<String, dynamic>> _collectCoverage(
- {Set<String> scopedOutput = const {}, bool isolateIds = false}) async {
+ {Set<String> scopedOutput = const {},
+ bool isolateIds = false,
+ bool functionCoverage = false}) async {
final openPort = await getOpenPort();
// run the sample app, with the right flags
@@ -115,7 +143,9 @@
final isolateIdSet = isolateIds ? {isolateId} : null;
return collect(serviceUri, true, true, false, scopedOutput,
- timeout: timeout, isolateIds: isolateIdSet, functionCoverage: true);
+ timeout: timeout,
+ isolateIds: isolateIdSet,
+ functionCoverage: functionCoverage);
}
// Returns the first coverage hitmap for the script with with the specified
diff --git a/test/collect_coverage_test.dart b/test/collect_coverage_test.dart
index bc39bef..60484ae 100644
--- a/test/collect_coverage_test.dart
+++ b/test/collect_coverage_test.dart
@@ -81,7 +81,7 @@
});
test('createHitmap', () async {
- final resultString = await _getCoverageResult();
+ final resultString = await _collectCoverage(true);
final jsonResult = json.decode(resultString) as Map<String, dynamic>;
final coverage = jsonResult['coverage'] as List;
final hitMap = await createHitmap(
@@ -191,9 +191,9 @@
String? _coverageData;
Future<String> _getCoverageResult() async =>
- _coverageData ??= await _collectCoverage();
+ _coverageData ??= await _collectCoverage(false);
-Future<String> _collectCoverage() async {
+Future<String> _collectCoverage(bool functionCoverage) async {
expect(FileSystemEntity.isFileSync(testAppPath), isTrue);
final openPort = await getOpenPort();
@@ -220,7 +220,7 @@
// TODO: need to get all of this functionality in the lib
final toolResult = await Process.run('dart', [
_collectAppPath,
- '--function-coverage',
+ if (functionCoverage) '--function-coverage',
'--uri',
'$serviceUri',
'--resume-isolates',