Unverified Commit 2e18cd34 authored by Christopher Fujino's avatar Christopher Fujino Committed by GitHub

Fix simctl process exceptions not being caught (#51916)

parent a42d38a2
...@@ -137,7 +137,10 @@ Future<T> runInContext<T>( ...@@ -137,7 +137,10 @@ Future<T> runInContext<T>(
platform: globals.platform, platform: globals.platform,
processManager: globals.processManager, processManager: globals.processManager,
), ),
IOSSimulatorUtils: () => IOSSimulatorUtils(), IOSSimulatorUtils: () => IOSSimulatorUtils(
simControl: globals.simControl,
xcode: globals.xcode,
),
IOSWorkflow: () => const IOSWorkflow(), IOSWorkflow: () => const IOSWorkflow(),
KernelCompilerFactory: () => const KernelCompilerFactory(), KernelCompilerFactory: () => const KernelCompilerFactory(),
Logger: () => globals.platform.isWindows Logger: () => globals.platform.isWindows
...@@ -174,7 +177,10 @@ Future<T> runInContext<T>( ...@@ -174,7 +177,10 @@ Future<T> runInContext<T>(
Pub: () => const Pub(), Pub: () => const Pub(),
ShutdownHooks: () => ShutdownHooks(logger: globals.logger), ShutdownHooks: () => ShutdownHooks(logger: globals.logger),
Signals: () => Signals(), Signals: () => Signals(),
SimControl: () => SimControl(), SimControl: () => SimControl(
logger: globals.logger,
processManager: globals.processManager,
),
Stdio: () => Stdio(), Stdio: () => Stdio(),
SystemClock: () => const SystemClock(), SystemClock: () => const SystemClock(),
TimeoutConfiguration: () => const TimeoutConfiguration(), TimeoutConfiguration: () => const TimeoutConfiguration(),
......
...@@ -71,7 +71,7 @@ class DeviceManager { ...@@ -71,7 +71,7 @@ class DeviceManager {
final List<DeviceDiscovery> _deviceDiscoverers = List<DeviceDiscovery>.unmodifiable(<DeviceDiscovery>[ final List<DeviceDiscovery> _deviceDiscoverers = List<DeviceDiscovery>.unmodifiable(<DeviceDiscovery>[
AndroidDevices(), AndroidDevices(),
IOSDevices(), IOSDevices(),
IOSSimulators(), IOSSimulators(iosSimulatorUtils: globals.iosSimulatorUtils),
FuchsiaDevices(), FuchsiaDevices(),
FlutterTesterDevices(), FlutterTesterDevices(),
MacOSDevices(), MacOSDevices(),
......
...@@ -24,6 +24,7 @@ import 'cache.dart'; ...@@ -24,6 +24,7 @@ import 'cache.dart';
import 'ios/ios_deploy.dart'; import 'ios/ios_deploy.dart';
import 'ios/mac.dart'; import 'ios/mac.dart';
import 'ios/plist_parser.dart'; import 'ios/plist_parser.dart';
import 'ios/simulators.dart';
import 'macos/xcode.dart'; import 'macos/xcode.dart';
import 'persistent_tool_state.dart'; import 'persistent_tool_state.dart';
import 'reporting/reporting.dart'; import 'reporting/reporting.dart';
...@@ -69,6 +70,8 @@ AndroidSdk get androidSdk => context.get<AndroidSdk>(); ...@@ -69,6 +70,8 @@ AndroidSdk get androidSdk => context.get<AndroidSdk>();
FlutterVersion get flutterVersion => context.get<FlutterVersion>(); FlutterVersion get flutterVersion => context.get<FlutterVersion>();
IMobileDevice get iMobileDevice => context.get<IMobileDevice>(); IMobileDevice get iMobileDevice => context.get<IMobileDevice>();
IOSDeploy get iosDeploy => context.get<IOSDeploy>(); IOSDeploy get iosDeploy => context.get<IOSDeploy>();
IOSSimulatorUtils get iosSimulatorUtils => context.get<IOSSimulatorUtils>();
SimControl get simControl => context.get<SimControl>();
UserMessages get userMessages => context.get<UserMessages>(); UserMessages get userMessages => context.get<UserMessages>();
Xcode get xcode => context.get<Xcode>(); Xcode get xcode => context.get<Xcode>();
......
...@@ -6,12 +6,13 @@ import 'dart:async'; ...@@ -6,12 +6,13 @@ import 'dart:async';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:process/process.dart';
import '../application_package.dart'; import '../application_package.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/context.dart';
import '../base/file_system.dart'; import '../base/file_system.dart';
import '../base/io.dart'; import '../base/io.dart';
import '../base/logger.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
...@@ -19,6 +20,7 @@ import '../bundle.dart'; ...@@ -19,6 +20,7 @@ import '../bundle.dart';
import '../convert.dart'; import '../convert.dart';
import '../device.dart'; import '../device.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../macos/xcode.dart';
import '../project.dart'; import '../project.dart';
import '../protocol_discovery.dart'; import '../protocol_discovery.dart';
import 'ios_workflow.dart'; import 'ios_workflow.dart';
...@@ -29,7 +31,12 @@ const String _xcrunPath = '/usr/bin/xcrun'; ...@@ -29,7 +31,12 @@ const String _xcrunPath = '/usr/bin/xcrun';
const String iosSimulatorId = 'apple_ios_simulator'; const String iosSimulatorId = 'apple_ios_simulator';
class IOSSimulators extends PollingDeviceDiscovery { class IOSSimulators extends PollingDeviceDiscovery {
IOSSimulators() : super('iOS simulators'); IOSSimulators({
@required IOSSimulatorUtils iosSimulatorUtils,
}) : _iosSimulatorUtils = iosSimulatorUtils,
super('iOS simulators');
final IOSSimulatorUtils _iosSimulatorUtils;
@override @override
bool get supportsPlatform => globals.platform.isMacOS; bool get supportsPlatform => globals.platform.isMacOS;
...@@ -38,29 +45,47 @@ class IOSSimulators extends PollingDeviceDiscovery { ...@@ -38,29 +45,47 @@ class IOSSimulators extends PollingDeviceDiscovery {
bool get canListAnything => iosWorkflow.canListDevices; bool get canListAnything => iosWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async => IOSSimulatorUtils.instance.getAttachedDevices(); Future<List<Device>> pollingGetDevices() async => _iosSimulatorUtils.getAttachedDevices();
} }
class IOSSimulatorUtils { class IOSSimulatorUtils {
/// Returns [IOSSimulatorUtils] active in the current app context (i.e. zone). IOSSimulatorUtils({
static IOSSimulatorUtils get instance => context.get<IOSSimulatorUtils>(); @required SimControl simControl,
@required Xcode xcode,
}) : _simControl = simControl,
_xcode = xcode;
final SimControl _simControl;
final Xcode _xcode;
Future<List<IOSSimulator>> getAttachedDevices() async { Future<List<IOSSimulator>> getAttachedDevices() async {
if (!globals.xcode.isInstalledAndMeetsVersionCheck) { if (!_xcode.isInstalledAndMeetsVersionCheck) {
return <IOSSimulator>[]; return <IOSSimulator>[];
} }
final List<SimDevice> connected = await SimControl.instance.getConnectedDevices(); final List<SimDevice> connected = await _simControl.getConnectedDevices();
return connected.map<IOSSimulator>((SimDevice device) { return connected.map<IOSSimulator>((SimDevice device) {
return IOSSimulator(device.udid, name: device.name, simulatorCategory: device.category); return IOSSimulator(
device.udid,
name: device.name,
simControl: _simControl,
simulatorCategory: device.category,
xcode: _xcode,
);
}).toList(); }).toList();
} }
} }
/// A wrapper around the `simctl` command line tool. /// A wrapper around the `simctl` command line tool.
class SimControl { class SimControl {
/// Returns [SimControl] active in the current app context (i.e. zone). SimControl({
static SimControl get instance => context.get<SimControl>(); @required Logger logger,
@required ProcessManager processManager,
}) : _logger = logger,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
final Logger _logger;
final ProcessUtils _processUtils;
/// Runs `simctl list --json` and returns the JSON of the corresponding /// Runs `simctl list --json` and returns the JSON of the corresponding
/// [section]. /// [section].
...@@ -83,10 +108,10 @@ class SimControl { ...@@ -83,10 +108,10 @@ class SimControl {
// "pairs": { ... }, // "pairs": { ... },
final List<String> command = <String>[_xcrunPath, 'simctl', 'list', '--json', section.name]; final List<String> command = <String>[_xcrunPath, 'simctl', 'list', '--json', section.name];
globals.printTrace(command.join(' ')); _logger.printTrace(command.join(' '));
final ProcessResult results = await globals.processManager.run(command); final RunResult results = await _processUtils.run(command);
if (results.exitCode != 0) { if (results.exitCode != 0) {
globals.printError('Error executing simctl: ${results.exitCode}\n${results.stderr}'); _logger.printError('Error executing simctl: ${results.exitCode}\n${results.stderr}');
return <String, Map<String, dynamic>>{}; return <String, Map<String, dynamic>>{};
} }
try { try {
...@@ -94,13 +119,13 @@ class SimControl { ...@@ -94,13 +119,13 @@ class SimControl {
if (decodeResult is Map<String, dynamic>) { if (decodeResult is Map<String, dynamic>) {
return decodeResult; return decodeResult;
} }
globals.printError('simctl returned unexpected JSON response: ${results.stdout}'); _logger.printError('simctl returned unexpected JSON response: ${results.stdout}');
return <String, dynamic>{}; return <String, dynamic>{};
} on FormatException { } on FormatException {
// We failed to parse the simctl output, or it returned junk. // We failed to parse the simctl output, or it returned junk.
// One known message is "Install Started" isn't valid JSON but is // One known message is "Install Started" isn't valid JSON but is
// returned sometimes. // returned sometimes.
globals.printError('simctl returned non-JSON response: ${results.stdout}'); _logger.printError('simctl returned non-JSON response: ${results.stdout}');
return <String, dynamic>{}; return <String, dynamic>{};
} }
} }
...@@ -130,7 +155,7 @@ class SimControl { ...@@ -130,7 +155,7 @@ class SimControl {
} }
Future<bool> isInstalled(String deviceId, String appId) { Future<bool> isInstalled(String deviceId, String appId) {
return processUtils.exitsHappy(<String>[ return _processUtils.exitsHappy(<String>[
_xcrunPath, _xcrunPath,
'simctl', 'simctl',
'get_app_container', 'get_app_container',
...@@ -139,23 +164,23 @@ class SimControl { ...@@ -139,23 +164,23 @@ class SimControl {
]); ]);
} }
Future<RunResult> install(String deviceId, String appPath) { Future<RunResult> install(String deviceId, String appPath) async {
Future<RunResult> result; RunResult result;
try { try {
result = processUtils.run( result = await _processUtils.run(
<String>[_xcrunPath, 'simctl', 'install', deviceId, appPath], <String>[_xcrunPath, 'simctl', 'install', deviceId, appPath],
throwOnError: true, throwOnError: true,
); );
} on ProcessException catch (exception) { } on ProcessException catch (exception) {
throwToolExit('Unable to install $appPath on $deviceId:\n$exception'); throwToolExit('Unable to install $appPath on $deviceId. This is sometimes caused by a malformed plist file:\n$exception');
} }
return result; return result;
} }
Future<RunResult> uninstall(String deviceId, String appId) { Future<RunResult> uninstall(String deviceId, String appId) async {
Future<RunResult> result; RunResult result;
try { try {
result = processUtils.run( result = await _processUtils.run(
<String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId], <String>[_xcrunPath, 'simctl', 'uninstall', deviceId, appId],
throwOnError: true, throwOnError: true,
); );
...@@ -165,10 +190,10 @@ class SimControl { ...@@ -165,10 +190,10 @@ class SimControl {
return result; return result;
} }
Future<RunResult> launch(String deviceId, String appIdentifier, [ List<String> launchArgs ]) { Future<RunResult> launch(String deviceId, String appIdentifier, [ List<String> launchArgs ]) async {
Future<RunResult> result; RunResult result;
try { try {
result = processUtils.run( result = await _processUtils.run(
<String>[ <String>[
_xcrunPath, _xcrunPath,
'simctl', 'simctl',
...@@ -187,12 +212,12 @@ class SimControl { ...@@ -187,12 +212,12 @@ class SimControl {
Future<void> takeScreenshot(String deviceId, String outputPath) async { Future<void> takeScreenshot(String deviceId, String outputPath) async {
try { try {
await processUtils.run( await _processUtils.run(
<String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath], <String>[_xcrunPath, 'simctl', 'io', deviceId, 'screenshot', outputPath],
throwOnError: true, throwOnError: true,
); );
} on ProcessException catch (exception) { } on ProcessException catch (exception) {
throwToolExit('Unable to take screenshot of $deviceId:\n$exception'); _logger.printError('Unable to take screenshot of $deviceId:\n$exception');
} }
} }
} }
...@@ -248,7 +273,15 @@ class SimDevice { ...@@ -248,7 +273,15 @@ class SimDevice {
} }
class IOSSimulator extends Device { class IOSSimulator extends Device {
IOSSimulator(String id, { this.name, this.simulatorCategory }) : super( IOSSimulator(
String id, {
this.name,
this.simulatorCategory,
@required SimControl simControl,
@required Xcode xcode,
}) : _simControl = simControl,
_xcode = xcode,
super(
id, id,
category: Category.mobile, category: Category.mobile,
platformType: PlatformType.ios, platformType: PlatformType.ios,
...@@ -260,6 +293,9 @@ class IOSSimulator extends Device { ...@@ -260,6 +293,9 @@ class IOSSimulator extends Device {
final String simulatorCategory; final String simulatorCategory;
final SimControl _simControl;
final Xcode _xcode;
@override @override
Future<bool> get isLocalEmulator async => true; Future<bool> get isLocalEmulator async => true;
...@@ -279,7 +315,7 @@ class IOSSimulator extends Device { ...@@ -279,7 +315,7 @@ class IOSSimulator extends Device {
@override @override
Future<bool> isAppInstalled(ApplicationPackage app) { Future<bool> isAppInstalled(ApplicationPackage app) {
return SimControl.instance.isInstalled(id, app.id); return _simControl.isInstalled(id, app.id);
} }
@override @override
...@@ -289,7 +325,7 @@ class IOSSimulator extends Device { ...@@ -289,7 +325,7 @@ class IOSSimulator extends Device {
Future<bool> installApp(covariant IOSApp app) async { Future<bool> installApp(covariant IOSApp app) async {
try { try {
final IOSApp iosApp = app; final IOSApp iosApp = app;
await SimControl.instance.install(id, iosApp.simulatorBundlePath); await _simControl.install(id, iosApp.simulatorBundlePath);
return true; return true;
} on Exception { } on Exception {
return false; return false;
...@@ -299,7 +335,7 @@ class IOSSimulator extends Device { ...@@ -299,7 +335,7 @@ class IOSSimulator extends Device {
@override @override
Future<bool> uninstallApp(ApplicationPackage app) async { Future<bool> uninstallApp(ApplicationPackage app) async {
try { try {
await SimControl.instance.uninstall(id, app.id); await _simControl.uninstall(id, app.id);
return true; return true;
} on Exception { } on Exception {
return false; return false;
...@@ -394,7 +430,7 @@ class IOSSimulator extends Device { ...@@ -394,7 +430,7 @@ class IOSSimulator extends Device {
final String plistPath = globals.fs.path.join(package.simulatorBundlePath, 'Info.plist'); final String plistPath = globals.fs.path.join(package.simulatorBundlePath, 'Info.plist');
final String bundleIdentifier = globals.plistParser.getValueFromFile(plistPath, PlistParser.kCFBundleIdentifierKey); final String bundleIdentifier = globals.plistParser.getValueFromFile(plistPath, PlistParser.kCFBundleIdentifierKey);
await SimControl.instance.launch(id, bundleIdentifier, args); await _simControl.launch(id, bundleIdentifier, args);
} on Exception catch (error) { } on Exception catch (error) {
globals.printError('$error'); globals.printError('$error');
return LaunchResult.failed(); return LaunchResult.failed();
...@@ -449,7 +485,7 @@ class IOSSimulator extends Device { ...@@ -449,7 +485,7 @@ class IOSSimulator extends Device {
} }
// Step 3: Install the updated bundle to the simulator. // Step 3: Install the updated bundle to the simulator.
await SimControl.instance.install(id, globals.fs.path.absolute(bundle.path)); await _simControl.install(id, globals.fs.path.absolute(bundle.path));
} }
@visibleForTesting @visibleForTesting
...@@ -527,7 +563,7 @@ class IOSSimulator extends Device { ...@@ -527,7 +563,7 @@ class IOSSimulator extends Device {
} }
bool get _xcodeVersionSupportsScreenshot { bool get _xcodeVersionSupportsScreenshot {
return globals.xcode.majorVersion > 8 || (globals.xcode.majorVersion == 8 && globals.xcode.minorVersion >= 2); return _xcode.majorVersion > 8 || (_xcode.majorVersion == 8 && _xcode.minorVersion >= 2);
} }
@override @override
...@@ -535,7 +571,7 @@ class IOSSimulator extends Device { ...@@ -535,7 +571,7 @@ class IOSSimulator extends Device {
@override @override
Future<void> takeScreenshot(File outputFile) { Future<void> takeScreenshot(File outputFile) {
return SimControl.instance.takeScreenshot(id, outputFile.path); return _simControl.takeScreenshot(id, outputFile.path);
} }
@override @override
......
...@@ -8,6 +8,8 @@ import 'dart:io' show ProcessResult, Process; ...@@ -8,6 +8,8 @@ import 'dart:io' show ProcessResult, Process;
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/build_system/build_system.dart'; import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/build_system/targets/dart.dart'; import 'package:flutter_tools/src/build_system/targets/dart.dart';
import 'package:flutter_tools/src/build_system/targets/icon_tree_shaker.dart'; import 'package:flutter_tools/src/build_system/targets/icon_tree_shaker.dart';
...@@ -31,6 +33,7 @@ import '../../src/mocks.dart'; ...@@ -31,6 +33,7 @@ import '../../src/mocks.dart';
class MockFile extends Mock implements File {} class MockFile extends Mock implements File {}
class MockIMobileDevice extends Mock implements IMobileDevice {} class MockIMobileDevice extends Mock implements IMobileDevice {}
class MockLogger extends Mock implements Logger {}
class MockProcess extends Mock implements Process {} class MockProcess extends Mock implements Process {}
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockXcode extends Mock implements Xcode {} class MockXcode extends Mock implements Xcode {}
...@@ -50,8 +53,20 @@ void main() { ...@@ -50,8 +53,20 @@ void main() {
}); });
group('_IOSSimulatorDevicePortForwarder', () { group('_IOSSimulatorDevicePortForwarder', () {
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() {
mockSimControl = MockSimControl();
mockXcode = MockXcode();
});
testUsingContext('dispose() does not throw an exception', () async { testUsingContext('dispose() does not throw an exception', () async {
final IOSSimulator simulator = IOSSimulator('123'); final IOSSimulator simulator = IOSSimulator(
'123',
simControl: mockSimControl,
xcode: mockXcode,
);
final DevicePortForwarder portForwarder = simulator.portForwarder; final DevicePortForwarder portForwarder = simulator.portForwarder;
await portForwarder.forward(123); await portForwarder.forward(123);
await portForwarder.forward(124); await portForwarder.forward(124);
...@@ -70,9 +85,22 @@ void main() { ...@@ -70,9 +85,22 @@ void main() {
}); });
group('logFilePath', () { group('logFilePath', () {
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() {
mockSimControl = MockSimControl();
mockXcode = MockXcode();
});
testUsingContext('defaults to rooted from HOME', () { testUsingContext('defaults to rooted from HOME', () {
osx.environment['HOME'] = '/foo/bar'; osx.environment['HOME'] = '/foo/bar';
expect(IOSSimulator('123').logFilePath, '/foo/bar/Library/Logs/CoreSimulator/123/system.log'); final IOSSimulator simulator = IOSSimulator(
'123',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(simulator.logFilePath, '/foo/bar/Library/Logs/CoreSimulator/123/system.log');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystemUtils: () => fsUtils, FileSystemUtils: () => fsUtils,
...@@ -83,7 +111,12 @@ void main() { ...@@ -83,7 +111,12 @@ void main() {
testUsingContext('respects IOS_SIMULATOR_LOG_FILE_PATH', () { testUsingContext('respects IOS_SIMULATOR_LOG_FILE_PATH', () {
osx.environment['HOME'] = '/foo/bar'; osx.environment['HOME'] = '/foo/bar';
osx.environment['IOS_SIMULATOR_LOG_FILE_PATH'] = '/baz/qux/%{id}/system.log'; osx.environment['IOS_SIMULATOR_LOG_FILE_PATH'] = '/baz/qux/%{id}/system.log';
expect(IOSSimulator('456').logFilePath, '/baz/qux/456/system.log'); final IOSSimulator simulator = IOSSimulator(
'456',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(simulator.logFilePath, '/baz/qux/456/system.log');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystemUtils: () => fsUtils, FileSystemUtils: () => fsUtils,
...@@ -93,7 +126,7 @@ void main() { ...@@ -93,7 +126,7 @@ void main() {
}); });
group('compareIosVersions', () { group('compareIosVersions', () {
test('compares correctly', () { testWithoutContext('compares correctly', () {
// This list must be sorted in ascending preference order // This list must be sorted in ascending preference order
final List<String> testList = <String>[ final List<String> testList = <String>[
'8', '8.0', '8.1', '8.2', '8', '8.0', '8.1', '8.2',
...@@ -115,7 +148,7 @@ void main() { ...@@ -115,7 +148,7 @@ void main() {
}); });
group('compareIphoneVersions', () { group('compareIphoneVersions', () {
test('compares correctly', () { testWithoutContext('compares correctly', () {
// This list must be sorted in ascending preference order // This list must be sorted in ascending preference order
final List<String> testList = <String>[ final List<String> testList = <String>[
'com.apple.CoreSimulator.SimDeviceType.iPhone-4s', 'com.apple.CoreSimulator.SimDeviceType.iPhone-4s',
...@@ -142,29 +175,69 @@ void main() { ...@@ -142,29 +175,69 @@ void main() {
}); });
group('sdkMajorVersion', () { group('sdkMajorVersion', () {
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() {
mockSimControl = MockSimControl();
mockXcode = MockXcode();
});
// This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta. // This new version string appears in SimulatorApp-850 CoreSimulator-518.16 beta.
test('can be parsed from iOS-11-3', () async { testWithoutContext('can be parsed from iOS-11-3', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-11-3',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(await device.sdkMajorVersion, 11); expect(await device.sdkMajorVersion, 11);
}); });
test('can be parsed from iOS 11.2', () async { testWithoutContext('can be parsed from iOS 11.2', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(await device.sdkMajorVersion, 11); expect(await device.sdkMajorVersion, 11);
}); });
test('Has a simulator category', () async { testWithoutContext('Has a simulator category', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(device.category, Category.mobile); expect(device.category, Category.mobile);
}); });
}); });
group('IOSSimulator.isSupported', () { group('IOSSimulator.isSupported', () {
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() {
mockSimControl = MockSimControl();
mockXcode = MockXcode();
});
testUsingContext('Apple TV is unsupported', () { testUsingContext('Apple TV is unsupported', () {
expect(IOSSimulator('x', name: 'Apple TV').isSupported(), false); final IOSSimulator simulator = IOSSimulator(
'x',
name: 'Apple TV',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(simulator.isSupported(), false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -172,7 +245,12 @@ void main() { ...@@ -172,7 +245,12 @@ void main() {
}); });
testUsingContext('Apple Watch is unsupported', () { testUsingContext('Apple Watch is unsupported', () {
expect(IOSSimulator('x', name: 'Apple Watch').isSupported(), false); expect(IOSSimulator(
'x',
name: 'Apple Watch',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -180,7 +258,12 @@ void main() { ...@@ -180,7 +258,12 @@ void main() {
}); });
testUsingContext('iPad 2 is supported', () { testUsingContext('iPad 2 is supported', () {
expect(IOSSimulator('x', name: 'iPad 2').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPad 2',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -188,7 +271,12 @@ void main() { ...@@ -188,7 +271,12 @@ void main() {
}); });
testUsingContext('iPad Retina is supported', () { testUsingContext('iPad Retina is supported', () {
expect(IOSSimulator('x', name: 'iPad Retina').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPad Retina',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -196,7 +284,12 @@ void main() { ...@@ -196,7 +284,12 @@ void main() {
}); });
testUsingContext('iPhone 5 is supported', () { testUsingContext('iPhone 5 is supported', () {
expect(IOSSimulator('x', name: 'iPhone 5').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPhone 5',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -204,7 +297,12 @@ void main() { ...@@ -204,7 +297,12 @@ void main() {
}); });
testUsingContext('iPhone 5s is supported', () { testUsingContext('iPhone 5s is supported', () {
expect(IOSSimulator('x', name: 'iPhone 5s').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPhone 5s',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -212,7 +310,12 @@ void main() { ...@@ -212,7 +310,12 @@ void main() {
}); });
testUsingContext('iPhone SE is supported', () { testUsingContext('iPhone SE is supported', () {
expect(IOSSimulator('x', name: 'iPhone SE').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPhone SE',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -220,7 +323,12 @@ void main() { ...@@ -220,7 +323,12 @@ void main() {
}); });
testUsingContext('iPhone 7 Plus is supported', () { testUsingContext('iPhone 7 Plus is supported', () {
expect(IOSSimulator('x', name: 'iPhone 7 Plus').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPhone 7 Plus',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -228,7 +336,12 @@ void main() { ...@@ -228,7 +336,12 @@ void main() {
}); });
testUsingContext('iPhone X is supported', () { testUsingContext('iPhone X is supported', () {
expect(IOSSimulator('x', name: 'iPhone X').isSupported(), true); expect(IOSSimulator(
'x',
name: 'iPhone X',
simControl: mockSimControl,
xcode: mockXcode,
).isSupported(), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => osx, Platform: () => osx,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
...@@ -236,14 +349,30 @@ void main() { ...@@ -236,14 +349,30 @@ void main() {
}); });
}); });
group('buildSystem', () {
MockSimControl mockSimControl;
MockXcode mockXcode;
MockBuildSystem mockBuildSystem;
setUp(() {
mockBuildSystem = MockBuildSystem();
mockSimControl = MockSimControl();
mockXcode = MockXcode();
});
testUsingContext('builds with targetPlatform', () async { testUsingContext('builds with targetPlatform', () async {
final IOSSimulator simulator = IOSSimulator('x', name: 'iPhone X'); final IOSSimulator simulator = IOSSimulator(
when(buildSystem.build(any, any)).thenAnswer((Invocation invocation) async { 'x',
name: 'iPhone X',
simControl: mockSimControl,
xcode: mockXcode,
);
when(mockBuildSystem.build(any, any)).thenAnswer((Invocation invocation) async {
return BuildResult(success: true); return BuildResult(success: true);
}); });
await simulator.sideloadUpdatedAssetsForInstalledApplicationBundle(BuildInfo.debug, 'lib/main.dart'); await simulator.sideloadUpdatedAssetsForInstalledApplicationBundle(BuildInfo.debug, 'lib/main.dart');
final VerificationResult result = verify(buildSystem.build(any, captureAny)); final VerificationResult result = verify(mockBuildSystem.build(any, captureAny));
final Environment environment = result.captured.single as Environment; final Environment environment = result.captured.single as Environment;
expect(environment.defines, <String, String>{ expect(environment.defines, <String, String>{
kTargetFile: 'lib/main.dart', kTargetFile: 'lib/main.dart',
...@@ -253,18 +382,23 @@ void main() { ...@@ -253,18 +382,23 @@ void main() {
kIconTreeShakerFlag: null, kIconTreeShakerFlag: null,
}); });
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
BuildSystem: () => MockBuildSystem(), BuildSystem: () => mockBuildSystem,
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
});
group('Simulator screenshot', () { group('Simulator screenshot', () {
MockXcode mockXcode; MockXcode mockXcode;
MockLogger mockLogger;
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
IOSSimulator deviceUnderTest; IOSSimulator deviceUnderTest;
// only used for fs.path.join()
final FileSystem fs = globals.fs;
setUp(() { setUp(() {
mockXcode = MockXcode(); mockXcode = MockXcode();
mockLogger = MockLogger();
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
// Let everything else return exit code 0 so process.dart doesn't crash. // Let everything else return exit code 0 so process.dart doesn't crash.
when( when(
...@@ -272,32 +406,34 @@ void main() { ...@@ -272,32 +406,34 @@ void main() {
).thenAnswer((Invocation invocation) => ).thenAnswer((Invocation invocation) =>
Future<ProcessResult>.value(ProcessResult(2, 0, '', '')) Future<ProcessResult>.value(ProcessResult(2, 0, '', ''))
); );
// Test a real one. Screenshot doesn't require instance states.
final SimControl simControl = SimControl(processManager: mockProcessManager, logger: mockLogger);
// Doesn't matter what the device is. // Doesn't matter what the device is.
deviceUnderTest = IOSSimulator('x', name: 'iPhone SE'); deviceUnderTest = IOSSimulator(
'x',
name: 'iPhone SE',
simControl: simControl,
xcode: mockXcode,
);
}); });
testUsingContext( testWithoutContext(
"old Xcode doesn't support screenshot", "old Xcode doesn't support screenshot",
() { () {
when(mockXcode.majorVersion).thenReturn(7); when(mockXcode.majorVersion).thenReturn(7);
when(mockXcode.minorVersion).thenReturn(1); when(mockXcode.minorVersion).thenReturn(1);
expect(deviceUnderTest.supportsScreenshot, false); expect(deviceUnderTest.supportsScreenshot, false);
}, },
overrides: <Type, Generator>{
Xcode: () => mockXcode,
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
},
); );
testUsingContext( testWithoutContext(
'Xcode 8.2+ supports screenshots', 'Xcode 8.2+ supports screenshots',
() async { () async {
when(mockXcode.majorVersion).thenReturn(8); when(mockXcode.majorVersion).thenReturn(8);
when(mockXcode.minorVersion).thenReturn(2); when(mockXcode.minorVersion).thenReturn(2);
expect(deviceUnderTest.supportsScreenshot, true); expect(deviceUnderTest.supportsScreenshot, true);
final MockFile mockFile = MockFile(); final MockFile mockFile = MockFile();
when(mockFile.path).thenReturn(globals.fs.path.join('some', 'path', 'to', 'screenshot.png')); when(mockFile.path).thenReturn(fs.path.join('some', 'path', 'to', 'screenshot.png'));
await deviceUnderTest.takeScreenshot(mockFile); await deviceUnderTest.takeScreenshot(mockFile);
verify(mockProcessManager.run( verify(mockProcessManager.run(
<String>[ <String>[
...@@ -306,33 +442,36 @@ void main() { ...@@ -306,33 +442,36 @@ void main() {
'io', 'io',
'x', 'x',
'screenshot', 'screenshot',
globals.fs.path.join('some', 'path', 'to', 'screenshot.png'), fs.path.join('some', 'path', 'to', 'screenshot.png'),
], ],
environment: null, environment: null,
workingDirectory: null, workingDirectory: null,
)); ));
}, },
overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
// Test a real one. Screenshot doesn't require instance states.
SimControl: () => SimControl(),
Xcode: () => mockXcode,
FileSystem: () => fileSystem,
},
); );
}); });
group('launchDeviceLogTool', () { group('launchDeviceLogTool', () {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockXcode mockXcode;
MockSimControl mockSimControl;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
when(mockProcessManager.start(any, environment: null, workingDirectory: null)) when(mockProcessManager.start(any, environment: null, workingDirectory: null))
.thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess())); .thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess()));
mockSimControl = MockSimControl();
mockXcode = MockXcode();
}); });
testUsingContext('uses tail on iOS versions prior to iOS 11', () async { testUsingContext('uses tail on iOS versions prior to iOS 11', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 9.3'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 9.3',
simControl: mockSimControl,
xcode: mockXcode,
);
await launchDeviceLogTool(device); await launchDeviceLogTool(device);
expect( expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
...@@ -345,7 +484,13 @@ void main() { ...@@ -345,7 +484,13 @@ void main() {
}); });
testUsingContext('uses /usr/bin/log on iOS 11 and above', () async { testUsingContext('uses /usr/bin/log on iOS 11 and above', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.0'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.0',
simControl: mockSimControl,
xcode: mockXcode,
);
await launchDeviceLogTool(device); await launchDeviceLogTool(device);
expect( expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
...@@ -361,14 +506,25 @@ void main() { ...@@ -361,14 +506,25 @@ void main() {
group('launchSystemLogTool', () { group('launchSystemLogTool', () {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() { setUp(() {
mockSimControl = MockSimControl();
mockXcode = MockXcode();
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
when(mockProcessManager.start(any, environment: null, workingDirectory: null)) when(mockProcessManager.start(any, environment: null, workingDirectory: null))
.thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess())); .thenAnswer((Invocation invocation) => Future<Process>.value(MockProcess()));
}); });
testUsingContext('uses tail on iOS versions prior to iOS 11', () async { testUsingContext('uses tail on iOS versions prior to iOS 11', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 9.3'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 9.3',
simControl: mockSimControl,
xcode: mockXcode,
);
await launchSystemLogTool(device); await launchSystemLogTool(device);
expect( expect(
verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single, verify(mockProcessManager.start(captureAny, environment: null, workingDirectory: null)).captured.single,
...@@ -381,7 +537,13 @@ void main() { ...@@ -381,7 +537,13 @@ void main() {
}); });
testUsingContext('uses /usr/bin/log on iOS 11 and above', () async { testUsingContext('uses /usr/bin/log on iOS 11 and above', () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.0'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.0',
simControl: mockSimControl,
xcode: mockXcode,
);
await launchSystemLogTool(device); await launchSystemLogTool(device);
verifyNever(mockProcessManager.start(any, environment: null, workingDirectory: null)); verifyNever(mockProcessManager.start(any, environment: null, workingDirectory: null));
}, },
...@@ -394,10 +556,14 @@ void main() { ...@@ -394,10 +556,14 @@ void main() {
group('log reader', () { group('log reader', () {
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockIosProject mockIosProject; MockIosProject mockIosProject;
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
mockIosProject = MockIosProject(); mockIosProject = MockIosProject();
mockSimControl = MockSimControl();
mockXcode = MockXcode();
}); });
testUsingContext('simulator can output `)`', () async { testUsingContext('simulator can output `)`', () async {
...@@ -420,7 +586,12 @@ void main() { ...@@ -420,7 +586,12 @@ void main() {
return Future<Process>.value(mockProcess); return Future<Process>.value(mockProcess);
}); });
final IOSSimulator device = IOSSimulator('123456', simulatorCategory: 'iOS 11.0'); final IOSSimulator device = IOSSimulator(
'123456',
simulatorCategory: 'iOS 11.0',
simControl: mockSimControl,
xcode: mockXcode,
);
final DeviceLogReader logReader = device.getLogReader( final DeviceLogReader logReader = device.getLogReader(
app: await BuildableIOSApp.fromProject(mockIosProject), app: await BuildableIOSApp.fromProject(mockIosProject),
); );
...@@ -470,19 +641,28 @@ void main() { ...@@ -470,19 +641,28 @@ void main() {
} }
'''; ''';
MockLogger mockLogger;
MockProcessManager mockProcessManager; MockProcessManager mockProcessManager;
MockXcode mockXcode;
SimControl simControl; SimControl simControl;
const String deviceId = 'smart-phone';
const String appId = 'flutterApp';
setUp(() { setUp(() {
mockLogger = MockLogger();
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
when(mockProcessManager.run(any)).thenAnswer((Invocation _) async { when(mockProcessManager.run(any)).thenAnswer((Invocation _) async {
return ProcessResult(mockPid, 0, validSimControlOutput, ''); return ProcessResult(mockPid, 0, validSimControlOutput, '');
}); });
simControl = SimControl(); simControl = SimControl(
logger: mockLogger,
processManager: mockProcessManager,
);
mockXcode = MockXcode();
}); });
testUsingContext('getDevices succeeds', () async { testWithoutContext('getDevices succeeds', () async {
final List<SimDevice> devices = await simControl.getDevices(); final List<SimDevice> devices = await simControl.getDevices();
final SimDevice watch = devices[0]; final SimDevice watch = devices[0];
...@@ -508,40 +688,82 @@ void main() { ...@@ -508,40 +688,82 @@ void main() {
expect(tv.name, 'Apple TV'); expect(tv.name, 'Apple TV');
expect(tv.udid, 'TEST-TV-UDID'); expect(tv.udid, 'TEST-TV-UDID');
expect(tv.isBooted, isFalse); expect(tv.isBooted, isFalse);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
SimControl: () => simControl,
FileSystem: () => fileSystem,
}); });
testUsingContext('getDevices handles bad simctl output', () async { testWithoutContext('getDevices handles bad simctl output', () async {
when(mockProcessManager.run(any)) when(mockProcessManager.run(any))
.thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', '')); .thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', ''));
final List<SimDevice> devices = await simControl.getDevices(); final List<SimDevice> devices = await simControl.getDevices();
expect(devices, isEmpty); expect(devices, isEmpty);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
SimControl: () => simControl,
FileSystem: () => fileSystem,
}); });
testUsingContext('sdkMajorVersion defaults to 11 when sdkNameAndVersion is junk', () async { testWithoutContext('sdkMajorVersion defaults to 11 when sdkNameAndVersion is junk', () async {
final IOSSimulator iosSimulatorA = IOSSimulator('x', name: 'Testo', simulatorCategory: 'NaN'); final IOSSimulator iosSimulatorA = IOSSimulator(
'x',
name: 'Testo',
simulatorCategory: 'NaN',
simControl: simControl,
xcode: mockXcode,
);
expect(await iosSimulatorA.sdkMajorVersion, 11); expect(await iosSimulatorA.sdkMajorVersion, 11);
}); });
testWithoutContext('.install() handles exceptions', () async {
when(mockProcessManager.run(
<String>['/usr/bin/xcrun', 'simctl', 'install', deviceId, appId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenThrow(const ProcessException('xcrun', <String>[]));
expect(
() async => await simControl.install(deviceId, appId),
throwsToolExit(message: r'Unable to install'),
);
});
testWithoutContext('.uninstall() handles exceptions', () async {
when(mockProcessManager.run(
<String>['/usr/bin/xcrun', 'simctl', 'uninstall', deviceId, appId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenThrow(const ProcessException('xcrun', <String>[]));
expect(
() async => await simControl.uninstall(deviceId, appId),
throwsToolExit(message: r'Unable to uninstall'),
);
});
testWithoutContext('.launch() handles exceptions', () async {
when(mockProcessManager.run(
<String>['/usr/bin/xcrun', 'simctl', 'launch', deviceId, appId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
)).thenThrow(const ProcessException('xcrun', <String>[]));
expect(
() async => await simControl.launch(deviceId, appId),
throwsToolExit(message: r'Unable to launch'),
);
});
}); });
group('startApp', () { group('startApp', () {
SimControl simControl; SimControl simControl;
MockXcode mockXcode;
setUp(() { setUp(() {
simControl = MockSimControl(); simControl = MockSimControl();
mockXcode = MockXcode();
}); });
testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async { testUsingContext("startApp uses compiled app's Info.plist to find CFBundleIdentifier", () async {
final IOSSimulator device = IOSSimulator('x', name: 'iPhone SE', simulatorCategory: 'iOS 11.2'); final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: simControl,
xcode: mockXcode,
);
when(globals.plistParser.getValueFromFile(any, any)).thenReturn('correct'); when(globals.plistParser.getValueFromFile(any, any)).thenReturn('correct');
final Directory mockDir = globals.fs.currentDirectory; final Directory mockDir = globals.fs.currentDirectory;
...@@ -553,14 +775,22 @@ void main() { ...@@ -553,14 +775,22 @@ void main() {
verify(simControl.launch(any, 'correct', any)); verify(simControl.launch(any, 'correct', any));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
SimControl: () => simControl,
PlistParser: () => MockPlistUtils(), PlistParser: () => MockPlistUtils(),
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
}); });
testUsingContext('IOSDevice.isSupportedForProject is true on module project', () async { group('IOSDevice.isSupportedForProject', () {
MockSimControl mockSimControl;
MockXcode mockXcode;
setUp(() {
mockSimControl = MockSimControl();
mockXcode = MockXcode();
});
testUsingContext('is true on module project', () async {
globals.fs.file('pubspec.yaml') globals.fs.file('pubspec.yaml')
..createSync() ..createSync()
..writeAsStringSync(r''' ..writeAsStringSync(r'''
...@@ -572,34 +802,51 @@ flutter: ...@@ -572,34 +802,51 @@ flutter:
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
expect(IOSSimulator('test').isSupportedForProject(flutterProject), true); final IOSSimulator simulator = IOSSimulator(
'test',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(simulator.isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(), FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
testUsingContext('IOSDevice.isSupportedForProject is true with editable host app', () async {
testUsingContext('is true with editable host app', () async {
globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
globals.fs.directory('ios').createSync(); globals.fs.directory('ios').createSync();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
expect(IOSSimulator('test').isSupportedForProject(flutterProject), true); final IOSSimulator simulator = IOSSimulator(
'test',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(simulator.isSupportedForProject(flutterProject), true);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
FileSystem: () => fileSystem,
}); });
testUsingContext('IOSDevice.isSupportedForProject is false with no host app and no module', () async { testUsingContext('is false with no host app and no module', () async {
globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
final FlutterProject flutterProject = FlutterProject.current(); final FlutterProject flutterProject = FlutterProject.current();
expect(IOSSimulator('test').isSupportedForProject(flutterProject), false); final IOSSimulator simulator = IOSSimulator(
'test',
simControl: mockSimControl,
xcode: mockXcode,
);
expect(simulator.isSupportedForProject(flutterProject), false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fileSystem, FileSystem: () => MemoryFileSystem(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
}); });
});
} }
class MockBuildSystem extends Mock implements BuildSystem {} class MockBuildSystem extends Mock implements BuildSystem {}
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