blob: d9ceff059c2df51e29e90164ad03ab6f936f43ca [file] [log] [blame]
// 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();
}
}