Use Xcode instruments to list devices (#10801)
Eliminates the dependency on idevice_id from libimobiledevice. Instead, uses Xcode built-in functionality.
This commit is contained in:
@@ -40,7 +40,7 @@ class IOSDevices extends PollingDeviceDiscovery {
|
||||
}
|
||||
|
||||
class IOSDevice extends Device {
|
||||
IOSDevice(String id, { this.name }) : super(id) {
|
||||
IOSDevice(String id, { this.name, String sdkVersion }) : _sdkVersion = sdkVersion, super(id) {
|
||||
_installerPath = _checkForCommand('ideviceinstaller');
|
||||
_iproxyPath = _checkForCommand('iproxy');
|
||||
_pusherPath = _checkForCommand(
|
||||
@@ -55,6 +55,8 @@ class IOSDevice extends Device {
|
||||
String _iproxyPath;
|
||||
String _pusherPath;
|
||||
|
||||
final String _sdkVersion;
|
||||
|
||||
@override
|
||||
bool get supportsHotMode => true;
|
||||
|
||||
@@ -71,14 +73,30 @@ class IOSDevice extends Device {
|
||||
@override
|
||||
bool get supportsStartPaused => false;
|
||||
|
||||
// Physical device line format to be matched:
|
||||
// My iPhone (10.3.2) [75b90e947c5f429fa67f3e9169fda0d89f0492f1]
|
||||
//
|
||||
// Other formats in output (desktop, simulator) to be ignored:
|
||||
// my-mac-pro [2C10513E-4dA5-405C-8EF5-C44353DB3ADD]
|
||||
// iPhone 6s (9.3) [F6CEE7CF-81EB-4448-81B4-1755288C7C11] (Simulator)
|
||||
static final RegExp _deviceRegex = new RegExp(r'^(.*) +\((.*)\) +\[(.*)\]$');
|
||||
|
||||
static List<IOSDevice> getAttachedDevices() {
|
||||
if (!iMobileDevice.isInstalled)
|
||||
if (!xcode.isInstalled)
|
||||
return <IOSDevice>[];
|
||||
|
||||
final List<IOSDevice> devices = <IOSDevice>[];
|
||||
for (String id in iMobileDevice.getAttachedDeviceIDs()) {
|
||||
final String name = iMobileDevice.getInfoForDevice(id, 'DeviceName');
|
||||
devices.add(new IOSDevice(id, name: name));
|
||||
final Iterable<String> deviceLines = xcode.getAvailableDevices()
|
||||
.split('\n')
|
||||
.map((String line) => line.trim());
|
||||
for (String line in deviceLines) {
|
||||
final Match match = _deviceRegex.firstMatch(line);
|
||||
if (match != null) {
|
||||
final String deviceName = match.group(1);
|
||||
final String sdkVersion = match.group(2);
|
||||
final String deviceID = match.group(3);
|
||||
devices.add(new IOSDevice(deviceID, name: deviceName, sdkVersion: sdkVersion));
|
||||
}
|
||||
}
|
||||
return devices;
|
||||
}
|
||||
@@ -311,11 +329,7 @@ class IOSDevice extends Device {
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
|
||||
|
||||
@override
|
||||
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion ($_buildVersion)';
|
||||
|
||||
String get _sdkVersion => iMobileDevice.getInfoForDevice(id, 'ProductVersion');
|
||||
|
||||
String get _buildVersion => iMobileDevice.getInfoForDevice(id, 'BuildVersion');
|
||||
Future<String> get sdkNameAndVersion async => 'iOS $_sdkVersion';
|
||||
|
||||
@override
|
||||
DeviceLogReader getLogReader({ApplicationPackage app}) {
|
||||
|
||||
@@ -70,14 +70,6 @@ class IMobileDevice {
|
||||
return await exitsHappyAsync(<String>['idevicename']);
|
||||
}
|
||||
|
||||
List<String> getAttachedDeviceIDs() {
|
||||
return runSync(<String>['idevice_id', '-l'])
|
||||
.trim()
|
||||
.split('\n')
|
||||
.where((String line) => line.isNotEmpty)
|
||||
.toList();
|
||||
}
|
||||
|
||||
/// Returns the value associated with the specified `ideviceinfo` key for a device.
|
||||
///
|
||||
/// If either the specified key or device does not exist, returns the empty string.
|
||||
@@ -165,6 +157,8 @@ class Xcode {
|
||||
|
||||
return _xcodeVersionCheckValid(_xcodeMajorVersion, _xcodeMinorVersion);
|
||||
}
|
||||
|
||||
String getAvailableDevices() => runSync(<String>['/usr/bin/instruments', '-s', 'devices']);
|
||||
}
|
||||
|
||||
bool _xcodeVersionCheckValid(int major, int minor) {
|
||||
|
||||
@@ -14,7 +14,7 @@ import 'package:test/test.dart';
|
||||
import '../src/context.dart';
|
||||
|
||||
class MockProcessManager extends Mock implements ProcessManager {}
|
||||
class MockIMobileDevice extends Mock implements IMobileDevice {}
|
||||
class MockXcode extends Mock implements Xcode {}
|
||||
class MockFile extends Mock implements File {}
|
||||
|
||||
void main() {
|
||||
@@ -22,38 +22,40 @@ void main() {
|
||||
osx.operatingSystem = 'macos';
|
||||
|
||||
group('getAttachedDevices', () {
|
||||
MockIMobileDevice mockIMobileDevice;
|
||||
MockXcode mockXcode;
|
||||
|
||||
setUp(() {
|
||||
mockIMobileDevice = new MockIMobileDevice();
|
||||
mockXcode = new MockXcode();
|
||||
});
|
||||
|
||||
testUsingContext('return no devices if libimobiledevice is not installed', () async {
|
||||
when(mockIMobileDevice.isInstalled).thenReturn(false);
|
||||
testUsingContext('return no devices if Xcode is not installed', () async {
|
||||
when(mockXcode.isInstalled).thenReturn(false);
|
||||
expect(IOSDevice.getAttachedDevices(), isEmpty);
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Xcode: () => mockXcode,
|
||||
});
|
||||
|
||||
testUsingContext('returns no devices if none are attached', () async {
|
||||
when(mockIMobileDevice.isInstalled).thenReturn(true);
|
||||
when(mockIMobileDevice.getAttachedDeviceIDs()).thenReturn(<String>[]);
|
||||
when(mockXcode.isInstalled).thenReturn(true);
|
||||
when(mockXcode.getAvailableDevices()).thenReturn('');
|
||||
final List<IOSDevice> devices = IOSDevice.getAttachedDevices();
|
||||
expect(devices, isEmpty);
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Xcode: () => mockXcode,
|
||||
});
|
||||
|
||||
testUsingContext('returns attached devices', () async {
|
||||
when(mockIMobileDevice.isInstalled).thenReturn(true);
|
||||
when(mockIMobileDevice.getAttachedDeviceIDs()).thenReturn(<String>[
|
||||
'98206e7a4afd4aedaff06e687594e089dede3c44',
|
||||
'f577a7903cc54959be2e34bc4f7f80b7009efcf4',
|
||||
]);
|
||||
when(mockIMobileDevice.getInfoForDevice('98206e7a4afd4aedaff06e687594e089dede3c44', 'DeviceName'))
|
||||
.thenReturn('La tele me regarde');
|
||||
when(mockIMobileDevice.getInfoForDevice('f577a7903cc54959be2e34bc4f7f80b7009efcf4', 'DeviceName'))
|
||||
.thenReturn('Puits sans fond');
|
||||
when(mockXcode.isInstalled).thenReturn(true);
|
||||
when(mockXcode.getAvailableDevices()).thenReturn('''
|
||||
Known Devices:
|
||||
je-mappelle-horse [ED6552C4-B774-5A4E-8B5A-606710C87C77]
|
||||
La tele me regarde (10.3.2) [98206e7a4afd4aedaff06e687594e089dede3c44]
|
||||
Puits sans fond (10.3.2) [f577a7903cc54959be2e34bc4f7f80b7009efcf4]
|
||||
iPhone 6 Plus (9.3) [FBA880E6-4020-49A5-8083-DCD50CA5FA09] (Simulator)
|
||||
iPhone 6s (11.0) [E805F496-FC6A-4EA4-92FF-B7901FF4E7CC] (Simulator)
|
||||
iPhone 7 (11.0) + Apple Watch Series 2 - 38mm (4.0) [60027FDD-4A7A-42BF-978F-C2209D27AD61] (Simulator)
|
||||
iPhone SE (11.0) [667E8DCD-5DCD-4C80-93A9-60D1D995206F] (Simulator)
|
||||
''');
|
||||
final List<IOSDevice> devices = IOSDevice.getAttachedDevices();
|
||||
expect(devices, hasLength(2));
|
||||
expect(devices[0].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
|
||||
@@ -61,7 +63,7 @@ void main() {
|
||||
expect(devices[1].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
|
||||
expect(devices[1].name, 'Puits sans fond');
|
||||
}, overrides: <Type, Generator>{
|
||||
IMobileDevice: () => mockIMobileDevice,
|
||||
Xcode: () => mockXcode,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user