Unverified Commit 5fa5701d authored by Brian Eaton's avatar Brian Eaton Committed by GitHub

Make sure all isolates start during flutter driver tests. (#61841)

parent d1411a16
...@@ -127,27 +127,6 @@ class VMServiceFlutterDriver extends FlutterDriver { ...@@ -127,27 +127,6 @@ class VMServiceFlutterDriver extends FlutterDriver {
driver._dartVmReconnectUrl = dartVmServiceUrl; driver._dartVmReconnectUrl = dartVmServiceUrl;
// Attempts to resume the isolate, but does not crash if it fails because
// the isolate is already resumed. There could be a race with other tools,
// such as a debugger, any of which could have resumed the isolate.
Future<dynamic> resumeLeniently() {
_log('Attempting to resume isolate');
return isolate.resume().catchError((dynamic e) {
const int vmMustBePausedCode = 101;
if (e is rpc.RpcException && e.code == vmMustBePausedCode) {
// No biggie; something else must have resumed the isolate
_log(
'Attempted to resume an already resumed isolate. This may happen '
'when we lose a race with another tool (usually a debugger) that '
'is connected to the same isolate.'
);
} else {
// Failed to resume due to another reason. Fail hard.
throw e;
}
});
}
/// Waits for a signal from the VM service that the extension is registered. /// Waits for a signal from the VM service that the extension is registered.
/// ///
/// Looks at the list of loaded extensions for the current [isolateRef], as /// Looks at the list of loaded extensions for the current [isolateRef], as
...@@ -195,11 +174,18 @@ class VMServiceFlutterDriver extends FlutterDriver { ...@@ -195,11 +174,18 @@ class VMServiceFlutterDriver extends FlutterDriver {
}); });
} }
// The Dart VM may be running with --pause-isolates-on-start.
// Set a listener to unpause new isolates as they are ready to run,
// otherwise they'll hang indefinitely.
client.onIsolateRunnable.listen((VMIsolateRef isolateRef) async {
_resumeLeniently(await isolateRef.load());
});
// Attempt to resume isolate if it was paused // Attempt to resume isolate if it was paused
if (isolate.pauseEvent is VMPauseStartEvent) { if (isolate.pauseEvent is VMPauseStartEvent) {
_log('Isolate is paused at start.'); _log('Isolate is paused at start.');
await resumeLeniently(); await _resumeLeniently(isolate);
} else if (isolate.pauseEvent is VMPauseExitEvent || } else if (isolate.pauseEvent is VMPauseExitEvent ||
isolate.pauseEvent is VMPauseBreakpointEvent || isolate.pauseEvent is VMPauseBreakpointEvent ||
isolate.pauseEvent is VMPauseExceptionEvent || isolate.pauseEvent is VMPauseExceptionEvent ||
...@@ -207,7 +193,7 @@ class VMServiceFlutterDriver extends FlutterDriver { ...@@ -207,7 +193,7 @@ class VMServiceFlutterDriver extends FlutterDriver {
// If the isolate is paused for any other reason, assume the extension is // If the isolate is paused for any other reason, assume the extension is
// already there. // already there.
_log('Isolate is paused mid-flight.'); _log('Isolate is paused mid-flight.');
await resumeLeniently(); await _resumeLeniently(isolate);
} else if (isolate.pauseEvent is VMResumeEvent) { } else if (isolate.pauseEvent is VMResumeEvent) {
_log('Isolate is not paused. Assuming application is ready.'); _log('Isolate is not paused. Assuming application is ready.');
} else { } else {
...@@ -240,6 +226,27 @@ class VMServiceFlutterDriver extends FlutterDriver { ...@@ -240,6 +226,27 @@ class VMServiceFlutterDriver extends FlutterDriver {
return driver; return driver;
} }
/// Attempts to resume the isolate, but does not crash if it fails because
/// the isolate is already resumed. There could be a race with other tools,
/// such as a debugger, any of which could have resumed the isolate.
static Future<dynamic> _resumeLeniently(VMIsolate isolate) {
_log('Attempting to resume isolate');
return isolate.resume().catchError((dynamic e) {
const int vmMustBePausedCode = 101;
if (e is rpc.RpcException && e.code == vmMustBePausedCode) {
// No biggie; something else must have resumed the isolate
_log(
'Attempted to resume an already resumed isolate. This may happen '
'when we lose a race with another tool (usually a debugger) that '
'is connected to the same isolate.'
);
} else {
// Failed to resume due to another reason. Fail hard.
throw e;
}
});
}
static int _nextDriverId = 0; static int _nextDriverId = 0;
static const String _flutterExtensionMethodName = 'ext.flutter.driver'; static const String _flutterExtensionMethodName = 'ext.flutter.driver';
......
...@@ -35,6 +35,7 @@ void main() { ...@@ -35,6 +35,7 @@ void main() {
MockVM mockVM; MockVM mockVM;
MockIsolate mockIsolate; MockIsolate mockIsolate;
MockPeer mockPeer; MockPeer mockPeer;
MockIsolate otherIsolate;
void expectLogContains(String message) { void expectLogContains(String message) {
expect(log, anyElement(contains(message))); expect(log, anyElement(contains(message)));
...@@ -45,8 +46,12 @@ void main() { ...@@ -45,8 +46,12 @@ void main() {
mockClient = MockVMServiceClient(); mockClient = MockVMServiceClient();
mockVM = MockVM(); mockVM = MockVM();
mockIsolate = MockIsolate(); mockIsolate = MockIsolate();
otherIsolate = MockIsolate();
mockPeer = MockPeer(); mockPeer = MockPeer();
when(mockClient.getVM()).thenAnswer((_) => Future<MockVM>.value(mockVM)); when(mockClient.getVM()).thenAnswer((_) => Future<MockVM>.value(mockVM));
when(mockClient.onIsolateRunnable).thenAnswer((Invocation invocation) {
return Stream<VMIsolateRef>.fromIterable(<VMIsolateRef>[otherIsolate]);
});
when(mockVM.isolates).thenReturn(<VMRunnableIsolate>[mockIsolate]); when(mockVM.isolates).thenReturn(<VMRunnableIsolate>[mockIsolate]);
when(mockIsolate.loadRunnable()).thenAnswer((_) => Future<MockIsolate>.value(mockIsolate)); when(mockIsolate.loadRunnable()).thenAnswer((_) => Future<MockIsolate>.value(mockIsolate));
when(mockIsolate.extensionRpcs).thenReturn(<String>[]); when(mockIsolate.extensionRpcs).thenReturn(<String>[]);
...@@ -60,6 +65,10 @@ void main() { ...@@ -60,6 +65,10 @@ void main() {
VMServiceClientConnection(mockClient, mockPeer) VMServiceClientConnection(mockClient, mockPeer)
); );
}; };
when(otherIsolate.load()).thenAnswer((_) => Future<MockIsolate>.value(otherIsolate));
when(otherIsolate.resume()).thenAnswer((Invocation invocation) {
return Future<dynamic>.value(null);
});
}); });
tearDown(() async { tearDown(() async {
...@@ -77,15 +86,20 @@ void main() { ...@@ -77,15 +86,20 @@ void main() {
connectionLog.add('resume'); connectionLog.add('resume');
return Future<dynamic>.value(null); return Future<dynamic>.value(null);
}); });
when(otherIsolate.pauseEvent).thenReturn(MockVMPauseStartEvent());
when(mockIsolate.onExtensionAdded).thenAnswer((Invocation invocation) { when(mockIsolate.onExtensionAdded).thenAnswer((Invocation invocation) {
connectionLog.add('onExtensionAdded'); connectionLog.add('onExtensionAdded');
return Stream<String>.fromIterable(<String>['ext.flutter.driver']); return Stream<String>.fromIterable(<String>['ext.flutter.driver']);
}); });
when(otherIsolate.resume()).thenAnswer((Invocation invocation) {
connectionLog.add('other-resume');
return Future<dynamic>.value(null);
});
final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: ''); final FlutterDriver driver = await FlutterDriver.connect(dartVmServiceUrl: '');
expect(driver, isNotNull); expect(driver, isNotNull);
expectLogContains('Isolate is paused at start'); expectLogContains('Isolate is paused at start');
expect(connectionLog, <String>['resume', 'streamListen', 'onExtensionAdded']); expect(connectionLog, <String>['resume', 'streamListen', 'other-resume', 'onExtensionAdded']);
}); });
test('connects to isolate paused mid-flight', () async { test('connects to isolate paused mid-flight', () async {
......
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