diff --git a/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart index 1220be0868..9f341f1c5a 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart @@ -19,20 +19,24 @@ import '../../src/context.dart'; void main() { group('clean command', () { - MockXcode mockXcode; + Xcode xcode; + MockXcodeProjectInterpreter mockXcodeProjectInterpreter; + setUp(() { - mockXcode = MockXcode(); + mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); + xcode = Xcode.test( + processManager: FakeProcessManager.any(), + xcodeProjectInterpreter: mockXcodeProjectInterpreter, + ); }); group('general', () { MemoryFileSystem fs; - MockXcodeProjectInterpreter mockXcodeProjectInterpreter; Directory buildDirectory; FlutterProject projectUnderTest; setUp(() { fs = MemoryFileSystem.test(); - mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); final Directory currentDirectory = fs.currentDirectory; buildDirectory = currentDirectory.childDirectory('build'); @@ -61,7 +65,9 @@ void main() { }); testUsingContext('$CleanCommand removes build and .dart_tool and ephemeral directories, cleans Xcode', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + // Xcode is installed and version satisfactory. + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(1000); await CleanCommand().runCommand(); expect(buildDirectory.existsSync(), isFalse); @@ -87,31 +93,33 @@ void main() { }, overrides: { FileSystem: () => fs, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); testUsingContext('$CleanCommand cleans Xcode verbosely', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + // Xcode is installed and version satisfactory. + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(1000); + await CleanCommand(verbose: true).runCommand(); verify(mockXcodeProjectInterpreter.cleanWorkspace(any, 'Runner', verbose: true)).called(2); }, overrides: { FileSystem: () => fs, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, }); }); group('Windows', () { - MockPlatform windowsPlatform; + FakePlatform windowsPlatform; setUp(() { - windowsPlatform = MockPlatform(); + windowsPlatform = FakePlatform(operatingSystem: 'windows'); }); testUsingContext('$CleanCommand prints a helpful error message on Windows', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); - when(windowsPlatform.isWindows).thenReturn(true); + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); final MockFile mockFile = MockFile(); when(mockFile.existsSync()).thenReturn(true); @@ -123,11 +131,11 @@ void main() { verify(mockFile.deleteSync(recursive: true)).called(1); }, overrides: { Platform: () => windowsPlatform, - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('$CleanCommand handles missing permissions;', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); + when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); final MockFile mockFile = MockFile(); when(mockFile.existsSync()).thenThrow(const FileSystemException('OS error: Access Denied')); @@ -139,15 +147,13 @@ void main() { verifyNever(mockFile.deleteSync(recursive: true)); }, overrides: { Platform: () => windowsPlatform, - Xcode: () => mockXcode, + Xcode: () => xcode, }); }); }); } class MockFile extends Mock implements File {} -class MockPlatform extends Mock implements Platform {} -class MockXcode extends Mock implements Xcode {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter { @override diff --git a/packages/flutter_tools/test/general.shard/base/build_test.dart b/packages/flutter_tools/test/general.shard/base/build_test.dart index 5527dd5c33..d82f916eb7 100644 --- a/packages/flutter_tools/test/general.shard/base/build_test.dart +++ b/packages/flutter_tools/test/general.shard/base/build_test.dart @@ -6,12 +6,8 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/build.dart'; import 'package:flutter_tools/src/base/logger.dart'; -import 'package:flutter_tools/src/base/platform.dart'; -import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/build_info.dart'; -import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/macos/xcode.dart'; -import 'package:flutter_tools/src/reporting/reporting.dart'; import '../../src/common.dart'; import '../../src/context.dart'; @@ -185,30 +181,16 @@ void main() { AOTSnapshotter snapshotter; Artifacts artifacts; FakeProcessManager processManager; - Logger logger; setUp(() async { - final Platform platform = FakePlatform(operatingSystem: 'macos'); - logger = BufferLogger.test(); fileSystem = MemoryFileSystem.test(); artifacts = Artifacts.test(); processManager = FakeProcessManager.list([]); snapshotter = AOTSnapshotter( fileSystem: fileSystem, - logger: logger, - xcode: Xcode( - fileSystem: fileSystem, - logger: logger, - platform: FakePlatform(operatingSystem: 'macos'), + logger: BufferLogger.test(), + xcode: Xcode.test( processManager: processManager, - xcodeProjectInterpreter: XcodeProjectInterpreter( - platform: platform, - processManager: processManager, - logger: logger, - fileSystem: fileSystem, - terminal: Terminal.test(), - usage: Usage.test(), - ), ), artifacts: artifacts, processManager: processManager, diff --git a/packages/flutter_tools/test/general.shard/emulator_test.dart b/packages/flutter_tools/test/general.shard/emulator_test.dart index 89d62087b1..04097b47aa 100644 --- a/packages/flutter_tools/test/general.shard/emulator_test.dart +++ b/packages/flutter_tools/test/general.shard/emulator_test.dart @@ -2,11 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:convert'; - +import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/android/android_workflow.dart'; -import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/emulator.dart'; @@ -45,14 +43,16 @@ const FakeCommand kListEmulatorsCommand = FakeCommand( ); void main() { - MockProcessManager mockProcessManager; + FakeProcessManager fakeProcessManager; MockAndroidSdk mockSdk; - MockXcode mockXcode; + FileSystem fileSystem; + Xcode xcode; setUp(() { - mockProcessManager = MockProcessManager(); + fileSystem = MemoryFileSystem.test(); + fakeProcessManager = FakeProcessManager.list([]); mockSdk = MockAndroidSdk(); - mockXcode = MockXcode(); + xcode = Xcode.test(processManager: fakeProcessManager, fileSystem: fileSystem); when(mockSdk.avdManagerPath).thenReturn('avdmanager'); when(mockSdk.getAvdManagerPath()).thenReturn('avdmanager'); @@ -299,25 +299,35 @@ void main() { }); group('ios_emulators', () { - bool didAttemptToRunSimulator = false; - setUp(() { - when(mockXcode.xcodeSelectPath).thenReturn('/fake/Xcode.app/Contents/Developer'); - when(mockXcode.getSimulatorPath()).thenAnswer((_) => '/fake/simulator.app'); - when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { - final List args = invocation.positionalArguments[0] as List; - if (args.length >= 3 && args[0] == 'open' && args[1] == '-a' && args[2] == '/fake/simulator.app') { - didAttemptToRunSimulator = true; - } - return ProcessResult(101, 0, '', ''); - }); - }); testUsingContext('runs correct launch commands', () async { + fileSystem.directory('/fake/Xcode.app/Contents/Developer/Applications/Simulator.app').createSync(recursive: true); + fakeProcessManager.addCommands( + [ + const FakeCommand( + command: ['/usr/bin/xcode-select', '--print-path'], + stdout: '/fake/Xcode.app/Contents/Developer', + ), + const FakeCommand(command: [ + 'open', + '-n', + '-a', + '/fake/Xcode.app/Contents/Developer/Applications/Simulator.app', + ]), + const FakeCommand(command: [ + 'open', + '-a', + '/fake/Xcode.app/Contents/Developer/Applications/Simulator.app', + ]) + ], + ); + const Emulator emulator = IOSEmulator('ios'); await emulator.launch(); - expect(didAttemptToRunSimulator, equals(true)); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, - Xcode: () => mockXcode, + ProcessManager: () => fakeProcessManager, + Xcode: () => xcode, + FileSystem: () => fileSystem, }); }); } @@ -354,28 +364,3 @@ class FakeEmulator extends Emulator { throw UnimplementedError('Not implemented in Mock'); } } - -class MockProcessManager extends Mock implements ProcessManager { - - @override - ProcessResult runSync( - List command, { - String workingDirectory, - Map environment, - bool includeParentEnvironment = true, - bool runInShell = false, - Encoding stdoutEncoding = systemEncoding, - Encoding stderrEncoding = systemEncoding, - }) { - final String program = command[0] as String; - final List args = command.sublist(1) as List; - switch (program) { - case '/usr/bin/xcode-select': - throw ProcessException(program, args); - break; - } - throw StateError('Unexpected process call: $command'); - } -} - -class MockXcode extends Mock implements Xcode {} 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 a7656a70ac..41d707b8f7 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 @@ -80,7 +80,7 @@ void main() { FileSystem fileSystem; FakeProcessManager processManager; BufferLogger logger; - MockXcode mockXcode; + Xcode xcode; MockXcodeProjectInterpreter mockXcodeProjectInterpreter; setUp(() { @@ -90,6 +90,8 @@ void main() { mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); + when(mockXcodeProjectInterpreter.majorVersion).thenReturn(1000); + when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(['xcrun']); when(mockXcodeProjectInterpreter.getInfo(any, projectFilename: anyNamed('projectFilename'))).thenAnswer( (_) { return Future.value(XcodeProjectInfo( @@ -100,9 +102,7 @@ void main() { )); } ); - mockXcode = MockXcode(); - when(mockXcode.isRequiredVersionSatisfactory).thenReturn(true); - when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); + xcode = Xcode.test(processManager: FakeProcessManager.any(), xcodeProjectInterpreter: mockXcodeProjectInterpreter); fileSystem.file('foo/.packages') ..createSync(recursive: true) ..writeAsStringSync('\n'); @@ -153,7 +153,7 @@ void main() { Logger: () => logger, Platform: () => macPlatform, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('with flaky buildSettings call', () async { @@ -227,7 +227,7 @@ void main() { Logger: () => logger, Platform: () => macPlatform, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('with concurrent build failures', () async { @@ -292,7 +292,7 @@ void main() { Logger: () => logger, Platform: () => macPlatform, XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, - Xcode: () => mockXcode, + Xcode: () => xcode, }, skip: true); // TODO(jonahwilliams): clean up with https://github.com/flutter/flutter/issues/60675 }); } @@ -347,6 +347,5 @@ IOSDevice setUpIOSDevice({ ); } -class MockXcode extends Mock implements Xcode {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} class MockVmService extends Mock implements VmService {} diff --git a/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart index c2eeede770..47f9cbce3e 100644 --- a/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart @@ -4,17 +4,18 @@ import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/ios/ios_workflow.dart'; +import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/macos/xcode.dart'; -import 'package:mockito/mockito.dart'; import '../../src/common.dart'; +import '../../src/context.dart'; import '../../src/testbed.dart'; void main() { testWithoutContext('iOS workflow is disabled if feature is disabled', () { final IOSWorkflow iosWorkflow = IOSWorkflow( platform: FakePlatform(operatingSystem: 'macOS'), - xcode: MockXcode(), + xcode: Xcode.test(processManager: FakeProcessManager.any()), featureFlags: TestFeatureFlags(isIOSEnabled: false), ); @@ -24,7 +25,7 @@ void main() { testWithoutContext('iOS workflow is disabled on Linux', () { final IOSWorkflow iosWorkflow = IOSWorkflow( platform: FakePlatform(operatingSystem: 'linux'), - xcode: MockXcode(), + xcode: Xcode.test(processManager: FakeProcessManager.any()), featureFlags: TestFeatureFlags(isIOSEnabled: true), ); @@ -34,7 +35,7 @@ void main() { testWithoutContext('iOS workflow is disabled on windows', () { final IOSWorkflow iosWorkflow = IOSWorkflow( platform: FakePlatform(operatingSystem: 'windows'), - xcode: MockXcode(), + xcode: Xcode.test(processManager: FakeProcessManager.any()), featureFlags: TestFeatureFlags(isIOSEnabled: true), ); @@ -44,7 +45,7 @@ void main() { testWithoutContext('iOS workflow is enabled on macOS', () { final IOSWorkflow iosWorkflow = IOSWorkflow( platform: FakePlatform(operatingSystem: 'macos'), - xcode: MockXcode(), + xcode: Xcode.test(processManager: FakeProcessManager.any()), featureFlags: TestFeatureFlags(isIOSEnabled: true), ); @@ -53,18 +54,26 @@ void main() { }); testWithoutContext('iOS workflow can launch and list devices when Xcode is set up', () { - final Xcode xcode = MockXcode(); + final Xcode xcode = Xcode.test( + processManager: FakeProcessManager.any(), + xcodeProjectInterpreter: XcodeProjectInterpreter.test( + processManager: FakeProcessManager.any(), + majorVersion: 1000, + minorVersion: 0, + patchVersion: 0, + ), + ); + final IOSWorkflow iosWorkflow = IOSWorkflow( platform: FakePlatform(operatingSystem: 'macos'), xcode: xcode, featureFlags: TestFeatureFlags(isIOSEnabled: true), ); - when(xcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - when(xcode.isSimctlInstalled).thenReturn(true); + // Make sure we're testing the right Xcode state. + expect(xcode.isInstalledAndMeetsVersionCheck, true); + expect(xcode.isSimctlInstalled, true); expect(iosWorkflow.canLaunchDevices, true); expect(iosWorkflow.canListDevices, true); }); } - -class MockXcode extends Mock implements Xcode {} diff --git a/packages/flutter_tools/test/general.shard/ios/mac_test.dart b/packages/flutter_tools/test/general.shard/ios/mac_test.dart index d32b6037b2..eda3faa915 100644 --- a/packages/flutter_tools/test/general.shard/ios/mac_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/mac_test.dart @@ -13,7 +13,6 @@ import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/mac.dart'; -import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:mockito/mockito.dart'; @@ -29,7 +28,6 @@ final Map noColorTerminalOverride = { }; class MockProcessManager extends Mock implements ProcessManager {} -class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} class MockIosProject extends Mock implements IosProject {} void main() { diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart index 1ef1cbd913..d4d1807044 100644 --- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart +++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart @@ -2,8 +2,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:io' show ProcessResult, Process; - import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/application_package.dart'; @@ -16,7 +14,6 @@ import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/devfs.dart'; import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/globals.dart' as globals; -import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/plist_parser.dart'; import 'package:flutter_tools/src/ios/simulators.dart'; import 'package:flutter_tools/src/macos/xcode.dart'; @@ -28,12 +25,6 @@ import '../../src/common.dart'; import '../../src/context.dart'; import '../../src/mocks.dart'; -class MockIMobileDevice extends Mock implements IMobileDevice {} -class MockLogger extends Mock implements Logger {} -class MockProcess extends Mock implements Process {} -class MockProcessManager extends Mock implements ProcessManager {} -class MockXcode extends Mock implements Xcode {} -class MockSimControl extends Mock implements SimControl {} class MockPlistUtils extends Mock implements PlistParser {} final Platform macosPlatform = FakePlatform( @@ -59,11 +50,11 @@ void main() { group('_IOSSimulatorDevicePortForwarder', () { MockSimControl mockSimControl; - MockXcode mockXcode; + Xcode xcode; setUp(() { mockSimControl = MockSimControl(); - mockXcode = MockXcode(); + xcode = Xcode.test(processManager: FakeProcessManager.any()); }); testUsingContext('dispose() does not throw an exception', () async { @@ -85,7 +76,7 @@ void main() { Platform: () => osx, FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, }, testOn: 'posix'); }); @@ -351,44 +342,12 @@ void main() { }); group('Simulator screenshot', () { - MockXcode mockXcode; - MockLogger mockLogger; - MockProcessManager mockProcessManager; - IOSSimulator deviceUnderTest; - - setUp(() { - mockXcode = MockXcode(); - mockLogger = MockLogger(); - mockProcessManager = MockProcessManager(); - // Let everything else return exit code 0 so process.dart doesn't crash. - when( - mockProcessManager.run(any, environment: null, workingDirectory: null) - ).thenAnswer((Invocation invocation) => - Future.value(ProcessResult(2, 0, '', '')) - ); - // Test a real one. Screenshot doesn't require instance states. - final SimControl simControl = SimControl( - processManager: mockProcessManager, - logger: mockLogger, - xcode: mockXcode, - ); - // Doesn't matter what the device is. - deviceUnderTest = IOSSimulator( - 'x', - name: 'iPhone SE', - simControl: simControl, - ); - when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); - }); - - - testWithoutContext( - 'supports screenshots', - () async { - final File screenshot = MemoryFileSystem.test().file('screenshot.png'); - await deviceUnderTest.takeScreenshot(screenshot); - verify(mockProcessManager.run( - [ + testWithoutContext('supports screenshots', () async { + final Xcode xcode = Xcode.test(processManager: FakeProcessManager.any()); + final Logger logger = BufferLogger.test(); + final FakeProcessManager fakeProcessManager = FakeProcessManager.list([ + const FakeCommand( + command: [ 'xcrun', 'simctl', 'io', @@ -396,25 +355,35 @@ void main() { 'screenshot', 'screenshot.png', ], - environment: null, - workingDirectory: null, - )); - }, - ); + ), + ]); + + // Test a real one. Screenshot doesn't require instance states. + final SimControl simControl = SimControl( + processManager: fakeProcessManager, + logger: logger, + xcode: xcode, + ); + // Doesn't matter what the device is. + final IOSSimulator deviceUnderTest = IOSSimulator( + 'x', + name: 'iPhone SE', + simControl: simControl, + ); + + final File screenshot = MemoryFileSystem.test().file('screenshot.png'); + await deviceUnderTest.takeScreenshot(screenshot); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); + }); }); group('device log tool', () { - MockProcessManager mockProcessManager; - MockXcode mockXcode; + FakeProcessManager fakeProcessManager; MockSimControl mockSimControl; setUp(() { - mockProcessManager = MockProcessManager(); - when(mockProcessManager.start(any, environment: null, workingDirectory: null)) - .thenAnswer((Invocation invocation) => Future.value(MockProcess())); + fakeProcessManager = FakeProcessManager.list([]); mockSimControl = MockSimControl(); - mockXcode = MockXcode(); - when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); }); testUsingContext('syslog uses tail', () async { @@ -424,14 +393,18 @@ void main() { simulatorCategory: 'iOS 9.3', simControl: mockSimControl, ); + fakeProcessManager.addCommand(const FakeCommand(command: [ + 'tail', + '-n', + '0', + '-F', + '/Library/Logs/CoreSimulator/x/system.log', + ])); await launchDeviceSystemLogTool(device); - expect( - verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, - contains('tail'), - ); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, Platform: () => macosPlatform, FileSystemUtils: () => FileSystemUtils( @@ -447,17 +420,13 @@ void main() { simulatorCategory: 'iOS 11.0', simControl: mockSimControl, ); - await launchDeviceUnifiedLogging(device, 'My Super Awesome App'); - const String expectedPredicate = 'eventType = logEvent AND ' - 'processImagePath ENDSWITH "My Super Awesome App" AND ' - '(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND ' - 'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND ' - 'NOT(eventMessage BEGINSWITH "assertion failed: ") AND ' - 'NOT(eventMessage CONTAINS " libxpc.dylib ")'; - - final List command = verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single as List; - expect(command, [ + 'processImagePath ENDSWITH "My Super Awesome App" AND ' + '(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND ' + 'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND ' + 'NOT(eventMessage BEGINSWITH "assertion failed: ") AND ' + 'NOT(eventMessage CONTAINS " libxpc.dylib ")'; + fakeProcessManager.addCommand(const FakeCommand(command: [ 'xcrun', 'simctl', 'spawn', @@ -467,13 +436,15 @@ void main() { '--style', 'json', '--predicate', - expectedPredicate - ]); + expectedPredicate, + ])); + + await launchDeviceUnifiedLogging(device, 'My Super Awesome App'); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, - Xcode: () => mockXcode, }); testUsingContext('unified logging without app name', () async { @@ -483,16 +454,12 @@ void main() { simulatorCategory: 'iOS 11.0', simControl: mockSimControl, ); - await launchDeviceUnifiedLogging(device, null); - const String expectedPredicate = 'eventType = logEvent AND ' - '(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND ' - 'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND ' - 'NOT(eventMessage BEGINSWITH "assertion failed: ") AND ' - 'NOT(eventMessage CONTAINS " libxpc.dylib ")'; - - final List command = verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single as List; - expect(command, [ + '(senderImagePath ENDSWITH "/Flutter" OR senderImagePath ENDSWITH "/libswiftCore.dylib" OR processImageUUID == senderImageUUID) AND ' + 'NOT(eventMessage CONTAINS ": could not find icon for representation -> com.apple.") AND ' + 'NOT(eventMessage BEGINSWITH "assertion failed: ") AND ' + 'NOT(eventMessage CONTAINS " libxpc.dylib ")'; + fakeProcessManager.addCommand(const FakeCommand(command: [ 'xcrun', 'simctl', 'spawn', @@ -502,11 +469,14 @@ void main() { '--style', 'json', '--predicate', - expectedPredicate - ]); + expectedPredicate, + ])); + + await launchDeviceUnifiedLogging(device, null); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }, overrides: { - ProcessManager: () => mockProcessManager, + ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, }); }); @@ -515,14 +485,13 @@ void main() { FakeProcessManager fakeProcessManager; MockIosProject mockIosProject; MockSimControl mockSimControl; - MockXcode mockXcode; + Xcode xcode; setUp(() { fakeProcessManager = FakeProcessManager.list([]); mockIosProject = MockIosProject(); mockSimControl = MockSimControl(); - mockXcode = MockXcode(); - when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); + xcode = Xcode.test(processManager: FakeProcessManager.any()); }); group('syslog', () { @@ -561,7 +530,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, Platform: () => osx, - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('simulator can output `)`', () async { @@ -597,7 +566,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, Platform: () => osx, - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('multiline messages', () async { @@ -649,7 +618,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ProcessManager: () => fakeProcessManager, FileSystem: () => fileSystem, Platform: () => osx, - Xcode: () => mockXcode, + Xcode: () => xcode, }); }); @@ -712,7 +681,6 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' }); group('SimControl', () { - const int mockPid = 123; const String validSimControlOutput = ''' { "devices" : { @@ -744,30 +712,34 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' } '''; - MockLogger mockLogger; - MockProcessManager mockProcessManager; - MockXcode mockXcode; + FakeProcessManager fakeProcessManager; + Xcode xcode; SimControl simControl; const String deviceId = 'smart-phone'; const String appId = 'flutterApp'; setUp(() { - mockLogger = MockLogger(); - mockProcessManager = MockProcessManager(); - when(mockProcessManager.run(any)).thenAnswer((Invocation _) async { - return ProcessResult(mockPid, 0, validSimControlOutput, ''); - }); - - mockXcode = MockXcode(); - when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); + fakeProcessManager = FakeProcessManager.list([]); + xcode = Xcode.test(processManager: FakeProcessManager.any()); simControl = SimControl( - logger: mockLogger, - processManager: mockProcessManager, - xcode: mockXcode, + logger: BufferLogger.test(), + processManager: fakeProcessManager, + xcode: xcode, ); }); testWithoutContext('getDevices succeeds', () async { + fakeProcessManager.addCommand(const FakeCommand( + command: [ + 'xcrun', + 'simctl', + 'list', + '--json', + 'devices', + ], + stdout: validSimControlOutput, + )); + final List devices = await simControl.getDevices(); final SimDevice watch = devices[0]; @@ -793,14 +765,25 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' expect(tv.name, 'Apple TV'); expect(tv.udid, 'TEST-TV-UDID'); expect(tv.isBooted, isFalse); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }); testWithoutContext('getDevices handles bad simctl output', () async { - when(mockProcessManager.run(any)) - .thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', '')); + fakeProcessManager.addCommand(const FakeCommand( + command: [ + 'xcrun', + 'simctl', + 'list', + '--json', + 'devices', + ], + stdout: 'Install Started', + )); + final List devices = await simControl.getDevices(); expect(devices, isEmpty); + expect(fakeProcessManager.hasRemainingExpectations, isFalse); }); testWithoutContext('sdkMajorVersion defaults to 11 when sdkNameAndVersion is junk', () async { @@ -815,11 +798,19 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' }); testWithoutContext('.install() handles exceptions', () async { - when(mockProcessManager.run( - ['xcrun', 'simctl', 'install', deviceId, appId], - environment: anyNamed('environment'), - workingDirectory: anyNamed('workingDirectory'), - )).thenThrow(const ProcessException('xcrun', [])); + fakeProcessManager.addCommand(FakeCommand( + command: const [ + 'xcrun', + 'simctl', + 'install', + deviceId, + appId, + ], + onRun: () { + throw const ProcessException('xcrun', []); + }, + )); + expect( () async => await simControl.install(deviceId, appId), throwsToolExit(message: r'Unable to install'), @@ -827,11 +818,19 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' }); testWithoutContext('.uninstall() handles exceptions', () async { - when(mockProcessManager.run( - ['xcrun', 'simctl', 'uninstall', deviceId, appId], - environment: anyNamed('environment'), - workingDirectory: anyNamed('workingDirectory'), - )).thenThrow(const ProcessException('xcrun', [])); + fakeProcessManager.addCommand(FakeCommand( + command: const [ + 'xcrun', + 'simctl', + 'uninstall', + deviceId, + appId, + ], + onRun: () { + throw const ProcessException('xcrun', []); + }, + )); + expect( () async => await simControl.uninstall(deviceId, appId), throwsToolExit(message: r'Unable to uninstall'), @@ -839,11 +838,19 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' }); testWithoutContext('.launch() handles exceptions', () async { - when(mockProcessManager.run( - ['xcrun', 'simctl', 'launch', deviceId, appId], - environment: anyNamed('environment'), - workingDirectory: anyNamed('workingDirectory'), - )).thenThrow(const ProcessException('xcrun', [])); + fakeProcessManager.addCommand(FakeCommand( + command: const [ + 'xcrun', + 'simctl', + 'launch', + deviceId, + appId, + ], + onRun: () { + throw const ProcessException('xcrun', []); + }, + )); + expect( () async => await simControl.launch(deviceId, appId), throwsToolExit(message: r'Unable to launch'), @@ -853,12 +860,11 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' group('startApp', () { SimControl simControl; - MockXcode mockXcode; + Xcode xcode; setUp(() { simControl = MockSimControl(); - mockXcode = MockXcode(); - when(mockXcode.xcrunCommand()).thenReturn(['xcrun']); + xcode = Xcode.test(processManager: FakeProcessManager.any()); }); testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async { @@ -882,7 +888,7 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' PlistParser: () => MockPlistUtils(), FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('startApp respects the enable software rendering flag', () async { @@ -905,17 +911,17 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' PlistParser: () => MockPlistUtils(), FileSystem: () => fileSystem, ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, }); }); group('IOSDevice.isSupportedForProject', () { MockSimControl mockSimControl; - MockXcode mockXcode; + Xcode xcode; setUp(() { mockSimControl = MockSimControl(); - mockXcode = MockXcode(); + xcode = Xcode.test(processManager: FakeProcessManager.any()); }); testUsingContext('is true on module project', () async { @@ -938,7 +944,7 @@ flutter: }, overrides: { FileSystem: () => MemoryFileSystem.test(), ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, }); @@ -956,7 +962,7 @@ flutter: }, overrides: { FileSystem: () => MemoryFileSystem.test(), ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('is false with no host app and no module', () async { @@ -972,7 +978,7 @@ flutter: }, overrides: { FileSystem: () => MemoryFileSystem.test(), ProcessManager: () => FakeProcessManager.any(), - Xcode: () => mockXcode, + Xcode: () => xcode, }); testUsingContext('createDevFSWriter returns a LocalDevFSWriter', () { diff --git a/packages/flutter_tools/test/general.shard/project_test.dart b/packages/flutter_tools/test/general.shard/project_test.dart index 1644368825..fc521f550a 100644 --- a/packages/flutter_tools/test/general.shard/project_test.dart +++ b/packages/flutter_tools/test/general.shard/project_test.dart @@ -306,12 +306,12 @@ void main() { }); group('language', () { - MockXcodeProjectInterpreter mockXcodeProjectInterpreter; + XcodeProjectInterpreter xcodeProjectInterpreter; MemoryFileSystem fs; FlutterProjectFactory flutterProjectFactory; setUp(() { fs = MemoryFileSystem.test(); - mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); + xcodeProjectInterpreter = XcodeProjectInterpreter.test(processManager: FakeProcessManager.any()); flutterProjectFactory = FlutterProjectFactory( logger: logger, fileSystem: fs, @@ -337,7 +337,7 @@ apply plugin: 'kotlin-android' }, overrides: { FileSystem: () => fs, ProcessManager: () => FakeProcessManager.any(), - XcodeProjectInterpreter: () => mockXcodeProjectInterpreter, + XcodeProjectInterpreter: () => xcodeProjectInterpreter, FlutterProjectFactory: () => flutterProjectFactory, }); });