Connects to a Flutter application.
Resumes the application if it is currently paused (e.g. at a breakpoint).
dartVmServiceUrl
is the URL to Dart observatory (a.k.a. VM service). If
not specified, the URL specified by the VM_SERVICE_URL
environment
variable is used, or 'http://localhost:8183'.
Source
static Future<FlutterDriver> connect({String dartVmServiceUrl}) async { dartVmServiceUrl ??= Platform.environment['VM_SERVICE_URL']; dartVmServiceUrl ??= 'http://localhost:8183'; // Connect to Dart VM servcies _log.info('Connecting to Flutter application at $dartVmServiceUrl'); VMServiceClientConnection connection = await vmServiceConnectFunction(dartVmServiceUrl); VMServiceClient client = connection.client; VM vm = await client.getVM(); _log.trace('Looking for the isolate'); VMIsolate isolate = await vm.isolates.first.loadRunnable(); // TODO(yjbanov): vm_service_client does not support "None" pause event yet. // It is currently reported as `null`, but we cannot rely on it because // eventually the event will be reported as a non-`null` object. For now, // list all the events we know about. Later we'll check for "None" event // explicitly. // // See: https://github.com/dart-lang/vm_service_client/issues/4 if (isolate.pauseEvent is! VMPauseStartEvent && isolate.pauseEvent is! VMPauseExitEvent && isolate.pauseEvent is! VMPauseBreakpointEvent && isolate.pauseEvent is! VMPauseExceptionEvent && isolate.pauseEvent is! VMPauseInterruptedEvent && isolate.pauseEvent is! VMResumeEvent) { await new Future<Null>.delayed(new Duration(milliseconds: 300)); isolate = await vm.isolates.first.loadRunnable(); } FlutterDriver driver = new FlutterDriver.connectedTo(client, connection.peer, isolate); // 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.trace('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.warning( '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; } }); } // Attempt to resume isolate if it was paused if (isolate.pauseEvent is VMPauseStartEvent) { _log.trace('Isolate is paused at start.'); // Waits for a signal from the VM service that the extension is registered Future<String> waitForServiceExtension() { return isolate.onExtensionAdded.firstWhere((String extension) { return extension == _kFlutterExtensionMethod; }); } // If the isolate is paused at the start, e.g. via the --start-paused // option, then the VM service extension is not registered yet. Wait for // it to be registered. Future<dynamic> whenResumed = resumeLeniently(); Future<dynamic> whenServiceExtensionReady = Future.any/*<dynamic>*/(<Future<dynamic>>[ waitForServiceExtension(), // We will never receive the extension event if the user does not // register it. If that happens time out. new Future<String>.delayed(const Duration(seconds: 10), () => 'timeout') ]); await whenResumed; _log.trace('Waiting for service extension'); dynamic signal = await whenServiceExtensionReady; if (signal == 'timeout') { throw new DriverError( 'Timed out waiting for Flutter Driver extension to become available. ' 'Ensure your test app (often: lib/main.dart) imports ' '"package:flutter_driver/driver_extension.dart" and ' 'calls enableFlutterDriverExtension() as the first call in main().' ); } } else if (isolate.pauseEvent is VMPauseExitEvent || isolate.pauseEvent is VMPauseBreakpointEvent || isolate.pauseEvent is VMPauseExceptionEvent || isolate.pauseEvent is VMPauseInterruptedEvent) { // If the isolate is paused for any other reason, assume the extension is // already there. _log.trace('Isolate is paused mid-flight.'); await resumeLeniently(); } else if (isolate.pauseEvent is VMResumeEvent) { _log.trace('Isolate is not paused. Assuming application is ready.'); } else { _log.warning( 'Unknown pause event type ${isolate.pauseEvent.runtimeType}. ' 'Assuming application is ready.' ); } // At this point the service extension must be installed. Verify it. Health health = await driver.checkHealth(); if (health.status != HealthStatus.ok) { await client.close(); throw new DriverError('Flutter application health check failed.'); } _log.info('Connected to Flutter application.'); return driver; }