@@ -8,6 +8,7 @@ import 'dart:io';
8
8
import 'package:vm_service/vm_service.dart' ;
9
9
10
10
import 'hitmap.dart' ;
11
+ import 'isolate_paused_listener.dart' ;
11
12
import 'util.dart' ;
12
13
13
14
const _retryInterval = Duration (milliseconds: 200 );
@@ -25,8 +26,8 @@ const _debugTokenPositions = bool.fromEnvironment('DEBUG_COVERAGE');
25
26
/// If [resume] is true, all isolates will be resumed once coverage collection
26
27
/// is complete.
27
28
///
28
- /// If [waitPaused] is true, collection will not begin until all isolates are
29
- /// in the paused state.
29
+ /// If [waitPaused] is true, collection will not begin for an isolate until it
30
+ /// is in the paused state.
30
31
///
31
32
/// If [includeDart] is true, code coverage for core `dart:*` libraries will be
32
33
/// collected.
@@ -93,14 +94,17 @@ Future<Map<String, dynamic>> collect(Uri serviceUri, bool resume,
93
94
}
94
95
95
96
try {
96
- if (waitPaused) {
97
- await _waitIsolatesPaused (service, timeout: timeout);
98
- }
99
-
100
- return await _getAllCoverage (service, includeDart, functionCoverage,
101
- branchCoverage, scopedOutput, isolateIds, coverableLineCache);
97
+ return await _getAllCoverage (
98
+ service,
99
+ includeDart,
100
+ functionCoverage,
101
+ branchCoverage,
102
+ scopedOutput,
103
+ isolateIds,
104
+ coverableLineCache,
105
+ waitPaused);
102
106
} finally {
103
- if (resume) {
107
+ if (resume && ! waitPaused ) {
104
108
await _resumeIsolates (service);
105
109
}
106
110
// The signature changed in vm_service version 6.0.0.
@@ -114,11 +118,10 @@ Future<Map<String, dynamic>> _getAllCoverage(
114
118
bool includeDart,
115
119
bool functionCoverage,
116
120
bool branchCoverage,
117
- Set <String >? scopedOutput,
121
+ Set <String > scopedOutput,
118
122
Set <String >? isolateIds,
119
- Map <String , Set <int >>? coverableLineCache) async {
120
- scopedOutput ?? = < String > {};
121
- final vm = await service.getVM ();
123
+ Map <String , Set <int >>? coverableLineCache,
124
+ bool waitPaused) async {
122
125
final allCoverage = < Map <String , dynamic >> [];
123
126
124
127
final sourceReportKinds = [
@@ -133,11 +136,15 @@ Future<Map<String, dynamic>> _getAllCoverage(
133
136
// group, otherwise we'll double count the hits.
134
137
final coveredIsolateGroups = < String > {};
135
138
136
- for (var isolateRef in vm.isolates! ) {
137
- if (isolateIds != null && ! isolateIds.contains (isolateRef.id)) continue ;
139
+ Future <void > collectIsolate (IsolateRef isolateRef) async {
140
+ if (! (isolateIds? .contains (isolateRef.id) ?? true )) return ;
141
+
142
+ // coveredIsolateGroups is only relevant for the !waitPaused flow. The
143
+ // waitPaused flow achieves the same once-per-group behavior using the
144
+ // isLastIsolateInGroup flag.
138
145
final isolateGroupId = isolateRef.isolateGroupId;
139
146
if (isolateGroupId != null ) {
140
- if (coveredIsolateGroups.contains (isolateGroupId)) continue ;
147
+ if (coveredIsolateGroups.contains (isolateGroupId)) return ;
141
148
coveredIsolateGroups.add (isolateGroupId);
142
149
}
143
150
@@ -154,8 +161,9 @@ Future<Map<String, dynamic>> _getAllCoverage(
154
161
librariesAlreadyCompiled: librariesAlreadyCompiled,
155
162
);
156
163
} on SentinelException {
157
- continue ;
164
+ return ;
158
165
}
166
+
159
167
final coverage = await _processSourceReport (
160
168
service,
161
169
isolateRef,
@@ -166,6 +174,21 @@ Future<Map<String, dynamic>> _getAllCoverage(
166
174
scopedOutput);
167
175
allCoverage.addAll (coverage);
168
176
}
177
+
178
+ if (waitPaused) {
179
+ await IsolatePausedListener (service,
180
+ (IsolateRef isolateRef, bool isLastIsolateInGroup) async {
181
+ if (isLastIsolateInGroup) {
182
+ await collectIsolate (isolateRef);
183
+ }
184
+ }, stderr.writeln)
185
+ .waitUntilAllExited ();
186
+ } else {
187
+ for (final isolateRef in await getAllIsolates (service)) {
188
+ await collectIsolate (isolateRef);
189
+ }
190
+ }
191
+
169
192
return < String , dynamic > {'type' : 'CodeCoverage' , 'coverage' : allCoverage};
170
193
}
171
194
@@ -190,29 +213,6 @@ Future _resumeIsolates(VmService service) async {
190
213
}
191
214
}
192
215
193
- Future _waitIsolatesPaused (VmService service, {Duration ? timeout}) async {
194
- final pauseEvents = < String > {
195
- EventKind .kPauseStart,
196
- EventKind .kPauseException,
197
- EventKind .kPauseExit,
198
- EventKind .kPauseInterrupted,
199
- EventKind .kPauseBreakpoint
200
- };
201
-
202
- Future allPaused () async {
203
- final vm = await service.getVM ();
204
- if (vm.isolates! .isEmpty) throw StateError ('No isolates.' );
205
- for (var isolateRef in vm.isolates! ) {
206
- final isolate = await service.getIsolate (isolateRef.id! );
207
- if (! pauseEvents.contains (isolate.pauseEvent! .kind)) {
208
- throw StateError ('Unpaused isolates remaining.' );
209
- }
210
- }
211
- }
212
-
213
- return retry (allPaused, _retryInterval, timeout: timeout);
214
- }
215
-
216
216
/// Returns the line number to which the specified token position maps.
217
217
///
218
218
/// Performs a binary search within the script's token position table to locate
@@ -278,6 +278,7 @@ Future<List<Map<String, dynamic>>> _processSourceReport(
278
278
return ;
279
279
}
280
280
final funcName = await _getFuncName (service, isolateRef, func);
281
+ // TODO(liama): Is this still necessary, or is location.line valid?
281
282
final tokenPos = location.tokenPos! ;
282
283
final line = _getLineFromTokenPos (script, tokenPos);
283
284
if (line == null ) {
@@ -299,7 +300,7 @@ Future<List<Map<String, dynamic>>> _processSourceReport(
299
300
if (! scopedOutput.includesScript (scriptUriString)) {
300
301
// Sometimes a range's script can be different to the function's script
301
302
// (eg mixins), so we have to re-check the scope filter.
302
- // See https://github.com/dart-lang/coverage /issues/495
303
+ // See https://github.com/dart-lang/tools /issues/530
303
304
continue ;
304
305
}
305
306
final scriptUri = Uri .parse (scriptUriString! );
@@ -308,7 +309,7 @@ Future<List<Map<String, dynamic>>> _processSourceReport(
308
309
// SourceReportCoverage.misses: to add zeros to the coverage result for all
309
310
// the lines that don't have a hit. Afterwards, add all the lines that were
310
311
// hit or missed to the cache, so that the next coverage collection won't
311
- // need to compile this libarry .
312
+ // need to compile this library .
312
313
final coverableLines =
313
314
coverableLineCache? .putIfAbsent (scriptUriString, () => < int > {});
314
315
0 commit comments