Only show iOS simulators, reduce output spew in verbose (#108345)
This commit is contained in:
parent
fe16c206ea
commit
167655e8a7
@ -25,7 +25,7 @@ class IOSWorkflow implements Workflow {
|
||||
|
||||
// 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.
|
||||
|
@ -71,8 +71,8 @@ class IOSSimulatorUtils {
|
||||
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 @@ class SimControl {
|
||||
|
||||
/// 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 @@ class SimControl {
|
||||
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 @@ class SimControl {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 @@ class SimControl {
|
||||
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 @@ class SimControl {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
|
@ -136,7 +136,7 @@ class Xcode {
|
||||
// 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 {
|
||||
|
@ -49,10 +49,18 @@ void main() {
|
||||
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 @@ void main() {
|
||||
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(
|
||||
|
@ -724,28 +724,43 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
|
||||
const String validSimControlOutput = '''
|
||||
{
|
||||
"devices" : {
|
||||
"watchOS 4.3" : [
|
||||
{
|
||||
"state" : "Shutdown",
|
||||
"availability" : "(available)",
|
||||
"name" : "Apple Watch - 38mm",
|
||||
"udid" : "TEST-WATCH-UDID"
|
||||
}
|
||||
],
|
||||
"iOS 11.4" : [
|
||||
"com.apple.CoreSimulator.SimRuntime.iOS-14-0" : [
|
||||
{
|
||||
"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 @@ Dec 20 17:04:32 md32-11-vm1 Another App[88374]: Ignore this text'''
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
|
@ -59,6 +59,8 @@ void main() {
|
||||
'xcrun',
|
||||
'simctl',
|
||||
'list',
|
||||
'devices',
|
||||
'booted',
|
||||
],
|
||||
),
|
||||
);
|
||||
@ -78,6 +80,8 @@ void main() {
|
||||
'xcrun',
|
||||
'simctl',
|
||||
'list',
|
||||
'devices',
|
||||
'booted',
|
||||
],
|
||||
exitCode: 1,
|
||||
),
|
||||
|
@ -98,7 +98,7 @@ void main() {
|
||||
'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 @@ void main() {
|
||||
command: <String>['xcrun', 'clang'],
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['xcrun', 'simctl', 'list'],
|
||||
command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted'],
|
||||
exitCode: 1,
|
||||
),
|
||||
]);
|
||||
@ -173,7 +173,7 @@ void main() {
|
||||
command: <String>['xcrun', 'clang'],
|
||||
),
|
||||
const FakeCommand(
|
||||
command: <String>['xcrun', 'simctl', 'list'],
|
||||
command: <String>['xcrun', 'simctl', 'list', 'devices', 'booted'],
|
||||
),
|
||||
]);
|
||||
final Xcode xcode = Xcode.test(
|
||||
|
Loading…
x
Reference in New Issue
Block a user