modernize iOS device lookup in driver (#10780)

This commit is contained in:
Yegor 2017-06-16 12:58:23 -07:00 committed by GitHub
parent b474557ef4
commit 3b6d84b083
3 changed files with 25 additions and 210 deletions

View file

@ -4,11 +4,9 @@
import 'dart:async';
import '../android/android_device.dart' show AndroidDevice;
import '../application_package.dart';
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/platform.dart';
import '../base/process.dart';
import '../build_info.dart';
import '../cache.dart';
@ -16,7 +14,6 @@ import '../dart/package_map.dart';
import '../dart/sdk.dart';
import '../device.dart';
import '../globals.dart';
import '../ios/simulators.dart' show SimControl, IOSSimulatorUtils;
import '../resident_runner.dart';
import 'run.dart';
@ -198,56 +195,15 @@ Future<Device> findTargetDevice() async {
return devices.first;
}
if (platform.isMacOS) {
// On Mac we look for the iOS Simulator. If available, we use that. Then
// we look for an Android device. If there's one, we use that. Otherwise,
// we launch a new iOS Simulator.
Device reusableDevice;
for (Device device in devices) {
if (await device.isLocalEmulator) {
reusableDevice = device;
break;
}
}
if (reusableDevice == null) {
for (Device device in devices) {
if (device is AndroidDevice) {
reusableDevice = device;
break;
}
}
}
if (reusableDevice != null) {
printStatus('Found connected ${await reusableDevice.isLocalEmulator ? "emulator" : "device"} "${reusableDevice.name}"; will reuse it.');
return reusableDevice;
}
// No running emulator found. Attempt to start one.
printStatus('Starting iOS Simulator, because did not find existing connected devices.');
final bool started = await SimControl.instance.boot();
if (started) {
return IOSSimulatorUtils.instance.getAttachedDevices().first;
} else {
printError('Failed to start iOS Simulator.');
return null;
}
} else if (platform.isLinux || platform.isWindows) {
// On Linux and Windows, for now, we just grab the first connected device we can find.
if (devices.isEmpty) {
printError('No devices found.');
return null;
} else if (devices.length > 1) {
printStatus('Found multiple connected devices:');
printStatus(devices.map((Device d) => ' - ${d.name}\n').join(''));
}
printStatus('Using device ${devices.first.name}.');
return devices.first;
} else {
printError('The operating system on this computer is not supported.');
if (devices.isEmpty) {
printError('No devices found.');
return null;
} else if (devices.length > 1) {
printStatus('Found multiple connected devices:');
printStatus(devices.map((Device d) => ' - ${d.name}\n').join(''));
}
printStatus('Using device ${devices.first.name}.');
return devices.first;
}
/// Starts the application on the device given command configuration.

View file

@ -59,117 +59,6 @@ class SimControl {
/// Returns [SimControl] active in the current app context (i.e. zone).
static SimControl get instance => context[SimControl];
Future<bool> boot({ String deviceName }) async {
if (_isAnyConnected())
return true;
if (deviceName == null) {
final SimDevice testDevice = _createTestDevice();
if (testDevice == null) {
return false;
}
deviceName = testDevice.name;
}
// `xcrun instruments` requires a template (-t). @yjbanov has no idea what
// "template" is but the built-in 'Blank' seems to work. -l causes xcrun to
// quit after a time limit without killing the simulator. We quit after
// 1 second.
final List<String> args = <String>[_xcrunPath, 'instruments', '-w', deviceName, '-t', 'Blank', '-l', '1'];
printTrace(args.join(' '));
runDetached(args);
printStatus('Waiting for iOS Simulator to boot...');
bool connected = false;
int attempted = 0;
while (!connected && attempted < 20) {
connected = _isAnyConnected();
if (!connected) {
printStatus('Still waiting for iOS Simulator to boot...');
await new Future<Null>.delayed(const Duration(seconds: 1));
}
attempted++;
}
if (connected) {
printStatus('Connected to iOS Simulator.');
return true;
} else {
printStatus('Timed out waiting for iOS Simulator to boot.');
return false;
}
}
SimDevice _createTestDevice() {
final SimDeviceType deviceType = _findSuitableDeviceType();
if (deviceType == null)
return null;
final String runtime = _findSuitableRuntime();
if (runtime == null)
return null;
// Delete any old test devices
getDevices()
.where((SimDevice d) => d.name.endsWith(_kFlutterTestDeviceSuffix))
.forEach(_deleteDevice);
// Create new device
final String deviceName = '${deviceType.name} $_kFlutterTestDeviceSuffix';
final List<String> args = <String>[_xcrunPath, 'simctl', 'create', deviceName, deviceType.identifier, runtime];
printTrace(args.join(' '));
runCheckedSync(args);
return getDevices().firstWhere((SimDevice d) => d.name == deviceName);
}
SimDeviceType _findSuitableDeviceType() {
final List<Map<String, dynamic>> allTypes = _list(SimControlListSection.devicetypes);
final List<Map<String, dynamic>> usableTypes = allTypes
.where((Map<String, dynamic> info) => info['name'].startsWith('iPhone'))
.toList()
..sort((Map<String, dynamic> r1, Map<String, dynamic> r2) => -compareIphoneVersions(r1['identifier'], r2['identifier']));
if (usableTypes.isEmpty) {
printError(
'No suitable device type found.\n'
'You may launch an iOS Simulator manually and Flutter will attempt to use it.'
);
}
return new SimDeviceType(
usableTypes.first['name'],
usableTypes.first['identifier']
);
}
String _findSuitableRuntime() {
final List<Map<String, dynamic>> allRuntimes = _list(SimControlListSection.runtimes);
final List<Map<String, dynamic>> usableRuntimes = allRuntimes
.where((Map<String, dynamic> info) => info['name'].startsWith('iOS'))
.toList()
..sort((Map<String, dynamic> r1, Map<String, dynamic> r2) => -compareIosVersions(r1['version'], r2['version']));
if (usableRuntimes.isEmpty) {
printError(
'No suitable iOS runtime found.\n'
'You may launch an iOS Simulator manually and Flutter will attempt to use it.'
);
}
return usableRuntimes.first['identifier'];
}
void _deleteDevice(SimDevice device) {
try {
final List<String> args = <String>[_xcrunPath, 'simctl', 'delete', device.name];
printTrace(args.join(' '));
runCheckedSync(args);
} catch(e) {
printError(e);
}
}
/// Runs `simctl list --json` and returns the JSON of the corresponding
/// [section].
///
@ -226,8 +115,6 @@ class SimControl {
return getDevices().where((SimDevice device) => device.isBooted).toList();
}
bool _isAnyConnected() => getConnectedDevices().isNotEmpty;
Future<bool> isInstalled(String deviceId, String appId) {
return exitsHappyAsync(<String>[
_xcrunPath,

View file

@ -13,7 +13,6 @@ import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/drive.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:mockito/mockito.dart';
import 'package:test/test.dart';
@ -238,52 +237,7 @@ void main() {
});
});
group('findTargetDevice on iOS', () {
Platform macOsPlatform() => new FakePlatform(operatingSystem: 'macos');
testUsingContext('uses existing emulator', () async {
withMockDevice();
when(mockDevice.name).thenReturn('mock-simulator');
when(mockDevice.isLocalEmulator).thenReturn(new Future<bool>.value(true));
final Device device = await findTargetDevice();
expect(device.name, 'mock-simulator');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: macOsPlatform,
});
testUsingContext('uses existing Android device if and there are no simulators', () async {
mockDevice = new MockAndroidDevice();
when(mockDevice.name).thenReturn('mock-android-device');
when(mockDevice.isLocalEmulator).thenReturn(new Future<bool>.value(false));
withMockDevice(mockDevice);
final Device device = await findTargetDevice();
expect(device.name, 'mock-android-device');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: macOsPlatform,
});
testUsingContext('launches emulator', () async {
when(SimControl.instance.boot()).thenReturn(true);
final Device emulator = new MockDevice();
when(emulator.name).thenReturn('new-simulator');
when(IOSSimulatorUtils.instance.getAttachedDevices())
.thenReturn(<Device>[emulator]);
final Device device = await findTargetDevice();
expect(device.name, 'new-simulator');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: macOsPlatform,
});
});
void findTargetDeviceOnOperatingSystem(String operatingSystem) {
assert(operatingSystem == 'windows' || operatingSystem == 'linux');
Platform platform() => new FakePlatform(operatingSystem: operatingSystem);
testUsingContext('returns null if no devices found', () async {
@ -313,6 +267,24 @@ void main() {
group('findTargetDevice on Windows', () {
findTargetDeviceOnOperatingSystem('windows');
});
group('findTargetDevice on macOS', () {
findTargetDeviceOnOperatingSystem('macos');
Platform macOsPlatform() => new FakePlatform(operatingSystem: 'macos');
testUsingContext('uses existing simulator', () async {
withMockDevice();
when(mockDevice.name).thenReturn('mock-simulator');
when(mockDevice.isLocalEmulator).thenReturn(new Future<bool>.value(true));
final Device device = await findTargetDevice();
expect(device.name, 'mock-simulator');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
Platform: macOsPlatform,
});
});
});
}