Only show iOS simulators, reduce output spew in verbose (#108345)
diff --git a/packages/flutter_tools/lib/src/ios/ios_workflow.dart b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
index a8d8f03..a656fd4 100644
--- a/packages/flutter_tools/lib/src/ios/ios_workflow.dart
+++ b/packages/flutter_tools/lib/src/ios/ios_workflow.dart
@@ -25,7 +25,7 @@
// We need xcode (+simctl) to list simulator devices, and libimobiledevice to list real devices.
@override
- bool get canListDevices => appliesToHostPlatform && _xcode.isInstalledAndMeetsVersionCheck && _xcode.isSimctlInstalled;
+ bool get canListDevices => appliesToHostPlatform && _xcode.isSimctlInstalled;
// We need xcode to launch simulator devices, and ios-deploy
// for real devices.
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 5484510..4bcfc9e 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -71,8 +71,8 @@
return <IOSSimulator>[];
}
- final List<SimDevice> connected = await _simControl.getConnectedDevices();
- return connected.map<IOSSimulator?>((SimDevice device) {
+ final List<BootedSimDevice> connected = await _simControl.getConnectedDevices();
+ return connected.map<IOSSimulator?>((BootedSimDevice device) {
final String? udid = device.udid;
final String? name = device.name;
if (udid == null) {
@@ -109,30 +109,45 @@
/// Runs `simctl list --json` and returns the JSON of the corresponding
/// [section].
- Future<Map<String, Object?>> _list(SimControlListSection section) async {
- // Sample output from `simctl list --json`:
+ Future<Map<String, Object?>> _listBootedDevices() async {
+ // Sample output from `simctl list available booted --json`:
//
// {
- // "devicetypes": { ... },
- // "runtimes": { ... },
// "devices" : {
- // "com.apple.CoreSimulator.SimRuntime.iOS-8-2" : [
+ // "com.apple.CoreSimulator.SimRuntime.iOS-14-0" : [
// {
- // "state" : "Shutdown",
- // "availability" : " (unavailable, runtime profile not found)",
- // "name" : "iPhone 4s",
- // "udid" : "1913014C-6DCB-485D-AC6B-7CD76D322F5B"
- // },
- // ...
- // },
- // "pairs": { ... },
+ // "lastBootedAt" : "2022-07-26T01:46:23Z",
+ // "dataPath" : "\/Users\/magder\/Library\/Developer\/CoreSimulator\/Devices\/9EC90A99-6924-472D-8CDD-4D8234AB4779\/data",
+ // "dataPathSize" : 1620578304,
+ // "logPath" : "\/Users\/magder\/Library\/Logs\/CoreSimulator\/9EC90A99-6924-472D-8CDD-4D8234AB4779",
+ // "udid" : "9EC90A99-6924-472D-8CDD-4D8234AB4779",
+ // "isAvailable" : true,
+ // "logPathSize" : 9740288,
+ // "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11",
+ // "state" : "Booted",
+ // "name" : "iPhone 11"
+ // }
+ // ],
+ // "com.apple.CoreSimulator.SimRuntime.iOS-13-0" : [
+ //
+ // ],
+ // "com.apple.CoreSimulator.SimRuntime.iOS-12-4" : [
+ //
+ // ],
+ // "com.apple.CoreSimulator.SimRuntime.iOS-16-0" : [
+ //
+ // ]
+ // }
+ // }
final List<String> command = <String>[
..._xcode.xcrunCommand(),
'simctl',
'list',
+ 'devices',
+ 'booted',
+ 'iOS',
'--json',
- section.name,
];
_logger.printTrace(command.join(' '));
final RunResult results = await _processUtils.run(command);
@@ -141,7 +156,7 @@
return <String, Map<String, Object?>>{};
}
try {
- final Object? decodeResult = (json.decode(results.stdout) as Map<String, Object?>)[section.name];
+ final Object? decodeResult = (json.decode(results.stdout) as Map<String, Object?>)['devices'];
if (decodeResult is Map<String, Object?>) {
return decodeResult;
}
@@ -156,17 +171,17 @@
}
}
- /// Returns a list of all available devices, both potential and connected.
- Future<List<SimDevice>> getDevices() async {
- final List<SimDevice> devices = <SimDevice>[];
+ /// Returns all the connected simulator devices.
+ Future<List<BootedSimDevice>> getConnectedDevices() async {
+ final List<BootedSimDevice> devices = <BootedSimDevice>[];
- final Map<String, Object?> devicesSection = await _list(SimControlListSection.devices);
+ final Map<String, Object?> devicesSection = await _listBootedDevices();
for (final String deviceCategory in devicesSection.keys) {
final Object? devicesData = devicesSection[deviceCategory];
if (devicesData != null && devicesData is List<Object?>) {
for (final Map<String, Object?> data in devicesData.map<Map<String, Object?>?>(castStringKeyedMap).whereType<Map<String, Object?>>()) {
- devices.add(SimDevice(deviceCategory, data));
+ devices.add(BootedSimDevice(deviceCategory, data));
}
}
}
@@ -174,12 +189,6 @@
return devices;
}
- /// Returns all the connected simulator devices.
- Future<List<SimDevice>> getConnectedDevices() async {
- final List<SimDevice> simDevices = await getDevices();
- return simDevices.where((SimDevice device) => device.isBooted).toList();
- }
-
Future<bool> isInstalled(String deviceId, String appId) {
return _processUtils.exitsHappy(<String>[
..._xcode.xcrunCommand(),
@@ -267,54 +276,15 @@
}
}
-/// Enumerates all data sections of `xcrun simctl list --json` command.
-class SimControlListSection {
- const SimControlListSection._(this.name);
- final String name;
-
- static const SimControlListSection devices = SimControlListSection._('devices');
- static const SimControlListSection devicetypes = SimControlListSection._('devicetypes');
- static const SimControlListSection runtimes = SimControlListSection._('runtimes');
- static const SimControlListSection pairs = SimControlListSection._('pairs');
-}
-
-/// A simulated device type.
-///
-/// Simulated device types can be listed using the command
-/// `xcrun simctl list devicetypes`.
-class SimDeviceType {
- SimDeviceType(this.name, this.identifier);
-
- /// The name of the device type.
- ///
- /// Examples:
- ///
- /// "iPhone 6s"
- /// "iPhone 6 Plus"
- final String name;
-
- /// The identifier of the device type.
- ///
- /// Examples:
- ///
- /// "com.apple.CoreSimulator.SimDeviceType.iPhone-6s"
- /// "com.apple.CoreSimulator.SimDeviceType.iPhone-6-Plus"
- final String identifier;
-}
-
-class SimDevice {
- SimDevice(this.category, this.data);
+class BootedSimDevice {
+ BootedSimDevice(this.category, this.data);
final String category;
final Map<String, Object?> data;
- String? get state => data['state']?.toString();
- String? get availability => data['availability']?.toString();
String? get name => data['name']?.toString();
String? get udid => data['udid']?.toString();
-
- bool get isBooted => state == 'Booted';
}
class IOSSimulator extends Device {
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart
index d92fd26..fa7212f 100644
--- a/packages/flutter_tools/lib/src/macos/xcode.dart
+++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -136,7 +136,7 @@
// This command will error if additional components need to be installed in
// xcode 9.2 and above.
final RunResult result = _processUtils.runSync(
- <String>[...xcrunCommand(), 'simctl', 'list'],
+ <String>[...xcrunCommand(), 'simctl', 'list', 'devices', 'booted'],
);
_isSimctlInstalled = result.exitCode == 0;
} on ProcessException {
diff --git a/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart b/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart
index 688b360..85b6f29 100644
--- a/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/ios_workflow_test.dart
@@ -49,10 +49,18 @@
expect(iosWorkflow.canListDevices, false);
});
- testWithoutContext('iOS workflow applies on macOS, no Xcode', () {
+ testWithoutContext('iOS workflow applies on macOS, no Xcode or simctl', () {
+ final FakeProcessManager xcodeProcessManager = FakeProcessManager.list(<FakeCommand>[
+ const FakeCommand(
+ command: <String>[
+ 'xcrun', 'simctl', 'list', 'devices', 'booted',
+ ],
+ exitCode: 1,
+ ),
+ ]);
final IOSWorkflow iosWorkflow = IOSWorkflow(
platform: FakePlatform(operatingSystem: 'macos'),
- xcode: Xcode.test(processManager: FakeProcessManager.any(),
+ xcode: Xcode.test(processManager: xcodeProcessManager,
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
processManager: FakeProcessManager.any(),
version: null,
@@ -65,9 +73,33 @@
expect(iosWorkflow.canLaunchDevices, false);
expect(iosWorkflow.canListDevices, false);
expect(iosWorkflow.canListEmulators, false);
+ expect(xcodeProcessManager, hasNoRemainingExpectations);
});
- testWithoutContext('iOS workflow can launch and list devices when Xcode is set up', () {
+ testWithoutContext('iOS workflow can list devices even when Xcode version is too low', () {
+ final Xcode xcode = Xcode.test(
+ processManager: FakeProcessManager.any(),
+ xcodeProjectInterpreter: XcodeProjectInterpreter.test(
+ processManager: FakeProcessManager.any(),
+ version: Version(1, 0, 0)
+ ),
+ );
+
+ final IOSWorkflow iosWorkflow = IOSWorkflow(
+ platform: FakePlatform(operatingSystem: 'macos'),
+ xcode: xcode,
+ featureFlags: TestFeatureFlags(),
+ );
+
+ // Make sure we're testing the right Xcode state.
+ // expect(xcode.isInstalledAndMeetsVersionCheck, true);
+ expect(xcode.isSimctlInstalled, true);
+ expect(iosWorkflow.canLaunchDevices, false);
+ expect(iosWorkflow.canListDevices, true);
+ expect(iosWorkflow.canListEmulators, false);
+ });
+
+ testWithoutContext('iOS workflow can launch devices when Xcode is set up', () {
final Xcode xcode = Xcode.test(
processManager: FakeProcessManager.any(),
xcodeProjectInterpreter: XcodeProjectInterpreter.test(
diff --git a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
index 2177afa..f7d8d8e 100644
--- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
@@ -724,28 +724,43 @@
const String validSimControlOutput = '''
{
"devices" : {
- "watchOS 4.3" : [
+ "com.apple.CoreSimulator.SimRuntime.iOS-14-0" : [
{
- "state" : "Shutdown",
- "availability" : "(available)",
- "name" : "Apple Watch - 38mm",
- "udid" : "TEST-WATCH-UDID"
- }
- ],
- "iOS 11.4" : [
- {
+ "dataPathSize" : 1734569984,
+ "udid" : "iPhone 11-UDID",
+ "isAvailable" : true,
+ "logPathSize" : 9506816,
+ "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11",
"state" : "Booted",
- "availability" : "(available)",
- "name" : "iPhone 5s",
- "udid" : "TEST-PHONE-UDID"
+ "name" : "iPhone 11"
}
],
- "tvOS 11.4" : [
+ "com.apple.CoreSimulator.SimRuntime.iOS-13-0" : [
+ ],
+ "com.apple.CoreSimulator.SimRuntime.iOS-12-4" : [
+ ],
+ "com.apple.CoreSimulator.SimRuntime.tvOS-16-0" : [
+ ],
+ "com.apple.CoreSimulator.SimRuntime.watchOS-9-0" : [
+ ],
+ "com.apple.CoreSimulator.SimRuntime.iOS-16-0" : [
{
- "state" : "Shutdown",
- "availability" : "(available)",
- "name" : "Apple TV",
- "udid" : "TEST-TV-UDID"
+ "dataPathSize" : 552366080,
+ "udid" : "Phone w Watch-UDID",
+ "isAvailable" : true,
+ "logPathSize" : 90112,
+ "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-11",
+ "state" : "Booted",
+ "name" : "Phone w Watch"
+ },
+ {
+ "dataPathSize" : 2186457088,
+ "udid" : "iPhone 13-UDID",
+ "isAvailable" : true,
+ "logPathSize" : 151552,
+ "deviceTypeIdentifier" : "com.apple.CoreSimulator.SimDeviceType.iPhone-13",
+ "state" : "Booted",
+ "name" : "iPhone 13"
}
]
}
@@ -768,59 +783,54 @@
);
});
- testWithoutContext('getDevices succeeds', () async {
+ testWithoutContext('getConnectedDevices succeeds', () async {
fakeProcessManager.addCommand(const FakeCommand(
command: <String>[
'xcrun',
'simctl',
'list',
- '--json',
'devices',
+ 'booted',
+ 'iOS',
+ '--json',
],
stdout: validSimControlOutput,
));
- final List<SimDevice> devices = await simControl.getDevices();
+ final List<BootedSimDevice> devices = await simControl.getConnectedDevices();
- final SimDevice watch = devices[0];
- expect(watch.category, 'watchOS 4.3');
- expect(watch.state, 'Shutdown');
- expect(watch.availability, '(available)');
- expect(watch.name, 'Apple Watch - 38mm');
- expect(watch.udid, 'TEST-WATCH-UDID');
- expect(watch.isBooted, isFalse);
+ final BootedSimDevice phone1 = devices[0];
+ expect(phone1.category, 'com.apple.CoreSimulator.SimRuntime.iOS-14-0');
+ expect(phone1.name, 'iPhone 11');
+ expect(phone1.udid, 'iPhone 11-UDID');
- final SimDevice phone = devices[1];
- expect(phone.category, 'iOS 11.4');
- expect(phone.state, 'Booted');
- expect(phone.availability, '(available)');
- expect(phone.name, 'iPhone 5s');
- expect(phone.udid, 'TEST-PHONE-UDID');
- expect(phone.isBooted, isTrue);
+ final BootedSimDevice phone2 = devices[1];
+ expect(phone2.category, 'com.apple.CoreSimulator.SimRuntime.iOS-16-0');
+ expect(phone2.name, 'Phone w Watch');
+ expect(phone2.udid, 'Phone w Watch-UDID');
- final SimDevice tv = devices[2];
- expect(tv.category, 'tvOS 11.4');
- expect(tv.state, 'Shutdown');
- expect(tv.availability, '(available)');
- expect(tv.name, 'Apple TV');
- expect(tv.udid, 'TEST-TV-UDID');
- expect(tv.isBooted, isFalse);
+ final BootedSimDevice phone3 = devices[2];
+ expect(phone3.category, 'com.apple.CoreSimulator.SimRuntime.iOS-16-0');
+ expect(phone3.name, 'iPhone 13');
+ expect(phone3.udid, 'iPhone 13-UDID');
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
});
- testWithoutContext('getDevices handles bad simctl output', () async {
+ testWithoutContext('getConnectedDevices handles bad simctl output', () async {
fakeProcessManager.addCommand(const FakeCommand(
command: <String>[
'xcrun',
'simctl',
'list',
- '--json',
'devices',
+ 'booted',
+ 'iOS',
+ '--json',
],
stdout: 'Install Started',
));
- final List<SimDevice> devices = await simControl.getDevices();
+ final List<BootedSimDevice> devices = await simControl.getConnectedDevices();
expect(devices, isEmpty);
expect(fakeProcessManager.hasRemainingExpectations, isFalse);
diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
index 3969151..d4473e1 100644
--- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart
@@ -59,6 +59,8 @@
'xcrun',
'simctl',
'list',
+ 'devices',
+ 'booted',
],
),
);
@@ -78,6 +80,8 @@
'xcrun',
'simctl',
'list',
+ 'devices',
+ 'booted',
],
exitCode: 1,
),
diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart
index 9ddd7e7..cf540c5 100644
--- a/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart
+++ b/packages/flutter_tools/test/general.shard/macos/xcode_validator_test.dart
@@ -98,7 +98,7 @@
'Xcode EULA has not been accepted.\nLaunch Xcode and accept the license.',
),
const FakeCommand(
- command: <String>['xcrun', 'simctl', 'list'],
+ command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted'],
),
]);
final Xcode xcode = Xcode.test(
@@ -135,7 +135,7 @@
command: <String>['xcrun', 'clang'],
),
const FakeCommand(
- command: <String>['xcrun', 'simctl', 'list'],
+ command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted'],
exitCode: 1,
),
]);
@@ -173,7 +173,7 @@
command: <String>['xcrun', 'clang'],
),
const FakeCommand(
- command: <String>['xcrun', 'simctl', 'list'],
+ command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted'],
),
]);
final Xcode xcode = Xcode.test(