Skip to content

Commit 3b6d84b

Browse files
authored
modernize iOS device lookup in driver (flutter#10780)
1 parent b474557 commit 3b6d84b

File tree

3 files changed

+25
-210
lines changed

3 files changed

+25
-210
lines changed

packages/flutter_tools/lib/src/commands/drive.dart

Lines changed: 7 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,16 @@
44

55
import 'dart:async';
66

7-
import '../android/android_device.dart' show AndroidDevice;
87
import '../application_package.dart';
98
import '../base/common.dart';
109
import '../base/file_system.dart';
11-
import '../base/platform.dart';
1210
import '../base/process.dart';
1311
import '../build_info.dart';
1412
import '../cache.dart';
1513
import '../dart/package_map.dart';
1614
import '../dart/sdk.dart';
1715
import '../device.dart';
1816
import '../globals.dart';
19-
import '../ios/simulators.dart' show SimControl, IOSSimulatorUtils;
2017
import '../resident_runner.dart';
2118
import 'run.dart';
2219

@@ -198,56 +195,15 @@ Future<Device> findTargetDevice() async {
198195
return devices.first;
199196
}
200197

201-
202-
if (platform.isMacOS) {
203-
// On Mac we look for the iOS Simulator. If available, we use that. Then
204-
// we look for an Android device. If there's one, we use that. Otherwise,
205-
// we launch a new iOS Simulator.
206-
Device reusableDevice;
207-
for (Device device in devices) {
208-
if (await device.isLocalEmulator) {
209-
reusableDevice = device;
210-
break;
211-
}
212-
}
213-
if (reusableDevice == null) {
214-
for (Device device in devices) {
215-
if (device is AndroidDevice) {
216-
reusableDevice = device;
217-
break;
218-
}
219-
}
220-
}
221-
222-
if (reusableDevice != null) {
223-
printStatus('Found connected ${await reusableDevice.isLocalEmulator ? "emulator" : "device"} "${reusableDevice.name}"; will reuse it.');
224-
return reusableDevice;
225-
}
226-
227-
// No running emulator found. Attempt to start one.
228-
printStatus('Starting iOS Simulator, because did not find existing connected devices.');
229-
final bool started = await SimControl.instance.boot();
230-
if (started) {
231-
return IOSSimulatorUtils.instance.getAttachedDevices().first;
232-
} else {
233-
printError('Failed to start iOS Simulator.');
234-
return null;
235-
}
236-
} else if (platform.isLinux || platform.isWindows) {
237-
// On Linux and Windows, for now, we just grab the first connected device we can find.
238-
if (devices.isEmpty) {
239-
printError('No devices found.');
240-
return null;
241-
} else if (devices.length > 1) {
242-
printStatus('Found multiple connected devices:');
243-
printStatus(devices.map((Device d) => ' - ${d.name}\n').join(''));
244-
}
245-
printStatus('Using device ${devices.first.name}.');
246-
return devices.first;
247-
} else {
248-
printError('The operating system on this computer is not supported.');
198+
if (devices.isEmpty) {
199+
printError('No devices found.');
249200
return null;
201+
} else if (devices.length > 1) {
202+
printStatus('Found multiple connected devices:');
203+
printStatus(devices.map((Device d) => ' - ${d.name}\n').join(''));
250204
}
205+
printStatus('Using device ${devices.first.name}.');
206+
return devices.first;
251207
}
252208

253209
/// Starts the application on the device given command configuration.

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

Lines changed: 0 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -59,117 +59,6 @@ class SimControl {
5959
/// Returns [SimControl] active in the current app context (i.e. zone).
6060
static SimControl get instance => context[SimControl];
6161

62-
Future<bool> boot({ String deviceName }) async {
63-
if (_isAnyConnected())
64-
return true;
65-
66-
if (deviceName == null) {
67-
final SimDevice testDevice = _createTestDevice();
68-
if (testDevice == null) {
69-
return false;
70-
}
71-
deviceName = testDevice.name;
72-
}
73-
74-
// `xcrun instruments` requires a template (-t). @yjbanov has no idea what
75-
// "template" is but the built-in 'Blank' seems to work. -l causes xcrun to
76-
// quit after a time limit without killing the simulator. We quit after
77-
// 1 second.
78-
final List<String> args = <String>[_xcrunPath, 'instruments', '-w', deviceName, '-t', 'Blank', '-l', '1'];
79-
printTrace(args.join(' '));
80-
runDetached(args);
81-
printStatus('Waiting for iOS Simulator to boot...');
82-
83-
bool connected = false;
84-
int attempted = 0;
85-
while (!connected && attempted < 20) {
86-
connected = _isAnyConnected();
87-
if (!connected) {
88-
printStatus('Still waiting for iOS Simulator to boot...');
89-
await new Future<Null>.delayed(const Duration(seconds: 1));
90-
}
91-
attempted++;
92-
}
93-
94-
if (connected) {
95-
printStatus('Connected to iOS Simulator.');
96-
return true;
97-
} else {
98-
printStatus('Timed out waiting for iOS Simulator to boot.');
99-
return false;
100-
}
101-
}
102-
103-
SimDevice _createTestDevice() {
104-
final SimDeviceType deviceType = _findSuitableDeviceType();
105-
if (deviceType == null)
106-
return null;
107-
108-
final String runtime = _findSuitableRuntime();
109-
if (runtime == null)
110-
return null;
111-
112-
// Delete any old test devices
113-
getDevices()
114-
.where((SimDevice d) => d.name.endsWith(_kFlutterTestDeviceSuffix))
115-
.forEach(_deleteDevice);
116-
117-
// Create new device
118-
final String deviceName = '${deviceType.name} $_kFlutterTestDeviceSuffix';
119-
final List<String> args = <String>[_xcrunPath, 'simctl', 'create', deviceName, deviceType.identifier, runtime];
120-
printTrace(args.join(' '));
121-
runCheckedSync(args);
122-
123-
return getDevices().firstWhere((SimDevice d) => d.name == deviceName);
124-
}
125-
126-
SimDeviceType _findSuitableDeviceType() {
127-
final List<Map<String, dynamic>> allTypes = _list(SimControlListSection.devicetypes);
128-
final List<Map<String, dynamic>> usableTypes = allTypes
129-
.where((Map<String, dynamic> info) => info['name'].startsWith('iPhone'))
130-
.toList()
131-
..sort((Map<String, dynamic> r1, Map<String, dynamic> r2) => -compareIphoneVersions(r1['identifier'], r2['identifier']));
132-
133-
if (usableTypes.isEmpty) {
134-
printError(
135-
'No suitable device type found.\n'
136-
'You may launch an iOS Simulator manually and Flutter will attempt to use it.'
137-
);
138-
}
139-
140-
return new SimDeviceType(
141-
usableTypes.first['name'],
142-
usableTypes.first['identifier']
143-
);
144-
}
145-
146-
String _findSuitableRuntime() {
147-
final List<Map<String, dynamic>> allRuntimes = _list(SimControlListSection.runtimes);
148-
final List<Map<String, dynamic>> usableRuntimes = allRuntimes
149-
.where((Map<String, dynamic> info) => info['name'].startsWith('iOS'))
150-
.toList()
151-
..sort((Map<String, dynamic> r1, Map<String, dynamic> r2) => -compareIosVersions(r1['version'], r2['version']));
152-
153-
if (usableRuntimes.isEmpty) {
154-
printError(
155-
'No suitable iOS runtime found.\n'
156-
'You may launch an iOS Simulator manually and Flutter will attempt to use it.'
157-
);
158-
}
159-
160-
return usableRuntimes.first['identifier'];
161-
}
162-
163-
void _deleteDevice(SimDevice device) {
164-
try {
165-
final List<String> args = <String>[_xcrunPath, 'simctl', 'delete', device.name];
166-
printTrace(args.join(' '));
167-
runCheckedSync(args);
168-
} catch(e) {
169-
printError(e);
170-
}
171-
}
172-
17362
/// Runs `simctl list --json` and returns the JSON of the corresponding
17463
/// [section].
17564
///
@@ -226,8 +115,6 @@ class SimControl {
226115
return getDevices().where((SimDevice device) => device.isBooted).toList();
227116
}
228117

229-
bool _isAnyConnected() => getConnectedDevices().isNotEmpty;
230-
231118
Future<bool> isInstalled(String deviceId, String appId) {
232119
return exitsHappyAsync(<String>[
233120
_xcrunPath,

packages/flutter_tools/test/commands/drive_test.dart

Lines changed: 18 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import 'package:flutter_tools/src/base/platform.dart';
1313
import 'package:flutter_tools/src/cache.dart';
1414
import 'package:flutter_tools/src/commands/drive.dart';
1515
import 'package:flutter_tools/src/device.dart';
16-
import 'package:flutter_tools/src/ios/simulators.dart';
1716
import 'package:mockito/mockito.dart';
1817
import 'package:test/test.dart';
1918

@@ -238,52 +237,7 @@ void main() {
238237
});
239238
});
240239

241-
group('findTargetDevice on iOS', () {
242-
Platform macOsPlatform() => new FakePlatform(operatingSystem: 'macos');
243-
244-
testUsingContext('uses existing emulator', () async {
245-
withMockDevice();
246-
when(mockDevice.name).thenReturn('mock-simulator');
247-
when(mockDevice.isLocalEmulator).thenReturn(new Future<bool>.value(true));
248-
249-
final Device device = await findTargetDevice();
250-
expect(device.name, 'mock-simulator');
251-
}, overrides: <Type, Generator>{
252-
FileSystem: () => fs,
253-
Platform: macOsPlatform,
254-
});
255-
256-
testUsingContext('uses existing Android device if and there are no simulators', () async {
257-
mockDevice = new MockAndroidDevice();
258-
when(mockDevice.name).thenReturn('mock-android-device');
259-
when(mockDevice.isLocalEmulator).thenReturn(new Future<bool>.value(false));
260-
withMockDevice(mockDevice);
261-
262-
final Device device = await findTargetDevice();
263-
expect(device.name, 'mock-android-device');
264-
}, overrides: <Type, Generator>{
265-
FileSystem: () => fs,
266-
Platform: macOsPlatform,
267-
});
268-
269-
testUsingContext('launches emulator', () async {
270-
when(SimControl.instance.boot()).thenReturn(true);
271-
final Device emulator = new MockDevice();
272-
when(emulator.name).thenReturn('new-simulator');
273-
when(IOSSimulatorUtils.instance.getAttachedDevices())
274-
.thenReturn(<Device>[emulator]);
275-
276-
final Device device = await findTargetDevice();
277-
expect(device.name, 'new-simulator');
278-
}, overrides: <Type, Generator>{
279-
FileSystem: () => fs,
280-
Platform: macOsPlatform,
281-
});
282-
});
283-
284240
void findTargetDeviceOnOperatingSystem(String operatingSystem) {
285-
assert(operatingSystem == 'windows' || operatingSystem == 'linux');
286-
287241
Platform platform() => new FakePlatform(operatingSystem: operatingSystem);
288242

289243
testUsingContext('returns null if no devices found', () async {
@@ -313,6 +267,24 @@ void main() {
313267
group('findTargetDevice on Windows', () {
314268
findTargetDeviceOnOperatingSystem('windows');
315269
});
270+
271+
group('findTargetDevice on macOS', () {
272+
findTargetDeviceOnOperatingSystem('macos');
273+
274+
Platform macOsPlatform() => new FakePlatform(operatingSystem: 'macos');
275+
276+
testUsingContext('uses existing simulator', () async {
277+
withMockDevice();
278+
when(mockDevice.name).thenReturn('mock-simulator');
279+
when(mockDevice.isLocalEmulator).thenReturn(new Future<bool>.value(true));
280+
281+
final Device device = await findTargetDevice();
282+
expect(device.name, 'mock-simulator');
283+
}, overrides: <Type, Generator>{
284+
FileSystem: () => fs,
285+
Platform: macOsPlatform,
286+
});
287+
});
316288
});
317289
}
318290

0 commit comments

Comments
 (0)