Unverified Commit 6a69f8c9 authored by Zachary Anderson's avatar Zachary Anderson Committed by GitHub

[fuchsia] Add support for the 'device' command using the SDK (#31910)

parent 06973f58
Ow5Xdviq7OwKr7XNuf-Bw0nBMeAr849mFn7gc_RUpzUC mfXzGfxNWcf6BHsv083b56vQcj96yCo0exBFBdjE4gMC
...@@ -72,7 +72,7 @@ Future<T> runInContext<T>( ...@@ -72,7 +72,7 @@ Future<T> runInContext<T>(
DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance, DoctorValidatorsProvider: () => DoctorValidatorsProvider.defaultInstance,
EmulatorManager: () => EmulatorManager(), EmulatorManager: () => EmulatorManager(),
FuchsiaSdk: () => FuchsiaSdk(), FuchsiaSdk: () => FuchsiaSdk(),
FuchsiaArtifacts: () => FuchsiaArtifacts(), FuchsiaArtifacts: () => FuchsiaArtifacts.find(),
FuchsiaWorkflow: () => FuchsiaWorkflow(), FuchsiaWorkflow: () => FuchsiaWorkflow(),
Flags: () => const EmptyFlags(), Flags: () => const EmptyFlags(),
FlutterVersion: () => FlutterVersion(const SystemClock()), FlutterVersion: () => FlutterVersion(const SystemClock()),
......
...@@ -35,7 +35,8 @@ Future<VMService> _kDefaultFuchsiaIsolateDiscoveryConnector(Uri uri) { ...@@ -35,7 +35,8 @@ Future<VMService> _kDefaultFuchsiaIsolateDiscoveryConnector(Uri uri) {
class _FuchsiaLogReader extends DeviceLogReader { class _FuchsiaLogReader extends DeviceLogReader {
_FuchsiaLogReader(this._device, [this._app]); _FuchsiaLogReader(this._device, [this._app]);
static final RegExp _flutterLogOutput = RegExp(r'INFO: \w+\(flutter\): '); // \S matches non-whitespace characters.
static final RegExp _flutterLogOutput = RegExp(r'INFO: \S+\(flutter\): ');
FuchsiaDevice _device; FuchsiaDevice _device;
ApplicationPackage _app; ApplicationPackage _app;
...@@ -46,7 +47,7 @@ class _FuchsiaLogReader extends DeviceLogReader { ...@@ -46,7 +47,7 @@ class _FuchsiaLogReader extends DeviceLogReader {
Stream<String> _logLines; Stream<String> _logLines;
@override @override
Stream<String> get logLines { Stream<String> get logLines {
_logLines ??= _processLogs(fuchsiaSdk.syslogs()); _logLines ??= _processLogs(fuchsiaSdk.syslogs(_device.id));
return _logLines; return _logLines;
} }
...@@ -90,16 +91,19 @@ class _FuchsiaLogSink implements EventSink<String> { ...@@ -90,16 +91,19 @@ class _FuchsiaLogSink implements EventSink<String> {
if (logTime.millisecondsSinceEpoch < _startTime.millisecondsSinceEpoch) { if (logTime.millisecondsSinceEpoch < _startTime.millisecondsSinceEpoch) {
return; return;
} }
_outputSink.add('[${logTime.toLocal()}] Flutter: ${line.split(_matchRegExp).last}'); _outputSink.add(
'[${logTime.toLocal()}] Flutter: ${line.split(_matchRegExp).last}');
} }
@override @override
void addError(Object error, [ StackTrace stackTrace ]) { void addError(Object error, [StackTrace stackTrace]) {
_outputSink.addError(error, stackTrace); _outputSink.addError(error, stackTrace);
} }
@override @override
void close() { _outputSink.close(); } void close() {
_outputSink.close();
}
} }
class FuchsiaDevices extends PollingDeviceDiscovery { class FuchsiaDevices extends PollingDeviceDiscovery {
...@@ -146,7 +150,7 @@ List<FuchsiaDevice> parseListDevices(String text) { ...@@ -146,7 +150,7 @@ List<FuchsiaDevice> parseListDevices(String text) {
} }
class FuchsiaDevice extends Device { class FuchsiaDevice extends Device {
FuchsiaDevice(String id, { this.name }) : super(id); FuchsiaDevice(String id, {this.name}) : super(id);
@override @override
bool get supportsHotReload => true; bool get supportsHotReload => true;
...@@ -191,7 +195,8 @@ class FuchsiaDevice extends Device { ...@@ -191,7 +195,8 @@ class FuchsiaDevice extends Device {
bool prebuiltApplication = false, bool prebuiltApplication = false,
bool usesTerminalUi = true, bool usesTerminalUi = true,
bool ipv6 = false, bool ipv6 = false,
}) => Future<void>.error('unimplemented'); }) =>
Future<void>.error('unimplemented');
@override @override
Future<bool> stopApp(ApplicationPackage app) async { Future<bool> stopApp(ApplicationPackage app) async {
...@@ -206,15 +211,17 @@ class FuchsiaDevice extends Device { ...@@ -206,15 +211,17 @@ class FuchsiaDevice extends Device {
Future<String> get sdkNameAndVersion async => 'Fuchsia'; Future<String> get sdkNameAndVersion async => 'Fuchsia';
@override @override
DeviceLogReader getLogReader({ ApplicationPackage app }) => _logReader ??= _FuchsiaLogReader(this, app); DeviceLogReader getLogReader({ApplicationPackage app}) =>
_logReader ??= _FuchsiaLogReader(this, app);
_FuchsiaLogReader _logReader; _FuchsiaLogReader _logReader;
@override @override
DevicePortForwarder get portForwarder => _portForwarder ??= _FuchsiaPortForwarder(this); DevicePortForwarder get portForwarder =>
_portForwarder ??= _FuchsiaPortForwarder(this);
_FuchsiaPortForwarder _portForwarder; _FuchsiaPortForwarder _portForwarder;
@override @override
void clearLogs() { } void clearLogs() {}
@override @override
bool get supportsScreenshot => false; bool get supportsScreenshot => false;
...@@ -234,7 +241,8 @@ class FuchsiaDevice extends Device { ...@@ -234,7 +241,8 @@ class FuchsiaDevice extends Device {
Future<List<int>> servicePorts() async { Future<List<int>> servicePorts() async {
final String findOutput = await shell('find /hub -name vmservice-port'); final String findOutput = await shell('find /hub -name vmservice-port');
if (findOutput.trim() == '') { if (findOutput.trim() == '') {
throwToolExit('No Dart Observatories found. Are you running a debug build?'); throwToolExit(
'No Dart Observatories found. Are you running a debug build?');
return null; return null;
} }
final List<int> ports = <int>[]; final List<int> ports = <int>[];
...@@ -259,9 +267,15 @@ class FuchsiaDevice extends Device { ...@@ -259,9 +267,15 @@ class FuchsiaDevice extends Device {
/// Run `command` on the Fuchsia device shell. /// Run `command` on the Fuchsia device shell.
Future<String> shell(String command) async { Future<String> shell(String command) async {
final RunResult result = await runAsync(<String>[ final RunResult result = await runAsync(<String>[
'ssh', '-F', fuchsiaArtifacts.sshConfig.absolute.path, id, command]); 'ssh',
'-F',
fuchsiaArtifacts.sshConfig.absolute.path,
id,
command
]);
if (result.exitCode != 0) { if (result.exitCode != 0) {
throwToolExit('Command failed: $command\nstdout: ${result.stdout}\nstderr: ${result.stderr}'); throwToolExit(
'Command failed: $command\nstdout: ${result.stdout}\nstderr: ${result.stderr}');
return null; return null;
} }
return result.stdout; return result.stdout;
...@@ -302,7 +316,9 @@ class FuchsiaDevice extends Device { ...@@ -302,7 +316,9 @@ class FuchsiaDevice extends Device {
return null; return null;
} }
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(String isolateName) => FuchsiaIsolateDiscoveryProtocol(this, isolateName); FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(
String isolateName) =>
FuchsiaIsolateDiscoveryProtocol(this, isolateName);
@override @override
bool isSupportedForProject(FlutterProject flutterProject) => true; bool isSupportedForProject(FlutterProject flutterProject) => true;
...@@ -341,6 +357,7 @@ class FuchsiaIsolateDiscoveryProtocol { ...@@ -341,6 +357,7 @@ class FuchsiaIsolateDiscoveryProtocol {
return uri; return uri;
}); });
} }
Uri _uri; Uri _uri;
void dispose() { void dispose() {
...@@ -402,13 +419,22 @@ class _FuchsiaPortForwarder extends DevicePortForwarder { ...@@ -402,13 +419,22 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
final Map<int, Process> _processes = <int, Process>{}; final Map<int, Process> _processes = <int, Process>{};
@override @override
Future<int> forward(int devicePort, { int hostPort }) async { Future<int> forward(int devicePort, {int hostPort}) async {
hostPort ??= await _findPort(); hostPort ??= await _findPort();
// Note: the provided command works around a bug in -N, see US-515 // Note: the provided command works around a bug in -N, see US-515
// for more explanation. // for more explanation.
final List<String> command = <String>[ final List<String> command = <String>[
'ssh', '-6', '-F', fuchsiaArtifacts.sshConfig.absolute.path, '-nNT', '-vvv', '-f', 'ssh',
'-L', '$hostPort:$_ipv4Loopback:$devicePort', device.id, 'true', '-6',
'-F',
fuchsiaArtifacts.sshConfig.absolute.path,
'-nNT',
'-vvv',
'-f',
'-L',
'$hostPort:$_ipv4Loopback:$devicePort',
device.id,
'true',
]; ];
final Process process = await processManager.start(command); final Process process = await processManager.start(command);
unawaited(process.exitCode.then((int exitCode) { unawaited(process.exitCode.then((int exitCode) {
...@@ -431,8 +457,16 @@ class _FuchsiaPortForwarder extends DevicePortForwarder { ...@@ -431,8 +457,16 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
final Process process = _processes.remove(forwardedPort.hostPort); final Process process = _processes.remove(forwardedPort.hostPort);
process?.kill(); process?.kill();
final List<String> command = <String>[ final List<String> command = <String>[
'ssh', '-F', fuchsiaArtifacts.sshConfig.absolute.path, '-O', 'cancel', '-vvv', 'ssh',
'-L', '${forwardedPort.hostPort}:$_ipv4Loopback:${forwardedPort.devicePort}', device.id]; '-F',
fuchsiaArtifacts.sshConfig.absolute.path,
'-O',
'cancel',
'-vvv',
'-L',
'${forwardedPort.hostPort}:$_ipv4Loopback:${forwardedPort.devicePort}',
device.id
];
final ProcessResult result = await processManager.run(command); final ProcessResult result = await processManager.run(command);
if (result.exitCode != 0) { if (result.exitCode != 0) {
throwToolExit(result.stderr); throwToolExit(result.stderr);
...@@ -449,8 +483,9 @@ class _FuchsiaPortForwarder extends DevicePortForwarder { ...@@ -449,8 +483,9 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
// Failures are signaled by a return value of 0 from this function. // Failures are signaled by a return value of 0 from this function.
printTrace('_findPort failed: $e'); printTrace('_findPort failed: $e');
} }
if (serverSocket != null) if (serverSocket != null) {
await serverSocket.close(); await serverSocket.close();
}
return port; return port;
} }
} }
......
...@@ -7,8 +7,10 @@ import 'dart:async'; ...@@ -7,8 +7,10 @@ import 'dart:async';
import '../base/context.dart'; import '../base/context.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/process_manager.dart'; import '../base/process_manager.dart';
import '../cache.dart';
import '../convert.dart'; import '../convert.dart';
import '../globals.dart'; import '../globals.dart';
...@@ -23,8 +25,6 @@ FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>(); ...@@ -23,8 +25,6 @@ FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>();
/// This workflow assumes development within the fuchsia source tree, /// This workflow assumes development within the fuchsia source tree,
/// including a working fx command-line tool in the user's PATH. /// including a working fx command-line tool in the user's PATH.
class FuchsiaSdk { class FuchsiaSdk {
static const List<String> _syslogCommand = <String>['fx', 'syslog', '--clock', 'Local'];
/// Example output: /// Example output:
/// $ dev_finder list -full /// $ dev_finder list -full
/// > 192.168.42.56 paper-pulp-bush-angel /// > 192.168.42.56 paper-pulp-bush-angel
...@@ -42,19 +42,33 @@ class FuchsiaSdk { ...@@ -42,19 +42,33 @@ class FuchsiaSdk {
/// Returns the fuchsia system logs for an attached device. /// Returns the fuchsia system logs for an attached device.
/// ///
/// Does not currently support multiple attached devices. /// Does not currently support multiple attached devices.
Stream<String> syslogs() { Stream<String> syslogs(String id) {
Process process; Process process;
try { try {
final StreamController<String> controller = StreamController<String>(onCancel: () { final StreamController<String> controller =
StreamController<String>(onCancel: () {
process.kill(); process.kill();
}); });
processManager.start(_syslogCommand).then((Process newProcess) { if (fuchsiaArtifacts.sshConfig == null) {
return null;
}
const String remoteCommand = 'log_listener --clock Local';
final List<String> cmd = <String>[
'ssh',
'-F',
fuchsiaArtifacts.sshConfig.absolute.path,
id,
remoteCommand
];
processManager.start(cmd).then((Process newProcess) {
if (controller.isClosed) { if (controller.isClosed) {
return; return;
} }
process = newProcess; process = newProcess;
process.exitCode.whenComplete(controller.close); process.exitCode.whenComplete(controller.close);
controller.addStream(process.stdout.transform(utf8.decoder).transform(const LineSplitter())); controller.addStream(process.stdout
.transform(utf8.decoder)
.transform(const LineSplitter()));
}); });
return controller.stream; return controller.stream;
} catch (exception) { } catch (exception) {
...@@ -69,6 +83,35 @@ class FuchsiaArtifacts { ...@@ -69,6 +83,35 @@ class FuchsiaArtifacts {
/// Creates a new [FuchsiaArtifacts]. /// Creates a new [FuchsiaArtifacts].
FuchsiaArtifacts({this.sshConfig, this.devFinder}); FuchsiaArtifacts({this.sshConfig, this.devFinder});
/// Creates a new [FuchsiaArtifacts] using the cached Fuchsia SDK.
///
/// Finds tools under bin/cache/artifacts/fuchsia/tools.
/// Queries environment variables (first FUCHSIA_BUILD_DIR, then
/// FUCHSIA_SSH_CONFIG) to find the ssh configuration needed to talk to
/// a device.
factory FuchsiaArtifacts.find() {
final String fuchsia = Cache.instance.getArtifactDirectory('fuchsia').path;
final String tools = fs.path.join(fuchsia, 'tools');
// If FUCHSIA_BUILD_DIR is defined, then look for the ssh_config dir
// relative to it. Next, if FUCHSIA_SSH_CONFIG is defined, then use it.
// TODO(zra): Consider passing the ssh config path in with a flag.
File sshConfig;
if (platform.environment.containsKey(_kFuchsiaBuildDir)) {
sshConfig = fs.file(fs.path.join(
platform.environment[_kFuchsiaSshConfig], 'ssh-keys', 'ssh_config'));
} else if (platform.environment.containsKey(_kFuchsiaSshConfig)) {
sshConfig = fs.file(platform.environment[_kFuchsiaSshConfig]);
}
return FuchsiaArtifacts(
sshConfig: sshConfig,
devFinder: fs.file(fs.path.join(tools, 'dev_finder')),
);
}
static const String _kFuchsiaSshConfig = 'FUCHSIA_SSH_CONFIG';
static const String _kFuchsiaBuildDir = 'FUCHSIA_BUILD_DIR';
/// The location of the SSH configuration file used to interact with a /// The location of the SSH configuration file used to interact with a
/// Fuchsia device. /// Fuchsia device.
final File sshConfig; final File sshConfig;
......
...@@ -66,7 +66,8 @@ void main() { ...@@ -66,7 +66,8 @@ void main() {
any, any,
environment: anyNamed('environment'), environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
)).thenAnswer((Invocation invocation) => Future<ProcessResult>.value(mockProcessResult)); )).thenAnswer((Invocation invocation) =>
Future<ProcessResult>.value(mockProcessResult));
when(mockProcessResult.exitCode).thenReturn(1); when(mockProcessResult.exitCode).thenReturn(1);
when<String>(mockProcessResult.stdout).thenReturn(''); when<String>(mockProcessResult.stdout).thenReturn('');
when<String>(mockProcessResult.stderr).thenReturn(''); when<String>(mockProcessResult.stderr).thenReturn('');
...@@ -79,7 +80,8 @@ void main() { ...@@ -79,7 +80,8 @@ void main() {
any, any,
environment: anyNamed('environment'), environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
)).thenAnswer((Invocation invocation) => Future<ProcessResult>.value(emptyStdoutProcessResult)); )).thenAnswer((Invocation invocation) =>
Future<ProcessResult>.value(emptyStdoutProcessResult));
when(emptyStdoutProcessResult.exitCode).thenReturn(0); when(emptyStdoutProcessResult.exitCode).thenReturn(0);
when<String>(emptyStdoutProcessResult.stdout).thenReturn(''); when<String>(emptyStdoutProcessResult.stdout).thenReturn('');
when<String>(emptyStdoutProcessResult.stderr).thenReturn(''); when<String>(emptyStdoutProcessResult.stderr).thenReturn('');
...@@ -92,7 +94,10 @@ void main() { ...@@ -92,7 +94,10 @@ void main() {
} on ToolExit catch (err) { } on ToolExit catch (err) {
toolExit = err; toolExit = err;
} }
expect(toolExit.message, contains('No Dart Observatories found. Are you running a debug build?')); expect(
toolExit.message,
contains(
'No Dart Observatories found. Are you running a debug build?'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => emptyStdoutProcessManager, ProcessManager: () => emptyStdoutProcessManager,
FuchsiaArtifacts: () => FuchsiaArtifacts( FuchsiaArtifacts: () => FuchsiaArtifacts(
...@@ -103,12 +108,12 @@ void main() { ...@@ -103,12 +108,12 @@ void main() {
group('device logs', () { group('device logs', () {
const String exampleUtcLogs = ''' const String exampleUtcLogs = '''
[2018-11-09 01:27:45][3][297950920][log] INFO: example_app(flutter): Error doing thing [2018-11-09 01:27:45][3][297950920][log] INFO: example_app.cmx(flutter): Error doing thing
[2018-11-09 01:27:58][46257][46269][foo] INFO: Using a thing [2018-11-09 01:27:58][46257][46269][foo] INFO: Using a thing
[2018-11-09 01:29:58][46257][46269][foo] INFO: Blah blah blah [2018-11-09 01:29:58][46257][46269][foo] INFO: Blah blah blah
[2018-11-09 01:29:58][46257][46269][foo] INFO: other_app(flutter): Do thing [2018-11-09 01:29:58][46257][46269][foo] INFO: other_app.cmx(flutter): Do thing
[2018-11-09 01:30:02][41175][41187][bar] INFO: Invoking a bar [2018-11-09 01:30:02][41175][41187][bar] INFO: Invoking a bar
[2018-11-09 01:30:12][52580][52983][log] INFO: example_app(flutter): Did thing this time [2018-11-09 01:30:12][52580][52983][log] INFO: example_app.cmx(flutter): Did thing this time
'''; ''';
final MockProcessManager mockProcessManager = MockProcessManager(); final MockProcessManager mockProcessManager = MockProcessManager();
...@@ -116,11 +121,17 @@ void main() { ...@@ -116,11 +121,17 @@ void main() {
Completer<int> exitCode; Completer<int> exitCode;
StreamController<List<int>> stdout; StreamController<List<int>> stdout;
StreamController<List<int>> stderr; StreamController<List<int>> stderr;
when(mockProcessManager.start(any)).thenAnswer((Invocation _) => Future<Process>.value(mockProcess)); when(mockProcessManager.start(any))
.thenAnswer((Invocation _) => Future<Process>.value(mockProcess));
when(mockProcess.exitCode).thenAnswer((Invocation _) => exitCode.future); when(mockProcess.exitCode).thenAnswer((Invocation _) => exitCode.future);
when(mockProcess.stdout).thenAnswer((Invocation _) => stdout.stream); when(mockProcess.stdout).thenAnswer((Invocation _) => stdout.stream);
when(mockProcess.stderr).thenAnswer((Invocation _) => stderr.stream); when(mockProcess.stderr).thenAnswer((Invocation _) => stderr.stream);
final MockFile devFinder = MockFile();
final MockFile sshConfig = MockFile();
when(devFinder.absolute).thenReturn(devFinder);
when(sshConfig.absolute).thenReturn(sshConfig);
setUp(() { setUp(() {
stdout = StreamController<List<int>>(sync: true); stdout = StreamController<List<int>>(sync: true);
stderr = StreamController<List<int>>(sync: true); stderr = StreamController<List<int>>(sync: true);
...@@ -133,7 +144,8 @@ void main() { ...@@ -133,7 +144,8 @@ void main() {
testUsingContext('can be parsed for an app', () async { testUsingContext('can be parsed for an app', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester'); final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app')); final DeviceLogReader reader = device.getLogReader(
app: FuchsiaModulePackage(name: 'example_app.cmx'));
final List<String> logLines = <String>[]; final List<String> logLines = <String>[];
final Completer<void> lock = Completer<void>(); final Completer<void> lock = Completer<void>();
reader.logLines.listen((String line) { reader.logLines.listen((String line) {
...@@ -155,11 +167,14 @@ void main() { ...@@ -155,11 +167,14 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)), SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
FuchsiaArtifacts: () =>
FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig),
}); });
testUsingContext('cuts off prior logs', () async { testUsingContext('cuts off prior logs', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester'); final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app')); final DeviceLogReader reader = device.getLogReader(
app: FuchsiaModulePackage(name: 'example_app.cmx'));
final List<String> logLines = <String>[]; final List<String> logLines = <String>[];
final Completer<void> lock = Completer<void>(); final Completer<void> lock = Completer<void>();
reader.logLines.listen((String line) { reader.logLines.listen((String line) {
...@@ -178,6 +193,8 @@ void main() { ...@@ -178,6 +193,8 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 29, 45)), SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 29, 45)),
FuchsiaArtifacts: () =>
FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig),
}); });
testUsingContext('can be parsed for all apps', () async { testUsingContext('can be parsed for all apps', () async {
...@@ -205,12 +222,15 @@ void main() { ...@@ -205,12 +222,15 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)), SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
FuchsiaArtifacts: () =>
FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig),
}); });
}); });
}); });
group(FuchsiaIsolateDiscoveryProtocol, () { group(FuchsiaIsolateDiscoveryProtocol, () {
Future<Uri> findUri(List<MockFlutterView> views, String expectedIsolateName) { Future<Uri> findUri(
List<MockFlutterView> views, String expectedIsolateName) {
final MockPortForwarder portForwarder = MockPortForwarder(); final MockPortForwarder portForwarder = MockPortForwarder();
final MockVMService vmService = MockVMService(); final MockVMService vmService = MockVMService();
final MockVM vm = MockVM(); final MockVM vm = MockVM();
...@@ -220,33 +240,43 @@ void main() { ...@@ -220,33 +240,43 @@ void main() {
for (MockFlutterView view in views) { for (MockFlutterView view in views) {
view.owner = vm; view.owner = vm;
} }
final MockFuchsiaDevice fuchsiaDevice = MockFuchsiaDevice('123', portForwarder, false); final MockFuchsiaDevice fuchsiaDevice =
final FuchsiaIsolateDiscoveryProtocol discoveryProtocol = FuchsiaIsolateDiscoveryProtocol( MockFuchsiaDevice('123', portForwarder, false);
final FuchsiaIsolateDiscoveryProtocol discoveryProtocol =
FuchsiaIsolateDiscoveryProtocol(
fuchsiaDevice, fuchsiaDevice,
expectedIsolateName, expectedIsolateName,
(Uri uri) async => vmService, (Uri uri) async => vmService,
true, // only poll once. true, // only poll once.
); );
when(fuchsiaDevice.servicePorts()).thenAnswer((Invocation invocation) async => <int>[1]); when(fuchsiaDevice.servicePorts())
when(portForwarder.forward(1)).thenAnswer((Invocation invocation) async => 2); .thenAnswer((Invocation invocation) async => <int>[1]);
when(vmService.getVM()).thenAnswer((Invocation invocation) => Future<void>.value(null)); when(portForwarder.forward(1))
when(vmService.refreshViews()).thenAnswer((Invocation invocation) => Future<void>.value(null)); .thenAnswer((Invocation invocation) async => 2);
when(vmService.getVM())
.thenAnswer((Invocation invocation) => Future<void>.value(null));
when(vmService.refreshViews())
.thenAnswer((Invocation invocation) => Future<void>.value(null));
when(vmService.httpAddress).thenReturn(Uri.parse('example')); when(vmService.httpAddress).thenReturn(Uri.parse('example'));
return discoveryProtocol.uri; return discoveryProtocol.uri;
} }
testUsingContext('can find flutter view with matching isolate name', () async {
testUsingContext('can find flutter view with matching isolate name',
() async {
const String expectedIsolateName = 'foobar'; const String expectedIsolateName = 'foobar';
final Uri uri = await findUri(<MockFlutterView>[ final Uri uri = await findUri(<MockFlutterView>[
MockFlutterView(null), // no ui isolate. MockFlutterView(null), // no ui isolate.
MockFlutterView(MockIsolate('wrong name')), // wrong name. MockFlutterView(MockIsolate('wrong name')), // wrong name.
MockFlutterView(MockIsolate(expectedIsolateName)), // matching name. MockFlutterView(MockIsolate(expectedIsolateName)), // matching name.
], expectedIsolateName); ], expectedIsolateName);
expect(uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/'); expect(
uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Logger: () => StdoutLogger(), Logger: () => StdoutLogger(),
}); });
testUsingContext('can handle flutter view without matching isolate name', () async { testUsingContext('can handle flutter view without matching isolate name',
() async {
const String expectedIsolateName = 'foobar'; const String expectedIsolateName = 'foobar';
final Future<Uri> uri = findUri(<MockFlutterView>[ final Future<Uri> uri = findUri(<MockFlutterView>[
MockFlutterView(null), // no ui isolate. MockFlutterView(null), // no ui isolate.
......
...@@ -12,37 +12,47 @@ import '../src/common.dart'; ...@@ -12,37 +12,47 @@ import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {} class MockOperatingSystemUtils extends Mock implements OperatingSystemUtils {}
class MockFile extends Mock implements File {} class MockFile extends Mock implements File {}
void main() { void main() {
group('android workflow', () { group('Fuchsia workflow', () {
final MockFile devFinder = MockFile(); final MockFile devFinder = MockFile();
final MockFile sshConfig = MockFile(); final MockFile sshConfig = MockFile();
when(devFinder.absolute).thenReturn(devFinder); when(devFinder.absolute).thenReturn(devFinder);
when(sshConfig.absolute).thenReturn(sshConfig); when(sshConfig.absolute).thenReturn(sshConfig);
testUsingContext('can not list and launch devices if there is not ssh config and dev finder', () { testUsingContext(
'can not list and launch devices if there is not ssh config and dev finder',
() {
expect(fuchsiaWorkflow.canLaunchDevices, false); expect(fuchsiaWorkflow.canLaunchDevices, false);
expect(fuchsiaWorkflow.canListDevices, false); expect(fuchsiaWorkflow.canListDevices, false);
expect(fuchsiaWorkflow.canListEmulators, false); expect(fuchsiaWorkflow.canListEmulators, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FuchsiaArtifacts: () => FuchsiaArtifacts(devFinder: null, sshConfig: null), FuchsiaArtifacts: () =>
FuchsiaArtifacts(devFinder: null, sshConfig: null),
}); });
testUsingContext('can not list and launch devices if there is not ssh config and dev finder', () { testUsingContext(
'can not list and launch devices if there is not ssh config and dev finder',
() {
expect(fuchsiaWorkflow.canLaunchDevices, false); expect(fuchsiaWorkflow.canLaunchDevices, false);
expect(fuchsiaWorkflow.canListDevices, true); expect(fuchsiaWorkflow.canListDevices, true);
expect(fuchsiaWorkflow.canListEmulators, false); expect(fuchsiaWorkflow.canListEmulators, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FuchsiaArtifacts: () => FuchsiaArtifacts(devFinder: devFinder, sshConfig: null), FuchsiaArtifacts: () =>
FuchsiaArtifacts(devFinder: devFinder, sshConfig: null),
}); });
testUsingContext('can list and launch devices supported if there is a `fx` command', () { testUsingContext(
'can list and launch devices supported with sufficient SDK artifacts',
() {
expect(fuchsiaWorkflow.canLaunchDevices, true); expect(fuchsiaWorkflow.canLaunchDevices, true);
expect(fuchsiaWorkflow.canListDevices, true); expect(fuchsiaWorkflow.canListDevices, true);
expect(fuchsiaWorkflow.canListEmulators, false); expect(fuchsiaWorkflow.canListEmulators, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FuchsiaArtifacts: () => FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig), FuchsiaArtifacts: () =>
FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig),
}); });
}); });
} }
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