Skip to content
This repository was archived by the owner on Aug 28, 2024. It is now read-only.

Commit 06d2897

Browse files
committed
Add coverableLineCache param to collect
1 parent bcfd888 commit 06d2897

File tree

4 files changed

+253
-21
lines changed

4 files changed

+253
-21
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## 1.7.0
2+
3+
- Update `package:vm_service` constraints to '>=12.0.0 <13.0.0'.
4+
- Add `coverableLineCache` parameter to `collect`. This allows the set of
5+
coverable lines to be cached between calls to `collect`, avoiding the need to
6+
force compile the same libraries repeatedly. This is only useful when running
7+
multiple coverage collections over the same libraries.
8+
19
## 1.6.4
210

311
- allow omitting space between `//` and `coverage` in coverage ignore comments

lib/src/collect.dart

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,15 @@ const _debugTokenPositions = bool.fromEnvironment('DEBUG_COVERAGE');
4141
/// If [scopedOutput] is non-empty, coverage will be restricted so that only
4242
/// scripts that start with any of the provided paths are considered.
4343
///
44-
/// if [isolateIds] is set, the coverage gathering will be restricted to only
44+
/// If [isolateIds] is set, the coverage gathering will be restricted to only
4545
/// those VM isolates.
4646
///
47+
/// If [coverableLineCache] is set, the collector will avoid recompiling
48+
/// libraries it has already seen (see VmService.getSourceReport's
49+
/// librariesAlreadyCompiled parameter). This is only useful when doing more
50+
/// than one [collect] call over the same libraries. Pass an empty map to the
51+
/// first call, and then pass the same map to all subsequent calls.
52+
///
4753
/// [serviceOverrideForTesting] is for internal testing only, and should not be
4854
/// set by users.
4955
Future<Map<String, dynamic>> collect(Uri serviceUri, bool resume,
@@ -52,6 +58,7 @@ Future<Map<String, dynamic>> collect(Uri serviceUri, bool resume,
5258
Duration? timeout,
5359
bool functionCoverage = false,
5460
bool branchCoverage = false,
61+
Map<String, Set<int>>? coverableLineCache,
5562
VmService? serviceOverrideForTesting}) async {
5663
scopedOutput ??= <String>{};
5764

@@ -92,7 +99,7 @@ Future<Map<String, dynamic>> collect(Uri serviceUri, bool resume,
9299
}
93100

94101
return await _getAllCoverage(service, includeDart, functionCoverage,
95-
branchCoverage, scopedOutput, isolateIds);
102+
branchCoverage, scopedOutput, isolateIds, coverableLineCache);
96103
} finally {
97104
if (resume) {
98105
await _resumeIsolates(service);
@@ -115,7 +122,8 @@ Future<Map<String, dynamic>> _getAllCoverage(
115122
bool functionCoverage,
116123
bool branchCoverage,
117124
Set<String>? scopedOutput,
118-
Set<String>? isolateIds) async {
125+
Set<String>? isolateIds,
126+
Map<String, Set<int>>? coverableLineCache) async {
119127
scopedOutput ??= <String>{};
120128
final vm = await service.getVM();
121129
final allCoverage = <Map<String, dynamic>>[];
@@ -124,16 +132,22 @@ Future<Map<String, dynamic>> _getAllCoverage(
124132
final branchCoverageSupported = _versionCheck(version, 3, 56);
125133
final libraryFilters = _versionCheck(version, 3, 57);
126134
final fastIsoGroups = _versionCheck(version, 3, 61);
135+
final lineCacheSupported = _versionCheck(version, 4, 13);
136+
127137
if (branchCoverage && !branchCoverageSupported) {
128138
branchCoverage = false;
129139
stderr.writeln('Branch coverage was requested, but is not supported'
130140
' by the VM version. Try updating to a newer version of Dart');
131141
}
142+
132143
final sourceReportKinds = [
133144
SourceReportKind.kCoverage,
134145
if (branchCoverage) SourceReportKind.kBranchCoverage,
135146
];
136147

148+
final librariesAlreadyCompiled =
149+
lineCacheSupported ? coverableLineCache?.keys.toList() : null;
150+
137151
// Program counters are shared between isolates in the same group. So we need
138152
// to make sure we're only gathering coverage data for one isolate in each
139153
// group, otherwise we'll double count the hits.
@@ -173,15 +187,24 @@ Future<Map<String, dynamic>> _getAllCoverage(
173187
late final SourceReport scriptReport;
174188
try {
175189
scriptReport = await service.getSourceReport(
176-
isolateRef.id!, sourceReportKinds,
177-
forceCompile: true,
178-
scriptId: script.id,
179-
reportLines: reportLines ? true : null);
190+
isolateRef.id!,
191+
sourceReportKinds,
192+
forceCompile: true,
193+
scriptId: script.id,
194+
reportLines: reportLines ? true : null,
195+
librariesAlreadyCompiled: librariesAlreadyCompiled,
196+
);
180197
} on SentinelException {
181198
continue;
182199
}
183-
final coverage = await _getCoverageJson(service, isolateRef,
184-
scriptReport, includeDart, functionCoverage, reportLines);
200+
final coverage = await _processSourceReport(
201+
service,
202+
isolateRef,
203+
scriptReport,
204+
includeDart,
205+
functionCoverage,
206+
reportLines,
207+
coverableLineCache);
185208
allCoverage.addAll(coverage);
186209
}
187210
} else {
@@ -195,12 +218,19 @@ Future<Map<String, dynamic>> _getAllCoverage(
195218
libraryFilters: scopedOutput.isNotEmpty && libraryFilters
196219
? List.from(scopedOutput.map((filter) => 'package:$filter/'))
197220
: null,
221+
librariesAlreadyCompiled: librariesAlreadyCompiled,
198222
);
199223
} on SentinelException {
200224
continue;
201225
}
202-
final coverage = await _getCoverageJson(service, isolateRef,
203-
isolateReport, includeDart, functionCoverage, reportLines);
226+
final coverage = await _processSourceReport(
227+
service,
228+
isolateRef,
229+
isolateReport,
230+
includeDart,
231+
functionCoverage,
232+
reportLines,
233+
coverableLineCache);
204234
allCoverage.addAll(coverage);
205235
}
206236
}
@@ -276,13 +306,14 @@ int? _getLineFromTokenPos(Script script, int tokenPos) {
276306
}
277307

278308
/// Returns a JSON coverage list backward-compatible with pre-1.16.0 SDKs.
279-
Future<List<Map<String, dynamic>>> _getCoverageJson(
309+
Future<List<Map<String, dynamic>>> _processSourceReport(
280310
VmService service,
281311
IsolateRef isolateRef,
282312
SourceReport report,
283313
bool includeDart,
284314
bool functionCoverage,
285-
bool reportLines) async {
315+
bool reportLines,
316+
Map<String, Set<int>>? coverableLineCache) async {
286317
final hitMaps = <Uri, HitMap>{};
287318
final scripts = <ScriptRef, Script>{};
288319
final libraries = <LibraryRef>{};
@@ -333,7 +364,18 @@ Future<List<Map<String, dynamic>>> _getCoverageJson(
333364

334365
for (var range in report.ranges!) {
335366
final scriptRef = report.scripts![range.scriptIndex!];
336-
final scriptUri = Uri.parse(scriptRef.uri!);
367+
final scriptUriString = scriptRef.uri!;
368+
final scriptUri = Uri.parse(scriptUriString);
369+
370+
// If we have a coverableLineCache, use it in the same way we use
371+
// SourceReportCoverage.misses: to add zeros to the coverage result for all
372+
// the lines that don't have a hit. Afterwards, add all the lines that were
373+
// hit or missed to the cache, so that the next coverage collection won't
374+
// need to compile this libarry.
375+
Set<int>? coverableLines;
376+
if (coverableLineCache != null) {
377+
coverableLines = coverableLineCache[scriptUriString] ??= <int>{};
378+
}
337379

338380
// Not returned in scripts section of source report.
339381
if (scriptUri.scheme == 'evaluate') continue;
@@ -379,7 +421,8 @@ Future<List<Map<String, dynamic>>> _getCoverageJson(
379421

380422
if (coverage == null) continue;
381423

382-
void forEachLine(List<int> tokenPositions, void Function(int line) body) {
424+
void forEachLine(List<int>? tokenPositions, void Function(int line) body) {
425+
if (tokenPositions == null) return;
383426
for (final pos in tokenPositions) {
384427
final line = reportLines ? pos : _getLineFromTokenPos(script!, pos);
385428
if (line == null) {
@@ -393,14 +436,22 @@ Future<List<Map<String, dynamic>>> _getCoverageJson(
393436
}
394437
}
395438

396-
forEachLine(coverage.hits!, (line) {
439+
if (coverableLines != null) {
440+
for (final line in coverableLines) {
441+
hits.lineHits.putIfAbsent(line, () => 0);
442+
}
443+
}
444+
445+
forEachLine(coverage.hits, (line) {
397446
hits.lineHits.increment(line);
447+
coverableLines?.add(line);
398448
if (hits.funcNames != null && hits.funcNames!.containsKey(line)) {
399449
hits.funcHits!.increment(line);
400450
}
401451
});
402-
forEachLine(coverage.misses!, (line) {
452+
forEachLine(coverage.misses, (line) {
403453
hits.lineHits.putIfAbsent(line, () => 0);
454+
coverableLines?.add(line);
404455
});
405456
hits.funcNames?.forEach((line, funcName) {
406457
hits.funcHits?.putIfAbsent(line, () => 0);
@@ -409,10 +460,10 @@ Future<List<Map<String, dynamic>>> _getCoverageJson(
409460
final branchCoverage = range.branchCoverage;
410461
if (branchCoverage != null) {
411462
hits.branchHits ??= <int, int>{};
412-
forEachLine(branchCoverage.hits!, (line) {
463+
forEachLine(branchCoverage.hits, (line) {
413464
hits.branchHits!.increment(line);
414465
});
415-
forEachLine(branchCoverage.misses!, (line) {
466+
forEachLine(branchCoverage.misses, (line) {
416467
hits.branchHits!.putIfAbsent(line, () => 0);
417468
});
418469
}

pubspec.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
name: coverage
2-
version: 1.6.4
2+
version: 1.7.0
33
description: Coverage data manipulation and formatting
44
repository: https://github.com/dart-lang/coverage
55

@@ -13,7 +13,7 @@ dependencies:
1313
path: ^1.8.0
1414
source_maps: ^0.10.10
1515
stack_trace: ^1.10.0
16-
vm_service: '>=11.9.0 <13.0.0'
16+
vm_service: '>=12.0.0 <13.0.0'
1717

1818
dev_dependencies:
1919
benchmark_harness: ^2.2.0

0 commit comments

Comments
 (0)