Revert "Launch named iOS simulators (#72323)" (#72447)
This reverts commit 84a7a611b0989d2d7e93d6b98aad2f6b68596b9f.
diff --git a/packages/flutter_tools/lib/src/android/android_emulator.dart b/packages/flutter_tools/lib/src/android/android_emulator.dart
index 3b11621..aee624a 100644
--- a/packages/flutter_tools/lib/src/android/android_emulator.dart
+++ b/packages/flutter_tools/lib/src/android/android_emulator.dart
@@ -142,7 +142,7 @@
Category get category => Category.mobile;
@override
- String get platformDisplay => PlatformType.android.toString();
+ PlatformType get platformType => PlatformType.android;
String _prop(String name) => _properties != null ? _properties[name] : null;
diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart
index f72b135..7b28ea8 100644
--- a/packages/flutter_tools/lib/src/commands/daemon.dart
+++ b/packages/flutter_tools/lib/src/commands/daemon.dart
@@ -933,7 +933,7 @@
'id': emulator.id,
'name': emulator.name,
'category': emulator.category?.toString(),
- 'platformType': emulator.platformDisplay,
+ 'platformType': emulator.platformType?.toString(),
};
}
diff --git a/packages/flutter_tools/lib/src/commands/emulators.dart b/packages/flutter_tools/lib/src/commands/emulators.dart
index 859e8f6..580e322 100644
--- a/packages/flutter_tools/lib/src/commands/emulators.dart
+++ b/packages/flutter_tools/lib/src/commands/emulators.dart
@@ -27,7 +27,7 @@
final String description = 'List, launch and create emulators.';
@override
- final List<String> aliases = <String>['emulator', 'simulators', 'simulator'];
+ final List<String> aliases = <String>['emulator'];
@override
Future<FlutterCommandResult> runCommand() async {
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 579b70a..019c129 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -614,6 +614,10 @@
/// Check if the device is supported by Flutter.
bool isSupported();
+ // String meant to be displayed to the user indicating if the device is
+ // supported by Flutter, and, if not, why.
+ String supportMessage() => isSupported() ? 'Supported' : 'Unsupported';
+
/// The device's platform.
Future<TargetPlatform> get targetPlatform;
diff --git a/packages/flutter_tools/lib/src/emulator.dart b/packages/flutter_tools/lib/src/emulator.dart
index d0c2500..f8600eb 100644
--- a/packages/flutter_tools/lib/src/emulator.dart
+++ b/packages/flutter_tools/lib/src/emulator.dart
@@ -252,7 +252,7 @@
String get name;
String get manufacturer;
Category get category;
- String get platformDisplay;
+ PlatformType get platformType;
@override
int get hashCode => id.hashCode;
@@ -283,7 +283,7 @@
emulator.id ?? '',
emulator.name ?? '',
emulator.manufacturer ?? '',
- emulator.platformDisplay ?? '',
+ emulator.platformType?.toString() ?? '',
],
];
diff --git a/packages/flutter_tools/lib/src/ios/ios_emulators.dart b/packages/flutter_tools/lib/src/ios/ios_emulators.dart
index dcb7b08..f54748b 100644
--- a/packages/flutter_tools/lib/src/ios/ios_emulators.dart
+++ b/packages/flutter_tools/lib/src/ios/ios_emulators.dart
@@ -16,26 +16,17 @@
bool get canListAnything => globals.iosWorkflow.canListEmulators;
@override
- Future<List<Emulator>> get emulators async {
- final List<IOSSimulator> simulators = await globals.iosSimulatorUtils.getAvailableDevices();
- return simulators.map<Emulator>((IOSSimulator device) {
- return IOSEmulator(device);
- }).toList();
- }
+ Future<List<Emulator>> get emulators async => getEmulators();
@override
bool get canLaunchAnything => canListAnything;
}
class IOSEmulator extends Emulator {
- IOSEmulator(IOSSimulator simulator)
- : _simulator = simulator,
- super(simulator.id, true);
-
- final IOSSimulator _simulator;
+ const IOSEmulator(String id) : super(id, true);
@override
- String get name => _simulator.name;
+ String get name => 'iOS Simulator';
@override
String get manufacturer => 'Apple';
@@ -44,20 +35,43 @@
Category get category => Category.mobile;
@override
- String get platformDisplay =>
- // com.apple.CoreSimulator.SimRuntime.iOS-10-3 => iOS-10-3
- _simulator.simulatorCategory?.split('.')?.last ?? 'ios';
+ PlatformType get platformType => PlatformType.ios;
@override
Future<void> launch() async {
- final RunResult launchResult = await globals.processUtils.run(<String>[
- 'open',
- '-a',
- globals.xcode.getSimulatorPath(),
- ]);
- if (launchResult.exitCode != 0) {
- globals.printError('$launchResult');
+ Future<bool> launchSimulator(List<String> additionalArgs) async {
+ final List<String> args = <String>[
+ 'open',
+ ...additionalArgs,
+ '-a',
+ globals.xcode.getSimulatorPath(),
+ ];
+
+ final RunResult launchResult = await globals.processUtils.run(args);
+ if (launchResult.exitCode != 0) {
+ globals.printError('$launchResult');
+ return false;
+ }
+ return true;
}
- return _simulator.boot();
+
+ // First run with `-n` to force a device to boot if there isn't already one
+ if (!await launchSimulator(<String>['-n'])) {
+ return;
+ }
+
+ // Run again to force it to Foreground (using -n doesn't force existing
+ // devices to the foreground)
+ await launchSimulator(<String>[]);
}
}
+
+/// Return the list of iOS Simulators (there can only be zero or one).
+List<IOSEmulator> getEmulators() {
+ final String simulatorPath = globals.xcode.getSimulatorPath();
+ if (simulatorPath == null) {
+ return <IOSEmulator>[];
+ }
+
+ return <IOSEmulator>[const IOSEmulator(iosSimulatorId)];
+}
diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart
index 7ad881e..5e32615 100644
--- a/packages/flutter_tools/lib/src/ios/simulators.dart
+++ b/packages/flutter_tools/lib/src/ios/simulators.dart
@@ -66,9 +66,7 @@
return <IOSSimulator>[];
}
- final List<SimDevice> connected = (await _simControl.getAvailableDevices())
- .where((SimDevice device) => device.isBooted)
- .toList();
+ final List<SimDevice> connected = await _simControl.getConnectedDevices();
return connected.map<IOSSimulator>((SimDevice device) {
return IOSSimulator(
device.udid,
@@ -79,26 +77,6 @@
);
}).toList();
}
-
- Future<List<IOSSimulator>> getAvailableDevices() async {
- if (!_xcode.isInstalledAndMeetsVersionCheck) {
- return <IOSSimulator>[];
- }
-
- final List<SimDevice> available = await _simControl.getAvailableDevices();
- return available
- .map<IOSSimulator>((SimDevice device) {
- return IOSSimulator(
- device.udid,
- name: device.name,
- simControl: _simControl,
- simulatorCategory: device.category,
- xcode: _xcode,
- );
- })
- .where((IOSSimulator simulator) => simulator.isSupported())
- .toList();
- }
}
/// A wrapper around the `simctl` command line tool.
@@ -111,23 +89,6 @@
_xcode = xcode,
_processUtils = ProcessUtils(processManager: processManager, logger: logger);
- /// Create a [SimControl] for testing.
- ///
- /// Defaults to a buffer logger.
- @visibleForTesting
- factory SimControl.test({
- @required ProcessManager processManager,
- Logger logger,
- Xcode xcode,
- }) {
- logger ??= BufferLogger.test();
- return SimControl(
- logger: logger,
- xcode: xcode,
- processManager: processManager,
- );
- }
-
final Logger _logger;
final ProcessUtils _processUtils;
final Xcode _xcode;
@@ -199,10 +160,10 @@
return devices;
}
- /// Returns all the available simulator devices.
- Future<List<SimDevice>> getAvailableDevices() async {
+ /// Returns all the connected simulator devices.
+ Future<List<SimDevice>> getConnectedDevices() async {
final List<SimDevice> simDevices = await getDevices();
- return simDevices.where((SimDevice device) => device.isAvailable).toList();
+ return simDevices.where((SimDevice device) => device.isBooted).toList();
}
Future<bool> isInstalled(String deviceId, String appId) {
@@ -273,17 +234,6 @@
return result;
}
- Future<RunResult> boot(String deviceId) {
- return _processUtils.run(
- <String>[
- ..._xcode.xcrunCommand(),
- 'simctl',
- 'boot',
- deviceId,
- ],
- );
- }
-
Future<void> takeScreenshot(String deviceId, String outputPath) async {
try {
await _processUtils.run(
@@ -346,11 +296,7 @@
final Map<String, dynamic> data;
String get state => data['state']?.toString();
-
- bool get isAvailable =>
- data['isAvailable'] == true ||
- data['availability']?.toString() == '(available)';
-
+ String get availability => data['availability']?.toString();
String get name => data['name']?.toString();
String get udid => data['udid']?.toString();
@@ -448,6 +394,7 @@
@override
bool isSupported() {
if (!globals.platform.isMacOS) {
+ _supportMessage = 'iOS devices require a Mac host machine.';
return false;
}
@@ -455,25 +402,21 @@
// We do not yet support WatchOS or tvOS devices.
final RegExp blocklist = RegExp(r'Apple (TV|Watch)', caseSensitive: false);
if (blocklist.hasMatch(name)) {
+ _supportMessage = 'Flutter does not support Apple TV or Apple Watch.';
return false;
}
return true;
}
- Future<bool> boot() async {
- final RunResult result = await _simControl.boot(id);
+ String _supportMessage;
- if (result.exitCode == 0) {
- return true;
- }
- // 149 exit code means the device is already booted. Ignore this error.
- if (result.exitCode == 149) {
- globals.printTrace('Simulator "$id" already booted.');
- return true;
+ @override
+ String supportMessage() {
+ if (isSupported()) {
+ return 'Supported';
}
- globals.logger.printError('$result');
- return false;
+ return _supportMessage ?? 'Unknown';
}
@override
diff --git a/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart b/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
index 0d46c51..82a1b10 100644
--- a/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
+++ b/packages/flutter_tools/test/general.shard/android/android_emulator_test.dart
@@ -71,7 +71,7 @@
expect(emulator.name, displayName);
expect(emulator.manufacturer, manufacturer);
expect(emulator.category, Category.mobile);
- expect(emulator.platformDisplay, 'android');
+ expect(emulator.platformType, PlatformType.android);
});
testWithoutContext('prefers displayname for name', () {
diff --git a/packages/flutter_tools/test/general.shard/emulator_test.dart b/packages/flutter_tools/test/general.shard/emulator_test.dart
index e51a649..89d6208 100644
--- a/packages/flutter_tools/test/general.shard/emulator_test.dart
+++ b/packages/flutter_tools/test/general.shard/emulator_test.dart
@@ -2,13 +2,15 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+import 'dart:convert';
+
import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_workflow.dart';
+import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/emulator.dart';
import 'package:flutter_tools/src/ios/ios_emulators.dart';
-import 'package:flutter_tools/src/ios/simulators.dart';
import 'package:flutter_tools/src/macos/xcode.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
@@ -43,10 +45,14 @@
);
void main() {
+ MockProcessManager mockProcessManager;
MockAndroidSdk mockSdk;
+ MockXcode mockXcode;
setUp(() {
+ mockProcessManager = MockProcessManager();
mockSdk = MockAndroidSdk();
+ mockXcode = MockXcode();
when(mockSdk.avdManagerPath).thenReturn('avdmanager');
when(mockSdk.getAvdManagerPath()).thenReturn('avdmanager');
@@ -293,53 +299,24 @@
});
group('ios_emulators', () {
- MockXcode mockXcode;
- FakeProcessManager fakeProcessManager;
-
+ bool didAttemptToRunSimulator = false;
setUp(() {
- fakeProcessManager = FakeProcessManager.list(<FakeCommand>[]);
- mockXcode = MockXcode();
- when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
- when(mockXcode.getSimulatorPath())
- .thenAnswer((_) => '/fake/simulator.app');
+ when(mockXcode.xcodeSelectPath).thenReturn('/fake/Xcode.app/Contents/Developer');
+ when(mockXcode.getSimulatorPath()).thenAnswer((_) => '/fake/simulator.app');
+ when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
+ final List<String> args = invocation.positionalArguments[0] as List<String>;
+ if (args.length >= 3 && args[0] == 'open' && args[1] == '-a' && args[2] == '/fake/simulator.app') {
+ didAttemptToRunSimulator = true;
+ }
+ return ProcessResult(101, 0, '', '');
+ });
});
-
testUsingContext('runs correct launch commands', () async {
- fakeProcessManager.addCommands(<FakeCommand>[
- const FakeCommand(command: <String>[
- 'open',
- '-a',
- '/fake/simulator.app',
- ]),
- const FakeCommand(command: <String>[
- 'xcrun',
- 'simctl',
- 'boot',
- '1234',
- ]),
- ]);
- final SimControl simControl = SimControl.test(
- processManager: fakeProcessManager,
- xcode: mockXcode,
- );
-
- final IOSSimulator simulator = IOSSimulator(
- '1234',
- name: 'iPhone 12',
- simulatorCategory: 'com.apple.CoreSimulator.SimRuntime.iOS-14-3',
- simControl: simControl,
- xcode: mockXcode,
- );
- final IOSEmulator emulator = IOSEmulator(simulator);
-
- expect(emulator.id, '1234');
- expect(emulator.name, 'iPhone 12');
- expect(emulator.category, Category.mobile);
- expect(emulator.platformDisplay, 'iOS-14-3');
+ const Emulator emulator = IOSEmulator('ios');
await emulator.launch();
- expect(fakeProcessManager.hasRemainingExpectations, false);
+ expect(didAttemptToRunSimulator, equals(true));
}, overrides: <Type, Generator>{
- ProcessManager: () => fakeProcessManager,
+ ProcessManager: () => mockProcessManager,
Xcode: () => mockXcode,
});
});
@@ -370,11 +347,35 @@
Category get category => Category.mobile;
@override
- String get platformDisplay => PlatformType.android.toString();
+ PlatformType get platformType => PlatformType.android;
@override
Future<void> launch() {
throw UnimplementedError('Not implemented in Mock');
}
}
+
+class MockProcessManager extends Mock implements ProcessManager {
+
+ @override
+ ProcessResult runSync(
+ List<dynamic> command, {
+ String workingDirectory,
+ Map<String, String> environment,
+ bool includeParentEnvironment = true,
+ bool runInShell = false,
+ Encoding stdoutEncoding = systemEncoding,
+ Encoding stderrEncoding = systemEncoding,
+ }) {
+ final String program = command[0] as String;
+ final List<String> args = command.sublist(1) as List<String>;
+ switch (program) {
+ case '/usr/bin/xcode-select':
+ throw ProcessException(program, args);
+ break;
+ }
+ throw StateError('Unexpected process call: $command');
+ }
+}
+
class MockXcode extends Mock implements Xcode {}
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 3048f2f..6b921ec 100644
--- a/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
+++ b/packages/flutter_tools/test/general.shard/ios/simulators_test.dart
@@ -11,7 +11,6 @@
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
-import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/build_system/build_system.dart';
import 'package:flutter_tools/src/devfs.dart';
@@ -786,19 +785,12 @@
"availability" : "(available)",
"name" : "iPhone 5s",
"udid" : "TEST-PHONE-UDID"
- },
- {
- "state" : "Shutdown",
- "isAvailable" : false,
- "name" : "iPhone 11",
- "udid" : "TEST-PHONE-UNAVAILABLE-UDID",
- "availabilityError" : "runtime profile not found"
}
],
"tvOS 11.4" : [
{
"state" : "Shutdown",
- "isAvailable" : true,
+ "availability" : "(available)",
"name" : "Apple TV",
"udid" : "TEST-TV-UDID"
}
@@ -832,12 +824,11 @@
testWithoutContext('getDevices succeeds', () async {
final List<SimDevice> devices = await simControl.getDevices();
- expect(devices.length, 4);
final SimDevice watch = devices[0];
expect(watch.category, 'watchOS 4.3');
expect(watch.state, 'Shutdown');
- expect(watch.isAvailable, true);
+ expect(watch.availability, '(available)');
expect(watch.name, 'Apple Watch - 38mm');
expect(watch.udid, 'TEST-WATCH-UDID');
expect(watch.isBooted, isFalse);
@@ -845,33 +836,20 @@
final SimDevice phone = devices[1];
expect(phone.category, 'iOS 11.4');
expect(phone.state, 'Booted');
- expect(phone.isAvailable, true);
+ expect(phone.availability, '(available)');
expect(phone.name, 'iPhone 5s');
expect(phone.udid, 'TEST-PHONE-UDID');
expect(phone.isBooted, isTrue);
- final SimDevice unavailablePhone = devices[2];
- expect(unavailablePhone.category, 'iOS 11.4');
- expect(unavailablePhone.state, 'Shutdown');
- expect(unavailablePhone.isAvailable, isFalse);
- expect(unavailablePhone.name, 'iPhone 11');
- expect(unavailablePhone.udid, 'TEST-PHONE-UNAVAILABLE-UDID');
- expect(unavailablePhone.isBooted, isFalse);
-
- final SimDevice tv = devices[3];
+ final SimDevice tv = devices[2];
expect(tv.category, 'tvOS 11.4');
expect(tv.state, 'Shutdown');
- expect(tv.isAvailable, true);
+ expect(tv.availability, '(available)');
expect(tv.name, 'Apple TV');
expect(tv.udid, 'TEST-TV-UDID');
expect(tv.isBooted, isFalse);
});
- testWithoutContext('getAvailableDevices succeeds', () async {
- final List<SimDevice> devices = await simControl.getAvailableDevices();
- expect(devices.length, 3);
- });
-
testWithoutContext('getDevices handles bad simctl output', () async {
when(mockProcessManager.run(any))
.thenAnswer((Invocation _) async => ProcessResult(mockPid, 0, 'Install Started', ''));
@@ -927,78 +905,6 @@
throwsToolExit(message: r'Unable to launch'),
);
});
-
- testWithoutContext('.boot() calls the right command', () async {
- await simControl.boot(deviceId);
- verify(mockProcessManager.run(
- <String>['xcrun', 'simctl', 'boot', deviceId],
- environment: anyNamed('environment'),
- workingDirectory: anyNamed('workingDirectory'),
- ));
- });
- });
-
- group('boot', () {
- SimControl simControl;
- MockXcode mockXcode;
-
- setUp(() {
- simControl = MockSimControl();
- mockXcode = MockXcode();
- when(mockXcode.xcrunCommand()).thenReturn(<String>['xcrun']);
- });
-
- testUsingContext('success', () async {
- final IOSSimulator device = IOSSimulator(
- 'x',
- name: 'iPhone SE',
- simulatorCategory: 'iOS 11.2',
- simControl: simControl,
- xcode: mockXcode,
- );
- when(simControl.boot(any)).thenAnswer((_) async =>
- RunResult(ProcessResult(0, 0, '', ''), <String>['simctl']));
-
- expect(await device.boot(), isTrue);
- }, overrides: <Type, Generator>{
- FileSystem: () => fileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('already booted', () async {
- final IOSSimulator device = IOSSimulator(
- 'x',
- name: 'iPhone SE',
- simulatorCategory: 'iOS 11.2',
- simControl: simControl,
- xcode: mockXcode,
- );
- // 149 means the device is already booted.
- when(simControl.boot(any)).thenAnswer((_) async =>
- RunResult(ProcessResult(0, 149, '', ''), <String>['simctl']));
-
- expect(await device.boot(), isTrue);
- }, overrides: <Type, Generator>{
- FileSystem: () => fileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
-
- testUsingContext('failed', () async {
- final IOSSimulator device = IOSSimulator(
- 'x',
- name: 'iPhone SE',
- simulatorCategory: 'iOS 11.2',
- simControl: simControl,
- xcode: mockXcode,
- );
- when(simControl.boot(any)).thenAnswer((_) async =>
- RunResult(ProcessResult(0, 1, '', ''), <String>['simctl']));
-
- expect(await device.boot(), isFalse);
- }, overrides: <Type, Generator>{
- FileSystem: () => fileSystem,
- ProcessManager: () => FakeProcessManager.any(),
- });
});
group('startApp', () {
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index 5e89ddf..b1616fe 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -114,7 +114,6 @@
IOSSimulatorUtils: () {
final MockIOSSimulatorUtils mock = MockIOSSimulatorUtils();
when(mock.getAttachedDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
- when(mock.getAvailableDevices()).thenAnswer((Invocation _) async => <IOSSimulator>[]);
return mock;
},
OutputPreferences: () => OutputPreferences.test(),
@@ -287,7 +286,7 @@
class MockSimControl extends Mock implements SimControl {
MockSimControl() {
- when(getAvailableDevices()).thenAnswer((Invocation _) async => <SimDevice>[]);
+ when(getConnectedDevices()).thenAnswer((Invocation _) async => <SimDevice>[]);
}
}