Use ProcessManager for Xcode tool invocations (#10955)
1. Run all Xcode tool invocations through ProcessManager, which allows us to mock out failures, etc. for tests. 2. Add said tests.
This commit is contained in:
parent
bbea0c3d1a
commit
2ebb9e5dc6
@ -86,7 +86,7 @@ class Xcode {
|
|||||||
String get xcodeSelectPath {
|
String get xcodeSelectPath {
|
||||||
if (_xcodeSelectPath == null) {
|
if (_xcodeSelectPath == null) {
|
||||||
try {
|
try {
|
||||||
_xcodeSelectPath = runSync(<String>['/usr/bin/xcode-select', '--print-path'])?.trim();
|
_xcodeSelectPath = processManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']).stdout.trim();
|
||||||
} on ProcessException {
|
} on ProcessException {
|
||||||
// Ignore: return null below.
|
// Ignore: return null below.
|
||||||
}
|
}
|
||||||
@ -125,7 +125,7 @@ class Xcode {
|
|||||||
String get xcodeVersionText {
|
String get xcodeVersionText {
|
||||||
if (_xcodeVersionText == null) {
|
if (_xcodeVersionText == null) {
|
||||||
try {
|
try {
|
||||||
_xcodeVersionText = runSync(<String>['/usr/bin/xcodebuild', '-version']).replaceAll('\n', ', ');
|
_xcodeVersionText = processManager.runSync(<String>['/usr/bin/xcodebuild', '-version']).stdout.replaceAll('\n', ', ');
|
||||||
} on ProcessException {
|
} on ProcessException {
|
||||||
// Ignore: return null below.
|
// Ignore: return null below.
|
||||||
}
|
}
|
||||||
@ -155,10 +155,15 @@ class Xcode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String> getAvailableDevices() async {
|
Future<String> getAvailableDevices() async {
|
||||||
final RunResult result = await runAsync(<String>['/usr/bin/instruments', '-s', 'devices']);
|
try {
|
||||||
if (result.exitCode != 0)
|
final ProcessResult result = await processManager.run(
|
||||||
|
<String>['/usr/bin/instruments', '-s', 'devices']);
|
||||||
|
if (result.exitCode != 0)
|
||||||
|
throw new ToolExit('/usr/bin/instruments returned an error:\n${result.stderr}');
|
||||||
|
return result.stdout;
|
||||||
|
} on ProcessException {
|
||||||
throw new ToolExit('Failed to invoke /usr/bin/instruments. Is Xcode installed?');
|
throw new ToolExit('Failed to invoke /usr/bin/instruments. Is Xcode installed?');
|
||||||
return result.stdout;
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,13 +7,14 @@ import 'dart:async';
|
|||||||
import 'package:file/file.dart';
|
import 'package:file/file.dart';
|
||||||
import 'package:flutter_tools/src/application_package.dart';
|
import 'package:flutter_tools/src/application_package.dart';
|
||||||
import 'package:flutter_tools/src/base/file_system.dart';
|
import 'package:flutter_tools/src/base/file_system.dart';
|
||||||
import 'package:flutter_tools/src/base/io.dart' show ProcessResult;
|
import 'package:flutter_tools/src/base/io.dart' show ProcessException, ProcessResult;
|
||||||
import 'package:flutter_tools/src/ios/mac.dart';
|
import 'package:flutter_tools/src/ios/mac.dart';
|
||||||
import 'package:mockito/mockito.dart';
|
import 'package:mockito/mockito.dart';
|
||||||
import 'package:platform/platform.dart';
|
import 'package:platform/platform.dart';
|
||||||
import 'package:process/process.dart';
|
import 'package:process/process.dart';
|
||||||
import 'package:test/test.dart';
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
import '../src/common.dart';
|
||||||
import '../src/context.dart';
|
import '../src/context.dart';
|
||||||
|
|
||||||
class MockProcessManager extends Mock implements ProcessManager {}
|
class MockProcessManager extends Mock implements ProcessManager {}
|
||||||
@ -65,6 +66,97 @@ void main() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
group('Xcode', () {
|
||||||
|
MockProcessManager mockProcessManager;
|
||||||
|
Xcode xcode;
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
mockProcessManager = new MockProcessManager();
|
||||||
|
xcode = new Xcode();
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('xcodeSelectPath returns null when xcode-select is not installed', () {
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||||
|
.thenThrow(const ProcessException('/usr/bin/xcode-select', const <String>['--print-path']));
|
||||||
|
expect(xcode.xcodeSelectPath, isNull);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('xcodeSelectPath returns path when xcode-select is installed', () {
|
||||||
|
final String xcodePath = '/Applications/Xcode8.0.app/Contents/Developer';
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcode-select', '--print-path']))
|
||||||
|
.thenReturn(new ProcessResult(1, 0, xcodePath, ''));
|
||||||
|
expect(xcode.xcodeSelectPath, xcodePath);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('xcodeVersionText returns null when xcodebuild is not installed', () {
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
|
||||||
|
.thenThrow(const ProcessException('/usr/bin/xcodebuild', const <String>['-version']));
|
||||||
|
expect(xcode.xcodeVersionText, isNull);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('xcodeVersionText returns null when xcodebuild is not installed', () {
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcodebuild', '-version']))
|
||||||
|
.thenReturn(new ProcessResult(1, 0, 'Xcode 8.3.3\nBuild version 8E3004b', ''));
|
||||||
|
expect(xcode.xcodeVersionText, 'Xcode 8.3.3, Build version 8E3004b');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('eulaSigned is false when clang is not installed', () {
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||||
|
.thenThrow(const ProcessException('/usr/bin/xcrun', const <String>['clang']));
|
||||||
|
expect(xcode.eulaSigned, isFalse);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('eulaSigned is false when clang output indicates EULA not yet accepted', () {
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||||
|
.thenReturn(new ProcessResult(1, 1, '', 'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.'));
|
||||||
|
expect(xcode.eulaSigned, isFalse);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('eulaSigned is true when clang output indicates EULA has been accepted', () {
|
||||||
|
when(mockProcessManager.runSync(<String>['/usr/bin/xcrun', 'clang']))
|
||||||
|
.thenReturn(new ProcessResult(1, 1, '', 'clang: error: no input files'));
|
||||||
|
expect(xcode.eulaSigned, isTrue);
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('getAvailableDevices throws ToolExit when instruments is not installed', () async {
|
||||||
|
when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
|
||||||
|
.thenThrow(const ProcessException('/usr/bin/instruments', const <String>['-s', 'devices']));
|
||||||
|
expect(() async => await xcode.getAvailableDevices(), throwsToolExit());
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('getAvailableDevices throws ToolExit when instruments returns non-zero', () async {
|
||||||
|
when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
|
||||||
|
.thenReturn(new ProcessResult(1, 1, '', 'Sad today'));
|
||||||
|
expect(() async => await xcode.getAvailableDevices(), throwsToolExit());
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
|
||||||
|
testUsingContext('getAvailableDevices returns instruments output when installed', () async {
|
||||||
|
when(mockProcessManager.run(<String>['/usr/bin/instruments', '-s', 'devices']))
|
||||||
|
.thenReturn(new ProcessResult(1, 0, 'Known Devices:\niPhone 6s (10.3.3) [foo]', ''));
|
||||||
|
expect(await xcode.getAvailableDevices(), 'Known Devices:\niPhone 6s (10.3.3) [foo]');
|
||||||
|
}, overrides: <Type, Generator>{
|
||||||
|
ProcessManager: () => mockProcessManager,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
group('Diagnose Xcode build failure', () {
|
group('Diagnose Xcode build failure', () {
|
||||||
BuildableIOSApp app;
|
BuildableIOSApp app;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user