From 51ca131e394fd80fab2c6de1980d862e2e76a04e Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 3 May 2021 19:54:04 -0700 Subject: [PATCH] [flutter_tools] remove most mocks from Fuchsia device tests (#81789) --- .../fuchsia/fuchsia_dev_finder_test.dart | 24 +- .../fuchsia/fuchsia_device_start_test.dart | 678 ++++++++++ .../fuchsia/fuchsia_device_test.dart | 1168 ++++------------- 3 files changed, 953 insertions(+), 917 deletions(-) create mode 100644 packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_start_test.dart diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_dev_finder_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_dev_finder_test.dart index b2588b8b1a0..c69a7ea4e5a 100644 --- a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_dev_finder_test.dart +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_dev_finder_test.dart @@ -10,30 +10,29 @@ import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_dev_finder.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_sdk.dart'; -import 'package:mockito/mockito.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/fake_process_manager.dart'; void main() { - MockFuchsiaArtifacts mockFuchsiaArtifacts; + FakeFuchsiaArtifacts fuchsiaArtifacts; BufferLogger logger; MemoryFileSystem memoryFileSystem; File deviceFinder; setUp(() { - mockFuchsiaArtifacts = MockFuchsiaArtifacts(); + fuchsiaArtifacts = FakeFuchsiaArtifacts(); memoryFileSystem = MemoryFileSystem.test(); logger = BufferLogger.test(); deviceFinder = memoryFileSystem.file('device-finder'); - - when(mockFuchsiaArtifacts.devFinder).thenReturn(deviceFinder); + fuchsiaArtifacts.devFinder = deviceFinder; }); group('device-finder list', () { testWithoutContext('device-finder not found', () { final FuchsiaDevFinder fuchsiaDevFinder = FuchsiaDevFinder( - fuchsiaArtifacts: mockFuchsiaArtifacts, + fuchsiaArtifacts: fuchsiaArtifacts, logger: logger, processManager: FakeProcessManager.any(), ); @@ -54,7 +53,7 @@ void main() { ]); final FuchsiaDevFinder fuchsiaDevFinder = FuchsiaDevFinder( - fuchsiaArtifacts: mockFuchsiaArtifacts, + fuchsiaArtifacts: fuchsiaArtifacts, logger: logger, processManager: processManager, ); @@ -75,7 +74,7 @@ void main() { ]); final FuchsiaDevFinder fuchsiaDevFinder = FuchsiaDevFinder( - fuchsiaArtifacts: mockFuchsiaArtifacts, + fuchsiaArtifacts: fuchsiaArtifacts, logger: logger, processManager: processManager, ); @@ -96,7 +95,7 @@ void main() { ]); final FuchsiaDevFinder fuchsiaDevFinder = FuchsiaDevFinder( - fuchsiaArtifacts: mockFuchsiaArtifacts, + fuchsiaArtifacts: fuchsiaArtifacts, logger: logger, processManager: processManager, ); @@ -123,7 +122,7 @@ void main() { ]); final FuchsiaDevFinder fuchsiaDevFinder = FuchsiaDevFinder( - fuchsiaArtifacts: mockFuchsiaArtifacts, + fuchsiaArtifacts: fuchsiaArtifacts, logger: logger, processManager: processManager, ); @@ -133,4 +132,7 @@ void main() { }); } -class MockFuchsiaArtifacts extends Mock implements FuchsiaArtifacts {} +class FakeFuchsiaArtifacts extends Fake implements FuchsiaArtifacts { + @override + File devFinder; +} diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_start_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_start_test.dart new file mode 100644 index 00000000000..124b0494826 --- /dev/null +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_start_test.dart @@ -0,0 +1,678 @@ +// 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 'dart:convert'; + +import 'package:file/memory.dart'; +import 'package:flutter_tools/src/artifacts.dart'; +import 'package:flutter_tools/src/base/common.dart'; +import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; +import 'package:flutter_tools/src/base/os.dart'; +import 'package:flutter_tools/src/build_info.dart'; +import 'package:flutter_tools/src/device.dart'; +import 'package:flutter_tools/src/fuchsia/amber_ctl.dart'; +import 'package:flutter_tools/src/fuchsia/application_package.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/tiles_ctl.dart'; +import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; +import 'package:flutter_tools/src/project.dart'; +import 'package:meta/meta.dart'; +import 'package:mockito/mockito.dart'; + +import '../../src/common.dart'; +import '../../src/context.dart'; +import '../../src/fake_vm_services.dart'; +import '../../src/fakes.dart'; + +void main() { + group('Fuchsia app start and stop: ', () { + MemoryFileSystem memoryFileSystem; + FakeOperatingSystemUtils osUtils; + FakeFuchsiaDeviceTools fuchsiaDeviceTools; + MockFuchsiaSdk fuchsiaSdk; + Artifacts artifacts; + FakeProcessManager fakeSuccessfulProcessManager; + FakeProcessManager fakeFailedProcessManager; + File sshConfig; + + setUp(() { + memoryFileSystem = MemoryFileSystem.test(); + osUtils = FakeOperatingSystemUtils(); + fuchsiaDeviceTools = FakeFuchsiaDeviceTools(); + fuchsiaSdk = MockFuchsiaSdk(); + sshConfig = MemoryFileSystem.test().file('ssh_config')..writeAsStringSync('\n'); + artifacts = Artifacts.test(); + for (final BuildMode mode in [BuildMode.debug, BuildMode.release]) { + memoryFileSystem.file( + artifacts.getArtifactPath(Artifact.fuchsiaKernelCompiler, + platform: TargetPlatform.fuchsia_arm64, mode: mode), + ).createSync(); + + memoryFileSystem.file( + artifacts.getArtifactPath(Artifact.platformKernelDill, + platform: TargetPlatform.fuchsia_arm64, mode: mode), + ).createSync(); + + memoryFileSystem.file( + artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, + platform: TargetPlatform.fuchsia_arm64, mode: mode), + ).createSync(); + + memoryFileSystem.file( + artifacts.getArtifactPath(Artifact.fuchsiaFlutterRunner, + platform: TargetPlatform.fuchsia_arm64, mode: mode), + ).createSync(); + } + fakeSuccessfulProcessManager = FakeProcessManager.list([ + FakeCommand( + command: ['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'], + stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22', + ), + ]); + fakeFailedProcessManager = FakeProcessManager.list([ + FakeCommand( + command: ['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'], + stdout: '', + stderr: '', + exitCode: 1, + ), + ]); + }); + + Future setupAndStartApp({ + @required bool prebuilt, + @required BuildMode mode, + }) async { + const String appName = 'app_name'; + final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123'); + globals.fs.directory('fuchsia').createSync(recursive: true); + final File pubspecFile = globals.fs.file('pubspec.yaml')..createSync(); + pubspecFile.writeAsStringSync('name: $appName'); + + FuchsiaApp app; + if (prebuilt) { + final File far = globals.fs.file('app_name-0.far')..createSync(); + app = FuchsiaApp.fromPrebuiltApp(far); + } else { + globals.fs.file(globals.fs.path.join('fuchsia', 'meta', '$appName.cmx')) + ..createSync(recursive: true) + ..writeAsStringSync('{}'); + globals.fs.file('.packages').createSync(); + globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); + app = BuildableFuchsiaApp(project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory).fuchsia); + } + + final DebuggingOptions debuggingOptions = DebuggingOptions.disabled(BuildInfo(mode, null, treeShakeIcons: false)); + return 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('start and stop prebuilt in release mode', () async { + const String appName = 'app_name'; + final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123'); + globals.fs.directory('fuchsia').createSync(recursive: true); + final File pubspecFile = globals.fs.file('pubspec.yaml')..createSync(); + pubspecFile.writeAsStringSync('name: $appName'); + final File far = globals.fs.file('app_name-0.far')..createSync(); + + final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far); + final DebuggingOptions debuggingOptions = + DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null, treeShakeIcons: false)); + 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); + }, overrides: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => FakeProcessManager.list([ + const FakeCommand( + command: [ + 'Artifact.genSnapshot.TargetPlatform.fuchsia_arm64.release', + '--deterministic', + '--snapshot_kind=app-aot-elf', + '--elf=build/fuchsia/elf.aotsnapshot', + 'build/fuchsia/app_name.dil' + ], + ), + FakeCommand( + command: ['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'], + stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22', + ), + ]), + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('fail when cant get ssh config', () async { + expect(() async => + setupAndStartApp(prebuilt: true, mode: BuildMode.release), + throwsToolExit(message: 'Cannot interact with device. No ssh config.\n' + 'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.')); + }, overrides: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => FakeProcessManager.any(), + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: null), + OperatingSystemUtils: () => osUtils, + }); + + testUsingContext('fail when cant get host address', () async { + expect(() async => + setupAndStartApp(prebuilt: true, mode: BuildMode.release), + throwsToolExit(message: 'Failed to get local address, aborting.')); + }, overrides: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeFailedProcessManager, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => fuchsiaDeviceTools, + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(amber: FailingAmberCtl()), + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + 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: { + Artifacts: () => artifacts, + FileSystem: () => memoryFileSystem, + ProcessManager: () => fakeSuccessfulProcessManager, + FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(tiles: FailingTilesCtl()), + FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), + FuchsiaSdk: () => fuchsiaSdk, + OperatingSystemUtils: () => osUtils, + }); + + }); +} + +class MockProcessManager extends Mock implements ProcessManager {} + +class MockProcessResult extends Mock implements ProcessResult {} + +class MockProcess extends Mock implements Process {} + +Process _createMockProcess({ + int exitCode = 0, + String stdout = '', + String stderr = '', + bool persistent = false, +}) { + final Stream> stdoutStream = Stream>.fromIterable(>[ + utf8.encode(stdout), + ]); + final Stream> stderrStream = Stream>.fromIterable(>[ + utf8.encode(stderr), + ]); + final Process process = MockProcess(); + + when(process.stdout).thenAnswer((_) => stdoutStream); + when(process.stderr).thenAnswer((_) => stderrStream); + + if (persistent) { + final Completer exitCodeCompleter = Completer(); + when(process.kill()).thenAnswer((_) { + exitCodeCompleter.complete(-11); + return true; + }); + when(process.exitCode).thenAnswer((_) => exitCodeCompleter.future); + } else { + when(process.exitCode).thenAnswer((_) => Future.value(exitCode)); + } + return process; +} + + +class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice { + FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name); + + @override + FuchsiaIsolateDiscoveryProtocol getIsolateDiscoveryProtocol(String isolateName) { + return FakeFuchsiaIsolateDiscoveryProtocol(); + } + + @override + Future get targetPlatform async => TargetPlatform.fuchsia_arm64; +} + +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; + } + + @override + Future rmSrc(FuchsiaDevice device, FuchsiaPackageServer server) async { + return true; + } + + @override + Future getUp(FuchsiaDevice device, String packageName) async { + return true; + } + + @override + Future addRepoCfg(FuchsiaDevice device, FuchsiaPackageServer server) async { + return true; + } + + @override + Future pkgCtlResolve(FuchsiaDevice device, FuchsiaPackageServer server, String packageName) async { + return true; + } + + @override + Future pkgCtlRepoRemove(FuchsiaDevice device, FuchsiaPackageServer server) async { + return true; + } +} + +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; + } + + @override + Future addRepoCfg(FuchsiaDevice device, FuchsiaPackageServer server) async { + return false; + } + + @override + Future pkgCtlResolve(FuchsiaDevice device, FuchsiaPackageServer server, String packageName) async { + return false; + } + + @override + Future pkgCtlRepoRemove(FuchsiaDevice device, FuchsiaPackageServer server) async { + return false; + } +} + +class FakeFuchsiaTilesCtl implements FuchsiaTilesCtl { + final Map _runningApps = {}; + bool _started = false; + int _nextAppId = 1; + + @override + Future start(FuchsiaDevice device) async { + _started = true; + return true; + } + + @override + Future> list(FuchsiaDevice device) async { + if (!_started) { + return null; + } + return _runningApps; + } + + @override + Future add(FuchsiaDevice device, String url, List args) async { + if (!_started) { + return false; + } + _runningApps[_nextAppId] = url; + _nextAppId++; + return true; + } + + @override + Future remove(FuchsiaDevice device, int key) async { + if (!_started) { + return false; + } + _runningApps.remove(key); + return true; + } + + @override + Future quit(FuchsiaDevice device) async { + if (!_started) { + return false; + } + _started = false; + return true; + } +} + +class FailingTilesCtl implements FuchsiaTilesCtl { + @override + Future start(FuchsiaDevice device) async { + return false; + } + + @override + 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 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 + Future init(String buildPath, String appName) async { + if (!globals.fs.directory(buildPath).existsSync()) { + return false; + } + globals.fs + .file(globals.fs.path.join(buildPath, 'meta', 'package')) + .createSync(recursive: true); + _appName = appName; + return true; + } + + @override + Future build(String buildPath, String manifestPath) async { + if (!globals.fs.file(globals.fs.path.join(buildPath, 'meta', 'package')).existsSync() || + !globals.fs.file(manifestPath).existsSync()) { + return false; + } + globals.fs.file(globals.fs.path.join(buildPath, 'meta.far')).createSync(recursive: true); + return true; + } + + @override + Future archive(String buildPath, String manifestPath) async { + if (!globals.fs.file(globals.fs.path.join(buildPath, 'meta', 'package')).existsSync() || + !globals.fs.file(manifestPath).existsSync()) { + return false; + } + if (_appName == null) { + return false; + } + globals.fs + .file(globals.fs.path.join(buildPath, '$_appName-0.far')) + .createSync(recursive: true); + return true; + } + + @override + Future newrepo(String repoPath) async { + if (!globals.fs.directory(repoPath).existsSync()) { + return false; + } + return true; + } + + @override + Future serve(String repoPath, String host, int port) async { + return _createMockProcess(persistent: true); + } + + @override + Future publish(String repoPath, String packagePath) async { + if (!globals.fs.directory(repoPath).existsSync()) { + return false; + } + if (!globals.fs.file(packagePath).existsSync()) { + return false; + } + return true; + } +} + +class FailingPM implements FuchsiaPM { + @override + Future init(String buildPath, String appName) async { + return false; + } + + + @override + Future build(String buildPath, String manifestPath) async { + return false; + } + + @override + Future archive(String buildPath, 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, + @required String target, // E.g., lib/main.dart + BuildInfo buildInfo = BuildInfo.debug, + }) async { + final String outDir = getFuchsiaBuildDirectory(); + final String appName = fuchsiaProject.project.manifest.appName; + final String manifestPath = globals.fs.path.join(outDir, '$appName.dilpmanifest'); + globals.fs.file(manifestPath).createSync(recursive: true); + } +} + +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({ Duration timeout }) async { + return ['192.168.11.999 scare-cable-device-finder']; + } + + @override + Future resolve(String deviceName) async { + return '192.168.11.999'; + } +} + +class FakeFuchsiaFfx implements FuchsiaFfx { + @override + Future> list({Duration timeout}) async { + return ['192.168.42.172 scare-cable-skip-ffx']; + } + + @override + Future resolve(String deviceName) async { + return '192.168.42.10'; + } +} + +class MockFuchsiaSdk extends Mock implements FuchsiaSdk { + MockFuchsiaSdk({ + FuchsiaPM pm, + FuchsiaKernelCompiler compiler, + FuchsiaDevFinder devFinder, + FuchsiaFfx ffx, + }) : fuchsiaPM = pm ?? FakeFuchsiaPM(), + fuchsiaKernelCompiler = compiler ?? FakeFuchsiaKernelCompiler(), + fuchsiaDevFinder = devFinder ?? FakeFuchsiaDevFinder(), + fuchsiaFfx = ffx ?? FakeFuchsiaFfx(); + + @override + final FuchsiaPM fuchsiaPM; + + @override + final FuchsiaKernelCompiler fuchsiaKernelCompiler; + + @override + final FuchsiaDevFinder fuchsiaDevFinder; + + @override + final FuchsiaFfx fuchsiaFfx; +} diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart index ff92544ecd2..2c55a59a4df 100644 --- a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart @@ -5,25 +5,20 @@ // @dart = 2.8 import 'dart:async'; -import 'dart:convert'; 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/common.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/os.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/amber_ctl.dart'; -import 'package:flutter_tools/src/fuchsia/application_package.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'; @@ -31,18 +26,16 @@ 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/fuchsia/tiles_ctl.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:mockito/mockito.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'; -import '../../src/fakes.dart'; final vm_service.Isolate fakeIsolate = vm_service.Isolate( id: '1', @@ -67,10 +60,12 @@ 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', () { @@ -94,33 +89,27 @@ void main() { }); testWithoutContext('lists nothing when workflow cannot list devices', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); + final FakeFuchsiaWorkflow fuchsiaWorkflow = FakeFuchsiaWorkflow(canListDevices: false); final FuchsiaDevices fuchsiaDevices = FuchsiaDevices( platform: FakePlatform(operatingSystem: 'linux'), fuchsiaSdk: null, fuchsiaWorkflow: fuchsiaWorkflow, logger: BufferLogger.test(), ); - when(fuchsiaWorkflow.canListDevices).thenReturn(false); expect(fuchsiaDevices.canListAnything, false); expect(await fuchsiaDevices.pollingGetDevices(), isEmpty); }); testWithoutContext('can parse ffx output for single device', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); - final MockFuchsiaSdk fuchsiaSdk = MockFuchsiaSdk(); + 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: {},), fuchsiaSdk: fuchsiaSdk, fuchsiaWorkflow: fuchsiaWorkflow, logger: BufferLogger.test(), ); - when(fuchsiaWorkflow.shouldUseDeviceFinder).thenReturn(false); - when(fuchsiaWorkflow.canListDevices).thenReturn(true); - when(fuchsiaSdk.listDevices(useDeviceFinder: false)).thenAnswer((Invocation invocation) async { - return '2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel'; - }); final Device device = (await fuchsiaDevices.pollingGetDevices()).single; @@ -129,19 +118,14 @@ void main() { }); testWithoutContext('can parse device-finder output for single device', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); - final MockFuchsiaSdk fuchsiaSdk = MockFuchsiaSdk(); + 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: {'FUCHSIA_DISABLED_ffx_discovery': '1'},), fuchsiaSdk: fuchsiaSdk, fuchsiaWorkflow: fuchsiaWorkflow, logger: BufferLogger.test(), ); - when(fuchsiaWorkflow.shouldUseDeviceFinder).thenReturn(true); - when(fuchsiaWorkflow.canListDevices).thenReturn(true); - when(fuchsiaSdk.listDevices(useDeviceFinder: true)).thenAnswer((Invocation invocation) async { - return '2001:0db8:85a3:0000:0000:8a2e:0370:7334 paper-pulp-bush-angel'; - }); final Device device = (await fuchsiaDevices.pollingGetDevices()).single; @@ -150,20 +134,17 @@ void main() { }); testWithoutContext('can parse ffx output for multiple devices', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); - final MockFuchsiaSdk fuchsiaSdk = MockFuchsiaSdk(); + 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(), ); - when(fuchsiaWorkflow.shouldUseDeviceFinder).thenReturn(false); - when(fuchsiaWorkflow.canListDevices).thenReturn(true); - when(fuchsiaSdk.listDevices(useDeviceFinder: false)).thenAnswer((Invocation invocation) async { - return '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 List devices = await fuchsiaDevices.pollingGetDevices(); @@ -174,20 +155,17 @@ void main() { }); testWithoutContext('can parse device-finder output for multiple devices', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); - final MockFuchsiaSdk fuchsiaSdk = MockFuchsiaSdk(); + 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(), ); - when(fuchsiaWorkflow.shouldUseDeviceFinder).thenReturn(true); - when(fuchsiaWorkflow.canListDevices).thenReturn(true); - when(fuchsiaSdk.listDevices(useDeviceFinder: true)).thenAnswer((Invocation invocation) async { - return '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 List devices = await fuchsiaDevices.pollingGetDevices(); @@ -198,19 +176,14 @@ void main() { }); testWithoutContext('can parse junk output from ffx', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); - final MockFuchsiaSdk fuchsiaSdk = MockFuchsiaSdk(); + 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(), ); - when(fuchsiaWorkflow.shouldUseDeviceFinder).thenReturn(false); - when(fuchsiaWorkflow.canListDevices).thenReturn(true); - when(fuchsiaSdk.listDevices()).thenAnswer((Invocation invocation) async { - return 'junk'; - }); final List devices = await fuchsiaDevices.pollingGetDevices(); @@ -218,19 +191,14 @@ void main() { }); testWithoutContext('can parse junk output from device-finder', () async { - final MockFuchsiaWorkflow fuchsiaWorkflow = MockFuchsiaWorkflow(); - final MockFuchsiaSdk fuchsiaSdk = MockFuchsiaSdk(); + 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(), ); - when(fuchsiaWorkflow.shouldUseDeviceFinder).thenReturn(true); - when(fuchsiaWorkflow.canListDevices).thenReturn(true); - when(fuchsiaSdk.listDevices(useDeviceFinder: true)).thenAnswer((Invocation invocation) async { - return 'junk'; - }); final List devices = await fuchsiaDevices.pollingGetDevices(); @@ -238,11 +206,12 @@ void main() { }); testUsingContext('disposing device disposes the portForwarder', () async { - final MockPortForwarder mockPortForwarder = MockPortForwarder(); + final FakePortForwarder portForwarder = FakePortForwarder(); final FuchsiaDevice device = FuchsiaDevice('123'); - device.portForwarder = mockPortForwarder; + device.portForwarder = portForwarder; await device.dispose(); - verify(mockPortForwarder.dispose()).called(1); + + expect(portForwarder.disposed, true); }); testWithoutContext('default capabilities', () async { @@ -286,130 +255,104 @@ void main() { expect(await device.targetPlatform, TargetPlatform.fuchsia_arm64); }, overrides: { FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: null), - FuchsiaSdk: () => MockFuchsiaSdk(), - ProcessManager: () => MockProcessManager(), + FuchsiaSdk: () => FakeFuchsiaSdk(), + ProcessManager: () => processManager, }); testUsingContext('targetPlatform arm64 works', () async { - when(globals.processManager.run(any)).thenAnswer((Invocation _) async { - return ProcessResult(1, 0, 'aarch64', ''); - }); + processManager.addCommand(const FakeCommand( + command: ['ssh', '-F', '/ssh_config', '123', 'uname -m'], + stdout: 'aarch64', + )); + final FuchsiaDevice device = FuchsiaDevice('123'); expect(await device.targetPlatform, TargetPlatform.fuchsia_arm64); }, overrides: { FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), - ProcessManager: () => MockProcessManager(), + FuchsiaSdk: () => FakeFuchsiaSdk(), + ProcessManager: () => processManager, }); testUsingContext('targetPlatform x64 works', () async { - when(globals.processManager.run(any)).thenAnswer((Invocation _) async { - return ProcessResult(1, 0, 'x86_64', ''); - }); + processManager.addCommand(const FakeCommand( + command: ['ssh', '-F', '/ssh_config', '123', 'uname -m'], + stdout: 'x86_64', + )); + final FuchsiaDevice device = FuchsiaDevice('123'); expect(await device.targetPlatform, TargetPlatform.fuchsia_x64); }, overrides: { FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), - ProcessManager: () => MockProcessManager(), + FuchsiaSdk: () => FakeFuchsiaSdk(), + ProcessManager: () => processManager, }); testUsingContext('hostAddress parsing works', () async { - when(globals.processManager.run(any)).thenAnswer((Invocation _) async { - return ProcessResult( - 1, - 0, - 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22', - '', - ); - }); + processManager.addCommand(const FakeCommand( + command: ['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: { FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), - ProcessManager: () => MockProcessManager(), + FuchsiaSdk: () => FakeFuchsiaSdk(), + ProcessManager: () => processManager, }); testUsingContext('hostAddress parsing throws tool error on failure', () async { - when(globals.processManager.run(any)).thenAnswer((Invocation _) async { - return ProcessResult(1, 1, '', ''); - }); + processManager.addCommand(const FakeCommand( + command: ['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'], + exitCode: 1, + )); + final FuchsiaDevice device = FuchsiaDevice('id'); - expect(() async => device.hostAddress, throwsToolExit()); + await expectLater(() => device.hostAddress, throwsToolExit()); }, overrides: { FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), - ProcessManager: () => MockProcessManager(), + FuchsiaSdk: () => FakeFuchsiaSdk(), + ProcessManager: () => processManager, }); testUsingContext('hostAddress parsing throws tool error on empty response', () async { - when(globals.processManager.run(any)).thenAnswer((Invocation _) async { - return ProcessResult(1, 0, '', ''); - }); + processManager.addCommand(const FakeCommand( + command: ['ssh', '-F', '/ssh_config', 'id', r'echo $SSH_CONNECTION'], + )); + final FuchsiaDevice device = FuchsiaDevice('id'); expect(() async => device.hostAddress, throwsToolExit()); }, overrides: { FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), - ProcessManager: () => MockProcessManager(), + FuchsiaSdk: () => FakeFuchsiaSdk(), + ProcessManager: () => processManager, }); }); group('displays friendly error when', () { - MockProcessManager mockProcessManager; - MockProcessResult mockProcessResult; File artifactFile; - MockProcessManager emptyStdoutProcessManager; - MockProcessResult emptyStdoutProcessResult; + FakeProcessManager processManager; setUp(() { - mockProcessManager = MockProcessManager(); - mockProcessResult = MockProcessResult(); + processManager = FakeProcessManager.empty(); artifactFile = MemoryFileSystem.test().file('artifact'); - when(mockProcessManager.run( - any, - environment: anyNamed('environment'), - workingDirectory: anyNamed('workingDirectory'), - )).thenAnswer((Invocation invocation) => - Future.value(mockProcessResult)); - when(mockProcessResult.exitCode).thenReturn(1); - when(mockProcessResult.stdout as String).thenReturn(''); - when(mockProcessResult.stderr as String).thenReturn(''); - - emptyStdoutProcessManager = MockProcessManager(); - emptyStdoutProcessResult = MockProcessResult(); - when(emptyStdoutProcessManager.run( - any, - environment: anyNamed('environment'), - workingDirectory: anyNamed('workingDirectory'), - )).thenAnswer((Invocation invocation) => - Future.value(emptyStdoutProcessResult)); - when(emptyStdoutProcessResult.exitCode).thenReturn(0); - when(emptyStdoutProcessResult.stdout as String).thenReturn(''); - when(emptyStdoutProcessResult.stderr as String).thenReturn(''); }); testUsingContext('No vmservices found', () async { + processManager.addCommand(const FakeCommand( + command: ['ssh', '-F', '/artifact', 'id', 'find /hub -name vmservice-port'], + )); final FuchsiaDevice device = FuchsiaDevice('id'); - ToolExit toolExit; - try { - await device.servicePorts(); - } on ToolExit catch (err) { - toolExit = err; - } - expect( - toolExit.message, - contains( - 'No Dart Observatories found. Are you running a debug build?')); + + await expectLater(device.servicePorts, throwsToolExit(message: 'No Dart Observatories found. Are you running a debug build?')); }, overrides: { - ProcessManager: () => emptyStdoutProcessManager, + ProcessManager: () => processManager, FuchsiaArtifacts: () => FuchsiaArtifacts( - sshConfig: artifactFile, - devFinder: artifactFile, - ffx: artifactFile, - ), - FuchsiaSdk: () => MockFuchsiaSdk(), + sshConfig: artifactFile, + devFinder: artifactFile, + ffx: artifactFile, + ), + FuchsiaSdk: () => FakeFuchsiaSdk(), }); group('device logs', () { @@ -422,42 +365,29 @@ void main() { [2018-11-09 01:30:12][52580][52983][log] INFO: example_app.cmx(flutter): Did thing this time '''; - MockProcessManager mockProcessManager; - MockProcess mockProcess; - Completer exitCode; - StreamController> stdout; - StreamController> stderr; + FakeProcessManager processManager; File devFinder; File ffx; File sshConfig; setUp(() { - mockProcessManager = MockProcessManager(); - mockProcess = MockProcess(); - stdout = StreamController>(sync: true); - stderr = StreamController>(sync: true); - exitCode = Completer(); - when(mockProcessManager.start(any)) - .thenAnswer((Invocation _) => Future.value(mockProcess)); - when(mockProcess.exitCode).thenAnswer((Invocation _) => exitCode.future); - when(mockProcess.stdout).thenAnswer((Invocation _) => stdout.stream); - when(mockProcess.stderr).thenAnswer((Invocation _) => stderr.stream); + 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'); }); - tearDown(() { - exitCode.complete(0); - }); - testUsingContext('can be parsed for an app', () async { - final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester'); - final DeviceLogReader reader = device.getLogReader( - app: FuchsiaModulePackage(name: 'example_app')); - final List logLines = []; final Completer lock = Completer(); + processManager.addCommand(FakeCommand( + command: const ['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 logLines = []; reader.logLines.listen((String line) { logLines.add(line); if (logLines.length == 2) { @@ -466,52 +396,55 @@ void main() { }); expect(logLines, isEmpty); - stdout.add(utf8.encode(exampleUtcLogs)); - await stdout.close(); - await lock.future.timeout(const Duration(seconds: 1)); - + await lock.future; expect(logLines, [ '[2018-11-09 01:27:45.000] Flutter: Error doing thing', '[2018-11-09 01:30:12.000] Flutter: Did thing this time', ]); }, overrides: { - ProcessManager: () => mockProcessManager, + 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 FuchsiaDevice device = FuchsiaDevice('id', name: 'tester'); - final DeviceLogReader reader = device.getLogReader( - app: FuchsiaModulePackage(name: 'example_app')); - final List logLines = []; final Completer lock = Completer(); + processManager.addCommand(FakeCommand( + command: const ['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 logLines = []; reader.logLines.listen((String line) { logLines.add(line); lock.complete(); }); expect(logLines, isEmpty); - stdout.add(utf8.encode(exampleUtcLogs)); - await stdout.close(); await lock.future.timeout(const Duration(seconds: 1)); expect(logLines, [ '[2018-11-09 01:30:12.000] Flutter: Did thing this time', ]); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => processManager, SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 29, 45)), - FuchsiaArtifacts: () => - FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig, ffx: ffx), + FuchsiaArtifacts: () => FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig, ffx: ffx), }); testUsingContext('can be parsed for all apps', () async { + final Completer lock = Completer(); + processManager.addCommand(FakeCommand( + command: const ['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 logLines = []; - final Completer lock = Completer(); reader.logLines.listen((String line) { logLines.add(line); if (logLines.length == 3) { @@ -520,8 +453,6 @@ void main() { }); expect(logLines, isEmpty); - stdout.add(utf8.encode(exampleUtcLogs)); - await stdout.close(); await lock.future.timeout(const Duration(seconds: 1)); expect(logLines, [ @@ -530,15 +461,20 @@ void main() { '[2018-11-09 01:30:12.000] Flutter: Did thing this time', ]); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => processManager, SystemClock: () => SystemClock.fixed(DateTime(2018, 11, 9, 1, 25, 45)), - FuchsiaArtifacts: () => - FuchsiaArtifacts(devFinder: devFinder, sshConfig: sshConfig, ffx: ffx), + 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); @@ -567,26 +503,25 @@ void main() { }); testUsingContext('takeScreenshot throws if screencap failed', () async { - final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester'); - - when(globals.processManager.run( - const [ + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'screencap > /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 1, '', '')); + exitCode: 1, + stderr: '' + )); + 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')), ); }, overrides: { - ProcessManager: () => MockProcessManager(), + ProcessManager: () => processManager, FileSystem: () => MemoryFileSystem.test(), Platform: () => FakePlatform( environment: { @@ -598,49 +533,42 @@ void main() { testUsingContext('takeScreenshot throws if scp failed', () async { final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester'); - - when(globals.processManager.run( - const [ + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'screencap > /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); - - when(globals.processManager.run( - const [ + )); + processManager.addCommand(const FakeCommand( + command: [ 'scp', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0:/tmp/screenshot.ppm', 'file.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 1, '', '')); - - when(globals.processManager.run( - const [ + exitCode: 1, + stderr: '', + )); + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'rm /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); + )); await expectLater( () => device.takeScreenshot(globals.fs.file('file.ppm')), throwsA(equals('Failed to copy screenshot from device:\n')), ); }, overrides: { - ProcessManager: () => MockProcessManager(), + ProcessManager: () => processManager, FileSystem: () => MemoryFileSystem.test(), Platform: () => FakePlatform( environment: { @@ -652,54 +580,43 @@ void main() { testUsingContext("takeScreenshot prints error if can't delete file from device", () async { final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester'); - - when(globals.processManager.run( - const [ + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'screencap > /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); - - when(globals.processManager.run( - const [ + )); + processManager.addCommand(const FakeCommand( + command: [ 'scp', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0:/tmp/screenshot.ppm', 'file.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); - - when(globals.processManager.run( - const [ + )); + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'rm /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 1, '', '')); + exitCode: 1, + stderr: '', + )); - try { - await device.takeScreenshot(globals.fs.file('file.ppm')); - } on Exception { - assert(false); - } + await device.takeScreenshot(globals.fs.file('file.ppm')); expect( testLogger.errorText, contains('Failed to delete screenshot.ppm from the device:\n'), ); }, overrides: { - ProcessManager: () => MockProcessManager(), + ProcessManager: () => processManager, FileSystem: () => MemoryFileSystem.test(), Platform: () => FakePlatform( environment: { @@ -711,47 +628,37 @@ void main() { testUsingContext('takeScreenshot returns', () async { final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester'); - - when(globals.processManager.run( - const [ + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'screencap > /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); - - when(globals.processManager.run( - const [ + )); + processManager.addCommand(const FakeCommand( + command: [ 'scp', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0:/tmp/screenshot.ppm', 'file.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); - - when(globals.processManager.run( - const [ + )); + processManager.addCommand(const FakeCommand( + command: [ 'ssh', '-F', '/fuchsia/out/default/.ssh', '0.0.0.0', 'rm /tmp/screenshot.ppm', ], - workingDirectory: anyNamed('workingDirectory'), - environment: anyNamed('environment'), - )).thenAnswer((_) async => ProcessResult(0, 0, '', '')); + )); - expect(() async => device.takeScreenshot(globals.fs.file('file.ppm')), - returnsNormally); + expect(() => device.takeScreenshot(globals.fs.file('file.ppm')), returnsNormally); }, overrides: { - ProcessManager: () => MockProcessManager(), + ProcessManager: () => processManager, FileSystem: () => MemoryFileSystem.test(), Platform: () => FakePlatform( environment: { @@ -763,52 +670,35 @@ void main() { }); group('portForwarder', () { - MockProcessManager mockProcessManager; + FakeProcessManager processManager; File sshConfig; setUp(() { - mockProcessManager = MockProcessManager(); + 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: ['ssh', '-F', '/irrelevant', '-O', 'cancel', '-vvv', '-L', '0:127.0.0.1:1', 'id'], + exitCode: 1, + stdout: '', + stderr: '', + )); - final MockProcessResult mockFailureProcessResult = MockProcessResult(); - when(mockFailureProcessResult.exitCode).thenReturn(1); - when(mockFailureProcessResult.stdout as String).thenReturn(''); - when(mockFailureProcessResult.stderr as String).thenReturn(''); - when(mockProcessManager.run([ - 'ssh', - '-F', - sshConfig.absolute.path, - '-O', - 'cancel', - '-vvv', - '-L', - '0:127.0.0.1:1', - 'id', - ])).thenAnswer((Invocation invocation) { - return Future.value(mockFailureProcessResult); - }); await expectLater( () => device.portForwarder.unforward(ForwardedPort(/*hostPort=*/ 0, /*devicePort=*/ 1)), throwsToolExit(message: 'Unforward command failed:\nstdout: \nstderr: '), ); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => processManager, FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), }); }); group('FuchsiaIsolateDiscoveryProtocol', () { - MockPortForwarder portForwarder; - - setUp(() { - portForwarder = MockPortForwarder(); - }); - Future findUri(List views, String expectedIsolateName) async { final FakeVmServiceHost fakeVmServiceHost = FakeVmServiceHost( requests: [ @@ -824,8 +714,7 @@ void main() { ], httpAddress: Uri.parse('example'), ); - final MockFuchsiaDevice fuchsiaDevice = - MockFuchsiaDevice('123', portForwarder, false); + final MockFuchsiaDevice fuchsiaDevice = MockFuchsiaDevice('123', const NoOpDevicePortForwarder(), false); final FuchsiaIsolateDiscoveryProtocol discoveryProtocol = FuchsiaIsolateDiscoveryProtocol( fuchsiaDevice, @@ -834,15 +723,7 @@ void main() { (Device device, Uri uri, bool enableServiceAuthCodes) => null, true, // only poll once. ); - final MockDartDevelopmentService mockDds = MockDartDevelopmentService(); - when(fuchsiaDevice.dds).thenReturn(mockDds); - when(mockDds.startDartDevelopmentService(any, any, any, any, logger: anyNamed('logger'))).thenReturn(null); - when(mockDds.uri).thenReturn(Uri.parse('example')); - when(fuchsiaDevice.servicePorts()) - .thenAnswer((Invocation invocation) async => [1]); - when(portForwarder.forward(1)) - .thenAnswer((Invocation invocation) async => 2); - return await discoveryProtocol.uri; + return discoveryProtocol.uri; } testUsingContext('can find flutter view with matching isolate name', () async { @@ -868,8 +749,7 @@ void main() { ), ], expectedIsolateName); - expect( - uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/'); + expect(uri.toString(), 'http://${InternetAddress.loopbackIPv4.address}:0/'); }); testUsingContext('can handle flutter view without matching isolate name', () async { @@ -938,337 +818,64 @@ void main() { ); }); - group('Fuchsia app start and stop: ', () { - MemoryFileSystem memoryFileSystem; - FakeOperatingSystemUtils osUtils; - FakeFuchsiaDeviceTools fuchsiaDeviceTools; - MockFuchsiaSdk fuchsiaSdk; - Artifacts artifacts; - FakeProcessManager fakeSuccessfulProcessManager; - FakeProcessManager fakeFailedProcessManager; - File sshConfig; - - setUp(() { - memoryFileSystem = MemoryFileSystem.test(); - osUtils = FakeOperatingSystemUtils(); - fuchsiaDeviceTools = FakeFuchsiaDeviceTools(); - fuchsiaSdk = MockFuchsiaSdk(); - sshConfig = MemoryFileSystem.test().file('ssh_config')..writeAsStringSync('\n'); - artifacts = Artifacts.test(); - for (final BuildMode mode in [BuildMode.debug, BuildMode.release]) { - memoryFileSystem.file( - artifacts.getArtifactPath(Artifact.fuchsiaKernelCompiler, - platform: TargetPlatform.fuchsia_arm64, mode: mode), - ).createSync(); - - memoryFileSystem.file( - artifacts.getArtifactPath(Artifact.platformKernelDill, - platform: TargetPlatform.fuchsia_arm64, mode: mode), - ).createSync(); - - memoryFileSystem.file( - artifacts.getArtifactPath(Artifact.flutterPatchedSdkPath, - platform: TargetPlatform.fuchsia_arm64, mode: mode), - ).createSync(); - - memoryFileSystem.file( - artifacts.getArtifactPath(Artifact.fuchsiaFlutterRunner, - platform: TargetPlatform.fuchsia_arm64, mode: mode), - ).createSync(); - } - fakeSuccessfulProcessManager = FakeProcessManager.list([ - FakeCommand( - command: ['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'], - stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22', - ), - ]); - fakeFailedProcessManager = FakeProcessManager.list([ - FakeCommand( - command: ['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'], - stdout: '', - stderr: '', - exitCode: 1, - ), - ]); - }); - - Future setupAndStartApp({ - @required bool prebuilt, - @required BuildMode mode, - }) async { - const String appName = 'app_name'; - final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123'); - globals.fs.directory('fuchsia').createSync(recursive: true); - final File pubspecFile = globals.fs.file('pubspec.yaml')..createSync(); - pubspecFile.writeAsStringSync('name: $appName'); - - FuchsiaApp app; - if (prebuilt) { - final File far = globals.fs.file('app_name-0.far')..createSync(); - app = FuchsiaApp.fromPrebuiltApp(far); - } else { - globals.fs.file(globals.fs.path.join('fuchsia', 'meta', '$appName.cmx')) - ..createSync(recursive: true) - ..writeAsStringSync('{}'); - globals.fs.file('.packages').createSync(); - globals.fs.file(globals.fs.path.join('lib', 'main.dart')).createSync(recursive: true); - app = BuildableFuchsiaApp(project: FlutterProject.fromDirectoryTest(globals.fs.currentDirectory).fuchsia); - } - - final DebuggingOptions debuggingOptions = DebuggingOptions.disabled(BuildInfo(mode, null, treeShakeIcons: false)); - return 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => fuchsiaSdk, - OperatingSystemUtils: () => osUtils, - }); - - testUsingContext('start and stop prebuilt in release mode', () async { - const String appName = 'app_name'; - final FuchsiaDevice device = FuchsiaDeviceWithFakeDiscovery('123'); - globals.fs.directory('fuchsia').createSync(recursive: true); - final File pubspecFile = globals.fs.file('pubspec.yaml')..createSync(); - pubspecFile.writeAsStringSync('name: $appName'); - final File far = globals.fs.file('app_name-0.far')..createSync(); - - final FuchsiaApp app = FuchsiaApp.fromPrebuiltApp(far); - final DebuggingOptions debuggingOptions = - DebuggingOptions.disabled(const BuildInfo(BuildMode.release, null, treeShakeIcons: false)); - 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); - }, overrides: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => FakeProcessManager.list([ - const FakeCommand( - command: [ - 'Artifact.genSnapshot.TargetPlatform.fuchsia_arm64.release', - '--deterministic', - '--snapshot_kind=app-aot-elf', - '--elf=build/fuchsia/elf.aotsnapshot', - 'build/fuchsia/app_name.dil' - ], - ), - FakeCommand( - command: ['ssh', '-F', sshConfig.absolute.path, '123', r'echo $SSH_CONNECTION'], - stdout: 'fe80::8c6c:2fff:fe3d:c5e1%ethp0003 50666 fe80::5054:ff:fe63:5e7a%ethp0003 22', - ), - ]), - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => fuchsiaSdk, - OperatingSystemUtils: () => osUtils, - }); - - testUsingContext('fail when cant get ssh config', () async { - expect(() async => - setupAndStartApp(prebuilt: true, mode: BuildMode.release), - throwsToolExit(message: 'Cannot interact with device. No ssh config.\n' - 'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.')); - }, overrides: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => FakeProcessManager.any(), - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: null), - OperatingSystemUtils: () => osUtils, - }); - - testUsingContext('fail when cant get host address', () async { - expect(() async => - setupAndStartApp(prebuilt: true, mode: BuildMode.release), - throwsToolExit(message: 'Failed to get local address, aborting.')); - }, overrides: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeFailedProcessManager, - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => fuchsiaDeviceTools, - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(amber: FailingAmberCtl()), - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - 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: { - Artifacts: () => artifacts, - FileSystem: () => memoryFileSystem, - ProcessManager: () => fakeSuccessfulProcessManager, - FuchsiaDeviceTools: () => FakeFuchsiaDeviceTools(tiles: FailingTilesCtl()), - FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => fuchsiaSdk, - OperatingSystemUtils: () => osUtils, - }); - - }); - group('sdkNameAndVersion: ', () { File sshConfig; - MockProcessManager mockSuccessProcessManager; - MockProcessResult mockSuccessProcessResult; - MockProcessManager mockFailureProcessManager; - MockProcessResult mockFailureProcessResult; - MockProcessManager emptyStdoutProcessManager; - MockProcessResult emptyStdoutProcessResult; + FakeProcessManager processManager; setUp(() { sshConfig = MemoryFileSystem.test().file('ssh_config')..writeAsStringSync('\n'); - - mockSuccessProcessManager = MockProcessManager(); - mockSuccessProcessResult = MockProcessResult(); - when(mockSuccessProcessManager.run(any)).thenAnswer( - (Invocation invocation) => Future.value(mockSuccessProcessResult)); - when(mockSuccessProcessResult.exitCode).thenReturn(0); - when(mockSuccessProcessResult.stdout as String).thenReturn('version'); - when(mockSuccessProcessResult.stderr as String).thenReturn(''); - - mockFailureProcessManager = MockProcessManager(); - mockFailureProcessResult = MockProcessResult(); - when(mockFailureProcessManager.run(any)).thenAnswer( - (Invocation invocation) => Future.value(mockFailureProcessResult)); - when(mockFailureProcessResult.exitCode).thenReturn(1); - when(mockFailureProcessResult.stdout as String).thenReturn(''); - when(mockFailureProcessResult.stderr as String).thenReturn(''); - - emptyStdoutProcessManager = MockProcessManager(); - emptyStdoutProcessResult = MockProcessResult(); - when(emptyStdoutProcessManager.run(any)).thenAnswer((Invocation invocation) => - Future.value(emptyStdoutProcessResult)); - when(emptyStdoutProcessResult.exitCode).thenReturn(0); - when(emptyStdoutProcessResult.stdout as String).thenReturn(''); - when(emptyStdoutProcessResult.stderr as String).thenReturn(''); + 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: { - ProcessManager: () => mockSuccessProcessManager, + ProcessManager: () => processManager, FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: null), - FuchsiaSdk: () => MockFuchsiaSdk(), + FuchsiaSdk: () => FakeFuchsiaSdk(), }); testUsingContext('returns what we get from the device on success', () async { + processManager.addCommand(const FakeCommand( + command: ['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: { - ProcessManager: () => mockSuccessProcessManager, + ProcessManager: () => processManager, FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), + FuchsiaSdk: () => FakeFuchsiaSdk(), }); testUsingContext('returns "Fuchsia" when device command fails', () async { + processManager.addCommand(const FakeCommand( + command: ['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: { - ProcessManager: () => mockFailureProcessManager, + ProcessManager: () => processManager, FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), + FuchsiaSdk: () => FakeFuchsiaSdk(), }); testUsingContext('returns "Fuchsia" when device gives an empty result', () async { + processManager.addCommand(const FakeCommand( + command: ['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: { - ProcessManager: () => emptyStdoutProcessManager, + ProcessManager: () => processManager, FuchsiaArtifacts: () => FuchsiaArtifacts(sshConfig: sshConfig), - FuchsiaSdk: () => MockFuchsiaSdk(), + FuchsiaSdk: () => FakeFuchsiaSdk(), }); }); } @@ -1280,43 +887,7 @@ class FuchsiaModulePackage extends ApplicationPackage { final String name; } -class MockProcessManager extends Mock implements ProcessManager {} - -class MockProcessResult extends Mock implements ProcessResult {} - -class MockProcess extends Mock implements Process {} - -Process _createMockProcess({ - int exitCode = 0, - String stdout = '', - String stderr = '', - bool persistent = false, -}) { - final Stream> stdoutStream = Stream>.fromIterable(>[ - utf8.encode(stdout), - ]); - final Stream> stderrStream = Stream>.fromIterable(>[ - utf8.encode(stderr), - ]); - final Process process = MockProcess(); - - when(process.stdout).thenAnswer((_) => stdoutStream); - when(process.stderr).thenAnswer((_) => stderrStream); - - if (persistent) { - final Completer exitCodeCompleter = Completer(); - when(process.kill()).thenAnswer((_) { - exitCodeCompleter.complete(-11); - return true; - }); - when(process.exitCode).thenAnswer((_) => exitCodeCompleter.future); - } else { - when(process.exitCode).thenAnswer((_) => Future.value(exitCode)); - } - return process; -} - -class MockFuchsiaDevice extends Mock implements FuchsiaDevice { +class MockFuchsiaDevice extends Fake implements FuchsiaDevice { MockFuchsiaDevice(this.id, this.portForwarder, this._ipv6); final bool _ipv6; @@ -1332,9 +903,25 @@ class MockFuchsiaDevice extends Mock implements FuchsiaDevice { @override Future get targetPlatform async => TargetPlatform.fuchsia_arm64; + + @override + String get name => 'fuchsia'; + + @override + Future> servicePorts() async => [1]; + + @override + DartDevelopmentService get dds => FakeDartDevelopmentService(); } -class MockPortForwarder extends Mock implements DevicePortForwarder {} +class FakePortForwarder extends Fake implements DevicePortForwarder { + bool disposed = false; + + @override + Future dispose() async { + disposed = true; + } +} class FuchsiaDeviceWithFakeDiscovery extends FuchsiaDevice { FuchsiaDeviceWithFakeDiscovery(String id, {String name}) : super(id, name: name); @@ -1356,282 +943,6 @@ class FakeFuchsiaIsolateDiscoveryProtocol implements FuchsiaIsolateDiscoveryProt void dispose() {} } -class FakeFuchsiaAmberCtl implements FuchsiaAmberCtl { - @override - Future addSrc(FuchsiaDevice device, FuchsiaPackageServer server) async { - return true; - } - - @override - Future rmSrc(FuchsiaDevice device, FuchsiaPackageServer server) async { - return true; - } - - @override - Future getUp(FuchsiaDevice device, String packageName) async { - return true; - } - - @override - Future addRepoCfg(FuchsiaDevice device, FuchsiaPackageServer server) async { - return true; - } - - @override - Future pkgCtlResolve(FuchsiaDevice device, FuchsiaPackageServer server, String packageName) async { - return true; - } - - @override - Future pkgCtlRepoRemove(FuchsiaDevice device, FuchsiaPackageServer server) async { - return true; - } -} - -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; - } - - @override - Future addRepoCfg(FuchsiaDevice device, FuchsiaPackageServer server) async { - return false; - } - - @override - Future pkgCtlResolve(FuchsiaDevice device, FuchsiaPackageServer server, String packageName) async { - return false; - } - - @override - Future pkgCtlRepoRemove(FuchsiaDevice device, FuchsiaPackageServer server) async { - return false; - } -} - -class FakeFuchsiaTilesCtl implements FuchsiaTilesCtl { - final Map _runningApps = {}; - bool _started = false; - int _nextAppId = 1; - - @override - Future start(FuchsiaDevice device) async { - _started = true; - return true; - } - - @override - Future> list(FuchsiaDevice device) async { - if (!_started) { - return null; - } - return _runningApps; - } - - @override - Future add(FuchsiaDevice device, String url, List args) async { - if (!_started) { - return false; - } - _runningApps[_nextAppId] = url; - _nextAppId++; - return true; - } - - @override - Future remove(FuchsiaDevice device, int key) async { - if (!_started) { - return false; - } - _runningApps.remove(key); - return true; - } - - @override - Future quit(FuchsiaDevice device) async { - if (!_started) { - return false; - } - _started = false; - return true; - } -} - -class FailingTilesCtl implements FuchsiaTilesCtl { - @override - Future start(FuchsiaDevice device) async { - return false; - } - - @override - 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 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 - Future init(String buildPath, String appName) async { - if (!globals.fs.directory(buildPath).existsSync()) { - return false; - } - globals.fs - .file(globals.fs.path.join(buildPath, 'meta', 'package')) - .createSync(recursive: true); - _appName = appName; - return true; - } - - @override - Future build(String buildPath, String manifestPath) async { - if (!globals.fs.file(globals.fs.path.join(buildPath, 'meta', 'package')).existsSync() || - !globals.fs.file(manifestPath).existsSync()) { - return false; - } - globals.fs.file(globals.fs.path.join(buildPath, 'meta.far')).createSync(recursive: true); - return true; - } - - @override - Future archive(String buildPath, String manifestPath) async { - if (!globals.fs.file(globals.fs.path.join(buildPath, 'meta', 'package')).existsSync() || - !globals.fs.file(manifestPath).existsSync()) { - return false; - } - if (_appName == null) { - return false; - } - globals.fs - .file(globals.fs.path.join(buildPath, '$_appName-0.far')) - .createSync(recursive: true); - return true; - } - - @override - Future newrepo(String repoPath) async { - if (!globals.fs.directory(repoPath).existsSync()) { - return false; - } - return true; - } - - @override - Future serve(String repoPath, String host, int port) async { - return _createMockProcess(persistent: true); - } - - @override - Future publish(String repoPath, String packagePath) async { - if (!globals.fs.directory(repoPath).existsSync()) { - return false; - } - if (!globals.fs.file(packagePath).existsSync()) { - return false; - } - return true; - } -} - -class FailingPM implements FuchsiaPM { - @override - Future init(String buildPath, String appName) async { - return false; - } - - - @override - Future build(String buildPath, String manifestPath) async { - return false; - } - - @override - Future archive(String buildPath, 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, - @required String target, // E.g., lib/main.dart - BuildInfo buildInfo = BuildInfo.debug, - }) async { - final String outDir = getFuchsiaBuildDirectory(); - final String appName = fuchsiaProject.project.manifest.appName; - final String manifestPath = globals.fs.path.join(outDir, '$appName.dilpmanifest'); - globals.fs.file(manifestPath).createSync(recursive: true); - } -} - -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({ Duration timeout }) async { @@ -1656,16 +967,20 @@ class FakeFuchsiaFfx implements FuchsiaFfx { } } -class MockFuchsiaSdk extends Mock implements FuchsiaSdk { - MockFuchsiaSdk({ +class FakeFuchsiaSdk extends Fake implements FuchsiaSdk { + FakeFuchsiaSdk({ FuchsiaPM pm, FuchsiaKernelCompiler compiler, FuchsiaDevFinder devFinder, FuchsiaFfx ffx, - }) : fuchsiaPM = pm ?? FakeFuchsiaPM(), - fuchsiaKernelCompiler = compiler ?? FakeFuchsiaKernelCompiler(), + String devices, + String devFinderDevices, + }) : fuchsiaPM = pm, + fuchsiaKernelCompiler = compiler, fuchsiaDevFinder = devFinder ?? FakeFuchsiaDevFinder(), - fuchsiaFfx = ffx ?? FakeFuchsiaFfx(); + fuchsiaFfx = ffx ?? FakeFuchsiaFfx(), + _devices = devices, + _devFinderDevices = devFinderDevices; @override final FuchsiaPM fuchsiaPM; @@ -1678,7 +993,48 @@ class MockFuchsiaSdk extends Mock implements FuchsiaSdk { @override final FuchsiaFfx fuchsiaFfx; + + final String _devices; + final String _devFinderDevices; + + @override + Future listDevices({Duration timeout, bool useDeviceFinder = false}) async { + if (useDeviceFinder) { + return _devFinderDevices; + } + return _devices; + } } -class MockDartDevelopmentService extends Mock implements DartDevelopmentService {} -class MockFuchsiaWorkflow extends Mock implements FuchsiaWorkflow {} +class FakeDartDevelopmentService extends Fake implements DartDevelopmentService { + @override + Future 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; +}