Unverified Commit 21858253 authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Catch FormatException caused by bad simctl output (#37958)

parent 78cca625
...@@ -46,11 +46,12 @@ class IOSSimulatorUtils { ...@@ -46,11 +46,12 @@ class IOSSimulatorUtils {
/// Returns [IOSSimulatorUtils] active in the current app context (i.e. zone). /// Returns [IOSSimulatorUtils] active in the current app context (i.e. zone).
static IOSSimulatorUtils get instance => context.get<IOSSimulatorUtils>(); static IOSSimulatorUtils get instance => context.get<IOSSimulatorUtils>();
List<IOSSimulator> getAttachedDevices() { Future<List<IOSSimulator>> getAttachedDevices() async {
if (!xcode.isInstalledAndMeetsVersionCheck) if (!xcode.isInstalledAndMeetsVersionCheck)
return <IOSSimulator>[]; return <IOSSimulator>[];
return SimControl.instance.getConnectedDevices().map<IOSSimulator>((SimDevice device) { final List<SimDevice> connected = await SimControl.instance.getConnectedDevices();
return connected.map<IOSSimulator>((SimDevice device) {
return IOSSimulator(device.udid, name: device.name, simulatorCategory: device.category); return IOSSimulator(device.udid, name: device.name, simulatorCategory: device.category);
}).toList(); }).toList();
} }
...@@ -63,7 +64,7 @@ class SimControl { ...@@ -63,7 +64,7 @@ class SimControl {
/// Runs `simctl list --json` and returns the JSON of the corresponding /// Runs `simctl list --json` and returns the JSON of the corresponding
/// [section]. /// [section].
Map<String, dynamic> _list(SimControlListSection section) { Future<Map<String, dynamic>> _list(SimControlListSection section) async {
// Sample output from `simctl list --json`: // Sample output from `simctl list --json`:
// //
// { // {
...@@ -83,20 +84,27 @@ class SimControl { ...@@ -83,20 +84,27 @@ class SimControl {
final List<String> command = <String>[_xcrunPath, 'simctl', 'list', '--json', section.name]; final List<String> command = <String>[_xcrunPath, 'simctl', 'list', '--json', section.name];
printTrace(command.join(' ')); printTrace(command.join(' '));
final ProcessResult results = processManager.runSync(command); final ProcessResult results = await processManager.run(command);
if (results.exitCode != 0) { if (results.exitCode != 0) {
printError('Error executing simctl: ${results.exitCode}\n${results.stderr}'); printError('Error executing simctl: ${results.exitCode}\n${results.stderr}');
return <String, Map<String, dynamic>>{}; return <String, Map<String, dynamic>>{};
} }
try {
return json.decode(results.stdout)[section.name]; return json.decode(results.stdout)[section.name];
} on FormatException {
// We failed to parse the simctl output, or it returned junk.
// One known message is "Install Started" isn't valid JSON but is
// returned sometimes.
printError('simctl returned non-JSON response: ${results.stdout}');
return <String, dynamic>{};
}
} }
/// Returns a list of all available devices, both potential and connected. /// Returns a list of all available devices, both potential and connected.
List<SimDevice> getDevices() { Future<List<SimDevice>> getDevices() async {
final List<SimDevice> devices = <SimDevice>[]; final List<SimDevice> devices = <SimDevice>[];
final Map<String, dynamic> devicesSection = _list(SimControlListSection.devices); final Map<String, dynamic> devicesSection = await _list(SimControlListSection.devices);
for (String deviceCategory in devicesSection.keys) { for (String deviceCategory in devicesSection.keys) {
final List<dynamic> devicesData = devicesSection[deviceCategory]; final List<dynamic> devicesData = devicesSection[deviceCategory];
...@@ -109,8 +117,9 @@ class SimControl { ...@@ -109,8 +117,9 @@ class SimControl {
} }
/// Returns all the connected simulator devices. /// Returns all the connected simulator devices.
List<SimDevice> getConnectedDevices() { Future<List<SimDevice>> getConnectedDevices() async {
return getDevices().where((SimDevice device) => device.isBooted).toList(); final List<SimDevice> simDevices = await getDevices();
return simDevices.where((SimDevice device) => device.isBooted).toList();
} }
Future<bool> isInstalled(String deviceId, String appId) { Future<bool> isInstalled(String deviceId, String appId) {
......
...@@ -390,14 +390,15 @@ void main() { ...@@ -390,14 +390,15 @@ void main() {
setUp(() { setUp(() {
mockProcessManager = MockProcessManager(); mockProcessManager = MockProcessManager();
when(mockProcessManager.runSync(any)) when(mockProcessManager.run(any)).thenAnswer((Invocation _) async {
.thenReturn(ProcessResult(mockPid, 0, validSimControlOutput, '')); return ProcessResult(mockPid, 0, validSimControlOutput, '');
});
simControl = SimControl(); simControl = SimControl();
}); });
testUsingContext('getDevices succeeds', () { testUsingContext('getDevices succeeds', () async {
final List<SimDevice> devices = simControl.getDevices(); final List<SimDevice> devices = await simControl.getDevices();
final SimDevice watch = devices[0]; final SimDevice watch = devices[0];
expect(watch.category, 'watchOS 4.3'); expect(watch.category, 'watchOS 4.3');
...@@ -426,6 +427,17 @@ void main() { ...@@ -426,6 +427,17 @@ void main() {
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
SimControl: () => simControl, SimControl: () => simControl,
}); });
testUsingContext('getDevices handles bad simctl output', () async {
when(mockProcessManager.run(any))
.thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', ''));
final List<SimDevice> devices = await simControl.getDevices();
expect(devices, isEmpty);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
SimControl: () => simControl,
});
}); });
group('startApp', () { group('startApp', () {
......
...@@ -77,7 +77,7 @@ void testUsingContext( ...@@ -77,7 +77,7 @@ void testUsingContext(
HttpClient: () => MockHttpClient(), HttpClient: () => MockHttpClient(),
IOSSimulatorUtils: () { IOSSimulatorUtils: () {
final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils(); final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenReturn(<IOSSimulator>[]); when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
return mock; return mock;
}, },
OutputPreferences: () => OutputPreferences(showColor: false), OutputPreferences: () => OutputPreferences(showColor: false),
...@@ -227,7 +227,7 @@ class FakeDoctor extends Doctor { ...@@ -227,7 +227,7 @@ class FakeDoctor extends Doctor {
class MockSimControl extends Mock implements SimControl { class MockSimControl extends Mock implements SimControl {
MockSimControl() { MockSimControl() {
when(getConnectedDevices()).thenReturn(<SimDevice>[]); when(getConnectedDevices()).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