Unverified Commit c9b28338 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

ensure test isolate is paused before collecting coverage (#35188)

parent 919dcf53
......@@ -47,16 +47,16 @@ class CoverageCollector extends TestWatcher {
/// has been run to completion so that all coverage data has been recorded.
///
/// The returned [Future] completes when the coverage is collected.
Future<void> collectCoverageIsolate(Uri observatoryUri) async {
Future<void> collectCoverageIsolate(Uri observatoryUri, String debugName) async {
assert(observatoryUri != null);
printTrace('collecting coverage data from $observatoryUri...');
print('collecting coverage data from $observatoryUri...');
final Map<String, dynamic> data = await collect(observatoryUri, (String libraryName) {
// If we have a specified coverage directory or could not find the package name, then
// accept all libraries.
return (coverageDirectory != null)
|| (flutterProject == null)
|| libraryName.contains(flutterProject.manifest.appName);
});
}, waitPaused: true, debugName: debugName);
if (data == null) {
throw Exception('Failed to collect coverage.');
}
......@@ -185,13 +185,39 @@ class CoverageCollector extends TestWatcher {
}
Future<VMService> _defaultConnect(Uri serviceUri) {
return VMService.connect(serviceUri, compression: CompressionOptions.compressionOff);
return VMService.connect(
serviceUri, compression: CompressionOptions.compressionOff);
}
Future<Map<String, dynamic>> collect(Uri serviceUri, bool Function(String) libraryPredicate,
[Future<VMService> Function(Uri) connector = _defaultConnect]) async {
Future<Map<String, dynamic>> collect(Uri serviceUri, bool Function(String) libraryPredicate, {
bool waitPaused = false,
String debugName,
Future<VMService> Function(Uri) connector = _defaultConnect,
}) async {
final VMService vmService = await connector(serviceUri);
await vmService.getVM();
final Isolate isolate = vmService.vm.isolates.firstWhere((Isolate isolate) => isolate.name == debugName);
if (!waitPaused) {
return _getAllCoverage(vmService, libraryPredicate);
}
const int kPollAttempts = 20;
int i = 0;
while (i < kPollAttempts) {
await isolate.load();
if (isolate.pauseEvent?.kind == ServiceEvent.kPauseStart) {
break;
}
await Future<void>.delayed(const Duration(milliseconds: 50));
i += 1;
}
if (i == kPollAttempts) {
print('Isolate $debugName was never paused, refusing to collect coverage');
return const <String, dynamic>{
'type': 'CodeCoverage',
'coverage': <Object>[]
};
}
print('isolate is paused, collecting coverage...');
return _getAllCoverage(vmService, libraryPredicate);
}
......
......@@ -23,7 +23,7 @@ void main() {
.thenAnswer((Invocation invocation) async {
return <String, Object>{'type': 'Sentinel', 'kind': 'Collected', 'valueAsString': '<collected>'};
});
final Map<String, Object> result = await collect(null, (String predicate) => true, (Uri uri) async {
final Map<String, Object> result = await collect(null, (String predicate) => true, connector: (Uri uri) async {
return mockVMService;
});
......
......@@ -77,8 +77,10 @@ class VMPlatform extends PlatformPlugin {
final dynamic channel = IsolateChannel<Object>.connectReceive(receivePort)
.transformStream(StreamTransformer<Object, Object>.fromHandlers(handleDone: (EventSink<Object> sink) async {
try {
// Pause the isolate so it is ready for coverage collection.
isolate.pause();
// this will throw if collection fails.
await coverageCollector.collectCoverageIsolate(info.serverUri);
await coverageCollector.collectCoverageIsolate(info.serverUri, path);
} finally {
isolate.kill(priority: Isolate.immediate);
isolate = null;
......@@ -115,6 +117,7 @@ class VMPlatform extends PlatformPlugin {
return await Isolate.spawnUri(p.toUri(testPath), <String>[], message,
packageConfig: p.toUri('.packages'),
checked: true,
debugName: path,
);
}
......
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