From 886eb3e6341660a1c8bbb86ff280bd2ff09c03c0 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 10 May 2021 17:44:03 -0700 Subject: [PATCH] [flutter_tools] remove mocks from ios_device, project, flutter_command test (#82195) --- .../ios/ios_device_logger_test.dart | 36 +++-- .../test/general.shard/project_test.dart | 124 ++++++++---------- .../runner/flutter_command_test.dart | 94 ++++++------- 3 files changed, 115 insertions(+), 139 deletions(-) diff --git a/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart index 24d76f7eae9..9d85fcfe291 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_device_logger_test.dart @@ -15,7 +15,7 @@ import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/ios_deploy.dart'; import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/vmservice.dart'; -import 'package:mockito/mockito.dart'; +import 'package:test/fake.dart'; import 'package:vm_service/vm_service.dart'; import '../../src/common.dart'; @@ -226,13 +226,13 @@ Runner(libsystem_asl.dylib)[297] : libMobileGestalt ); logReader.connectedVMService = vmService; - final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger(); - when(iosDeployDebugger.debuggerAttached).thenReturn(true); + final FakeIOSDeployDebugger iosDeployDebugger = FakeIOSDeployDebugger(); + iosDeployDebugger.debuggerAttached = true; final Stream debuggingLogs = Stream.fromIterable([ 'Message from debugger' ]); - when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => debuggingLogs); + iosDeployDebugger.logLines = debuggingLogs; logReader.debuggerStream = iosDeployDebugger; // Wait for stream listeners to fire. @@ -260,8 +260,8 @@ Runner(libsystem_asl.dylib)[297] : libMobileGestalt ), useSyslog: false, ); - final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger(); - when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => debuggingLogs); + final FakeIOSDeployDebugger iosDeployDebugger = FakeIOSDeployDebugger(); + iosDeployDebugger.logLines = debuggingLogs; logReader.debuggerStream = iosDeployDebugger; final Future> logLines = logReader.logLines.toList(); @@ -285,8 +285,8 @@ Runner(libsystem_asl.dylib)[297] : libMobileGestalt useSyslog: false, ); final Completer streamComplete = Completer(); - final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger(); - when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => debuggingLogs); + final FakeIOSDeployDebugger iosDeployDebugger = FakeIOSDeployDebugger(); + iosDeployDebugger.logLines = debuggingLogs; logReader.logLines.listen(null, onError: (Object error) => streamComplete.complete()); logReader.debuggerStream = iosDeployDebugger; @@ -303,14 +303,26 @@ Runner(libsystem_asl.dylib)[297] : libMobileGestalt ), useSyslog: false, ); - final MockIOSDeployDebugger iosDeployDebugger = MockIOSDeployDebugger(); - when(iosDeployDebugger.logLines).thenAnswer((Invocation invocation) => const Stream.empty()); + final FakeIOSDeployDebugger iosDeployDebugger = FakeIOSDeployDebugger(); logReader.debuggerStream = iosDeployDebugger; logReader.dispose(); - verify(iosDeployDebugger.detach()); + expect(iosDeployDebugger.detached, true); }); }); } -class MockIOSDeployDebugger extends Mock implements IOSDeployDebugger {} +class FakeIOSDeployDebugger extends Fake implements IOSDeployDebugger { + bool detached = false; + + @override + bool debuggerAttached = false; + + @override + Stream logLines = const Stream.empty(); + + @override + void detach() { + detached = true; + } +} diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart index 5f050686897..9c40bfbd84c 100644 --- a/packages/flutter_tools/test/general.shard/project_test.dart +++ b/packages/flutter_tools/test/general.shard/project_test.dart @@ -19,7 +19,7 @@ import 'package:flutter_tools/src/ios/plist_parser.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:meta/meta.dart'; -import 'package:mockito/mockito.dart'; +import 'package:test/fake.dart'; import '../src/common.dart'; import '../src/context.dart'; @@ -397,16 +397,11 @@ apply plugin: 'kotlin-android' testWithMocks('from build settings, if no plist', () async { final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer( - (_) { - return Future>.value({ - 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', - }); - } - ); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Runner'], logger)); - }); + mockXcodeProjectInterpreter.buildSettings = { + 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', + }; + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['Runner'], logger); + expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject'); }); @@ -428,18 +423,12 @@ apply plugin: 'kotlin-android' testWithMocks('from build settings and plist, if default variable', () async { final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer( - (_) { - return Future>.value({ - 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', - }); - } - ); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Runner'], logger)); - }); - + mockXcodeProjectInterpreter.buildSettings = { + 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', + }; + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['Runner'], logger); testPlistUtils.setProperty('CFBundleIdentifier', r'$(PRODUCT_BUNDLE_IDENTIFIER)'); + expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject'); }); @@ -447,28 +436,21 @@ apply plugin: 'kotlin-android' final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); project.ios.defaultHostInfoPlist.createSync(recursive: true); - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer( - (_) { - return Future>.value({ - 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', - 'SUFFIX': 'suffix', - }); - } - ); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Runner'], logger)); - }); - + mockXcodeProjectInterpreter.buildSettings = { + 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', + 'SUFFIX': 'suffix', + }; + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['Runner'], logger); testPlistUtils.setProperty('CFBundleIdentifier', r'$(PRODUCT_BUNDLE_IDENTIFIER).$(SUFFIX)'); + expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject.suffix'); }); testWithMocks('fails with no flavor and defined schemes', () async { final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['free', 'paid'], logger)); - }); + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['free', 'paid'], logger); + await expectToolExitLater( project.ios.productBundleIdentifier(null), contains('You must specify a --flavor option to select one of the available schemes.') @@ -478,29 +460,21 @@ apply plugin: 'kotlin-android' testWithMocks('handles case insensitive flavor', () async { final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer( - (_) { - return Future>.value({ - 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', - }); - } - ); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Free'], logger)); - }); - + mockXcodeProjectInterpreter.buildSettings = { + 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', + }; + mockXcodeProjectInterpreter.xcodeProjectInfo =XcodeProjectInfo([], [], ['Free'], logger); const BuildInfo buildInfo = BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false); + expect(await project.ios.productBundleIdentifier(buildInfo), 'io.flutter.someProject'); }); testWithMocks('fails with flavor and default schemes', () async { final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Runner'], logger)); - }); - + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['Runner'], logger); const BuildInfo buildInfo = BuildInfo(BuildMode.debug, 'free', treeShakeIcons: false); + await expectToolExitLater( project.ios.productBundleIdentifier(buildInfo), contains('The Xcode project does not define custom schemes. You cannot use the --flavor option.') @@ -514,6 +488,7 @@ apply plugin: 'kotlin-android' }); expect(await project.ios.productBundleIdentifier(null), ''); }); + testWithMocks('surrounded by double quotes', () async { final FlutterProject project = await someProject(); addIosProjectFile(project.directory, projectFileContent: () { @@ -521,6 +496,7 @@ apply plugin: 'kotlin-android' }); expect(await project.ios.productBundleIdentifier(null), 'io.flutter.someProject'); }); + testWithMocks('surrounded by single quotes', () async { final FlutterProject project = await someProject(); addIosProjectFile(project.directory, projectFileContent: () { @@ -550,14 +526,10 @@ apply plugin: 'kotlin-android' testUsingContext('app product name xcodebuild settings', () async { final FlutterProject project = await someProject(); project.ios.xcodeProject.createSync(); - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer((_) { - return Future>.value({ - 'FULL_PRODUCT_NAME': 'My App.app' - }); - }); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Runner'], logger)); - }); + mockXcodeProjectInterpreter.buildSettings = { + 'FULL_PRODUCT_NAME': 'My App.app' + }; + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['Runner'], logger); expect(await project.ios.hostAppBundleName(null), 'My App.app'); }, overrides: { @@ -667,16 +639,10 @@ apply plugin: 'kotlin-android' group('with bundle identifier', () { setUp(() { - when(mockXcodeProjectInterpreter.getBuildSettings(any, buildContext: anyNamed('buildContext'))).thenAnswer( - (_) { - return Future>.value({ - 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', - }); - } - ); - when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { - return Future.value(XcodeProjectInfo([], [], ['Runner'], logger)); - }); + mockXcodeProjectInterpreter.buildSettings = { + 'PRODUCT_BUNDLE_IDENTIFIER': 'io.flutter.someProject', + }; + mockXcodeProjectInterpreter.xcodeProjectInfo = XcodeProjectInfo([], [], ['Runner'], logger); }); testUsingContext('no Info.plist in target', () async { @@ -1005,7 +971,23 @@ File androidPluginRegistrant(Directory parent) { .childFile('GeneratedPluginRegistrant.java'); } -class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter { +class MockXcodeProjectInterpreter extends Fake implements XcodeProjectInterpreter { + Map buildSettings = {}; + XcodeProjectInfo xcodeProjectInfo; + + @override + Future> getBuildSettings(String projectPath, { + XcodeProjectBuildContext buildContext, + Duration timeout = const Duration(minutes: 1), + }) async { + return buildSettings; + } + + @override + Future getInfo(String projectPath, {String projectFilename}) async { + return xcodeProjectInfo; + } + @override bool get isInstalled => true; } diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart index c93e0126550..69174279bae 100644 --- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart @@ -21,7 +21,7 @@ import 'package:flutter_tools/src/dart/pub.dart'; import 'package:flutter_tools/src/globals_null_migrated.dart' as globals; import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart'; -import 'package:mockito/mockito.dart'; +import 'package:test/fake.dart'; import '../../src/common.dart'; import '../../src/context.dart'; @@ -30,19 +30,18 @@ import 'utils.dart'; void main() { group('Flutter Command', () { - MockCache cache; + FakeCache cache; TestUsage usage; FakeClock clock; - MockProcessInfo mockProcessInfo; + FakeProcessInfo processInfo; setUp(() { Cache.disableLocking(); - cache = MockCache(); + cache = FakeCache(); usage = TestUsage(); clock = FakeClock(); - mockProcessInfo = MockProcessInfo(); - - when(mockProcessInfo.maxRss).thenReturn(10); + processInfo = FakeProcessInfo(); + processInfo.maxRss = 10; }); tearDown(() { @@ -58,7 +57,8 @@ void main() { testUsingContext('honors shouldUpdateCache false', () async { final DummyFlutterCommand flutterCommand = DummyFlutterCommand(shouldUpdateCache: false); await flutterCommand.run(); - verifyNever(cache.updateAll(any)); + + expect(cache.artifacts, isEmpty); expect(flutterCommand.deprecated, isFalse); expect(flutterCommand.hidden, isFalse); }, @@ -71,7 +71,7 @@ void main() { await flutterCommand.run(); // First call for universal, second for the rest expect( - verify(cache.updateAll(captureAny)).captured, + cache.artifacts, >[ {DevelopmentArtifact.universal}, {}, @@ -143,7 +143,7 @@ void main() { void testUsingCommandContext(String testName, dynamic Function() testBody) { testUsingContext(testName, testBody, overrides: { - ProcessInfo: () => mockProcessInfo, + ProcessInfo: () => processInfo, SystemClock: () => clock, Usage: () => usage, }); @@ -201,40 +201,6 @@ void main() { ]); }); - testUsingCommandContext('reports command that results in failure', () async { - // Crash if called a third time which is unexpected. - clock.times = [1000, 2000]; - - final DummyFlutterCommand flutterCommand = DummyFlutterCommand( - commandFunction: () async { - return const FlutterCommandResult(ExitStatus.fail); - } - ); - - try { - await flutterCommand.run(); - } on ToolExit { - verify(usage.sendCommand( - 'dummy', - parameters: anyNamed('parameters'), - )); - verify(usage.sendEvent( - 'tool-command-result', - 'dummy', - label: 'fail', - parameters: anyNamed('parameters'), - )); - expect(verify(usage.sendEvent( - 'tool-command-max-rss', - 'dummy', - label: 'fail', - value: captureAnyNamed('value'), - )).captured[0], - 10, - ); - } - }); - testUsingCommandContext('reports command that results in error', () async { // Crash if called a third time which is unexpected. clock.times = [1000, 2000]; @@ -315,15 +281,15 @@ void main() { }); group('signals tests', () { - MockIoProcessSignal mockSignal; + FakeIoProcessSignal mockSignal; ProcessSignal signalUnderTest; StreamController signalController; setUp(() { - mockSignal = MockIoProcessSignal(); + mockSignal = FakeIoProcessSignal(); signalUnderTest = ProcessSignal(mockSignal); signalController = StreamController(); - when(mockSignal.watch()).thenAnswer((Invocation invocation) => signalController.stream); + mockSignal.stream = signalController.stream; }); testUsingContext('reports command that is killed', () async { @@ -363,7 +329,7 @@ void main() { ), ]); }, overrides: { - ProcessInfo: () => mockProcessInfo, + ProcessInfo: () => processInfo, Signals: () => FakeSignals( subForSigTerm: signalUnderTest, exitSignals: [signalUnderTest], @@ -397,11 +363,8 @@ void main() { signalController.add(mockSignal); await completer.future; - - await globals.cache.lock(); - globals.cache.releaseLock(); }, overrides: { - ProcessInfo: () => mockProcessInfo, + ProcessInfo: () => processInfo, Signals: () => FakeSignals( subForSigTerm: signalUnderTest, exitSignals: [signalUnderTest], @@ -671,8 +634,29 @@ class FakeDdsCommand extends FlutterCommand { } } -class MockProcessInfo extends Mock implements ProcessInfo {} -class MockIoProcessSignal extends Mock implements io.ProcessSignal {} +class FakeProcessInfo extends Fake implements ProcessInfo { + @override + int maxRss = 0; +} + +class FakeIoProcessSignal extends Fake implements io.ProcessSignal { + Stream stream; + + @override + Stream watch() => stream; +} + +class FakeCache extends Fake implements Cache { + List> artifacts = >[]; + + @override + Future updateAll(Set requiredArtifacts) async { + artifacts.add(requiredArtifacts.toSet()); + } + + @override + void releaseLock() { } +} class FakeSignals implements Signals { FakeSignals({ @@ -708,8 +692,6 @@ class FakeClock extends Fake implements SystemClock { } } -class MockCache extends Mock implements Cache {} - class FakePub extends Fake implements Pub { @override Future get({