Replace MockXcode with Xcode.test in unit tests (#74777)
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart index 3ccdc34..9ea4427 100644 --- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -2,6 +2,7 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:file/memory.dart'; import 'package:meta/meta.dart'; import 'package:process/process.dart'; @@ -229,25 +230,78 @@ /// Interpreter of Xcode projects. class XcodeProjectInterpreter { - XcodeProjectInterpreter({ + factory XcodeProjectInterpreter({ @required Platform platform, @required ProcessManager processManager, @required Logger logger, @required FileSystem fileSystem, @required Terminal terminal, @required Usage usage, + }) { + return XcodeProjectInterpreter._( + platform: platform, + processManager: processManager, + logger: logger, + fileSystem: fileSystem, + terminal: terminal, + usage: usage, + ); + } + + XcodeProjectInterpreter._({ + @required Platform platform, + @required ProcessManager processManager, + @required Logger logger, + @required FileSystem fileSystem, + @required Terminal terminal, + @required Usage usage, + int majorVersion, + int minorVersion, + int patchVersion, }) : _platform = platform, - _fileSystem = fileSystem, - _terminal = terminal, - _logger = logger, - _processUtils = ProcessUtils(logger: logger, processManager: processManager), - _operatingSystemUtils = OperatingSystemUtils( - fileSystem: fileSystem, - logger: logger, - platform: platform, - processManager: processManager, - ), - _usage = usage; + _fileSystem = fileSystem, + _terminal = terminal, + _logger = logger, + _processUtils = ProcessUtils(logger: logger, processManager: processManager), + _operatingSystemUtils = OperatingSystemUtils( + fileSystem: fileSystem, + logger: logger, + platform: platform, + processManager: processManager, + ), + _majorVersion = majorVersion, + _minorVersion = minorVersion, + _patchVersion = patchVersion, + _usage = usage; + + /// Create an [XcodeProjectInterpreter] for testing. + /// + /// Defaults to installed with sufficient version, + /// a memory file system, fake platform, buffer logger, + /// test [Usage], and test [Terminal]. + /// Set [majorVersion] to null to simulate Xcode not being installed. + factory XcodeProjectInterpreter.test({ + @required ProcessManager processManager, + int majorVersion = 1000, + int minorVersion = 0, + int patchVersion = 0, + }) { + final Platform platform = FakePlatform( + operatingSystem: 'macos', + environment: <String, String>{}, + ); + return XcodeProjectInterpreter._( + fileSystem: MemoryFileSystem.test(), + platform: platform, + processManager: processManager, + usage: Usage.test(), + logger: BufferLogger.test(), + terminal: Terminal.test(), + majorVersion: majorVersion, + minorVersion: minorVersion, + patchVersion: patchVersion, + ); + } final Platform _platform; final FileSystem _fileSystem;
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart index 2691098..99a4008 100644 --- a/packages/flutter_tools/lib/src/macos/xcode.dart +++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -4,6 +4,7 @@ import 'dart:async'; +import 'package:file/memory.dart'; import 'package:meta/meta.dart'; import 'package:process/process.dart'; @@ -55,6 +56,30 @@ _processUtils = ProcessUtils(logger: logger, processManager: processManager); + /// Create an [Xcode] for testing. + /// + /// Defaults to a memory file system, fake platform, + /// buffer logger, and test [XcodeProjectInterpreter]. + @visibleForTesting + factory Xcode.test({ + @required ProcessManager processManager, + XcodeProjectInterpreter xcodeProjectInterpreter, + Platform platform, + FileSystem fileSystem, + }) { + platform ??= FakePlatform( + operatingSystem: 'macos', + environment: <String, String>{}, + ); + return Xcode( + platform: platform, + processManager: processManager, + fileSystem: fileSystem ?? MemoryFileSystem.test(), + logger: BufferLogger.test(), + xcodeProjectInterpreter: xcodeProjectInterpreter ?? XcodeProjectInterpreter.test(processManager: processManager), + ); + } + final Platform _platform; final ProcessUtils _processUtils; final FileSystem _fileSystem; @@ -78,12 +103,7 @@ return _xcodeSelectPath; } - bool get isInstalled { - if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) { - return false; - } - return _xcodeProjectInterpreter.isInstalled; - } + bool get isInstalled => _xcodeProjectInterpreter.isInstalled; Version get currentVersion => Version( _xcodeProjectInterpreter.majorVersion,
diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart index 68f4af6..693ca62 100644 --- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
@@ -4,7 +4,6 @@ import 'dart:async'; -import 'package:file/memory.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult; import 'package:flutter_tools/src/base/logger.dart'; @@ -43,10 +42,7 @@ setUp(() { mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); - xcode = Xcode( - logger: logger, - platform: FakePlatform(operatingSystem: 'macos'), - fileSystem: MemoryFileSystem.test(), + xcode = Xcode.test( processManager: processManager, xcodeProjectInterpreter: mockXcodeProjectInterpreter, ); @@ -78,25 +74,27 @@ group('xcdevice', () { XCDevice xcdevice; - MockXcode mockXcode; + Xcode xcode; setUp(() { - mockXcode = MockXcode(); + xcode = Xcode.test( + processManager: FakeProcessManager.any(), + xcodeProjectInterpreter: XcodeProjectInterpreter.test( + processManager: FakeProcessManager.any(), + ), + ); xcdevice = XCDevice( processManager: processManager, logger: logger, - xcode: mockXcode, + xcode: xcode, platform: null, artifacts: Artifacts.test(), cache: Cache.test(), iproxy: IProxy.test(logger: logger, processManager: processManager), ); - when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']); }); testWithoutContext('available devices xcdevice fails', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2'])) .thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2'])); @@ -104,8 +102,6 @@ }); testWithoutContext('diagnostics xcdevice fails', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2'])) .thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2'])); @@ -129,10 +125,8 @@ }); testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () { - final Xcode xcode = Xcode( - logger: logger, + final Xcode xcode = Xcode.test( platform: FakePlatform(operatingSystem: 'windows'), - fileSystem: MemoryFileSystem.test(), processManager: fakeProcessManager, xcodeProjectInterpreter: mockXcodeProjectInterpreter, ); @@ -151,10 +145,7 @@ ], ), ); - final Xcode xcode = Xcode( - logger: logger, - platform: FakePlatform(operatingSystem: 'macos'), - fileSystem: MemoryFileSystem.test(), + final Xcode xcode = Xcode.test( processManager: fakeProcessManager, xcodeProjectInterpreter: mockXcodeProjectInterpreter, ); @@ -175,10 +166,7 @@ exitCode: 1, ), ); - final Xcode xcode = Xcode( - logger: logger, - platform: FakePlatform(operatingSystem: 'macos'), - fileSystem: MemoryFileSystem.test(), + final Xcode xcode = Xcode.test( processManager: fakeProcessManager, xcodeProjectInterpreter: mockXcodeProjectInterpreter, ); @@ -189,16 +177,11 @@ group('macOS', () { Xcode xcode; - FakePlatform platform; setUp(() { mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']); - platform = FakePlatform(operatingSystem: 'macos'); - xcode = Xcode( - logger: logger, - platform: platform, - fileSystem: MemoryFileSystem.test(), + xcode = Xcode.test( processManager: fakeProcessManager, xcodeProjectInterpreter: mockXcodeProjectInterpreter, ); @@ -318,36 +301,13 @@ }); testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () { - fakeProcessManager.addCommand(const FakeCommand( - command: <String>['/usr/bin/xcode-select', '--print-path'], - stdout: '/Applications/Xcode8.0.app/Contents/Developer', - )); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false); expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); expect(fakeProcessManager.hasRemainingExpectations, isFalse); }); - testWithoutContext('isInstalledAndMeetsVersionCheck is false when no xcode-select', () { - fakeProcessManager.addCommand(const FakeCommand( - command: <String>['/usr/bin/xcode-select', '--print-path'], - exitCode: 127, - stderr: 'ERROR', - )); - when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); - when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); - when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); - when(mockXcodeProjectInterpreter.patchVersion).thenReturn(0); - - expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); - expect(fakeProcessManager.hasRemainingExpectations, isFalse); - }); - testWithoutContext('isInstalledAndMeetsVersionCheck is false when version not satisfied', () { - fakeProcessManager.addCommand(const FakeCommand( - command: <String>['/usr/bin/xcode-select', '--print-path'], - stdout: '/Applications/Xcode8.0.app/Contents/Developer', - )); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); @@ -358,10 +318,6 @@ }); testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () { - fakeProcessManager.addCommand(const FakeCommand( - command: <String>['/usr/bin/xcode-select', '--print-path'], - stdout: '/Applications/Xcode8.0.app/Contents/Developer', - )); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(true); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); @@ -432,42 +388,59 @@ }); }); - group('xcdevice', () { + group('xcdevice not installed', () { XCDevice xcdevice; - MockXcode mockXcode; + Xcode xcode; setUp(() { - mockXcode = MockXcode(); + xcode = Xcode.test( + processManager: FakeProcessManager.any(), + xcodeProjectInterpreter: XcodeProjectInterpreter.test( + processManager: FakeProcessManager.any(), + majorVersion: null, // Not installed. + ), + ); xcdevice = XCDevice( processManager: fakeProcessManager, logger: logger, - xcode: mockXcode, + xcode: xcode, platform: null, artifacts: Artifacts.test(), cache: Cache.test(), iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager), ); - when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']); }); - group('installed', () { - testWithoutContext('Xcode not installed', () { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); - expect(xcdevice.isInstalled, false); - }); + testWithoutContext('Xcode not installed', () async { + expect(xcode.isInstalled, false); + + expect(xcdevice.isInstalled, false); + expect(xcdevice.observedDeviceEvents(), isNull); + expect(logger.traceText, contains("Xcode not found. Run 'flutter doctor' for more information.")); + expect(await xcdevice.getAvailableIOSDevices(), isEmpty); + expect(await xcdevice.getDiagnostics(), isEmpty); + }); + }); + + group('xcdevice', () { + XCDevice xcdevice; + Xcode xcode; + + setUp(() { + xcode = Xcode.test(processManager: FakeProcessManager.any()); + xcdevice = XCDevice( + processManager: fakeProcessManager, + logger: logger, + xcode: xcode, + platform: null, + artifacts: Artifacts.test(), + cache: Cache.test(), + iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager), + ); }); group('observe device events', () { - testWithoutContext('Xcode not installed', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); - - expect(xcdevice.observedDeviceEvents(), isNull); - expect(logger.traceText, contains("Xcode not found. Run 'flutter doctor' for more information.")); - }); - testUsingContext('relays events', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - fakeProcessManager.addCommand(const FakeCommand( command: <String>[ 'script', @@ -516,16 +489,7 @@ group('available devices', () { final FakePlatform macPlatform = FakePlatform(operatingSystem: 'macos'); - - testWithoutContext('Xcode not installed', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); - - expect(await xcdevice.getAvailableIOSDevices(), isEmpty); - }); - testUsingContext('returns devices', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - const String devicesOutput = ''' [ { @@ -644,8 +608,6 @@ }); testWithoutContext('uses timeout', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - fakeProcessManager.addCommand(const FakeCommand( command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'], stdout: '[]', @@ -655,8 +617,6 @@ }); testUsingContext('ignores "Preparing debugger support for iPhone" error', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - const String devicesOutput = ''' [ { @@ -695,8 +655,6 @@ }); testUsingContext('handles unknown architectures', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - const String devicesOutput = ''' [ { @@ -742,16 +700,7 @@ group('diagnostics', () { final FakePlatform macPlatform = FakePlatform(operatingSystem: 'macos'); - - testWithoutContext('Xcode not installed', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); - - expect(await xcdevice.getDiagnostics(), isEmpty); - }); - testUsingContext('uses cache', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - const String devicesOutput = ''' [ { @@ -787,8 +736,6 @@ }); testUsingContext('returns error message', () async { - when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); - const String devicesOutput = ''' [ { @@ -894,6 +841,5 @@ }); } -class MockXcode extends Mock implements Xcode {} class MockProcessManager extends Mock implements ProcessManager {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}