mirror of
https://github.com/flutter/flutter
synced 2024-10-13 11:42:54 +00:00
1041 lines
36 KiB
Dart
1041 lines
36 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'dart:async';
|
|
|
|
import 'package:file/memory.dart';
|
|
import 'package:flutter_tools/src/application_package.dart';
|
|
import 'package:flutter_tools/src/artifacts.dart';
|
|
import 'package:flutter_tools/src/base/dds.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/base/time.dart';
|
|
import 'package:flutter_tools/src/build_info.dart';
|
|
import 'package:flutter_tools/src/cache.dart';
|
|
import 'package:flutter_tools/src/device.dart';
|
|
import 'package:flutter_tools/src/device_port_forwarder.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_dev_finder.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_device.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_ffx.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_kernel_compiler.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_pm.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart';
|
|
import 'package:flutter_tools/src/fuchsia/fuchsia_workflow.dart';
|
|
import 'package:flutter_tools/src/globals_null_migrated.dart' as globals;
|
|
import 'package:flutter_tools/src/project.dart';
|
|
import 'package:flutter_tools/src/vmservice.dart';
|
|
import 'package:meta/meta.dart';
|
|
import 'package:test/fake.dart';
|
|
import 'package:vm_service/vm_service.dart' as vm_service;
|
|
|
|
import '../../src/common.dart';
|
|
import '../../src/context.dart';
|
|
import '../../src/fake_vm_services.dart';
|
|
|
|
final vm_service.Isolate fakeIsolate = vm_service.Isolate(
|
|
id: '1',
|
|
pauseEvent: vm_service.Event(
|
|
kind: vm_service.EventKind.kResume,
|
|
timestamp: 0
|
|
),
|
|
breakpoints: <vm_service.Breakpoint>[],
|
|
exceptionPauseMode: null,
|
|
libraries: <vm_service.LibraryRef>[],
|
|
livePorts: 0,
|
|
name: 'wrong name',
|
|
number: '1',
|
|
pauseOnExit: false,
|
|
runnable: true,
|
|
startTime: 0,
|
|
isSystemIsolate: false,
|
|
isolateFlags: <vm_service.IsolateFlag>[],
|
|
);
|
|
|
|
void main() {
|
|
group('fuchsia device', () {
|
|
MemoryFileSystem memoryFileSystem;
|
|
File sshConfig;
|
|
FakeProcessManager processManager;
|
|
|
|
setUp(() {
|
|
memoryFileSystem = MemoryFileSystem.test();
|
|
sshConfig = memoryFileSystem.file('ssh_config')..writeAsStringSync('\n');
|
|
processManager = FakeProcessManager.empty();
|
|
});
|
|
|
|
testWithoutContext('stores the requested id and name', () {
|
|
const String deviceId = 'e80::0000:a00a:f00f:2002/3';
|
|
const String name = 'halfbaked';
|
|
final FuchsiaDevice device = FuchsiaDevice(deviceId, name: name);
|
|
|
|
expect(device.id, deviceId);
|
|
expect(device.name, name);
|
|
});
|
|
|
|
testWithoutContext('supports all runtime modes besides jitRelease', () {
|
|
const String deviceId = 'e80::0000:a00a:f00f:2002/3';
|
|
const String name = 'halfbaked';
|
|
final FuchsiaDevice device = FuchsiaDevice(deviceId, name: name);
|
|
|
|
expect(device.supportsRuntimeMode(BuildMode.debug), true);
|
|
expect(device.supportsRuntimeMode(BuildMode.profile), true);
|
|
expect(device.supportsRuntimeMode(BuildMode.release), true);
|
|
expect(device.supportsRuntimeMode(BuildMode.jitRelease), false);
|
|
});
|
|
|
|
testWithoutContext('lists nothing when workflow cannot list devices', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(canListDevices: false);
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
fuchsiaSdk: null,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
expect(fuchsiaDevices.canListAnything, false);
|
|
expect(await fuchsiaDevices.pollingGetDevices(), isEmpty);
|
|
});
|
|
|
|
testWithoutContext('can parse ffx output for single device', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(shouldUseDeviceFinder: false, canListDevices: true);
|
|
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices: '2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel');
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux', environment: <String, String>{},),
|
|
fuchsiaSdk: fuchsiaSdk,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
final Device device = (await fuchsiaDevices.pollingGetDevices()).single;
|
|
|
|
expect(device.name, 'paper-pulp-bush-angel');
|
|
expect(device.id, '192.168.42.10');
|
|
});
|
|
|
|
testWithoutContext('can parse device-finder output for single device', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(shouldUseDeviceFinder: true, canListDevices: true);
|
|
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devFinderDevices: '2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel');
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux', environment: <String, String>{'FUCHSIA_DISABLED_ffx_discovery': '1'},),
|
|
fuchsiaSdk: fuchsiaSdk,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
final Device device = (await fuchsiaDevices.pollingGetDevices()).single;
|
|
|
|
expect(device.name, 'paper-pulp-bush-angel');
|
|
expect(device.id, '192.168.11.999');
|
|
});
|
|
|
|
testWithoutContext('can parse ffx output for multiple devices', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(shouldUseDeviceFinder: false, canListDevices: true);
|
|
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices:
|
|
'2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel\n'
|
|
'2001:0db8:85a3:0000:0000:8a2e:0370:7335 foo-bar-fiz-buzz'
|
|
);
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
fuchsiaSdk: fuchsiaSdk,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
final List<Device> devices = await fuchsiaDevices.pollingGetDevices();
|
|
|
|
expect(devices.first.name, 'paper-pulp-bush-angel');
|
|
expect(devices.first.id, '192.168.42.10');
|
|
expect(devices.last.name, 'foo-bar-fiz-buzz');
|
|
expect(devices.last.id, '192.168.42.10');
|
|
});
|
|
|
|
testWithoutContext('can parse device-finder output for multiple devices', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(shouldUseDeviceFinder: true, canListDevices: true);
|
|
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devFinderDevices:
|
|
'2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel\n'
|
|
'2001:0db8:85a3:0000:0000:8a2e:0370:7335 foo-bar-fiz-buzz'
|
|
);
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
fuchsiaSdk: fuchsiaSdk,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
final List<Device> devices = await fuchsiaDevices.pollingGetDevices();
|
|
|
|
expect(devices.first.name, 'paper-pulp-bush-angel');
|
|
expect(devices.first.id, '192.168.11.999');
|
|
expect(devices.last.name, 'foo-bar-fiz-buzz');
|
|
expect(devices.last.id, '192.168.11.999');
|
|
});
|
|
|
|
testWithoutContext('can parse junk output from ffx', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(shouldUseDeviceFinder: false, canListDevices: false);
|
|
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices: 'junk', devFinderDevices: 'junk');
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
fuchsiaSdk: fuchsiaSdk,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
final List<Device> devices = await fuchsiaDevices.pollingGetDevices();
|
|
|
|
expect(devices, isEmpty);
|
|
});
|
|
|
|
testWithoutContext('can parse junk output from device-finder', () async {
|
|
final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(shouldUseDeviceFinder: true, canListDevices: true);
|
|
final FakeFuchsiaSdk fuchsiaSdk = FakeFuchsiaSdk(devices: 'junk', devFinderDevices: 'junk');
|
|
final FuchsiaDevices fuchsiaDevices = FuchsiaDevices(
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
fuchsiaSdk: fuchsiaSdk,
|
|
fuchsiaWorkflow: fuchsiaWorkflow,
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
final List<Device> devices = await fuchsiaDevices.pollingGetDevices();
|
|
|
|
expect(devices, isEmpty);
|
|
});
|
|
|
|
testUsingContext('disposing device disposes the portForwarder', () async {
|
|
final FakePortForwarder portForwarder = FakePortForwarder();
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
device.portForwarder = portForwarder;
|
|
await device.dispose();
|
|
|
|
expect(portForwarder.disposed, true);
|
|
});
|
|
|
|
testWithoutContext('default capabilities', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
final FlutterProject project = FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
|
|
memoryFileSystem.directory('fuchsia').createSync(recursive: true);
|
|
memoryFileSystem.file('pubspec.yaml').createSync();
|
|
|
|
expect(device.supportsHotReload, true);
|
|
expect(device.supportsHotRestart, false);
|
|
expect(device.supportsFlutterExit, false);
|
|
expect(device.isSupportedForProject(project), true);
|
|
});
|
|
|
|
test('is ephemeral', () {
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
|
|
expect(device.ephemeral, true);
|
|
});
|
|
|
|
testWithoutContext('supported for project', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
final FlutterProject project = FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
|
|
memoryFileSystem.directory('fuchsia').createSync(recursive: true);
|
|
memoryFileSystem.file('pubspec.yaml').createSync();
|
|
|
|
expect(device.isSupportedForProject(project), true);
|
|
});
|
|
|
|
testWithoutContext('not supported for project', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
final FlutterProject project = FlutterProject.fromDirectoryTest(memoryFileSystem.currentDirectory);
|
|
memoryFileSystem.file('pubspec.yaml').createSync();
|
|
|
|
expect(device.isSupportedForProject(project), false);
|
|
});
|
|
|
|
testUsingContext('targetPlatform does not throw when sshConfig is missing', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
|
|
expect(await device.targetPlatform, TargetPlatform.fuchsia_arm64);
|
|
}, overrides: <Type, Generator>{
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: null),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('targetPlatform arm64 works', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', '123', 'uname -m'],
|
|
stdout: 'aarch64',
|
|
));
|
|
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
expect(await device.targetPlatform, TargetPlatform.fuchsia_arm64);
|
|
}, overrides: <Type, Generator>{
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('targetPlatform x64 works', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', '123', 'uname -m'],
|
|
stdout: 'x86_64',
|
|
));
|
|
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
expect(await device.targetPlatform, TargetPlatform.fuchsia_x64);
|
|
}, overrides: <Type, Generator>{
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('hostAddress parsing works', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'],
|
|
stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22',
|
|
));
|
|
|
|
final FuchsiaDevice device = FuchsiaDevice('id');
|
|
expect(await device.hostAddress, 'fe80::8c6c:2fff:fe3d:c5e1%25ethp0003');
|
|
}, overrides: <Type, Generator>{
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('hostAddress parsing throws tool error on failure', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'],
|
|
exitCode: 1,
|
|
));
|
|
|
|
final FuchsiaDevice device = FuchsiaDevice('id');
|
|
await expectLater(() => device.hostAddress, throwsToolExit());
|
|
}, overrides: <Type, Generator>{
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
ProcessManager: () => processManager,
|
|
});
|
|
|
|
testUsingContext('hostAddress parsing throws tool error on empty response', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'],
|
|
));
|
|
|
|
final FuchsiaDevice device = FuchsiaDevice('id');
|
|
expect(() async => device.hostAddress, throwsToolExit());
|
|
}, overrides: <Type, Generator>{
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
ProcessManager: () => processManager,
|
|
});
|
|
});
|
|
|
|
group('displays friendly error when', () {
|
|
File artifactFile;
|
|
FakeProcessManager processManager;
|
|
|
|
setUp(() {
|
|
processManager = FakeProcessManager.empty();
|
|
artifactFile = MemoryFileSystem.test().file('artifact');
|
|
});
|
|
|
|
testUsingContext('No vmservices found', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/artifact', 'id', 'find /hub -name vmservice-port'],
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('id');
|
|
|
|
await expectLater(device.servicePorts, throwsToolExit(message: 'No Dart Observatories found. Are you running a debug build?'));
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(
|
|
sshConfig: artifactFile,
|
|
devFinder: artifactFile,
|
|
ffx: artifactFile,
|
|
),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
});
|
|
|
|
group('device logs', () {
|
|
const String exampleUtcLogs = '''
|
|
[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:29:58][46257][46269][foo] INFO: Blah blah blah
|
|
[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:12][52580][52983][log] INFO: example_app.cmx(flutter): Did thing this time
|
|
|
|
''';
|
|
FakeProcessManager processManager;
|
|
File devFinder;
|
|
File ffx;
|
|
File sshConfig;
|
|
|
|
setUp(() {
|
|
processManager = FakeProcessManager.empty();
|
|
final FileSystem memoryFileSystem = MemoryFileSystem.test();
|
|
devFinder = memoryFileSystem.file('device-finder')..writeAsStringSync('\n');
|
|
ffx = memoryFileSystem.file('ffx')..writeAsStringSync('\n');
|
|
sshConfig = memoryFileSystem.file('ssh_config')..writeAsStringSync('\n');
|
|
});
|
|
|
|
testUsingContext('can be parsed for an app', () async {
|
|
final Completer<void> lock = Completer<void>();
|
|
processManager.addCommand(FakeCommand(
|
|
command: const <String>['ssh', '-F', '/ssh_config', 'id', 'log_listener --clock Local'],
|
|
stdout: exampleUtcLogs,
|
|
completer: lock,
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
|
|
final List<String> logLines = <String>[];
|
|
reader.logLines.listen((String line) {
|
|
logLines.add(line);
|
|
if (logLines.length == 2) {
|
|
lock.complete();
|
|
}
|
|
});
|
|
expect(logLines, isEmpty);
|
|
|
|
await lock.future;
|
|
expect(logLines, <String>[
|
|
'[2018-11-09 01:27:45.000] Flutter: Error doing thing',
|
|
'[2018-11-09 01:30:12.000] Flutter: Did thing this time',
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
|
|
FuchsiaArtifacts: () =>
|
|
FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig, ffx: ffx),
|
|
});
|
|
|
|
testUsingContext('cuts off prior logs', () async {
|
|
final Completer<void> lock = Completer<void>();
|
|
processManager.addCommand(FakeCommand(
|
|
command: const <String>['ssh', '-F', '/ssh_config', 'id', 'log_listener --clock Local'],
|
|
stdout: exampleUtcLogs,
|
|
completer: lock,
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
final DeviceLogReader reader = device.getLogReader(app: FuchsiaModulePackage(name: 'example_app'));
|
|
final List<String> logLines = <String>[];
|
|
reader.logLines.listen((String line) {
|
|
logLines.add(line);
|
|
lock.complete();
|
|
});
|
|
expect(logLines, isEmpty);
|
|
|
|
await lock.future.timeout(const Duration(seconds: 1));
|
|
|
|
expect(logLines, <String>[
|
|
'[2018-11-09 01:30:12.000] Flutter: Did thing this time',
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 29, 45)),
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig, ffx: ffx),
|
|
});
|
|
|
|
testUsingContext('can be parsed for all apps', () async {
|
|
final Completer<void> lock = Completer<void>();
|
|
processManager.addCommand(FakeCommand(
|
|
command: const <String>['ssh', '-F', '/ssh_config', 'id', 'log_listener --clock Local'],
|
|
stdout: exampleUtcLogs,
|
|
completer: lock,
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
final DeviceLogReader reader = device.getLogReader();
|
|
final List<String> logLines = <String>[];
|
|
reader.logLines.listen((String line) {
|
|
logLines.add(line);
|
|
if (logLines.length == 3) {
|
|
lock.complete();
|
|
}
|
|
});
|
|
expect(logLines, isEmpty);
|
|
|
|
await lock.future.timeout(const Duration(seconds: 1));
|
|
|
|
expect(logLines, <String>[
|
|
'[2018-11-09 01:27:45.000] Flutter: Error doing thing',
|
|
'[2018-11-09 01:29:58.000] Flutter: Do thing',
|
|
'[2018-11-09 01:30:12.000] Flutter: Did thing this time',
|
|
]);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)),
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig, ffx: ffx),
|
|
});
|
|
});
|
|
});
|
|
|
|
group('screenshot', () {
|
|
FakeProcessManager processManager;
|
|
|
|
setUp(() {
|
|
processManager = FakeProcessManager.empty();
|
|
});
|
|
|
|
testUsingContext('is supported on posix platforms', () {
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
expect(device.supportsScreenshot, true);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(
|
|
operatingSystem: 'linux',
|
|
),
|
|
});
|
|
|
|
testUsingContext('is not supported on Windows', () {
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
|
|
expect(device.supportsScreenshot, false);
|
|
}, overrides: <Type, Generator>{
|
|
Platform: () => FakePlatform(
|
|
operatingSystem: 'windows',
|
|
),
|
|
});
|
|
|
|
test("takeScreenshot throws if file isn't .ppm", () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
await expectLater(
|
|
() => device.takeScreenshot(globals.fs.file('file.invalid')),
|
|
throwsA(equals('file.invalid must be a .ppm file')),
|
|
);
|
|
});
|
|
|
|
testUsingContext('takeScreenshot throws if screencap failed', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'screencap > /tmp/screenshot.ppm',
|
|
],
|
|
exitCode: 1,
|
|
stderr: '<error-message>'
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
|
|
|
|
await expectLater(
|
|
() => device.takeScreenshot(globals.fs.file('file.ppm')),
|
|
throwsA(equals('Could not take a screenshot on device tester:\n<error-message>')),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
Platform: () => FakePlatform(
|
|
environment: <String, String>{
|
|
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
|
|
},
|
|
operatingSystem: 'linux',
|
|
),
|
|
});
|
|
|
|
testUsingContext('takeScreenshot throws if scp failed', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'screencap > /tmp/screenshot.ppm',
|
|
],
|
|
));
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'scp',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0:/tmp/screenshot.ppm',
|
|
'file.ppm',
|
|
],
|
|
exitCode: 1,
|
|
stderr: '<error-message>',
|
|
));
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'rm /tmp/screenshot.ppm',
|
|
],
|
|
));
|
|
|
|
await expectLater(
|
|
() => device.takeScreenshot(globals.fs.file('file.ppm')),
|
|
throwsA(equals('Failed to copy screenshot from device:\n<error-message>')),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
Platform: () => FakePlatform(
|
|
environment: <String, String>{
|
|
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
|
|
},
|
|
operatingSystem: 'linux',
|
|
),
|
|
});
|
|
|
|
testUsingContext("takeScreenshot prints error if can't delete file from device", () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'screencap > /tmp/screenshot.ppm',
|
|
],
|
|
));
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'scp',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0:/tmp/screenshot.ppm',
|
|
'file.ppm',
|
|
],
|
|
));
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'rm /tmp/screenshot.ppm',
|
|
],
|
|
exitCode: 1,
|
|
stderr: '<error-message>',
|
|
));
|
|
|
|
await device.takeScreenshot(globals.fs.file('file.ppm'));
|
|
expect(
|
|
testLogger.errorText,
|
|
contains('Failed to delete screenshot.ppm from the device:\n<error-message>'),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
Platform: () => FakePlatform(
|
|
environment: <String, String>{
|
|
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
|
|
},
|
|
operatingSystem: 'linux',
|
|
),
|
|
}, testOn: 'posix');
|
|
|
|
testUsingContext('takeScreenshot returns', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'screencap > /tmp/screenshot.ppm',
|
|
],
|
|
));
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'scp',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0:/tmp/screenshot.ppm',
|
|
'file.ppm',
|
|
],
|
|
));
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>[
|
|
'ssh',
|
|
'-F',
|
|
'/fuchsia/out/default/.ssh',
|
|
'0.0.0.0',
|
|
'rm /tmp/screenshot.ppm',
|
|
],
|
|
));
|
|
|
|
expect(() => device.takeScreenshot(globals.fs.file('file.ppm')), returnsNormally);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FileSystem: () => MemoryFileSystem.test(),
|
|
Platform: () => FakePlatform(
|
|
environment: <String, String>{
|
|
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
|
|
},
|
|
operatingSystem: 'linux',
|
|
),
|
|
});
|
|
});
|
|
|
|
group('portForwarder', () {
|
|
FakeProcessManager processManager;
|
|
File sshConfig;
|
|
|
|
setUp(() {
|
|
processManager = FakeProcessManager.empty();
|
|
sshConfig = MemoryFileSystem.test().file('irrelevant')..writeAsStringSync('\n');
|
|
});
|
|
|
|
testUsingContext('`unforward` prints stdout and stderr if ssh command failed', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/irrelevant', '-O', 'cancel', '-vvv', '-L', '0:127.0.0.1:1', 'id'],
|
|
exitCode: 1,
|
|
stdout: '<stdout>',
|
|
stderr: '<stderr>',
|
|
));
|
|
|
|
await expectLater(
|
|
() => device.portForwarder.unforward(ForwardedPort(/*hostPort=*/ 0, /*devicePort=*/ 1)),
|
|
throwsToolExit(message: 'Unforward command failed:\nstdout: <stdout>\nstderr: <stderr>'),
|
|
);
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
});
|
|
});
|
|
|
|
|
|
group('FuchsiaIsolateDiscoveryProtocol', () {
|
|
Future<Uri> findUri(List<FlutterView> views, String expectedIsolateName) async {
|
|
final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost(
|
|
requests: <VmServiceExpectation>[
|
|
FakeVmServiceRequest(
|
|
method: kListViewsMethod,
|
|
jsonResponse: <String, Object>{
|
|
'views': <Object>[
|
|
for (FlutterView view in views)
|
|
view.toJson()
|
|
],
|
|
},
|
|
),
|
|
],
|
|
httpAddress: Uri.parse('example'),
|
|
);
|
|
final MockFuchsiaDevice fuchsiaDevice = MockFuchsiaDevice('123', const NoOpDevicePortForwarder(), false);
|
|
final FuchsiaIsolateDiscoveryProtocol discoveryProtocol =
|
|
FuchsiaIsolateDiscoveryProtocol(
|
|
fuchsiaDevice,
|
|
expectedIsolateName,
|
|
(Uri uri) async => fakeVmServiceHost.vmService,
|
|
(Device device, Uri uri, bool enableServiceAuthCodes) => null,
|
|
true, // only poll once.
|
|
);
|
|
return discoveryProtocol.uri;
|
|
}
|
|
|
|
testUsingContext('can find flutter view with matching isolate name', () async {
|
|
const String expectedIsolateName = 'foobar';
|
|
final Uri uri = await findUri(<FlutterView>[
|
|
// no ui isolate.
|
|
FlutterView(id: '1', uiIsolate: null),
|
|
// wrong name.
|
|
FlutterView(
|
|
id: '2',
|
|
uiIsolate: vm_service.Isolate.parse(<String, dynamic>{
|
|
...fakeIsolate.toJson(),
|
|
'name': 'Wrong name',
|
|
}),
|
|
),
|
|
// matching name.
|
|
FlutterView(
|
|
id: '3',
|
|
uiIsolate: vm_service.Isolate.parse(<String, dynamic>{
|
|
...fakeIsolate.toJson(),
|
|
'name': expectedIsolateName,
|
|
}),
|
|
),
|
|
], expectedIsolateName);
|
|
|
|
expect(uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/');
|
|
});
|
|
|
|
testUsingContext('can handle flutter view without matching isolate name', () async {
|
|
const String expectedIsolateName = 'foobar';
|
|
final Future<Uri> uri = findUri(<FlutterView>[
|
|
// no ui isolate.
|
|
FlutterView(id: '1', uiIsolate: null),
|
|
// wrong name.
|
|
FlutterView(id: '2', uiIsolate: vm_service.Isolate.parse(<String, Object>{
|
|
...fakeIsolate.toJson(),
|
|
'name': 'wrong name',
|
|
})),
|
|
], expectedIsolateName);
|
|
|
|
expect(uri, throwsException);
|
|
});
|
|
|
|
testUsingContext('can handle non flutter view', () async {
|
|
const String expectedIsolateName = 'foobar';
|
|
final Future<Uri> uri = findUri(<FlutterView>[
|
|
FlutterView(id: '1', uiIsolate: null), // no ui isolate.
|
|
], expectedIsolateName);
|
|
|
|
expect(uri, throwsException);
|
|
});
|
|
});
|
|
|
|
testUsingContext('Correct flutter runner', () async {
|
|
final Cache cache = Cache.test(
|
|
processManager: FakeProcessManager.any(),
|
|
);
|
|
final FileSystem fileSystem = MemoryFileSystem.test();
|
|
final CachedArtifacts artifacts = CachedArtifacts(
|
|
cache: cache,
|
|
fileSystem: fileSystem,
|
|
platform: FakePlatform(operatingSystem: 'linux'),
|
|
operatingSystemUtils: globals.os,
|
|
);
|
|
expect(artifacts.getArtifactPath(
|
|
Artifact.fuchsiaFlutterRunner,
|
|
platform: TargetPlatform.fuchsia_x64,
|
|
mode: BuildMode.debug,
|
|
),
|
|
contains('flutter_jit_runner'),
|
|
);
|
|
expect(artifacts.getArtifactPath(
|
|
Artifact.fuchsiaFlutterRunner,
|
|
platform: TargetPlatform.fuchsia_x64,
|
|
mode: BuildMode.profile,
|
|
),
|
|
contains('flutter_aot_runner'),
|
|
);
|
|
expect(artifacts.getArtifactPath(
|
|
Artifact.fuchsiaFlutterRunner,
|
|
platform: TargetPlatform.fuchsia_x64,
|
|
mode: BuildMode.release,
|
|
),
|
|
contains('flutter_aot_product_runner'),
|
|
);
|
|
expect(artifacts.getArtifactPath(
|
|
Artifact.fuchsiaFlutterRunner,
|
|
platform: TargetPlatform.fuchsia_x64,
|
|
mode: BuildMode.jitRelease,
|
|
),
|
|
contains('flutter_jit_product_runner'),
|
|
);
|
|
});
|
|
|
|
group('sdkNameAndVersion: ', () {
|
|
File sshConfig;
|
|
FakeProcessManager processManager;
|
|
|
|
setUp(() {
|
|
sshConfig = MemoryFileSystem.test().file('ssh_config')..writeAsStringSync('\n');
|
|
processManager = FakeProcessManager.empty();
|
|
});
|
|
|
|
testUsingContext('does not throw on non-existent ssh config', () async {
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
|
|
expect(await device.sdkNameAndVersion, equals('Fuchsia'));
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: null),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
});
|
|
|
|
testUsingContext('returns what we get from the device on success', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', '123', 'cat /pkgfs/packages/build-info/0/data/version'],
|
|
stdout: 'version'
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
|
|
expect(await device.sdkNameAndVersion, equals('Fuchsia version'));
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
});
|
|
|
|
testUsingContext('returns "Fuchsia" when device command fails', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', '123', 'cat /pkgfs/packages/build-info/0/data/version'],
|
|
exitCode: 1,
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
|
|
expect(await device.sdkNameAndVersion, equals('Fuchsia'));
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
});
|
|
|
|
testUsingContext('returns "Fuchsia" when device gives an empty result', () async {
|
|
processManager.addCommand(const FakeCommand(
|
|
command: <String>['ssh', '-F', '/ssh_config', '123', 'cat /pkgfs/packages/build-info/0/data/version'],
|
|
));
|
|
final FuchsiaDevice device = FuchsiaDevice('123');
|
|
|
|
expect(await device.sdkNameAndVersion, equals('Fuchsia'));
|
|
}, overrides: <Type, Generator>{
|
|
ProcessManager: () => processManager,
|
|
FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig),
|
|
FuchsiaSdk: () => FakeFuchsiaSdk(),
|
|
});
|
|
});
|
|
}
|
|
|
|
class FuchsiaModulePackage extends ApplicationPackage {
|
|
FuchsiaModulePackage({@required this.name}) : super(id: name);
|
|
|
|
@override
|
|
final String name;
|
|
}
|
|
|
|
class MockFuchsiaDevice extends Fake implements FuchsiaDevice {
|
|
MockFuchsiaDevice(this.id, this.portForwarder, this._ipv6);
|
|
|
|
final bool _ipv6;
|
|
|
|
@override
|
|
bool get ipv6 => _ipv6;
|
|
|
|
@override
|
|
final String id;
|
|
|
|
@override
|
|
final DevicePortForwarder portForwarder;
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia_arm64;
|
|
|
|
@override
|
|
String get name => 'fuchsia';
|
|
|
|
@override
|
|
Future<List<int>> servicePorts() async => <int>[1];
|
|
|
|
@override
|
|
DartDevelopmentService get dds => FakeDartDevelopmentService();
|
|
}
|
|
|
|
class FakePortForwarder extends Fake implements DevicePortForwarder {
|
|
bool disposed = false;
|
|
|
|
@override
|
|
Future<void> dispose() async {
|
|
disposed = true;
|
|
}
|
|
}
|
|
|
|
class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice {
|
|
FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name);
|
|
|
|
@override
|
|
FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(String isolateName) {
|
|
return FakeFuchsiaIsolateDiscoveryProtocol();
|
|
}
|
|
|
|
@override
|
|
Future<TargetPlatform> get targetPlatform async => TargetPlatform.fuchsia_arm64;
|
|
}
|
|
|
|
class FakeFuchsiaIsolateDiscoveryProtocol implements FuchsiaIsolateDiscoveryProtocol {
|
|
@override
|
|
FutureOr<Uri> get uri => Uri.parse('http://[::1]:37');
|
|
|
|
@override
|
|
void dispose() {}
|
|
}
|
|
|
|
class FakeFuchsiaDevFinder implements FuchsiaDevFinder {
|
|
@override
|
|
Future<List<String>> list({ Duration timeout }) async {
|
|
return <String>['192.168.11.999 scare-cable-device-finder'];
|
|
}
|
|
|
|
@override
|
|
Future<String> resolve(String deviceName) async {
|
|
return '192.168.11.999';
|
|
}
|
|
}
|
|
|
|
class FakeFuchsiaFfx implements FuchsiaFfx {
|
|
@override
|
|
Future<List<String>> list({Duration timeout}) async {
|
|
return <String>['192.168.42.172 scare-cable-skip-ffx'];
|
|
}
|
|
|
|
@override
|
|
Future<String> resolve(String deviceName) async {
|
|
return '192.168.42.10';
|
|
}
|
|
}
|
|
|
|
class FakeFuchsiaSdk extends Fake implements FuchsiaSdk {
|
|
FakeFuchsiaSdk({
|
|
FuchsiaPM pm,
|
|
FuchsiaKernelCompiler compiler,
|
|
FuchsiaDevFinder devFinder,
|
|
FuchsiaFfx ffx,
|
|
String devices,
|
|
String devFinderDevices,
|
|
}) : fuchsiaPM = pm,
|
|
fuchsiaKernelCompiler = compiler,
|
|
fuchsiaDevFinder = devFinder ?? FakeFuchsiaDevFinder(),
|
|
fuchsiaFfx = ffx ?? FakeFuchsiaFfx(),
|
|
_devices = devices,
|
|
_devFinderDevices = devFinderDevices;
|
|
|
|
@override
|
|
final FuchsiaPM fuchsiaPM;
|
|
|
|
@override
|
|
final FuchsiaKernelCompiler fuchsiaKernelCompiler;
|
|
|
|
@override
|
|
final FuchsiaDevFinder fuchsiaDevFinder;
|
|
|
|
@override
|
|
final FuchsiaFfx fuchsiaFfx;
|
|
|
|
final String _devices;
|
|
final String _devFinderDevices;
|
|
|
|
@override
|
|
Future<String> listDevices({Duration timeout, bool useDeviceFinder = false}) async {
|
|
if (useDeviceFinder) {
|
|
return _devFinderDevices;
|
|
}
|
|
return _devices;
|
|
}
|
|
}
|
|
|
|
class FakeDartDevelopmentService extends Fake implements DartDevelopmentService {
|
|
@override
|
|
Future<void> startDartDevelopmentService(Uri observatoryUri, int hostPort, bool ipv6, bool disableServiceAuthCodes, {Logger logger}) async { }
|
|
|
|
@override
|
|
Uri get uri => Uri.parse('example');
|
|
}
|
|
|
|
class FakeFuchsiaWorkflow implements FuchsiaWorkflow {
|
|
FakeFuchsiaWorkflow({
|
|
this.appliesToHostPlatform = true,
|
|
this.canLaunchDevices = true,
|
|
this.canListDevices = true,
|
|
this.canListEmulators = true,
|
|
this.shouldUseDeviceFinder = true,
|
|
});
|
|
|
|
@override
|
|
bool appliesToHostPlatform;
|
|
|
|
@override
|
|
bool canLaunchDevices;
|
|
|
|
@override
|
|
bool canListDevices;
|
|
|
|
@override
|
|
bool canListEmulators;
|
|
|
|
@override
|
|
bool shouldUseDeviceFinder;
|
|
}
|