Skip to content

Commit 898ab24

Browse files
authored
Check if simctl is installed before trying to list devices or runtimes (#163895)
After flutter/flutter#163785 there was still an unexpected `xcrun simctl` output on a machine with Xcode only half installed flutter/flutter#161655. Check `simctl` is installed before trying to list booted simulator devices or runtimes. Should address flutter/flutter#161655. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
1 parent d0e8fa1 commit 898ab24

File tree

2 files changed

+77
-2
lines changed

2 files changed

+77
-2
lines changed

packages/flutter_tools/lib/src/ios/simulators.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class IOSSimulatorUtils {
6666
final Xcode _xcode;
6767

6868
Future<List<IOSSimulator>> getAttachedDevices() async {
69-
if (!_xcode.isInstalledAndMeetsVersionCheck) {
69+
if (!_xcode.isInstalledAndMeetsVersionCheck || !_xcode.isSimctlInstalled) {
7070
return <IOSSimulator>[];
7171
}
7272

@@ -96,7 +96,7 @@ class IOSSimulatorUtils {
9696
}
9797

9898
Future<List<IOSSimulatorRuntime>> getAvailableIOSRuntimes() async {
99-
if (!_xcode.isInstalledAndMeetsVersionCheck) {
99+
if (!_xcode.isInstalledAndMeetsVersionCheck || !_xcode.isSimctlInstalled) {
100100
return <IOSSimulatorRuntime>[];
101101
}
102102

packages/flutter_tools/test/general.shard/ios/simulators_test.dart

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,16 +894,43 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''',
894894

895895
late FakeProcessManager fakeProcessManager;
896896
Xcode xcode;
897+
Xcode xcodeBadSimctl;
897898
late SimControl simControl;
899+
late IOSSimulatorUtils simulatorUtils;
900+
late IOSSimulatorUtils simulatorUtilsBadSimctl;
898901
late BufferLogger logger;
899902
const String deviceId = 'smart-phone';
900903
const String appId = 'flutterApp';
901904

902905
setUp(() {
903906
fakeProcessManager = FakeProcessManager.empty();
904907
xcode = Xcode.test(processManager: FakeProcessManager.any());
908+
909+
final FakeProcessManager fakeProcessManagerBadSimctl = FakeProcessManager.list(<FakeCommand>[
910+
const FakeCommand(command: <String>['which', 'sysctl']),
911+
const FakeCommand(
912+
command: <String>['sysctl', 'hw.optional.arm64'],
913+
stdout: 'hw.optional.arm64: 0',
914+
),
915+
const FakeCommand(
916+
command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted'],
917+
stderr: 'failed to run',
918+
exitCode: 1,
919+
),
920+
]);
921+
xcodeBadSimctl = Xcode.test(processManager: fakeProcessManagerBadSimctl);
905922
logger = BufferLogger.test();
906923
simControl = SimControl(logger: logger, processManager: fakeProcessManager, xcode: xcode);
924+
simulatorUtils = IOSSimulatorUtils(
925+
logger: logger,
926+
processManager: fakeProcessManager,
927+
xcode: xcode,
928+
);
929+
simulatorUtilsBadSimctl = IOSSimulatorUtils(
930+
logger: logger,
931+
processManager: fakeProcessManager,
932+
xcode: xcodeBadSimctl,
933+
);
907934
});
908935

909936
testWithoutContext('getConnectedDevices succeeds', () async {
@@ -933,6 +960,33 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''',
933960
expect(fakeProcessManager, hasNoRemainingExpectations);
934961
});
935962

963+
testWithoutContext('IOSSimulatorUtils.getAttachedDevices succeeds', () async {
964+
fakeProcessManager.addCommand(
965+
const FakeCommand(
966+
command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted', 'iOS', '--json'],
967+
stdout: validSimControlOutput,
968+
),
969+
);
970+
971+
final List<IOSSimulator> devices = await simulatorUtils.getAttachedDevices();
972+
973+
final IOSSimulator phone1 = devices[0];
974+
expect(phone1.category, Category.mobile);
975+
expect(phone1.name, 'iPhone 11');
976+
expect(phone1.simulatorCategory, 'com.apple.CoreSimulator.SimRuntime.iOS-14-0');
977+
978+
final IOSSimulator phone2 = devices[1];
979+
expect(phone2.category, Category.mobile);
980+
expect(phone2.name, 'Phone w Watch');
981+
expect(phone2.simulatorCategory, 'com.apple.CoreSimulator.SimRuntime.iOS-16-0');
982+
983+
final IOSSimulator phone3 = devices[2];
984+
expect(phone3.category, Category.mobile);
985+
expect(phone3.name, 'iPhone 13');
986+
expect(phone3.simulatorCategory, 'com.apple.CoreSimulator.SimRuntime.iOS-16-0');
987+
expect(fakeProcessManager, hasNoRemainingExpectations);
988+
});
989+
936990
testWithoutContext('getConnectedDevices handles bad simctl output', () async {
937991
fakeProcessManager.addCommand(
938992
const FakeCommand(
@@ -947,6 +1001,16 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''',
9471001
expect(fakeProcessManager, hasNoRemainingExpectations);
9481002
});
9491003

1004+
testWithoutContext(
1005+
'IOSSimulatorUtils.getAttachedDevices handles simctl not properly installed',
1006+
() async {
1007+
final List<IOSSimulator> devices = await simulatorUtilsBadSimctl.getAttachedDevices();
1008+
1009+
expect(devices, isEmpty);
1010+
expect(fakeProcessManager, hasNoRemainingExpectations);
1011+
},
1012+
);
1013+
9501014
testWithoutContext('sdkMajorVersion defaults to 11 when sdkNameAndVersion is junk', () async {
9511015
final IOSSimulator iosSimulatorA = IOSSimulator(
9521016
'x',
@@ -1186,6 +1250,17 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text''',
11861250
expect(logger.errorText, contains('simctl returned non-JSON response:'));
11871251
expect(fakeProcessManager, hasNoRemainingExpectations);
11881252
});
1253+
1254+
testWithoutContext(
1255+
'IOSSimulatorUtils.getAvailableIOSRuntimes handles simctl not properly installed',
1256+
() async {
1257+
final List<IOSSimulatorRuntime> runtimes =
1258+
await simulatorUtilsBadSimctl.getAvailableIOSRuntimes();
1259+
1260+
expect(runtimes, isEmpty);
1261+
expect(fakeProcessManager, hasNoRemainingExpectations);
1262+
},
1263+
);
11891264
});
11901265

11911266
group('startApp', () {

0 commit comments

Comments
 (0)