Test hot reload targeting a Fuchsia device (#48472)
This commit is contained in:
parent
8b139a8c41
commit
fca5917519
237
dev/devicelab/bin/tasks/flutter_attach_test_fuchsia.dart
Normal file
237
dev/devicelab/bin/tasks/flutter_attach_test_fuchsia.dart
Normal file
@ -0,0 +1,237 @@
|
||||
// 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.
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:flutter_devicelab/framework/adb.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/utils.dart';
|
||||
|
||||
void generateMain(Directory appDir, String sentinel) {
|
||||
final String mainCode = '''
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_driver/driver_extension.dart';
|
||||
|
||||
class ReassembleListener extends StatefulWidget {
|
||||
const ReassembleListener({Key key, this.child})
|
||||
: super(key: key);
|
||||
|
||||
final Widget child;
|
||||
|
||||
@override
|
||||
_ReassembleListenerState createState() => _ReassembleListenerState();
|
||||
}
|
||||
|
||||
class _ReassembleListenerState extends State<ReassembleListener> {
|
||||
@override
|
||||
initState() {
|
||||
super.initState();
|
||||
print('$sentinel');
|
||||
}
|
||||
|
||||
@override
|
||||
void reassemble() {
|
||||
super.reassemble();
|
||||
print('$sentinel');
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return widget.child;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
runApp(
|
||||
ReassembleListener(
|
||||
child: Text(
|
||||
'Hello, word!',
|
||||
textDirection: TextDirection.rtl,
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
''';
|
||||
File(path.join(appDir.path, 'lib', 'fuchsia_main.dart'))
|
||||
.writeAsStringSync(mainCode, flush: true);
|
||||
}
|
||||
|
||||
void main() {
|
||||
deviceOperatingSystem = DeviceOperatingSystem.fuchsia;
|
||||
|
||||
task(() async {
|
||||
section('Checking environment variables');
|
||||
|
||||
if (Platform.environment['FUCHSIA_SSH_CONFIG'] == null &&
|
||||
Platform.environment['FUCHSIA_BUILD_DIR'] == null) {
|
||||
throw Exception('No FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR set');
|
||||
}
|
||||
|
||||
final String flutterBinary = path.join(flutterDirectory.path, 'bin', 'flutter');
|
||||
|
||||
section('Downloading Fuchsia SDK and flutter runner');
|
||||
|
||||
// Download the Fuchsia SDK.
|
||||
final int precacheResult = await exec(
|
||||
flutterBinary,
|
||||
<String>[
|
||||
'precache',
|
||||
'--fuchsia',
|
||||
'--flutter_runner',
|
||||
]
|
||||
);
|
||||
|
||||
if (precacheResult != 0) {
|
||||
throw Exception('flutter precache failed with exit code $precacheResult');
|
||||
}
|
||||
|
||||
final Directory fuchsiaToolDirectory =
|
||||
Directory(path.join(flutterDirectory.path, 'bin', 'cache', 'artifacts', 'fuchsia', 'tools'));
|
||||
if (!fuchsiaToolDirectory.existsSync()) {
|
||||
throw Exception('Expected Fuchsia tool directory at ${fuchsiaToolDirectory.path}');
|
||||
}
|
||||
|
||||
final Device device = await devices.workingDevice;
|
||||
final Directory appDir = dir(path.join(
|
||||
flutterDirectory.path,
|
||||
'dev',
|
||||
'integration_tests',
|
||||
'ui',
|
||||
));
|
||||
|
||||
await inDirectory(appDir, () async {
|
||||
final Random random = Random();
|
||||
final Map<String, Completer<void>> sentinelMessage = <String, Completer<void>>{
|
||||
'sentinel-${random.nextInt(1<<32)}': Completer<void>(),
|
||||
'sentinel-${random.nextInt(1<<32)}': Completer<void>(),
|
||||
};
|
||||
|
||||
Process runProcess;
|
||||
Process logsProcess;
|
||||
|
||||
try {
|
||||
section('Creating lib/fuchsia_main.dart');
|
||||
|
||||
generateMain(appDir, sentinelMessage.keys.toList()[0]);
|
||||
|
||||
section('Launching `flutter run` in ${appDir.path}');
|
||||
|
||||
runProcess = await startProcess(
|
||||
flutterBinary,
|
||||
<String>[
|
||||
'run',
|
||||
'--suppress-analytics',
|
||||
'-d', device.deviceId,
|
||||
'-t', 'lib/fuchsia_main.dart',
|
||||
],
|
||||
isBot: false, // We just want to test the output, not have any debugging info.
|
||||
);
|
||||
|
||||
logsProcess = await startProcess(
|
||||
flutterBinary,
|
||||
<String>['logs', '--suppress-analytics', '-d', device.deviceId],
|
||||
isBot: false, // We just want to test the output, not have any debugging info.
|
||||
);
|
||||
|
||||
Future<dynamic> eventOrExit(Future<void> event) {
|
||||
return Future.any<dynamic>(<Future<dynamic>>[
|
||||
event,
|
||||
runProcess.exitCode,
|
||||
logsProcess.exitCode,
|
||||
]);
|
||||
}
|
||||
|
||||
logsProcess.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String log) {
|
||||
print('logs:stdout: $log');
|
||||
for (final String sentinel in sentinelMessage.keys) {
|
||||
if (log.contains(sentinel)) {
|
||||
if (sentinelMessage[sentinel].isCompleted) {
|
||||
throw Exception(
|
||||
'Expected a single `$sentinel` message in the device log, but found more than one'
|
||||
);
|
||||
}
|
||||
sentinelMessage[sentinel].complete();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
final Completer<void> hotReloadCompleter = Completer<void>();
|
||||
final Completer<void> reloadedCompleter = Completer<void>();
|
||||
final RegExp observatoryRegexp = RegExp('An Observatory debugger and profiler on .+ is available at');
|
||||
runProcess.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
print('run:stdout: $line');
|
||||
if (observatoryRegexp.hasMatch(line)) {
|
||||
hotReloadCompleter.complete();
|
||||
} else if (line.contains('Reloaded')) {
|
||||
reloadedCompleter.complete();
|
||||
}
|
||||
});
|
||||
|
||||
final List<String> runStderr = <String>[];
|
||||
runProcess.stderr
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
runStderr.add(line);
|
||||
print('run:stderr: $line');
|
||||
});
|
||||
|
||||
section('Waiting for hot reload availability');
|
||||
await eventOrExit(hotReloadCompleter.future);
|
||||
|
||||
section('Waiting for Dart VM');
|
||||
// Wait for the first message in the log from the Dart VM.
|
||||
await eventOrExit(sentinelMessage.values.toList()[0].future);
|
||||
|
||||
// Change the dart file.
|
||||
generateMain(appDir, sentinelMessage.keys.toList()[1]);
|
||||
|
||||
section('Hot reload');
|
||||
runProcess.stdin.write('r');
|
||||
runProcess.stdin.flush();
|
||||
await eventOrExit(reloadedCompleter.future);
|
||||
|
||||
section('Waiting for Dart VM');
|
||||
// Wait for the second message in the log from the Dart VM.
|
||||
await eventOrExit(sentinelMessage.values.toList()[1].future);
|
||||
|
||||
section('Quitting flutter run');
|
||||
|
||||
runProcess.stdin.write('q');
|
||||
runProcess.stdin.flush();
|
||||
|
||||
final int runExitCode = await runProcess.exitCode;
|
||||
if (runExitCode != 0 || runStderr.isNotEmpty) {
|
||||
throw Exception(
|
||||
'flutter run exited with code $runExitCode and errors: ${runStderr.join('\n')}.'
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
runProcess.kill();
|
||||
logsProcess.kill();
|
||||
File(path.join(appDir.path, 'lib', 'fuchsia_main.dart')).deleteSync();
|
||||
}
|
||||
|
||||
for (final String sentinel in sentinelMessage.keys) {
|
||||
if (!sentinelMessage[sentinel].isCompleted) {
|
||||
throw Exception('Expected $sentinel in the device logs.');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return TaskResult.success(null);
|
||||
});
|
||||
}
|
@ -12,11 +12,22 @@ import 'package:path/path.dart' as path;
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
|
||||
/// Gets the artifact path relative to the current directory.
|
||||
String getArtifactPath() {
|
||||
return path.normalize(
|
||||
path.join(
|
||||
path.current,
|
||||
'../../bin/cache/artifacts',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/// The root of the API for controlling devices.
|
||||
DeviceDiscovery get devices => DeviceDiscovery();
|
||||
|
||||
/// Device operating system the test is configured to test.
|
||||
enum DeviceOperatingSystem { android, ios }
|
||||
enum DeviceOperatingSystem { android, ios, fuchsia }
|
||||
|
||||
/// Device OS to test on.
|
||||
DeviceOperatingSystem deviceOperatingSystem = DeviceOperatingSystem.android;
|
||||
@ -29,6 +40,8 @@ abstract class DeviceDiscovery {
|
||||
return AndroidDeviceDiscovery();
|
||||
case DeviceOperatingSystem.ios:
|
||||
return IosDeviceDiscovery();
|
||||
case DeviceOperatingSystem.fuchsia:
|
||||
return FuchsiaDeviceDiscovery();
|
||||
default:
|
||||
throw StateError('Unsupported device operating system: {config.deviceOperatingSystem}');
|
||||
}
|
||||
@ -198,6 +211,91 @@ class AndroidDeviceDiscovery implements DeviceDiscovery {
|
||||
}
|
||||
}
|
||||
|
||||
class FuchsiaDeviceDiscovery implements DeviceDiscovery {
|
||||
factory FuchsiaDeviceDiscovery() {
|
||||
return _instance ??= FuchsiaDeviceDiscovery._();
|
||||
}
|
||||
|
||||
FuchsiaDeviceDiscovery._();
|
||||
|
||||
static FuchsiaDeviceDiscovery _instance;
|
||||
|
||||
FuchsiaDevice _workingDevice;
|
||||
|
||||
String get _devFinder {
|
||||
final String devFinder = path.join(getArtifactPath(), 'fuchsia', 'tools', 'dev_finder');
|
||||
if (!File(devFinder).existsSync()) {
|
||||
throw FileSystemException('Couldn\'t find dev_finder at location $devFinder');
|
||||
}
|
||||
return devFinder;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<FuchsiaDevice> get workingDevice async {
|
||||
if (_workingDevice == null) {
|
||||
await chooseWorkingDevice();
|
||||
}
|
||||
return _workingDevice;
|
||||
}
|
||||
|
||||
/// Picks the first connected Fuchsia device.
|
||||
@override
|
||||
Future<void> chooseWorkingDevice() async {
|
||||
final List<FuchsiaDevice> allDevices = (await discoverDevices())
|
||||
.map<FuchsiaDevice>((String id) => FuchsiaDevice(deviceId: id))
|
||||
.toList();
|
||||
|
||||
if (allDevices.isEmpty) {
|
||||
throw Exception('No Fuchsia devices detected');
|
||||
}
|
||||
_workingDevice = allDevices.first;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> discoverDevices() async {
|
||||
final List<String> output = (await eval(_devFinder, <String>['list', '-full']))
|
||||
.trim()
|
||||
.split('\n');
|
||||
|
||||
final List<String> devices = <String>[];
|
||||
for (final String line in output) {
|
||||
final List<String> parts = line.split(' ');
|
||||
assert(parts.length == 2);
|
||||
devices.add(parts.last); // The device id.
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, HealthCheckResult>> checkDevices() async {
|
||||
final Map<String, HealthCheckResult> results = <String, HealthCheckResult>{};
|
||||
for (final String deviceId in await discoverDevices()) {
|
||||
try {
|
||||
final int resolveResult = await exec(
|
||||
_devFinder,
|
||||
<String>[
|
||||
'resolve',
|
||||
'-device-limit',
|
||||
'1',
|
||||
deviceId,
|
||||
]
|
||||
);
|
||||
if (resolveResult == 0) {
|
||||
results['fuchsia-device-$deviceId'] = HealthCheckResult.success();
|
||||
} else {
|
||||
results['fuchsia-device-$deviceId'] = HealthCheckResult.failure('Cannot resolve device $deviceId');
|
||||
}
|
||||
} catch (error, stacktrace) {
|
||||
results['fuchsia-device-$deviceId'] = HealthCheckResult.error(error, stacktrace);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> performPreflightTasks() async {}
|
||||
}
|
||||
|
||||
class AndroidDevice implements Device {
|
||||
AndroidDevice({@required this.deviceId});
|
||||
|
||||
@ -392,16 +490,6 @@ class IosDeviceDiscovery implements DeviceDiscovery {
|
||||
_workingDevice = allDevices[math.Random().nextInt(allDevices.length)];
|
||||
}
|
||||
|
||||
// Returns the path to cached binaries relative to devicelab directory
|
||||
String get _artifactDirPath {
|
||||
return path.normalize(
|
||||
path.join(
|
||||
path.current,
|
||||
'../../bin/cache/artifacts',
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Returns a colon-separated environment variable that contains the paths
|
||||
// of linked libraries for idevice_id
|
||||
Map<String, String> get _ideviceIdEnvironment {
|
||||
@ -413,13 +501,13 @@ class IosDeviceDiscovery implements DeviceDiscovery {
|
||||
'ideviceinstaller',
|
||||
'ios-deploy',
|
||||
'libzip',
|
||||
].map((String packageName) => path.join(_artifactDirPath, packageName)).join(':');
|
||||
].map((String packageName) => path.join(getArtifactPath(), packageName)).join(':');
|
||||
return <String, String>{'DYLD_LIBRARY_PATH': libPath};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<String>> discoverDevices() async {
|
||||
final String ideviceIdPath = path.join(_artifactDirPath, 'libimobiledevice', 'idevice_id');
|
||||
final String ideviceIdPath = path.join(getArtifactPath(), 'libimobiledevice', 'idevice_id');
|
||||
final List<String> iosDeviceIDs = LineSplitter.split(await eval(ideviceIdPath, <String>['-l'], environment: _ideviceIdEnvironment))
|
||||
.map<String>((String line) => line.trim())
|
||||
.where((String line) => line.isNotEmpty)
|
||||
@ -494,6 +582,49 @@ class IosDevice implements Device {
|
||||
Future<void> stop(String packageName) async {}
|
||||
}
|
||||
|
||||
/// Fuchsia device.
|
||||
class FuchsiaDevice implements Device {
|
||||
const FuchsiaDevice({ @required this.deviceId });
|
||||
|
||||
@override
|
||||
final String deviceId;
|
||||
|
||||
// TODO(egarciad): Implement these for Fuchsia.
|
||||
@override
|
||||
Future<bool> isAwake() async => true;
|
||||
|
||||
@override
|
||||
Future<bool> isAsleep() async => false;
|
||||
|
||||
@override
|
||||
Future<void> wakeUp() async {}
|
||||
|
||||
@override
|
||||
Future<void> sendToSleep() async {}
|
||||
|
||||
@override
|
||||
Future<void> togglePower() async {}
|
||||
|
||||
@override
|
||||
Future<void> unlock() async {}
|
||||
|
||||
@override
|
||||
Future<void> tap(int x, int y) async {}
|
||||
|
||||
@override
|
||||
Future<void> stop(String packageName) async {}
|
||||
|
||||
@override
|
||||
Future<Map<String, dynamic>> getMemoryStats(String packageName) async {
|
||||
throw 'Not implemented';
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<String> get logcat {
|
||||
throw 'Not implemented';
|
||||
}
|
||||
}
|
||||
|
||||
/// Path to the `adb` executable.
|
||||
String get adbPath {
|
||||
final String androidHome = Platform.environment['ANDROID_HOME'] ?? Platform.environment['ANDROID_SDK_ROOT'];
|
||||
|
@ -380,6 +380,8 @@ class CompileTest {
|
||||
case DeviceOperatingSystem.android:
|
||||
options.add('android-arm');
|
||||
break;
|
||||
case DeviceOperatingSystem.fuchsia:
|
||||
throw Exception('Unsupported option for Fuchsia devices');
|
||||
}
|
||||
final String compileLog = await evalFlutter('build', options: options);
|
||||
watch.stop();
|
||||
@ -434,6 +436,8 @@ class CompileTest {
|
||||
if (reportPackageContentSizes)
|
||||
metrics.addAll(await getSizesFromApk(apkPath));
|
||||
break;
|
||||
case DeviceOperatingSystem.fuchsia:
|
||||
throw Exception('Unsupported option for Fuchsia devices');
|
||||
}
|
||||
|
||||
metrics.addAll(<String, dynamic>{
|
||||
@ -456,6 +460,8 @@ class CompileTest {
|
||||
options.insert(0, 'apk');
|
||||
options.add('--target-platform=android-arm');
|
||||
break;
|
||||
case DeviceOperatingSystem.fuchsia:
|
||||
throw Exception('Unsupported option for Fuchsia devices');
|
||||
}
|
||||
watch.start();
|
||||
await flutter('build', options: options);
|
||||
|
@ -340,7 +340,7 @@ tasks:
|
||||
# required_agent_capabilities: ["linux/android"]
|
||||
# flaky: true
|
||||
|
||||
flutter_attach_test:
|
||||
flutter_attach_test_android:
|
||||
description: >
|
||||
Tests the `flutter attach` command.
|
||||
stage: devicelab
|
||||
|
22
dev/integration_tests/ui/fuchsia/meta/integration_ui.cmx
Normal file
22
dev/integration_tests/ui/fuchsia/meta/integration_ui.cmx
Normal file
@ -0,0 +1,22 @@
|
||||
{
|
||||
"program": {
|
||||
"data": "data/integration_ui"
|
||||
},
|
||||
"sandbox": {
|
||||
"services": [
|
||||
"fuchsia.cobalt.LoggerFactory",
|
||||
"fuchsia.fonts.Provider",
|
||||
"fuchsia.logger.LogSink",
|
||||
"fuchsia.modular.Clipboard",
|
||||
"fuchsia.modular.ContextWriter",
|
||||
"fuchsia.modular.DeviceMap",
|
||||
"fuchsia.modular.ModuleContext",
|
||||
"fuchsia.sys.Environment",
|
||||
"fuchsia.sys.Launcher",
|
||||
"fuchsia.testing.runner.TestRunner",
|
||||
"fuchsia.ui.input.ImeService",
|
||||
"fuchsia.ui.policy.Presenter",
|
||||
"fuchsia.ui.scenic.Scenic"
|
||||
]
|
||||
}
|
||||
}
|
@ -218,7 +218,7 @@ class AttachCommand extends FlutterCommand {
|
||||
if (module == null) {
|
||||
throwToolExit('\'--module\' is required for attaching to a Fuchsia device');
|
||||
}
|
||||
usesIpv6 = await device.ipv6;
|
||||
usesIpv6 = device.ipv6;
|
||||
FuchsiaIsolateDiscoveryProtocol isolateDiscoveryProtocol;
|
||||
try {
|
||||
isolateDiscoveryProtocol = device.getIsolateDiscoveryProtocol(module);
|
||||
|
@ -153,7 +153,7 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
|
||||
if (text == null || text.isEmpty) {
|
||||
return <Device>[];
|
||||
}
|
||||
final List<FuchsiaDevice> devices = parseListDevices(text);
|
||||
final List<FuchsiaDevice> devices = await parseListDevices(text);
|
||||
return devices;
|
||||
}
|
||||
|
||||
@ -162,7 +162,7 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
|
||||
}
|
||||
|
||||
@visibleForTesting
|
||||
List<FuchsiaDevice> parseListDevices(String text) {
|
||||
Future<List<FuchsiaDevice>> parseListDevices(String text) async {
|
||||
final List<FuchsiaDevice> devices = <FuchsiaDevice>[];
|
||||
for (final String rawLine in text.trim().split('\n')) {
|
||||
final String line = rawLine.trim();
|
||||
@ -172,8 +172,15 @@ List<FuchsiaDevice> parseListDevices(String text) {
|
||||
continue;
|
||||
}
|
||||
final String name = words[1];
|
||||
final String id = words[0];
|
||||
devices.add(FuchsiaDevice(id, name: name));
|
||||
final String resolvedHost = await fuchsiaSdk.fuchsiaDevFinder.resolve(
|
||||
name,
|
||||
local: false,
|
||||
);
|
||||
if (resolvedHost == null) {
|
||||
globals.printError('Failed to resolve host for Fuchsia device `$name`');
|
||||
continue;
|
||||
}
|
||||
devices.add(FuchsiaDevice(resolvedHost, name: name));
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
@ -240,7 +247,6 @@ class FuchsiaDevice extends Device {
|
||||
}
|
||||
// Stop the app if it's currently running.
|
||||
await stopApp(package);
|
||||
// Find out who the device thinks we are.
|
||||
final String host = await fuchsiaSdk.fuchsiaDevFinder.resolve(
|
||||
name,
|
||||
local: true,
|
||||
@ -249,6 +255,7 @@ class FuchsiaDevice extends Device {
|
||||
globals.printError('Failed to resolve host for Fuchsia device');
|
||||
return LaunchResult.failed();
|
||||
}
|
||||
// Find out who the device thinks we are.
|
||||
final int port = await os.findFreePort();
|
||||
if (port == 0) {
|
||||
globals.printError('Failed to find a free port');
|
||||
@ -475,11 +482,9 @@ class FuchsiaDevice extends Device {
|
||||
@override
|
||||
bool get supportsScreenshot => false;
|
||||
|
||||
Future<bool> get ipv6 async {
|
||||
// Workaround for https://github.com/dart-lang/sdk/issues/29456
|
||||
final String fragment = (await _resolvedIp).split('%').first;
|
||||
bool get ipv6 {
|
||||
try {
|
||||
Uri.parseIPv6Address(fragment);
|
||||
Uri.parseIPv6Address(id);
|
||||
return true;
|
||||
} on FormatException {
|
||||
return false;
|
||||
@ -525,15 +530,6 @@ class FuchsiaDevice extends Device {
|
||||
return ports;
|
||||
}
|
||||
|
||||
String _cachedResolvedIp;
|
||||
|
||||
Future<String> get _resolvedIp async {
|
||||
return _cachedResolvedIp ??= await fuchsiaSdk.fuchsiaDevFinder.resolve(
|
||||
name,
|
||||
local: false,
|
||||
);
|
||||
}
|
||||
|
||||
/// Run `command` on the Fuchsia device shell.
|
||||
Future<RunResult> shell(String command) async {
|
||||
if (fuchsiaArtifacts.sshConfig == null) {
|
||||
@ -544,7 +540,7 @@ class FuchsiaDevice extends Device {
|
||||
'ssh',
|
||||
'-F',
|
||||
fuchsiaArtifacts.sshConfig.absolute.path,
|
||||
await _resolvedIp,
|
||||
id, // Device's IP address.
|
||||
command,
|
||||
]);
|
||||
}
|
||||
@ -670,7 +666,7 @@ class FuchsiaIsolateDiscoveryProtocol {
|
||||
}
|
||||
final Uri address = flutterView.owner.vmService.httpAddress;
|
||||
if (flutterView.uiIsolate.name.contains(_isolateName)) {
|
||||
_foundUri.complete(await _device.ipv6
|
||||
_foundUri.complete(_device.ipv6
|
||||
? Uri.parse('http://[$_ipv6Loopback]:${address.port}/')
|
||||
: Uri.parse('http://$_ipv4Loopback:${address.port}/'));
|
||||
_status.stop();
|
||||
@ -711,7 +707,7 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
||||
'-f',
|
||||
'-L',
|
||||
'$hostPort:$_ipv4Loopback:$devicePort',
|
||||
await device._resolvedIp,
|
||||
device.id, // Device's IP address.
|
||||
'true',
|
||||
];
|
||||
final Process process = await globals.processManager.start(command);
|
||||
@ -743,7 +739,7 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
||||
'-vvv',
|
||||
'-L',
|
||||
'${forwardedPort.hostPort}:$_ipv4Loopback:${forwardedPort.devicePort}',
|
||||
await device._resolvedIp,
|
||||
device.id, // Device's IP address.
|
||||
];
|
||||
final ProcessResult result = await globals.processManager.run(command);
|
||||
if (result.exitCode != 0) {
|
||||
@ -753,7 +749,9 @@ class _FuchsiaPortForwarder extends DevicePortForwarder {
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
for (final ForwardedPort port in forwardedPorts) {
|
||||
final List<ForwardedPort> forwardedPortsCopy =
|
||||
List<ForwardedPort>.from(forwardedPorts);
|
||||
for (final ForwardedPort port in forwardedPortsCopy) {
|
||||
await unforward(port);
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,8 @@ class FuchsiaSdk {
|
||||
return devices.isNotEmpty ? devices[0] : null;
|
||||
}
|
||||
|
||||
/// Returns the fuchsia system logs for an attached device.
|
||||
/// Returns the fuchsia system logs for an attached device where
|
||||
/// [id] is the IP address of the device.
|
||||
Stream<String> syslogs(String id) {
|
||||
Process process;
|
||||
try {
|
||||
@ -72,7 +73,7 @@ class FuchsiaSdk {
|
||||
'ssh',
|
||||
'-F',
|
||||
fuchsiaArtifacts.sshConfig.absolute.path,
|
||||
id,
|
||||
id, // The device's IP.
|
||||
remoteCommand,
|
||||
];
|
||||
globals.processManager.start(cmd).then((Process newProcess) {
|
||||
|
@ -55,20 +55,24 @@ void main() {
|
||||
expect(device.name, name);
|
||||
});
|
||||
|
||||
test('parse dev_finder output', () {
|
||||
const String example = '192.168.42.56 paper-pulp-bush-angel';
|
||||
final List<FuchsiaDevice> names = parseListDevices(example);
|
||||
testUsingContext('parse dev_finder output', () async {
|
||||
const String example = '2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel';
|
||||
final List<FuchsiaDevice> names = await parseListDevices(example);
|
||||
|
||||
expect(names.length, 1);
|
||||
expect(names.first.name, 'paper-pulp-bush-angel');
|
||||
expect(names.first.id, '192.168.42.56');
|
||||
expect(names.first.id, '192.168.42.10');
|
||||
}, overrides: <Type, Generator>{
|
||||
FuchsiaSdk: () => MockFuchsiaSdk(),
|
||||
});
|
||||
|
||||
test('parse junk dev_finder output', () {
|
||||
testUsingContext('parse junk dev_finder output', () async {
|
||||
const String example = 'junk';
|
||||
final List<FuchsiaDevice> names = parseListDevices(example);
|
||||
final List<FuchsiaDevice> names = await parseListDevices(example);
|
||||
|
||||
expect(names.length, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
FuchsiaSdk: () => MockFuchsiaSdk(),
|
||||
});
|
||||
|
||||
testUsingContext('disposing device disposes the portForwarder', () async {
|
||||
@ -773,7 +777,7 @@ class MockFuchsiaDevice extends Mock implements FuchsiaDevice {
|
||||
final bool _ipv6;
|
||||
|
||||
@override
|
||||
Future<bool> get ipv6 async => _ipv6;
|
||||
bool get ipv6 => _ipv6;
|
||||
|
||||
@override
|
||||
final String id;
|
||||
|
Loading…
x
Reference in New Issue
Block a user