Skip to content

Commit 167655e

Browse files
authored
Only show iOS simulators, reduce output spew in verbose (#108345)
1 parent fe16c20 commit 167655e

7 files changed

Lines changed: 139 additions & 123 deletions

File tree

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ class IOSWorkflow implements Workflow {
2525

2626
// We need xcode (+simctl) to list simulator devices, and libimobiledevice to list real devices.
2727
@override
28-
bool get canListDevices => appliesToHostPlatform && _xcode.isInstalledAndMeetsVersionCheck && _xcode.isSimctlInstalled;
28+
bool get canListDevices => appliesToHostPlatform && _xcode.isSimctlInstalled;
2929

3030
// We need xcode to launch simulator devices, and ios-deploy
3131
// for real devices.

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

Lines changed: 39 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,8 @@ class IOSSimulatorUtils {
7171
return <IOSSimulator>[];
7272
}
7373

74-
final List<SimDevice> connected = await _simControl.getConnectedDevices();
75-
return connected.map<IOSSimulator?>((SimDevice device) {
74+
final List<BootedSimDevice> connected = await _simControl.getConnectedDevices();
75+
return connected.map<IOSSimulator?>((BootedSimDevice device) {
7676
final String? udid = device.udid;
7777
final String? name = device.name;
7878
if (udid == null) {
@@ -109,30 +109,45 @@ class SimControl {
109109

110110
/// Runs `simctl list --json` and returns the JSON of the corresponding
111111
/// [section].
112-
Future<Map<String, Object?>> _list(SimControlListSection section) async {
113-
// Sample output from `simctl list --json`:
112+
Future<Map<String, Object?>> _listBootedDevices() async {
113+
// Sample output from `simctl list available booted --json`:
114114
//
115115
// {
116-
// "devicetypes": { ... },
117-
// "runtimes": { ... },
118116
// "devices" : {
119-
// "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
117+
// "com.apple.CoreSimulator.SimRuntime.iOS-14-0" : [
120118
// {
121-
// "state" : "Shutdown",
122-
// "availability" : " (unavailable, runtime profile not found)",
123-
// "name" : "iPhone 4s",
124-
// "udid" : "1913014C-6DCB-485D-AC6B-7CD76D322F5B"
125-
// },
126-
// ...
127-
// },
128-
// "pairs": { ... },
119+
// "lastBootedAt" : "2022-07-26T01:46:23Z",
120+
// "dataPath" : "\/Users\/magder\/Library\/Developer\/CoreSimulator\/Devices\/9EC90A99-6924-472D-8CDD-4D8234AB4779\/data",
121+
// "dataPathSize" : 1620578304,
122+
// "logPath" : "\/Users\/magder\/Library\/Logs\/CoreSimulator\/9EC90A99-6924-472D-8CDD-4D8234AB4779",
123+
// "udid" : "9EC90A99-6924-472D-8CDD-4D8234AB4779",
124+
// "isAvailable" : true,
125+
// "logPathSize" : 9740288,
126+
// "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11",
127+
// "state" : "Booted",
128+
// "name" : "iPhone 11"
129+
// }
130+
// ],
131+
// "com.apple.CoreSimulator.SimRuntime.iOS-13-0" : [
132+
//
133+
// ],
134+
// "com.apple.CoreSimulator.SimRuntime.iOS-12-4" : [
135+
//
136+
// ],
137+
// "com.apple.CoreSimulator.SimRuntime.iOS-16-0" : [
138+
//
139+
// ]
140+
// }
141+
// }
129142

130143
final List<String> command = <String>[
131144
..._xcode.xcrunCommand(),
132145
'simctl',
133146
'list',
147+
'devices',
148+
'booted',
149+
'iOS',
134150
'--json',
135-
section.name,
136151
];
137152
_logger.printTrace(command.join(' '));
138153
final RunResult results = await _processUtils.run(command);
@@ -141,7 +156,7 @@ class SimControl {
141156
return <String, Map<String, Object?>>{};
142157
}
143158
try {
144-
final Object? decodeResult = (json.decode(results.stdout) as Map<String, Object?>)[section.name];
159+
final Object? decodeResult = (json.decode(results.stdout) as Map<String, Object?>)['devices'];
145160
if (decodeResult is Map<String, Object?>) {
146161
return decodeResult;
147162
}
@@ -156,30 +171,24 @@ class SimControl {
156171
}
157172
}
158173

159-
/// Returns a list of all available devices, both potential and connected.
160-
Future<List<SimDevice>> getDevices() async {
161-
final List<SimDevice> devices = <SimDevice>[];
174+
/// Returns all the connected simulator devices.
175+
Future<List<BootedSimDevice>> getConnectedDevices() async {
176+
final List<BootedSimDevice> devices = <BootedSimDevice>[];
162177

163-
final Map<String, Object?> devicesSection = await _list(SimControlListSection.devices);
178+
final Map<String, Object?> devicesSection = await _listBootedDevices();
164179

165180
for (final String deviceCategory in devicesSection.keys) {
166181
final Object? devicesData = devicesSection[deviceCategory];
167182
if (devicesData != null && devicesData is List<Object?>) {
168183
for (final Map<String, Object?> data in devicesData.map<Map<String, Object?>?>(castStringKeyedMap).whereType<Map<String, Object?>>()) {
169-
devices.add(SimDevice(deviceCategory, data));
184+
devices.add(BootedSimDevice(deviceCategory, data));
170185
}
171186
}
172187
}
173188

174189
return devices;
175190
}
176191

177-
/// Returns all the connected simulator devices.
178-
Future<List<SimDevice>> getConnectedDevices() async {
179-
final List<SimDevice> simDevices = await getDevices();
180-
return simDevices.where((SimDevice device) => device.isBooted).toList();
181-
}
182-
183192
Future<bool> isInstalled(String deviceId, String appId) {
184193
return _processUtils.exitsHappy(<String>[
185194
..._xcode.xcrunCommand(),
@@ -267,54 +276,15 @@ class SimControl {
267276
}
268277
}
269278

270-
/// Enumerates all data sections of `xcrun simctl list --json` command.
271-
class SimControlListSection {
272-
const SimControlListSection._(this.name);
273-
274-
final String name;
275-
276-
static const SimControlListSection devices = SimControlListSection._('devices');
277-
static const SimControlListSection devicetypes = SimControlListSection._('devicetypes');
278-
static const SimControlListSection runtimes = SimControlListSection._('runtimes');
279-
static const SimControlListSection pairs = SimControlListSection._('pairs');
280-
}
281-
282-
/// A simulated device type.
283-
///
284-
/// Simulated device types can be listed using the command
285-
/// `xcrun simctl list devicetypes`.
286-
class SimDeviceType {
287-
SimDeviceType(this.name, this.identifier);
288-
289-
/// The name of the device type.
290-
///
291-
/// Examples:
292-
///
293-
/// "iPhone 6s"
294-
/// "iPhone 6 Plus"
295-
final String name;
296-
297-
/// The identifier of the device type.
298-
///
299-
/// Examples:
300-
///
301-
/// "com.apple.CoreSimulator.SimDeviceType.iPhone-6s"
302-
/// "com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus"
303-
final String identifier;
304-
}
305279

306-
class SimDevice {
307-
SimDevice(this.category, this.data);
280+
class BootedSimDevice {
281+
BootedSimDevice(this.category, this.data);
308282

309283
final String category;
310284
final Map<String, Object?> data;
311285

312-
String? get state => data['state']?.toString();
313-
String? get availability => data['availability']?.toString();
314286
String? get name => data['name']?.toString();
315287
String? get udid => data['udid']?.toString();
316-
317-
bool get isBooted => state == 'Booted';
318288
}
319289

320290
class IOSSimulator extends Device {

packages/flutter_tools/lib/src/macos/xcode.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ class Xcode {
136136
// This command will error if additional components need to be installed in
137137
// xcode 9.2 and above.
138138
final RunResult result = _processUtils.runSync(
139-
<String>[...xcrunCommand(), 'simctl', 'list'],
139+
<String>[...xcrunCommand(), 'simctl', 'list', 'devices', 'booted'],
140140
);
141141
_isSimctlInstalled = result.exitCode == 0;
142142
} on ProcessException {

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

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,18 @@ void main() {
4949
expect(iosWorkflow.canListDevices, false);
5050
});
5151

52-
testWithoutContext('iOS workflow applies on macOS, no Xcode', () {
52+
testWithoutContext('iOS workflow applies on macOS, no Xcode or simctl', () {
53+
final FakeProcessManager xcodeProcessManager = FakeProcessManager.list(<FakeCommand>[
54+
const FakeCommand(
55+
command: <String>[
56+
'xcrun', 'simctl', 'list', 'devices', 'booted',
57+
],
58+
exitCode: 1,
59+
),
60+
]);
5361
final IOSWorkflow iosWorkflow = IOSWorkflow(
5462
platform: FakePlatform(operatingSystem: 'macos'),
55-
xcode: Xcode.test(processManager: FakeProcessManager.any(),
63+
xcode: Xcode.test(processManager: xcodeProcessManager,
5664
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
5765
processManager: FakeProcessManager.any(),
5866
version: null,
@@ -65,9 +73,33 @@ void main() {
6573
expect(iosWorkflow.canLaunchDevices, false);
6674
expect(iosWorkflow.canListDevices, false);
6775
expect(iosWorkflow.canListEmulators, false);
76+
expect(xcodeProcessManager, hasNoRemainingExpectations);
77+
});
78+
79+
testWithoutContext('iOS workflow can list devices even when Xcode version is too low', () {
80+
final Xcode xcode = Xcode.test(
81+
processManager: FakeProcessManager.any(),
82+
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
83+
processManager: FakeProcessManager.any(),
84+
version: Version(1, 0, 0)
85+
),
86+
);
87+
88+
final IOSWorkflow iosWorkflow = IOSWorkflow(
89+
platform: FakePlatform(operatingSystem: 'macos'),
90+
xcode: xcode,
91+
featureFlags: TestFeatureFlags(),
92+
);
93+
94+
// Make sure we're testing the right Xcode state.
95+
// expect(xcode.isInstalledAndMeetsVersionCheck, true);
96+
expect(xcode.isSimctlInstalled, true);
97+
expect(iosWorkflow.canLaunchDevices, false);
98+
expect(iosWorkflow.canListDevices, true);
99+
expect(iosWorkflow.canListEmulators, false);
68100
});
69101

70-
testWithoutContext('iOS workflow can launch and list devices when Xcode is set up', () {
102+
testWithoutContext('iOS workflow can launch devices when Xcode is set up', () {
71103
final Xcode xcode = Xcode.test(
72104
processManager: FakeProcessManager.any(),
73105
xcodeProjectInterpreter: XcodeProjectInterpreter.test(

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

Lines changed: 56 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -724,28 +724,43 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
724724
const String validSimControlOutput = '''
725725
{
726726
"devices" : {
727-
"watchOS 4.3" : [
727+
"com.apple.CoreSimulator.SimRuntime.iOS-14-0" : [
728728
{
729-
"state" : "Shutdown",
730-
"availability" : "(available)",
731-
"name" : "Apple Watch - 38mm",
732-
"udid" : "TEST-WATCH-UDID"
729+
"dataPathSize" : 1734569984,
730+
"udid" : "iPhone 11-UDID",
731+
"isAvailable" : true,
732+
"logPathSize" : 9506816,
733+
"deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11",
734+
"state" : "Booted",
735+
"name" : "iPhone 11"
733736
}
734737
],
735-
"iOS 11.4" : [
738+
"com.apple.CoreSimulator.SimRuntime.iOS-13-0" : [
739+
],
740+
"com.apple.CoreSimulator.SimRuntime.iOS-12-4" : [
741+
],
742+
"com.apple.CoreSimulator.SimRuntime.tvOS-16-0" : [
743+
],
744+
"com.apple.CoreSimulator.SimRuntime.watchOS-9-0" : [
745+
],
746+
"com.apple.CoreSimulator.SimRuntime.iOS-16-0" : [
736747
{
748+
"dataPathSize" : 552366080,
749+
"udid" : "Phone w Watch-UDID",
750+
"isAvailable" : true,
751+
"logPathSize" : 90112,
752+
"deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11",
737753
"state" : "Booted",
738-
"availability" : "(available)",
739-
"name" : "iPhone 5s",
740-
"udid" : "TEST-PHONE-UDID"
741-
}
742-
],
743-
"tvOS 11.4" : [
754+
"name" : "Phone w Watch"
755+
},
744756
{
745-
"state" : "Shutdown",
746-
"availability" : "(available)",
747-
"name" : "Apple TV",
748-
"udid" : "TEST-TV-UDID"
757+
"dataPathSize" : 2186457088,
758+
"udid" : "iPhone 13-UDID",
759+
"isAvailable" : true,
760+
"logPathSize" : 151552,
761+
"deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-13",
762+
"state" : "Booted",
763+
"name" : "iPhone 13"
749764
}
750765
]
751766
}
@@ -768,59 +783,54 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
768783
);
769784
});
770785

771-
testWithoutContext('getDevices succeeds', () async {
786+
testWithoutContext('getConnectedDevices succeeds', () async {
772787
fakeProcessManager.addCommand(const FakeCommand(
773788
command: <String>[
774789
'xcrun',
775790
'simctl',
776791
'list',
777-
'--json',
778792
'devices',
793+
'booted',
794+
'iOS',
795+
'--json',
779796
],
780797
stdout: validSimControlOutput,
781798
));
782799

783-
final List<SimDevice> devices = await simControl.getDevices();
784-
785-
final SimDevice watch = devices[0];
786-
expect(watch.category, 'watchOS 4.3');
787-
expect(watch.state, 'Shutdown');
788-
expect(watch.availability, '(available)');
789-
expect(watch.name, 'Apple Watch - 38mm');
790-
expect(watch.udid, 'TEST-WATCH-UDID');
791-
expect(watch.isBooted, isFalse);
792-
793-
final SimDevice phone = devices[1];
794-
expect(phone.category, 'iOS 11.4');
795-
expect(phone.state, 'Booted');
796-
expect(phone.availability, '(available)');
797-
expect(phone.name, 'iPhone 5s');
798-
expect(phone.udid, 'TEST-PHONE-UDID');
799-
expect(phone.isBooted, isTrue);
800-
801-
final SimDevice tv = devices[2];
802-
expect(tv.category, 'tvOS 11.4');
803-
expect(tv.state, 'Shutdown');
804-
expect(tv.availability, '(available)');
805-
expect(tv.name, 'Apple TV');
806-
expect(tv.udid, 'TEST-TV-UDID');
807-
expect(tv.isBooted, isFalse);
800+
final List<BootedSimDevice> devices = await simControl.getConnectedDevices();
801+
802+
final BootedSimDevice phone1 = devices[0];
803+
expect(phone1.category, 'com.apple.CoreSimulator.SimRuntime.iOS-14-0');
804+
expect(phone1.name, 'iPhone 11');
805+
expect(phone1.udid, 'iPhone 11-UDID');
806+
807+
final BootedSimDevice phone2 = devices[1];
808+
expect(phone2.category, 'com.apple.CoreSimulator.SimRuntime.iOS-16-0');
809+
expect(phone2.name, 'Phone w Watch');
810+
expect(phone2.udid, 'Phone w Watch-UDID');
811+
812+
final BootedSimDevice phone3 = devices[2];
813+
expect(phone3.category, 'com.apple.CoreSimulator.SimRuntime.iOS-16-0');
814+
expect(phone3.name, 'iPhone 13');
815+
expect(phone3.udid, 'iPhone 13-UDID');
808816
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
809817
});
810818

811-
testWithoutContext('getDevices handles bad simctl output', () async {
819+
testWithoutContext('getConnectedDevices handles bad simctl output', () async {
812820
fakeProcessManager.addCommand(const FakeCommand(
813821
command: <String>[
814822
'xcrun',
815823
'simctl',
816824
'list',
817-
'--json',
818825
'devices',
826+
'booted',
827+
'iOS',
828+
'--json',
819829
],
820830
stdout: 'Install Started',
821831
));
822832

823-
final List<SimDevice> devices = await simControl.getDevices();
833+
final List<BootedSimDevice> devices = await simControl.getConnectedDevices();
824834

825835
expect(devices, isEmpty);
826836
expect(fakeProcessManager.hasRemainingExpectations, isFalse);

0 commit comments

Comments
 (0)