Unverified Commit 675fd554 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Replace MockXcode with Xcode.test in unit tests (#74777)

parent c6a428d5
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:file/memory.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -229,13 +230,34 @@ List<String> _xcodeBuildSettingsLines({ ...@@ -229,13 +230,34 @@ List<String> _xcodeBuildSettingsLines({
/// Interpreter of Xcode projects. /// Interpreter of Xcode projects.
class XcodeProjectInterpreter { class XcodeProjectInterpreter {
XcodeProjectInterpreter({ factory XcodeProjectInterpreter({
@required Platform platform, @required Platform platform,
@required ProcessManager processManager, @required ProcessManager processManager,
@required Logger logger, @required Logger logger,
@required FileSystem fileSystem, @required FileSystem fileSystem,
@required Terminal terminal, @required Terminal terminal,
@required Usage usage, @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, }) : _platform = platform,
_fileSystem = fileSystem, _fileSystem = fileSystem,
_terminal = terminal, _terminal = terminal,
...@@ -247,8 +269,40 @@ class XcodeProjectInterpreter { ...@@ -247,8 +269,40 @@ class XcodeProjectInterpreter {
platform: platform, platform: platform,
processManager: processManager, processManager: processManager,
), ),
_majorVersion = majorVersion,
_minorVersion = minorVersion,
_patchVersion = patchVersion,
_usage = usage; _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 Platform _platform;
final FileSystem _fileSystem; final FileSystem _fileSystem;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import 'package:file/memory.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -55,6 +56,30 @@ class Xcode { ...@@ -55,6 +56,30 @@ class Xcode {
_processUtils = _processUtils =
ProcessUtils(logger: logger, processManager: processManager); 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 Platform _platform;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
final FileSystem _fileSystem; final FileSystem _fileSystem;
...@@ -78,12 +103,7 @@ class Xcode { ...@@ -78,12 +103,7 @@ class Xcode {
return _xcodeSelectPath; return _xcodeSelectPath;
} }
bool get isInstalled { bool get isInstalled => _xcodeProjectInterpreter.isInstalled;
if (xcodeSelectPath == null || xcodeSelectPath.isEmpty) {
return false;
}
return _xcodeProjectInterpreter.isInstalled;
}
Version get currentVersion => Version( Version get currentVersion => Version(
_xcodeProjectInterpreter.majorVersion, _xcodeProjectInterpreter.majorVersion,
......
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
import 'dart:async'; import 'dart:async';
import 'package:file/memory.dart';
import 'package:flutter_tools/src/artifacts.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/io.dart' show ProcessException, ProcessResult;
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
...@@ -43,10 +42,7 @@ void main() { ...@@ -43,10 +42,7 @@ void main() {
setUp(() { setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
xcode = Xcode( xcode = Xcode.test(
logger: logger,
platform: FakePlatform(operatingSystem: 'macos'),
fileSystem: MemoryFileSystem.test(),
processManager: processManager, processManager: processManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter, xcodeProjectInterpreter: mockXcodeProjectInterpreter,
); );
...@@ -78,25 +74,27 @@ void main() { ...@@ -78,25 +74,27 @@ void main() {
group('xcdevice', () { group('xcdevice', () {
XCDevice xcdevice; XCDevice xcdevice;
MockXcode mockXcode; Xcode xcode;
setUp(() { setUp(() {
mockXcode = MockXcode(); xcode = Xcode.test(
processManager: FakeProcessManager.any(),
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
processManager: FakeProcessManager.any(),
),
);
xcdevice = XCDevice( xcdevice = XCDevice(
processManager: processManager, processManager: processManager,
logger: logger, logger: logger,
xcode: mockXcode, xcode: xcode,
platform: null, platform: null,
artifacts: Artifacts.test(), artifacts: Artifacts.test(),
cache: Cache.test(), cache: Cache.test(),
iproxy: IProxy.test(logger: logger, processManager: processManager), iproxy: IProxy.test(logger: logger, processManager: processManager),
); );
when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
}); });
testWithoutContext('available devices xcdevice fails', () async { testWithoutContext('available devices xcdevice fails', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2'])) when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2'])); .thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
...@@ -104,8 +102,6 @@ void main() { ...@@ -104,8 +102,6 @@ void main() {
}); });
testWithoutContext('diagnostics xcdevice fails', () async { testWithoutContext('diagnostics xcdevice fails', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2'])) when(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '2']))
.thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2'])); .thenThrow(const ProcessException('xcrun', <String>['xcdevice', 'list', '--timeout', '2']));
...@@ -129,10 +125,8 @@ void main() { ...@@ -129,10 +125,8 @@ void main() {
}); });
testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () { testWithoutContext('isInstalledAndMeetsVersionCheck is false when not macOS', () {
final Xcode xcode = Xcode( final Xcode xcode = Xcode.test(
logger: logger,
platform: FakePlatform(operatingSystem: 'windows'), platform: FakePlatform(operatingSystem: 'windows'),
fileSystem: MemoryFileSystem.test(),
processManager: fakeProcessManager, processManager: fakeProcessManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter, xcodeProjectInterpreter: mockXcodeProjectInterpreter,
); );
...@@ -151,10 +145,7 @@ void main() { ...@@ -151,10 +145,7 @@ void main() {
], ],
), ),
); );
final Xcode xcode = Xcode( final Xcode xcode = Xcode.test(
logger: logger,
platform: FakePlatform(operatingSystem: 'macos'),
fileSystem: MemoryFileSystem.test(),
processManager: fakeProcessManager, processManager: fakeProcessManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter, xcodeProjectInterpreter: mockXcodeProjectInterpreter,
); );
...@@ -175,10 +166,7 @@ void main() { ...@@ -175,10 +166,7 @@ void main() {
exitCode: 1, exitCode: 1,
), ),
); );
final Xcode xcode = Xcode( final Xcode xcode = Xcode.test(
logger: logger,
platform: FakePlatform(operatingSystem: 'macos'),
fileSystem: MemoryFileSystem.test(),
processManager: fakeProcessManager, processManager: fakeProcessManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter, xcodeProjectInterpreter: mockXcodeProjectInterpreter,
); );
...@@ -189,16 +177,11 @@ void main() { ...@@ -189,16 +177,11 @@ void main() {
group('macOS', () { group('macOS', () {
Xcode xcode; Xcode xcode;
FakePlatform platform;
setUp(() { setUp(() {
mockXcodeProjectInterpreter = MockXcodeProjectInterpreter(); mockXcodeProjectInterpreter = MockXcodeProjectInterpreter();
when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']); when(mockXcodeProjectInterpreter.xcrunCommand()).thenReturn(<String>['xcrun']);
platform = FakePlatform(operatingSystem: 'macos'); xcode = Xcode.test(
xcode = Xcode(
logger: logger,
platform: platform,
fileSystem: MemoryFileSystem.test(),
processManager: fakeProcessManager, processManager: fakeProcessManager,
xcodeProjectInterpreter: mockXcodeProjectInterpreter, xcodeProjectInterpreter: mockXcodeProjectInterpreter,
); );
...@@ -318,36 +301,13 @@ void main() { ...@@ -318,36 +301,13 @@ void main() {
}); });
testWithoutContext('isInstalledAndMeetsVersionCheck is false when not installed', () { 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); when(mockXcodeProjectInterpreter.isInstalled).thenReturn(false);
expect(xcode.isInstalledAndMeetsVersionCheck, isFalse); expect(xcode.isInstalledAndMeetsVersionCheck, isFalse);
expect(fakeProcessManager.hasRemainingExpectations, 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', () { 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.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(10);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(2);
...@@ -358,10 +318,6 @@ void main() { ...@@ -358,10 +318,6 @@ void main() {
}); });
testWithoutContext('isInstalledAndMeetsVersionCheck is true when macOS and installed and version is satisfied', () { 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.isInstalled).thenReturn(true);
when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11); when(mockXcodeProjectInterpreter.majorVersion).thenReturn(11);
when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0); when(mockXcodeProjectInterpreter.minorVersion).thenReturn(0);
...@@ -432,42 +388,59 @@ void main() { ...@@ -432,42 +388,59 @@ void main() {
}); });
}); });
group('xcdevice', () { group('xcdevice not installed', () {
XCDevice xcdevice; XCDevice xcdevice;
MockXcode mockXcode; Xcode xcode;
setUp(() { setUp(() {
mockXcode = MockXcode(); xcode = Xcode.test(
processManager: FakeProcessManager.any(),
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
processManager: FakeProcessManager.any(),
majorVersion: null, // Not installed.
),
);
xcdevice = XCDevice( xcdevice = XCDevice(
processManager: fakeProcessManager, processManager: fakeProcessManager,
logger: logger, logger: logger,
xcode: mockXcode, xcode: xcode,
platform: null, platform: null,
artifacts: Artifacts.test(), artifacts: Artifacts.test(),
cache: Cache.test(), cache: Cache.test(),
iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager), iproxy: IProxy.test(logger: logger, processManager: fakeProcessManager),
); );
when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
}); });
group('installed', () { testWithoutContext('Xcode not installed', () async {
testWithoutContext('Xcode not installed', () { expect(xcode.isInstalled, false);
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false);
expect(xcdevice.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('observe device events', () { group('xcdevice', () {
testWithoutContext('Xcode not installed', () async { XCDevice xcdevice;
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(false); Xcode xcode;
expect(xcdevice.observedDeviceEvents(), isNull); setUp(() {
expect(logger.traceText, contains("Xcode not found. Run 'flutter doctor' for more information.")); 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', () {
testUsingContext('relays events', () async { testUsingContext('relays events', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>[ command: <String>[
'script', 'script',
...@@ -516,16 +489,7 @@ void main() { ...@@ -516,16 +489,7 @@ void main() {
group('available devices', () { group('available devices', () {
final FakePlatform macPlatform = FakePlatform(operatingSystem: 'macos'); 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 { testUsingContext('returns devices', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
const String devicesOutput = ''' const String devicesOutput = '''
[ [
{ {
...@@ -644,8 +608,6 @@ void main() { ...@@ -644,8 +608,6 @@ void main() {
}); });
testWithoutContext('uses timeout', () async { testWithoutContext('uses timeout', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
fakeProcessManager.addCommand(const FakeCommand( fakeProcessManager.addCommand(const FakeCommand(
command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'], command: <String>['xcrun', 'xcdevice', 'list', '--timeout', '20'],
stdout: '[]', stdout: '[]',
...@@ -655,8 +617,6 @@ void main() { ...@@ -655,8 +617,6 @@ void main() {
}); });
testUsingContext('ignores "Preparing debugger support for iPhone" error', () async { testUsingContext('ignores "Preparing debugger support for iPhone" error', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
const String devicesOutput = ''' const String devicesOutput = '''
[ [
{ {
...@@ -695,8 +655,6 @@ void main() { ...@@ -695,8 +655,6 @@ void main() {
}); });
testUsingContext('handles unknown architectures', () async { testUsingContext('handles unknown architectures', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
const String devicesOutput = ''' const String devicesOutput = '''
[ [
{ {
...@@ -742,16 +700,7 @@ void main() { ...@@ -742,16 +700,7 @@ void main() {
group('diagnostics', () { group('diagnostics', () {
final FakePlatform macPlatform = FakePlatform(operatingSystem: 'macos'); 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 { testUsingContext('uses cache', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
const String devicesOutput = ''' const String devicesOutput = '''
[ [
{ {
...@@ -787,8 +736,6 @@ void main() { ...@@ -787,8 +736,6 @@ void main() {
}); });
testUsingContext('returns error message', () async { testUsingContext('returns error message', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
const String devicesOutput = ''' const String devicesOutput = '''
[ [
{ {
...@@ -894,6 +841,5 @@ void main() { ...@@ -894,6 +841,5 @@ void main() {
}); });
} }
class MockXcode extends Mock implements Xcode {}
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment