| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| /// Convenience methods for location-agnostic Flutter application driving. Can |
| /// be run on either a host machine (making a remote connection to a Fuchsia |
| /// device), or on the target Fuchsia machine. |
| library fuchsia_driver; |
| |
| import 'dart:async'; |
| import 'dart:core'; |
| import 'dart:io'; |
| |
| import 'package:lib.app.dart/logging.dart'; |
| import 'package:flutter_driver/flutter_driver.dart'; |
| import 'package:fuchsia_remote_debug_protocol/fuchsia_remote_debug_protocol.dart'; |
| |
| /// Convenience method for driving an `Isolate` by pattern. |
| /// |
| /// Accepts a [FuchsiaRemoteConnection] that will be used to search for the |
| /// [Pattern] passed. If the pattern cannot be found an exception will be |
| /// raised. Once the `Isolate` is found, the [driverFunction] will be executed, |
| /// passing the [FlutterDriver] connection to the function to execute the series |
| /// of driver commands. |
| /// |
| /// example: |
| /// |
| /// ```dart |
| /// FuchsiaRemoteConnection connection = await FuchsiaDriver.connect(); |
| /// |
| /// Future<Null> tapWidget(FlutterDriver driver) { |
| /// await driver.tap(find.text('foo')); |
| /// } |
| /// |
| /// drive( |
| /// isolatePattern: 'bar', |
| /// driverFunction: tapWidget, |
| /// connection: connection, |
| /// ); |
| /// ``` |
| Future<Null> drive({ |
| FuchsiaRemoteConnection connection, |
| Future<Null> driverFunction(FlutterDriver driver), |
| Pattern isolatePattern, |
| }) async { |
| final List<IsolateRef> isolateRefs = |
| await connection.getMainIsolatesByPattern(isolatePattern); |
| final IsolateRef ref = isolateRefs.first; |
| final FlutterDriver driver = await FlutterDriver.connect( |
| dartVmServiceUrl: ref.dartVm.uri.toString(), |
| isolateNumber: ref.number, |
| printCommunication: true, |
| logCommunicationToFile: false, |
| ); |
| await driverFunction(driver); |
| await driver.close(); |
| } |
| |
| class _DummyPortForwarder implements PortForwarder { |
| _DummyPortForwarder(this._port, this._remotePort); |
| |
| final int _port; |
| final int _remotePort; |
| |
| @override |
| int get port => _port; |
| |
| @override |
| int get remotePort => _remotePort; |
| |
| @override |
| Future<Null> stop() async {} |
| } |
| |
| class _DummySshCommandRunner implements SshCommandRunner { |
| _DummySshCommandRunner(); |
| |
| @override |
| String get sshConfigPath => null; |
| |
| @override |
| String get address => InternetAddress.loopbackIPv4.address; |
| |
| @override |
| String get interface => null; |
| |
| @override |
| Future<List<String>> run(String command) async { |
| if (command.contains('"') || command.contains("'")) { |
| log.warning("The command runner does not support quotes: '$command'"); |
| return <String>[]; |
| } |
| try { |
| final List<String> splitCommand = command.split(' '); |
| final String exe = splitCommand[0]; |
| final List<String> args = splitCommand.skip(1).toList(); |
| final ProcessResult r = await Process.run(exe, args); |
| return r.stdout.split('\n'); |
| } on ProcessException catch (e) { |
| log.warning("Error running '$command': $e"); |
| } |
| return <String>[]; |
| } |
| } |
| |
| Future<PortForwarder> _dummyPortForwardingFunction( |
| String address, |
| int remotePort, [ |
| String interface = '', |
| String configFile, |
| ]) async { |
| return _DummyPortForwarder(remotePort, remotePort); |
| } |
| |
| /// Utility class for creating connections to the Fuchsia Device. |
| /// |
| /// If executed on a host (non-Fuchsia device), behaves the same as running |
| /// [FuchsiaRemoteConnection.connect] whereby the `FUCHSIA_REMOTE_URL` and |
| /// `FUCHSIA_SSH_CONFIG` variables must be set. If run on a Fuchsia device, will |
| /// connect locally without need for environment variables. |
| class FuchsiaDriver { |
| static Future<Null> _init() async { |
| fuchsiaPortForwardingFunction = _dummyPortForwardingFunction; |
| } |
| |
| /// Restores state to normal if running on a Fuchsia device. |
| /// |
| /// Noop if running on the host machine. |
| static Future<Null> cleanup() async { |
| restoreFuchsiaPortForwardingFunction(); |
| } |
| |
| /// Creates a connection to the Fuchsia device's Dart VM's. |
| /// |
| /// See [FuchsiaRemoteConnection.connect] for more details. |
| /// [FuchsiaDriver.cleanup] must be called when the connection is no longer in |
| /// use. It is the caller's responsibility to call |
| /// [FuchsiaRemoteConnection.stop]. |
| static Future<FuchsiaRemoteConnection> connect() async { |
| if (Platform.isFuchsia) { |
| // TODO(FL-74): This is a workaround for flutter driver code that |
| // writes directly to `stderr`, which causes an error in Fuchsia. |
| flutterDriverLog.listen(log.info); |
| await FuchsiaDriver._init(); |
| return FuchsiaRemoteConnection |
| // ignore: invalid_use_of_visible_for_testing_member |
| .connectWithSshCommandRunner(_DummySshCommandRunner()); |
| } |
| return FuchsiaRemoteConnection.connect(); |
| } |
| } |