From cbf885b74919bf4b0796cc216b8b80e6b3ccb266 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Wed, 28 Apr 2021 13:39:02 -0700 Subject: [PATCH] Replace MockAndroidDevice and MockIOSDevice with fakes (#81352) --- .../commands.shard/hermetic/attach_test.dart | 20 ++++- .../commands.shard/hermetic/daemon_test.dart | 76 ++++++++++++++----- .../commands.shard/hermetic/install_test.dart | 55 ++++++++++---- .../permeable/build_bundle_test.dart | 48 ++++++------ .../ios_device_start_nonprebuilt_test.dart | 71 ++++++++++------- packages/flutter_tools/test/src/mocks.dart | 32 -------- 6 files changed, 182 insertions(+), 120 deletions(-) diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart index a956854862d..95632f942c1 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart @@ -8,11 +8,13 @@ import 'dart:async'; import 'dart:io'; import 'package:file/memory.dart'; +import 'package:flutter_tools/src/android/android_device.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/logger.dart'; import 'package:flutter_tools/src/base/terminal.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/attach.dart'; import 'package:flutter_tools/src/device.dart'; @@ -31,7 +33,6 @@ import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_devices.dart'; import '../../src/fake_vm_services.dart'; -import '../../src/mocks.dart'; import '../../src/test_flutter_command_runner.dart'; final vm_service.Isolate fakeUnpausedIsolate = vm_service.Isolate( @@ -929,3 +930,20 @@ class TestHotRunnerFactory extends HotRunnerFactory { } class MockDartDevelopmentService extends Mock implements DartDevelopmentService {} + +class MockAndroidDevice extends Mock implements AndroidDevice { + @override + Future get targetPlatform async => TargetPlatform.android_arm; + + @override + bool isSupported() => true; + + @override + bool get supportsHotRestart => true; + + @override + bool get supportsFlutterExit => false; + + @override + bool isSupportedForProject(FlutterProject flutterProject) => true; +} diff --git a/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart index 97c0efd2e9f..874dde33b50 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/daemon_test.dart @@ -7,23 +7,25 @@ import 'dart:async'; import 'package:fake_async/fake_async.dart'; +import 'package:flutter_tools/src/android/android_device.dart'; import 'package:flutter_tools/src/android/android_workflow.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/utils.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/commands/daemon.dart'; +import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/fuchsia/fuchsia_workflow.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/ios/ios_workflow.dart'; import 'package:flutter_tools/src/resident_runner.dart'; -import 'package:mockito/mockito.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/fake_devices.dart'; import '../../src/fakes.dart'; -import '../../src/mocks.dart'; /// Runs a callback using FakeAsync.run while continually pumping the /// microtask queue. This avoids a deadlock when tests `await` a Future @@ -44,13 +46,11 @@ void main() { Daemon daemon; NotifyingLogger notifyingLogger; BufferLogger bufferLogger; - DevtoolsLauncher mockDevToolsLauncher; group('daemon', () { setUp(() { bufferLogger = BufferLogger.test(); notifyingLogger = NotifyingLogger(verbose: false, parent: bufferLogger); - mockDevToolsLauncher = MockDevToolsLauncher(); }); tearDown(() { @@ -241,7 +241,7 @@ void main() { ); final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery(); daemon.deviceDomain.addDeviceDiscoverer(discoverer); - discoverer.addDevice(MockAndroidDevice()); + discoverer.addDevice(FakeAndroidDevice()); commands.add({'id': 0, 'method': 'device.getDevices'}); final Map response = await responses.stream.firstWhere(_notEvent); expect(response['id'], 0); @@ -263,22 +263,22 @@ void main() { final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery(); daemon.deviceDomain.addDeviceDiscoverer(discoverer); - discoverer.addDevice(MockAndroidDevice()); + discoverer.addDevice(FakeAndroidDevice()); return responses.stream.skipWhile(_isConnectedEvent).first.then((Map response) async { expect(response['event'], 'device.added'); expect(response['params'], isMap); final Map params = castStringKeyedMap(response['params']); - expect(params['platform'], isNotEmpty); // the mock device has a platform of 'android-arm' + expect(params['platform'], isNotEmpty); // the fake device has a platform of 'android-arm' await responses.close(); await commands.close(); }); }, overrides: { - AndroidWorkflow: () => MockAndroidWorkflow(), - IOSWorkflow: () => MockIOSWorkflow(), - FuchsiaWorkflow: () => MockFuchsiaWorkflow(), + AndroidWorkflow: () => FakeAndroidWorkflow(), + IOSWorkflow: () => FakeIOSWorkflow(), + FuchsiaWorkflow: () => FakeFuchsiaWorkflow(), }); testUsingContext('emulator.launch without an emulatorId should report an error', () async { @@ -350,7 +350,6 @@ void main() { responses.add, notifyingLogger: notifyingLogger, ); - when(mockDevToolsLauncher.serve()).thenAnswer((_) async => DevToolsServerAddress('127.0.0.1', 1234)); commands.add({'id': 0, 'method': 'devtools.serve'}); final Map response = await responses.stream.firstWhere((Map response) => response['id'] == 0); @@ -360,7 +359,7 @@ void main() { await responses.close(); await commands.close(); }, overrides: { - DevtoolsLauncher: () => mockDevToolsLauncher, + DevtoolsLauncher: () => FakeDevtoolsLauncher(DevToolsServerAddress('127.0.0.1', 1234)), }); testUsingContext('devtools.serve command should return null fields if null returned', () async { @@ -371,7 +370,6 @@ void main() { responses.add, notifyingLogger: notifyingLogger, ); - when(mockDevToolsLauncher.serve()).thenAnswer((_) async => null); commands.add({'id': 0, 'method': 'devtools.serve'}); final Map response = await responses.stream.firstWhere((Map response) => response['id'] == 0); @@ -381,7 +379,7 @@ void main() { await responses.close(); await commands.close(); }, overrides: { - DevtoolsLauncher: () => mockDevToolsLauncher, + DevtoolsLauncher: () => FakeDevtoolsLauncher(null), }); }); @@ -519,25 +517,61 @@ bool _notEvent(Map map) => map['event'] == null; bool _isConnectedEvent(Map map) => map['event'] == 'daemon.connected'; -class MockFuchsiaWorkflow extends FuchsiaWorkflow { - MockFuchsiaWorkflow({ this.canListDevices = true }); +class FakeFuchsiaWorkflow extends Fake implements FuchsiaWorkflow { + FakeFuchsiaWorkflow({ this.canListDevices = true }); @override final bool canListDevices; } -class MockAndroidWorkflow extends AndroidWorkflow { - MockAndroidWorkflow({ this.canListDevices = true }); +class FakeAndroidWorkflow extends Fake implements AndroidWorkflow { + FakeAndroidWorkflow({ this.canListDevices = true }); @override final bool canListDevices; } -class MockIOSWorkflow extends IOSWorkflow { - MockIOSWorkflow({ this.canListDevices = true }); +class FakeIOSWorkflow extends Fake implements IOSWorkflow { + FakeIOSWorkflow({ this.canListDevices = true }); @override final bool canListDevices; } -class MockDevToolsLauncher extends Mock implements DevtoolsLauncher {} +class FakeAndroidDevice extends Fake implements AndroidDevice { + @override + final String id = 'device'; + + @override + final String name = 'device'; + + @override + Future get emulatorId async => 'device'; + + @override + Future get targetPlatform async => TargetPlatform.android_arm; + + @override + Future get isLocalEmulator async => false; + + @override + final Category category = Category.mobile; + + @override + final PlatformType platformType = PlatformType.android; + + @override + final bool ephemeral = false; +} + +class FakeDevtoolsLauncher extends Fake implements DevtoolsLauncher { + FakeDevtoolsLauncher(this._serverAddress); + + final DevToolsServerAddress _serverAddress; + + @override + Future serve() async => _serverAddress; + + @override + Future close() async {} +} diff --git a/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart index 6c848c3818a..1a7310fa749 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/install_test.dart @@ -5,6 +5,7 @@ // @dart = 2.8 import 'package:file/file.dart'; +import 'package:flutter_tools/src/android/android_device.dart'; import 'package:flutter_tools/src/android/application_package.dart'; import 'package:flutter_tools/src/application_package.dart'; import 'package:flutter_tools/src/base/file_system.dart'; @@ -12,11 +13,11 @@ import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/install.dart'; import 'package:flutter_tools/src/ios/application_package.dart'; -import 'package:mockito/mockito.dart'; +import 'package:flutter_tools/src/ios/devices.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; -import '../../src/mocks.dart'; import '../../src/test_flutter_command_runner.dart'; void main() { @@ -29,11 +30,7 @@ void main() { final InstallCommand command = InstallCommand(); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); - final MockAndroidDevice device = MockAndroidDevice(); - when(device.isAppInstalled(any, userIdentifier: anyNamed('userIdentifier'))) - .thenAnswer((_) async => false); - when(device.installApp(any, userIdentifier: anyNamed('userIdentifier'))) - .thenAnswer((_) async => true); + final FakeAndroidDevice device = FakeAndroidDevice(); testDeviceManager.addDevice(device); await createTestCommandRunner(command).run(['install']); @@ -45,11 +42,7 @@ void main() { final InstallCommand command = InstallCommand(); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); - final MockIOSDevice device = MockIOSDevice(); - when(device.isAppInstalled(any, userIdentifier: anyNamed('userIdentifier'))) - .thenAnswer((_) async => false); - when(device.installApp(any, userIdentifier: anyNamed('userIdentifier'))) - .thenAnswer((_) async => true); + final FakeIOSDevice device = FakeIOSDevice(); testDeviceManager.addDevice(device); expect(() async => createTestCommandRunner(command).run(['install', '--device-user', '10']), @@ -62,9 +55,7 @@ void main() { final InstallCommand command = InstallCommand(); command.applicationPackages = FakeApplicationPackageFactory(FakeIOSApp()); - final MockIOSDevice device = MockIOSDevice(); - when(device.isAppInstalled(any)).thenAnswer((_) async => false); - when(device.installApp(any)).thenAnswer((_) async => true); + final FakeIOSDevice device = FakeIOSDevice(); testDeviceManager.addDevice(device); await createTestCommandRunner(command).run(['install']); @@ -86,3 +77,37 @@ class FakeApplicationPackageFactory extends Fake implements ApplicationPackageFa } class FakeIOSApp extends Fake implements IOSApp {} class FakeAndroidApk extends Fake implements AndroidApk {} + +class FakeIOSDevice extends Fake implements IOSDevice { + @override + Future get targetPlatform async => TargetPlatform.ios; + + @override + Future isAppInstalled( + IOSApp app, { + String userIdentifier, + }) async => false; + + @override + Future installApp( + IOSApp app, { + String userIdentifier, + }) async => true; +} + +class FakeAndroidDevice extends Fake implements AndroidDevice { + @override + Future get targetPlatform async => TargetPlatform.android_arm; + + @override + Future isAppInstalled( + AndroidApk app, { + String userIdentifier, + }) async => false; + + @override + Future installApp( + AndroidApk app, { + String userIdentifier, + }) async => true; +} diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart index 13d43f245b3..8ad037fc9fd 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_bundle_test.dart @@ -16,8 +16,10 @@ import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/build_bundle.dart'; import 'package:flutter_tools/src/features.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; +import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; -import 'package:mockito/mockito.dart'; +import 'package:meta/meta.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; @@ -28,23 +30,12 @@ import '../../src/test_flutter_command_runner.dart'; void main() { Cache.disableLocking(); Directory tempDir; - MockBundleBuilder mockBundleBuilder; + FakeBundleBuilder fakeBundleBuilder; setUp(() { tempDir = globals.fs.systemTempDirectory.createTempSync('flutter_tools_packages_test.'); - mockBundleBuilder = MockBundleBuilder(); - when( - mockBundleBuilder.build( - platform: anyNamed('platform'), - buildInfo: anyNamed('buildInfo'), - mainPath: anyNamed('mainPath'), - manifestPath: anyNamed('manifestPath'), - applicationKernelFilePath: anyNamed('applicationKernelFilePath'), - depfilePath: anyNamed('depfilePath'), - assetDirPath: anyNamed('assetDirPath'), - ), - ).thenAnswer((_) => Future.value()); + fakeBundleBuilder = FakeBundleBuilder(); }); tearDown(() { @@ -52,7 +43,7 @@ void main() { }); Future runCommandIn(String projectPath, { List arguments }) async { - final BuildBundleCommand command = BuildBundleCommand(bundleBuilder: mockBundleBuilder); + final BuildBundleCommand command = BuildBundleCommand(bundleBuilder: fakeBundleBuilder); final CommandRunner runner = createTestCommandRunner(command); await runner.run([ 'bundle', @@ -98,7 +89,7 @@ void main() { globals.fs.file('pubspec.yaml').createSync(recursive: true); globals.fs.file('.packages').createSync(recursive: true); final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() - ..bundleBuilder = MockBundleBuilder()); + ..bundleBuilder = FakeBundleBuilder()); expect(() => runner.run([ 'bundle', @@ -116,7 +107,7 @@ void main() { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() - ..bundleBuilder = MockBundleBuilder()); + ..bundleBuilder = FakeBundleBuilder()); expect(() => runner.run([ 'bundle', @@ -134,7 +125,7 @@ void main() { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() - ..bundleBuilder = MockBundleBuilder()); + ..bundleBuilder = FakeBundleBuilder()); expect(() => runner.run([ 'bundle', @@ -152,7 +143,7 @@ void main() { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() - ..bundleBuilder = MockBundleBuilder()); + ..bundleBuilder = FakeBundleBuilder()); await runner.run([ 'bundle', @@ -170,7 +161,7 @@ void main() { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() - ..bundleBuilder = MockBundleBuilder()); + ..bundleBuilder = FakeBundleBuilder()); await runner.run([ 'bundle', @@ -188,7 +179,7 @@ void main() { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); final CommandRunner runner = createTestCommandRunner(BuildBundleCommand() - ..bundleBuilder = MockBundleBuilder()); + ..bundleBuilder = FakeBundleBuilder()); await runner.run([ 'bundle', @@ -380,4 +371,17 @@ void main() { }); } -class MockBundleBuilder extends Mock implements BundleBuilder {} +class FakeBundleBuilder extends Fake implements BundleBuilder { + @override + Future build({ + @required TargetPlatform platform, + @required BuildInfo buildInfo, + FlutterProject project, + String mainPath, + String manifestPath = defaultManifestPath, + String applicationKernelFilePath, + String depfilePath, + String assetDirPath, + @visibleForTesting BuildSystem buildSystem, + }) async {} +} diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart index 9492ea6bb44..bde5fcb7f8d 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_start_nonprebuilt_test.dart @@ -23,10 +23,11 @@ import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/macos/xcode.dart'; import 'package:flutter_tools/src/project.dart'; -import 'package:mockito/mockito.dart'; +import 'package:meta/meta.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; -import '../../src/context.dart'; +import '../../src/context.dart' hide FakeXcodeProjectInterpreter; import '../../src/fake_process_manager.dart'; import '../../src/fakes.dart'; @@ -84,35 +85,14 @@ void main() { FakeProcessManager processManager; BufferLogger logger; Xcode xcode; - MockXcodeProjectInterpreter mockXcodeProjectInterpreter; + FakeXcodeProjectInterpreter fakeXcodeProjectInterpreter; setUp(() { logger = BufferLogger.test(); fileSystem = MemoryFileSystem.test(); processManager = FakeProcessManager.empty(); - - mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); - when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.version).thenReturn(Version(1000, 0, 0)); - when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(['xcrun']); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( - (_) { - return Future.value(XcodeProjectInfo( - ['Runner'], - ['Debug', 'Release'], - ['Runner'], - logger, - )); - } - ); - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))) - .thenAnswer((_) async => { - 'TARGET_BUILD_DIR': 'build/ios/Release-iphoneos', - 'WRAPPER_NAME': 'My Super Awesome App.app', - 'DEVELOPMENT_TEAM': '3333CCCC33', - }); - - xcode = Xcode.test(processManager: FakeProcessManager.any(), xcodeProjectInterpreter: mockXcodeProjectInterpreter); + fakeXcodeProjectInterpreter = FakeXcodeProjectInterpreter(); + xcode = Xcode.test(processManager: FakeProcessManager.any(), xcodeProjectInterpreter: fakeXcodeProjectInterpreter); fileSystem.file('foo/.packages') ..createSync(recursive: true) ..writeAsStringSync('\n'); @@ -172,7 +152,7 @@ void main() { FileSystem: () => fileSystem, Logger: () => logger, Platform: () => macPlatform, - XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, + XcodeProjectInterpreter: () => fakeXcodeProjectInterpreter, Xcode: () => xcode, }); @@ -232,7 +212,7 @@ void main() { FileSystem: () => fileSystem, Logger: () => logger, Platform: () => macPlatform, - XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, + XcodeProjectInterpreter: () => fakeXcodeProjectInterpreter, Xcode: () => xcode, }, skip: true); // TODO(jonahwilliams): clean up with https://github.com/flutter/flutter/issues/60675 }); @@ -289,4 +269,37 @@ IOSDevice setUpIOSDevice({ ); } -class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} +class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter { + @override + final bool isInstalled = true; + + @override + final Version version = Version(1000, 0, 0); + + @override + List xcrunCommand() => ['xcrun']; + + @override + Future getInfo( + String projectPath, { + String projectFilename, + }) async => + XcodeProjectInfo( + ['Runner'], + ['Debug', 'Release'], + ['Runner'], + BufferLogger.test(), + ); + + @override + Future> getBuildSettings( + String projectPath, { + @required XcodeProjectBuildContext buildContext, + Duration timeout = const Duration(minutes: 1), + }) async => + { + 'TARGET_BUILD_DIR': 'build/ios/Release-iphoneos', + 'WRAPPER_NAME': 'My Super Awesome App.app', + 'DEVELOPMENT_TEAM': '3333CCCC33', + }; +} diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index 36cecb63072..847fe7b446f 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -6,14 +6,10 @@ import 'dart:async'; -import 'package:flutter_tools/src/android/android_device.dart'; import 'package:flutter_tools/src/android/android_sdk.dart' show AndroidSdk; import 'package:flutter_tools/src/base/file_system.dart' hide IOSink; import 'package:flutter_tools/src/base/io.dart'; -import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; -import 'package:flutter_tools/src/ios/devices.dart'; -import 'package:flutter_tools/src/project.dart'; import 'package:mockito/mockito.dart'; import 'package:process/process.dart'; @@ -154,31 +150,3 @@ _ProcessFactory flakyProcessFactory({ ); }; } - -class MockAndroidDevice extends Mock implements AndroidDevice { - @override - Future get targetPlatform async => TargetPlatform.android_arm; - - @override - bool isSupported() => true; - - @override - bool get supportsHotRestart => true; - - @override - bool get supportsFlutterExit => false; - - @override - bool isSupportedForProject(FlutterProject flutterProject) => true; -} - -class MockIOSDevice extends Mock implements IOSDevice { - @override - Future get targetPlatform async => TargetPlatform.ios; - - @override - bool isSupported() => true; - - @override - bool isSupportedForProject(FlutterProject flutterProject) => true; -}