Future<FlutterDriver> connect({String dartVmServiceUrl })

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;
}