diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart index a110ac8cf07..b3a9b7770ce 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart @@ -4,6 +4,7 @@ import '../base/common.dart'; import '../base/process.dart'; +import '../globals.dart'; import 'fuchsia_sdk.dart'; // Usage: dev_finder @@ -31,7 +32,11 @@ class FuchsiaDevFinder { '-full' ]; final RunResult result = await runAsync(command); - return (result.exitCode == 0) ? result.stdout.split('\n') : null; + if (result.exitCode != 0) { + printError('dev_finder failed: ${result.stderr}'); + return null; + } + return result.stdout.split('\n'); } /// Returns the host address by which the device [deviceName] should use for @@ -51,6 +56,10 @@ class FuchsiaDevFinder { deviceName ]; final RunResult result = await runAsync(command); - return (result.exitCode == 0) ? result.stdout.trim() : null; + if (result.exitCode != 0) { + printError('dev_finder failed: ${result.stderr}'); + return null; + } + return result.stdout.trim(); } } diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart index b48aac83a11..3a82092e58f 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart @@ -44,9 +44,6 @@ class FuchsiaDeviceTools { FuchsiaTilesCtl get tilesCtl => _tilesCtl ??= FuchsiaTilesCtl(); } -final FuchsiaAmberCtl _amberCtl = fuchsiaDeviceTools.amberCtl; -final FuchsiaTilesCtl _tilesCtl = fuchsiaDeviceTools.tilesCtl; - final String _ipv4Loopback = InternetAddress.loopbackIPv4.address; final String _ipv6Loopback = InternetAddress.loopbackIPv6.address; @@ -233,6 +230,10 @@ class FuchsiaDevice extends Device { await stopApp(package); // Find out who the device thinks we are. final String host = await fuchsiaSdk.fuchsiaDevFinder.resolve(name); + if (host == null) { + printError('Failed to resolve host for Fuchsia device'); + return LaunchResult.failed(); + } final int port = await os.findFreePort(); if (port == 0) { printError('Failed to find a free port'); @@ -265,14 +266,14 @@ class FuchsiaDevice extends Device { } // Teach amber about the package server. - if (!await _amberCtl.addSrc(this, fuchsiaPackageServer)) { + if (!await fuchsiaDeviceTools.amberCtl.addSrc(this, fuchsiaPackageServer)) { printError('Failed to teach amber about the package server'); return LaunchResult.failed(); } serverRegistered = true; // Tell amber to prefetch the app. - if (!await _amberCtl.getUp(this, appName)) { + if (!await fuchsiaDeviceTools.amberCtl.getUp(this, appName)) { printError('Failed to get amber to prefetch the package'); return LaunchResult.failed(); } @@ -286,14 +287,14 @@ class FuchsiaDevice extends Device { // Instruct tiles_ctl to start the app. final String fuchsiaUrl = 'fuchsia-pkg://fuchsia.com/$appName#meta/$appName.cmx'; - if (!await _tilesCtl.add(this, fuchsiaUrl, [])) { + if (!await fuchsiaDeviceTools.tilesCtl.add(this, fuchsiaUrl, [])) { printError('Failed to add the app to tiles'); return LaunchResult.failed(); } } finally { // Try to un-teach amber about the package server if needed. if (serverRegistered) { - await _amberCtl.rmSrc(this, fuchsiaPackageServer); + await fuchsiaDeviceTools.amberCtl.rmSrc(this, fuchsiaPackageServer); } // Shutdown the package server and delete the package repo; fuchsiaPackageServer.stop(); @@ -308,7 +309,7 @@ class FuchsiaDevice extends Device { // In a debug or profile build, try to find the observatory uri. final FuchsiaIsolateDiscoveryProtocol discovery = - FuchsiaIsolateDiscoveryProtocol(this, appName); + getIsolateDiscoveryProtocol(appName); try { final Uri observatoryUri = await discovery.uri; return LaunchResult.succeeded(observatoryUri: observatoryUri); @@ -321,7 +322,7 @@ class FuchsiaDevice extends Device { Future stopApp(covariant FuchsiaApp app) async { final int appKey = await FuchsiaTilesCtl.findAppKey(this, app.id); if (appKey != -1) { - if (!await _tilesCtl.remove(this, appKey)) { + if (!await fuchsiaDeviceTools.tilesCtl.remove(this, appKey)) { printError('tiles_ctl remove on ${app.id} failed.'); return false; } diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart index c2af0661374..0cda7a7edbd 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart @@ -50,6 +50,9 @@ class FuchsiaSdk { return null; } final List devices = await fuchsiaDevFinder.list(); + if (devices == null) { + return null; + } return devices.isNotEmpty ? devices[0] : null; } diff --git a/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart b/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart index 735df55c968..7e10b662303 100644 --- a/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart +++ b/packages/flutter_tools/test/fuchsia/fuchsa_device_test.dart @@ -407,32 +407,49 @@ void main() { group('fuchsia app start and stop: ', () { MemoryFileSystem memoryFileSystem; MockOperatingSystemUtils osUtils; - MockFuchsiaDeviceTools fuchsiaDeviceTools; + FakeFuchsiaDeviceTools fuchsiaDeviceTools; MockFuchsiaSdk fuchsiaSdk; setUp(() { memoryFileSystem = MemoryFileSystem(); osUtils = MockOperatingSystemUtils(); - fuchsiaDeviceTools = MockFuchsiaDeviceTools(); + fuchsiaDeviceTools = FakeFuchsiaDeviceTools(); fuchsiaSdk = MockFuchsiaSdk(); when(osUtils.findFreePort()).thenAnswer((_) => Future.value(12345)); }); - testUsingContext('start prebuilt app in release mode', () async { + Future setupAndStartApp({ + @required bool prebuilt, + @required BuildMode mode, + }) async { const String appName = 'app_name'; - final FuchsiaDevice device = FuchsiaDevice('123'); + final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123'); fs.directory('fuchsia').createSync(recursive: true); final File pubspecFile = fs.file('pubspec.yaml')..createSync(); pubspecFile.writeAsStringSync('name: $appName'); - final File far = fs.file('app_name-0.far')..createSync(); - final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far); + FuchsiaApp app; + if (prebuilt) { + final File far = fs.file('app_name-0.far')..createSync(); + app = FuchsiaApp.fromPrebuiltApp(far); + } else { + fs.file(fs.path.join('fuchsia', 'meta', '$appName.cmx')) + .createSync(recursive: true); + fs.file('.packages').createSync(); + fs.file(fs.path.join('lib', 'main.dart')).createSync(recursive: true); + app = BuildableFuchsiaApp(project: FlutterProject.current().fuchsia); + } + final DebuggingOptions debuggingOptions = - DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null)); - final LaunchResult launchResult = await device.startApp(app, - prebuiltApplication: true, + DebuggingOptions.disabled(BuildInfo(mode, null)); + return await device.startApp(app, + prebuiltApplication: prebuilt, debuggingOptions: debuggingOptions); + } + testUsingContext('start prebuilt in release mode', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: true, mode: BuildMode.release); expect(launchResult.started, isTrue); expect(launchResult.hasObservatory, isFalse); }, overrides: { @@ -442,9 +459,9 @@ void main() { OperatingSystemUtils: () => osUtils, }); - testUsingContext('start and stop prebuilt app in release mode', () async { + testUsingContext('start and stop prebuilt in release mode', () async { const String appName = 'app_name'; - final FuchsiaDevice device = FuchsiaDevice('123'); + final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123'); fs.directory('fuchsia').createSync(recursive: true); final File pubspecFile = fs.file('pubspec.yaml')..createSync(); pubspecFile.writeAsStringSync('name: $appName'); @@ -456,7 +473,6 @@ void main() { final LaunchResult launchResult = await device.startApp(app, prebuiltApplication: true, debuggingOptions: debuggingOptions); - expect(launchResult.started, isTrue); expect(launchResult.hasObservatory, isFalse); expect(await device.stopApp(app), isTrue); @@ -466,6 +482,91 @@ void main() { FuchsiaSdk: () => fuchsiaSdk, OperatingSystemUtils: () => osUtils, }); + + testUsingContext('start prebuilt in debug mode', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: true, mode: BuildMode.debug); + expect(launchResult.started, isTrue); + expect(launchResult.hasObservatory, isTrue); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('start buildable in release mode', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: false, mode: BuildMode.release); + expect(launchResult.started, isTrue); + expect(launchResult.hasObservatory, isFalse); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('start buildable in debug mode', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: false, mode: BuildMode.debug); + expect(launchResult.started, isTrue); + expect(launchResult.hasObservatory, isTrue); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('fail with correct LaunchResult when dev_finder fails', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: true, mode: BuildMode.release); + expect(launchResult.started, isFalse); + expect(launchResult.hasObservatory, isFalse); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaSdk: () => MockFuchsiaSdk(devFinder: FailingDevFinder()), + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('fail with correct LaunchResult when pm fails', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: true, mode: BuildMode.release); + expect(launchResult.started, isFalse); + expect(launchResult.hasObservatory, isFalse); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaSdk: () => MockFuchsiaSdk(pm: FailingPM()), + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('fail with correct LaunchResult when amber fails', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: true, mode: BuildMode.release); + expect(launchResult.started, isFalse); + expect(launchResult.hasObservatory, isFalse); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(amber: FailingAmberCtl()), + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('fail with correct LaunchResult when tiles fails', () async { + final LaunchResult launchResult = + await setupAndStartApp(prebuilt: true, mode: BuildMode.release); + expect(launchResult.started, isFalse); + expect(launchResult.hasObservatory, isFalse); + }, overrides: { + FileSystem: () => memoryFileSystem, + FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(tiles: FailingTilesCtl()), + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + }); } @@ -559,7 +660,24 @@ class MockIsolate extends Mock implements Isolate { final String name; } -class MockFuchsiaAmberCtl extends Mock implements FuchsiaAmberCtl { +class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice { + FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name); + + @override + FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol( + String isolateName) => + FakeFuchsiaIsolateDiscoveryProtocol(); +} + +class FakeFuchsiaIsolateDiscoveryProtocol implements FuchsiaIsolateDiscoveryProtocol { + @override + FutureOr get uri => Uri.parse('http://[::1]:37'); + + @override + void dispose() {} +} + +class FakeFuchsiaAmberCtl implements FuchsiaAmberCtl { @override Future addSrc(FuchsiaDevice device, FuchsiaPackageServer server) async { return true; @@ -576,7 +694,24 @@ class MockFuchsiaAmberCtl extends Mock implements FuchsiaAmberCtl { } } -class MockFuchsiaTilesCtl extends Mock implements FuchsiaTilesCtl { +class FailingAmberCtl implements FuchsiaAmberCtl { + @override + Future addSrc(FuchsiaDevice device, FuchsiaPackageServer server) async { + return false; + } + + @override + Future rmSrc(FuchsiaDevice device, FuchsiaPackageServer server) async { + return false; + } + + @override + Future getUp(FuchsiaDevice device, String packageName) async { + return false; + } +} + +class FakeFuchsiaTilesCtl implements FuchsiaTilesCtl { final Map _runningApps = {}; bool _started = false; int _nextAppId = 1; @@ -624,15 +759,48 @@ class MockFuchsiaTilesCtl extends Mock implements FuchsiaTilesCtl { } } -class MockFuchsiaDeviceTools extends Mock implements FuchsiaDeviceTools { +class FailingTilesCtl implements FuchsiaTilesCtl { @override - final FuchsiaAmberCtl amberCtl = MockFuchsiaAmberCtl(); + Future start(FuchsiaDevice device) async { + return false; + } @override - final FuchsiaTilesCtl tilesCtl = MockFuchsiaTilesCtl(); + Future> list(FuchsiaDevice device) async { + return null; + } + + @override + Future add(FuchsiaDevice device, String url, List args) async { + return false; + } + + @override + Future remove(FuchsiaDevice device, int key) async { + return false; + } + + @override + Future quit(FuchsiaDevice device) async { + return false; + } } -class MockFuchsiaPM extends Mock implements FuchsiaPM { +class FakeFuchsiaDeviceTools implements FuchsiaDeviceTools { + FakeFuchsiaDeviceTools({ + FuchsiaAmberCtl amber, + FuchsiaTilesCtl tiles, + }) : amberCtl = amber ?? FakeFuchsiaAmberCtl(), + tilesCtl = tiles ?? FakeFuchsiaTilesCtl(); + + @override + final FuchsiaAmberCtl amberCtl; + + @override + final FuchsiaTilesCtl tilesCtl; +} + +class FakeFuchsiaPM implements FuchsiaPM { String _appName; @override @@ -710,7 +878,46 @@ class MockFuchsiaPM extends Mock implements FuchsiaPM { } } -class MockFuchsiaKernelCompiler extends Mock implements FuchsiaKernelCompiler { +class FailingPM implements FuchsiaPM { + @override + Future init(String buildPath, String appName) async { + return false; + } + + @override + Future genkey(String buildPath, String outKeyPath) async { + return false; + } + + @override + Future build( + String buildPath, String keyPath, String manifestPath) async { + return false; + } + + @override + Future archive( + String buildPath, String keyPath, String manifestPath) async { + return false; + } + + @override + Future newrepo(String repoPath) async { + return false; + } + + @override + Future serve(String repoPath, String host, int port) async { + return _createMockProcess(exitCode: 6); + } + + @override + Future publish(String repoPath, String packagePath) async { + return false; + } +} + +class FakeFuchsiaKernelCompiler implements FuchsiaKernelCompiler { @override Future build({ @required FuchsiaProject fuchsiaProject, @@ -724,7 +931,18 @@ class MockFuchsiaKernelCompiler extends Mock implements FuchsiaKernelCompiler { } } -class MockFuchsiaDevFinder extends Mock implements FuchsiaDevFinder { +class FailingKernelCompiler implements FuchsiaKernelCompiler { + @override + Future build({ + @required FuchsiaProject fuchsiaProject, + @required String target, // E.g., lib/main.dart + BuildInfo buildInfo = BuildInfo.debug, + }) async { + throwToolExit('Build process failed'); + } +} + +class FakeFuchsiaDevFinder implements FuchsiaDevFinder { @override Future> list() async { return ['192.168.42.172 scare-cable-skip-joy']; @@ -736,14 +954,33 @@ class MockFuchsiaDevFinder extends Mock implements FuchsiaDevFinder { } } -class MockFuchsiaSdk extends Mock implements FuchsiaSdk { +class FailingDevFinder implements FuchsiaDevFinder { @override - final FuchsiaPM fuchsiaPM = MockFuchsiaPM(); + Future> list() async { + return null; + } @override - final FuchsiaKernelCompiler fuchsiaKernelCompiler = - MockFuchsiaKernelCompiler(); - - @override - final FuchsiaDevFinder fuchsiaDevFinder = MockFuchsiaDevFinder(); + Future resolve(String deviceName) async { + return null; + } +} + +class MockFuchsiaSdk extends Mock implements FuchsiaSdk { + MockFuchsiaSdk({ + FuchsiaPM pm, + FuchsiaKernelCompiler compiler, + FuchsiaDevFinder devFinder, + }) : fuchsiaPM = pm ?? FakeFuchsiaPM(), + fuchsiaKernelCompiler = compiler ?? FakeFuchsiaKernelCompiler(), + fuchsiaDevFinder = devFinder ?? FakeFuchsiaDevFinder(); + + @override + final FuchsiaPM fuchsiaPM; + + @override + final FuchsiaKernelCompiler fuchsiaKernelCompiler; + + @override + final FuchsiaDevFinder fuchsiaDevFinder; }