Unverified Commit 84a7a611 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Launch named iOS simulators (#72323)

parent d2d0c73f
...@@ -142,7 +142,7 @@ class AndroidEmulator extends Emulator { ...@@ -142,7 +142,7 @@ class AndroidEmulator extends Emulator {
Category get category => Category.mobile; Category get category => Category.mobile;
@override @override
PlatformType get platformType => PlatformType.android; String get platformDisplay => PlatformType.android.toString();
String _prop(String name) => _properties != null ? _properties[name] : null; String _prop(String name) => _properties != null ? _properties[name] : null;
......
...@@ -933,7 +933,7 @@ Map<String, dynamic> _emulatorToMap(Emulator emulator) { ...@@ -933,7 +933,7 @@ Map<String, dynamic> _emulatorToMap(Emulator emulator) {
'id': emulator.id, 'id': emulator.id,
'name': emulator.name, 'name': emulator.name,
'category': emulator.category?.toString(), 'category': emulator.category?.toString(),
'platformType': emulator.platformType?.toString(), 'platformType': emulator.platformDisplay,
}; };
} }
......
...@@ -27,7 +27,7 @@ class EmulatorsCommand extends FlutterCommand { ...@@ -27,7 +27,7 @@ class EmulatorsCommand extends FlutterCommand {
final String description = 'List, launch and create emulators.'; final String description = 'List, launch and create emulators.';
@override @override
final List<String> aliases = <String>['emulator']; final List<String> aliases = <String>['emulator', 'simulators', 'simulator'];
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
......
...@@ -614,10 +614,6 @@ abstract class Device { ...@@ -614,10 +614,6 @@ abstract class Device {
/// Check if the device is supported by Flutter. /// Check if the device is supported by Flutter.
bool isSupported(); bool isSupported();
// String meant to be displayed to the user indicating if the device is
// supported by Flutter, and, if not, why.
String supportMessage() => isSupported() ? 'Supported' : 'Unsupported';
/// The device's platform. /// The device's platform.
Future<TargetPlatform> get targetPlatform; Future<TargetPlatform> get targetPlatform;
......
...@@ -252,7 +252,7 @@ abstract class Emulator { ...@@ -252,7 +252,7 @@ abstract class Emulator {
String get name; String get name;
String get manufacturer; String get manufacturer;
Category get category; Category get category;
PlatformType get platformType; String get platformDisplay;
@override @override
int get hashCode => id.hashCode; int get hashCode => id.hashCode;
...@@ -283,7 +283,7 @@ abstract class Emulator { ...@@ -283,7 +283,7 @@ abstract class Emulator {
emulator.id ?? '', emulator.id ?? '',
emulator.name ?? '', emulator.name ?? '',
emulator.manufacturer ?? '', emulator.manufacturer ?? '',
emulator.platformType?.toString() ?? '', emulator.platformDisplay ?? '',
], ],
]; ];
......
...@@ -16,17 +16,26 @@ class IOSEmulators extends EmulatorDiscovery { ...@@ -16,17 +16,26 @@ class IOSEmulators extends EmulatorDiscovery {
bool get canListAnything => globals.iosWorkflow.canListEmulators; bool get canListAnything => globals.iosWorkflow.canListEmulators;
@override @override
Future<List<Emulator>> get emulators async => getEmulators(); Future<List<Emulator>> get emulators async {
final List<IOSSimulator> simulators = await globals.iosSimulatorUtils.getAvailableDevices();
return simulators.map<Emulator>((IOSSimulator device) {
return IOSEmulator(device);
}).toList();
}
@override @override
bool get canLaunchAnything => canListAnything; bool get canLaunchAnything => canListAnything;
} }
class IOSEmulator extends Emulator { class IOSEmulator extends Emulator {
const IOSEmulator(String id) : super(id, true); IOSEmulator(IOSSimulator simulator)
: _simulator = simulator,
super(simulator.id, true);
final IOSSimulator _simulator;
@override @override
String get name => 'iOS Simulator'; String get name => _simulator.name;
@override @override
String get manufacturer => 'Apple'; String get manufacturer => 'Apple';
...@@ -35,43 +44,20 @@ class IOSEmulator extends Emulator { ...@@ -35,43 +44,20 @@ class IOSEmulator extends Emulator {
Category get category => Category.mobile; Category get category => Category.mobile;
@override @override
PlatformType get platformType => PlatformType.ios; String get platformDisplay =>
// com.apple.CoreSimulator.SimRuntime.iOS-10-3 => iOS-10-3
_simulator.simulatorCategory?.split('.')?.last ?? 'ios';
@override @override
Future<void> launch() async { Future<void> launch() async {
Future<bool> launchSimulator(List<String> additionalArgs) async { final RunResult launchResult = await globals.processUtils.run(<String>[
final List<String> args = <String>[ 'open',
'open', '-a',
...additionalArgs, globals.xcode.getSimulatorPath(),
'-a', ]);
globals.xcode.getSimulatorPath(), if (launchResult.exitCode != 0) {
]; globals.printError('$launchResult');
final RunResult launchResult = await globals.processUtils.run(args);
if (launchResult.exitCode != 0) {
globals.printError('$launchResult');
return false;
}
return true;
}
// First run with `-n` to force a device to boot if there isn't already one
if (!await launchSimulator(<String>['-n'])) {
return;
} }
return _simulator.boot();
// Run again to force it to Foreground (using -n doesn't force existing
// devices to the foreground)
await launchSimulator(<String>[]);
} }
} }
/// Return the list of iOS Simulators (there can only be zero or one).
List<IOSEmulator> getEmulators() {
final String simulatorPath = globals.xcode.getSimulatorPath();
if (simulatorPath == null) {
return <IOSEmulator>[];
}
return <IOSEmulator>[const IOSEmulator(iosSimulatorId)];
}
...@@ -66,7 +66,9 @@ class IOSSimulatorUtils { ...@@ -66,7 +66,9 @@ class IOSSimulatorUtils {
return <IOSSimulator>[]; return <IOSSimulator>[];
} }
final List<SimDevice> connected = await _simControl.getConnectedDevices(); final List<SimDevice> connected = (await _simControl.getAvailableDevices())
.where((SimDevice device) => device.isBooted)
.toList();
return connected.map<IOSSimulator>((SimDevice device) { return connected.map<IOSSimulator>((SimDevice device) {
return IOSSimulator( return IOSSimulator(
device.udid, device.udid,
...@@ -77,6 +79,26 @@ class IOSSimulatorUtils { ...@@ -77,6 +79,26 @@ class IOSSimulatorUtils {
); );
}).toList(); }).toList();
} }
Future<List<IOSSimulator>> getAvailableDevices() async {
if (!_xcode.isInstalledAndMeetsVersionCheck) {
return <IOSSimulator>[];
}
final List<SimDevice> available = await _simControl.getAvailableDevices();
return available
.map<IOSSimulator>((SimDevice device) {
return IOSSimulator(
device.udid,
name: device.name,
simControl: _simControl,
simulatorCategory: device.category,
xcode: _xcode,
);
})
.where((IOSSimulator simulator) => simulator.isSupported())
.toList();
}
} }
/// A wrapper around the `simctl` command line tool. /// A wrapper around the `simctl` command line tool.
...@@ -89,6 +111,23 @@ class SimControl { ...@@ -89,6 +111,23 @@ class SimControl {
_xcode = xcode, _xcode = xcode,
_processUtils = ProcessUtils(processManager: processManager, logger: logger); _processUtils = ProcessUtils(processManager: processManager, logger: logger);
/// Create a [SimControl] for testing.
///
/// Defaults to a buffer logger.
@visibleForTesting
factory SimControl.test({
@required ProcessManager processManager,
Logger logger,
Xcode xcode,
}) {
logger ??= BufferLogger.test();
return SimControl(
logger: logger,
xcode: xcode,
processManager: processManager,
);
}
final Logger _logger; final Logger _logger;
final ProcessUtils _processUtils; final ProcessUtils _processUtils;
final Xcode _xcode; final Xcode _xcode;
...@@ -160,10 +199,10 @@ class SimControl { ...@@ -160,10 +199,10 @@ class SimControl {
return devices; return devices;
} }
/// Returns all the connected simulator devices. /// Returns all the available simulator devices.
Future<List<SimDevice>> getConnectedDevices() async { Future<List<SimDevice>> getAvailableDevices() async {
final List<SimDevice> simDevices = await getDevices(); final List<SimDevice> simDevices = await getDevices();
return simDevices.where((SimDevice device) => device.isBooted).toList(); return simDevices.where((SimDevice device) => device.isAvailable).toList();
} }
Future<bool> isInstalled(String deviceId, String appId) { Future<bool> isInstalled(String deviceId, String appId) {
...@@ -234,6 +273,17 @@ class SimControl { ...@@ -234,6 +273,17 @@ class SimControl {
return result; return result;
} }
Future<RunResult> boot(String deviceId) {
return _processUtils.run(
<String>[
..._xcode.xcrunCommand(),
'simctl',
'boot',
deviceId,
],
);
}
Future<void> takeScreenshot(String deviceId, String outputPath) async { Future<void> takeScreenshot(String deviceId, String outputPath) async {
try { try {
await _processUtils.run( await _processUtils.run(
...@@ -296,7 +346,11 @@ class SimDevice { ...@@ -296,7 +346,11 @@ class SimDevice {
final Map<String, dynamic> data; final Map<String, dynamic> data;
String get state => data['state']?.toString(); String get state => data['state']?.toString();
String get availability => data['availability']?.toString();
bool get isAvailable =>
data['isAvailable'] == true ||
data['availability']?.toString() == '(available)';
String get name => data['name']?.toString(); String get name => data['name']?.toString();
String get udid => data['udid']?.toString(); String get udid => data['udid']?.toString();
...@@ -394,7 +448,6 @@ class IOSSimulator extends Device { ...@@ -394,7 +448,6 @@ class IOSSimulator extends Device {
@override @override
bool isSupported() { bool isSupported() {
if (!globals.platform.isMacOS) { if (!globals.platform.isMacOS) {
_supportMessage = 'iOS devices require a Mac host machine.';
return false; return false;
} }
...@@ -402,21 +455,25 @@ class IOSSimulator extends Device { ...@@ -402,21 +455,25 @@ class IOSSimulator extends Device {
// We do not yet support WatchOS or tvOS devices. // We do not yet support WatchOS or tvOS devices.
final RegExp blocklist = RegExp(r'Apple (TV|Watch)', caseSensitive: false); final RegExp blocklist = RegExp(r'Apple (TV|Watch)', caseSensitive: false);
if (blocklist.hasMatch(name)) { if (blocklist.hasMatch(name)) {
_supportMessage = 'Flutter does not support Apple TV or Apple Watch.';
return false; return false;
} }
return true; return true;
} }
String _supportMessage; Future<bool> boot() async {
final RunResult result = await _simControl.boot(id);
@override if (result.exitCode == 0) {
String supportMessage() { return true;
if (isSupported()) { }
return 'Supported'; // 149 exit code means the device is already booted. Ignore this error.
if (result.exitCode == 149) {
globals.printTrace('Simulator "$id" already booted.');
return true;
} }
return _supportMessage ?? 'Unknown'; globals.logger.printError('$result');
return false;
} }
@override @override
......
...@@ -71,7 +71,7 @@ void main() { ...@@ -71,7 +71,7 @@ void main() {
expect(emulator.name, displayName); expect(emulator.name, displayName);
expect(emulator.manufacturer, manufacturer); expect(emulator.manufacturer, manufacturer);
expect(emulator.category, Category.mobile); expect(emulator.category, Category.mobile);
expect(emulator.platformType, PlatformType.android); expect(emulator.platformDisplay, 'android');
}); });
testWithoutContext('prefers displayname for name', () { testWithoutContext('prefers displayname for name', () {
......
...@@ -2,15 +2,13 @@ ...@@ -2,15 +2,13 @@
// 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 'dart:convert';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_workflow.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/base/logger.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/emulator.dart'; import 'package:flutter_tools/src/emulator.dart';
import 'package:flutter_tools/src/ios/ios_emulators.dart'; import 'package:flutter_tools/src/ios/ios_emulators.dart';
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:flutter_tools/src/macos/xcode.dart'; import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
...@@ -45,14 +43,10 @@ const FakeCommand kListEmulatorsCommand = FakeCommand( ...@@ -45,14 +43,10 @@ const FakeCommand kListEmulatorsCommand = FakeCommand(
); );
void main() { void main() {
MockProcessManager mockProcessManager;
MockAndroidSdk mockSdk; MockAndroidSdk mockSdk;
MockXcode mockXcode;
setUp(() { setUp(() {
mockProcessManager = MockProcessManager();
mockSdk = MockAndroidSdk(); mockSdk = MockAndroidSdk();
mockXcode = MockXcode();
when(mockSdk.avdManagerPath).thenReturn('avdmanager'); when(mockSdk.avdManagerPath).thenReturn('avdmanager');
when(mockSdk.getAvdManagerPath()).thenReturn('avdmanager'); when(mockSdk.getAvdManagerPath()).thenReturn('avdmanager');
...@@ -299,24 +293,53 @@ void main() { ...@@ -299,24 +293,53 @@ void main() {
}); });
group('ios_emulators', () { group('ios_emulators', () {
bool didAttemptToRunSimulator = false; MockXcode mockXcode;
FakeProcessManager fakeProcessManager;
setUp(() { setUp(() {
when(mockXcode.xcodeSelectPath).thenReturn('/fake/Xcode.app/Contents/Developer'); fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
when(mockXcode.getSimulatorPath()).thenAnswer((_) => '/fake/simulator.app'); mockXcode = MockXcode();
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
final List<String> args = invocation.positionalArguments[0] as List<String>; when(mockXcode.getSimulatorPath())
if (args.length >= 3 && args[0] == 'open' && args[1] == '-a' && args[2] == '/fake/simulator.app') { .thenAnswer((_) => '/fake/simulator.app');
didAttemptToRunSimulator = true;
}
return ProcessResult(101, 0, '', '');
});
}); });
testUsingContext('runs correct launch commands', () async { testUsingContext('runs correct launch commands', () async {
const Emulator emulator = IOSEmulator('ios'); fakeProcessManager.addCommands(<FakeCommand>[
const FakeCommand(command: <String>[
'open',
'-a',
'/fake/simulator.app',
]),
const FakeCommand(command: <String>[
'xcrun',
'simctl',
'boot',
'1234',
]),
]);
final SimControl simControl = SimControl.test(
processManager: fakeProcessManager,
xcode: mockXcode,
);
final IOSSimulator simulator = IOSSimulator(
'1234',
name: 'iPhone 12',
simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-3',
simControl: simControl,
xcode: mockXcode,
);
final IOSEmulator emulator = IOSEmulator(simulator);
expect(emulator.id, '1234');
expect(emulator.name, 'iPhone 12');
expect(emulator.category, Category.mobile);
expect(emulator.platformDisplay, 'iOS-14-3');
await emulator.launch(); await emulator.launch();
expect(didAttemptToRunSimulator, equals(true)); expect(fakeProcessManager.hasRemainingExpectations, false);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager, ProcessManager: () => fakeProcessManager,
Xcode: () => mockXcode, Xcode: () => mockXcode,
}); });
}); });
...@@ -347,35 +370,11 @@ class FakeEmulator extends Emulator { ...@@ -347,35 +370,11 @@ class FakeEmulator extends Emulator {
Category get category => Category.mobile; Category get category => Category.mobile;
@override @override
PlatformType get platformType => PlatformType.android; String get platformDisplay => PlatformType.android.toString();
@override @override
Future<void> launch() { Future<void> launch() {
throw UnimplementedError('Not implemented in Mock'); throw UnimplementedError('Not implemented in Mock');
} }
} }
class MockProcessManager extends Mock implements ProcessManager {
@override
ProcessResult runSync(
List<dynamic> command, {
String workingDirectory,
Map<String, String> environment,
bool includeParentEnvironment = true,
bool runInShell = false,
Encoding stdoutEncoding = systemEncoding,
Encoding stderrEncoding = systemEncoding,
}) {
final String program = command[0] as String;
final List<String> args = command.sublist(1) as List<String>;
switch (program) {
case '/usr/bin/xcode-select':
throw ProcessException(program, args);
break;
}
throw StateError('Unexpected process call: $command');
}
}
class MockXcode extends Mock implements Xcode {} class MockXcode extends Mock implements Xcode {}
...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.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/devfs.dart'; import 'package:flutter_tools/src/devfs.dart';
...@@ -785,12 +786,19 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ...@@ -785,12 +786,19 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
"availability" : "(available)", "availability" : "(available)",
"name" : "iPhone 5s", "name" : "iPhone 5s",
"udid" : "TEST-PHONE-UDID" "udid" : "TEST-PHONE-UDID"
},
{
"state" : "Shutdown",
"isAvailable" : false,
"name" : "iPhone 11",
"udid" : "TEST-PHONE-UNAVAILABLE-UDID",
"availabilityError" : "runtime profile not found"
} }
], ],
"tvOS 11.4" : [ "tvOS 11.4" : [
{ {
"state" : "Shutdown", "state" : "Shutdown",
"availability" : "(available)", "isAvailable" : true,
"name" : "Apple TV", "name" : "Apple TV",
"udid" : "TEST-TV-UDID" "udid" : "TEST-TV-UDID"
} }
...@@ -824,11 +832,12 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ...@@ -824,11 +832,12 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
testWithoutContext('getDevices succeeds', () async { testWithoutContext('getDevices succeeds', () async {
final List<SimDevice> devices = await simControl.getDevices(); final List<SimDevice> devices = await simControl.getDevices();
expect(devices.length, 4);
final SimDevice watch = devices[0]; final SimDevice watch = devices[0];
expect(watch.category, 'watchOS 4.3'); expect(watch.category, 'watchOS 4.3');
expect(watch.state, 'Shutdown'); expect(watch.state, 'Shutdown');
expect(watch.availability, '(available)'); expect(watch.isAvailable, true);
expect(watch.name, 'Apple Watch - 38mm'); expect(watch.name, 'Apple Watch - 38mm');
expect(watch.udid, 'TEST-WATCH-UDID'); expect(watch.udid, 'TEST-WATCH-UDID');
expect(watch.isBooted, isFalse); expect(watch.isBooted, isFalse);
...@@ -836,20 +845,33 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ...@@ -836,20 +845,33 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
final SimDevice phone = devices[1]; final SimDevice phone = devices[1];
expect(phone.category, 'iOS 11.4'); expect(phone.category, 'iOS 11.4');
expect(phone.state, 'Booted'); expect(phone.state, 'Booted');
expect(phone.availability, '(available)'); expect(phone.isAvailable, true);
expect(phone.name, 'iPhone 5s'); expect(phone.name, 'iPhone 5s');
expect(phone.udid, 'TEST-PHONE-UDID'); expect(phone.udid, 'TEST-PHONE-UDID');
expect(phone.isBooted, isTrue); expect(phone.isBooted, isTrue);
final SimDevice tv = devices[2]; final SimDevice unavailablePhone = devices[2];
expect(unavailablePhone.category, 'iOS 11.4');
expect(unavailablePhone.state, 'Shutdown');
expect(unavailablePhone.isAvailable, isFalse);
expect(unavailablePhone.name, 'iPhone 11');
expect(unavailablePhone.udid, 'TEST-PHONE-UNAVAILABLE-UDID');
expect(unavailablePhone.isBooted, isFalse);
final SimDevice tv = devices[3];
expect(tv.category, 'tvOS 11.4'); expect(tv.category, 'tvOS 11.4');
expect(tv.state, 'Shutdown'); expect(tv.state, 'Shutdown');
expect(tv.availability, '(available)'); expect(tv.isAvailable, true);
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);
}); });
testWithoutContext('getAvailableDevices succeeds', () async {
final List<SimDevice> devices = await simControl.getAvailableDevices();
expect(devices.length, 3);
});
testWithoutContext('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', ''));
...@@ -905,6 +927,78 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''' ...@@ -905,6 +927,78 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
throwsToolExit(message: r'Unable to launch'), throwsToolExit(message: r'Unable to launch'),
); );
}); });
testWithoutContext('.boot() calls the right command', () async {
await simControl.boot(deviceId);
verify(mockProcessManager.run(
<String>['xcrun', 'simctl', 'boot', deviceId],
environment: anyNamed('environment'),
workingDirectory: anyNamed('workingDirectory'),
));
});
});
group('boot', () {
SimControl simControl;
MockXcode mockXcode;
setUp(() {
simControl = MockSimControl();
mockXcode = MockXcode();
when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
});
testUsingContext('success', () async {
final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: simControl,
xcode: mockXcode,
);
when(simControl.boot(any)).thenAnswer((_) async =>
RunResult(ProcessResult(0, 0, '', ''), <String>['simctl']));
expect(await device.boot(), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('already booted', () async {
final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: simControl,
xcode: mockXcode,
);
// 149 means the device is already booted.
when(simControl.boot(any)).thenAnswer((_) async =>
RunResult(ProcessResult(0, 149, '', ''), <String>['simctl']));
expect(await device.boot(), isTrue);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
testUsingContext('failed', () async {
final IOSSimulator device = IOSSimulator(
'x',
name: 'iPhone SE',
simulatorCategory: 'iOS 11.2',
simControl: simControl,
xcode: mockXcode,
);
when(simControl.boot(any)).thenAnswer((_) async =>
RunResult(ProcessResult(0, 1, '', ''), <String>['simctl']));
expect(await device.boot(), isFalse);
}, overrides: <Type, Generator>{
FileSystem: () => fileSystem,
ProcessManager: () => FakeProcessManager.any(),
});
}); });
group('startApp', () { group('startApp', () {
......
...@@ -114,6 +114,7 @@ void testUsingContext( ...@@ -114,6 +114,7 @@ void testUsingContext(
IOSSimulatorUtils: () { IOSSimulatorUtils: () {
final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils(); final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]); when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
when(mock.getAvailableDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
return mock; return mock;
}, },
OutputPreferences: () => OutputPreferences.test(), OutputPreferences: () => OutputPreferences.test(),
...@@ -286,7 +287,7 @@ class FakeDoctor extends Doctor { ...@@ -286,7 +287,7 @@ class FakeDoctor extends Doctor {
class MockSimControl extends Mock implements SimControl { class MockSimControl extends Mock implements SimControl {
MockSimControl() { MockSimControl() {
when(getConnectedDevices()).thenAnswer((Invocation _) async => <SimDevice>[]); when(getAvailableDevices()).thenAnswer((Invocation _) async => <SimDevice>[]);
} }
} }
......
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