Refactor `DeviceManager.findTargetDevices()` and `FlutterCommand.findAllTargetDevices()`, and add a flag to not show prompt. (#112223)
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index 020efc5..36069c2 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -205,7 +205,6 @@
),
fuchsiaSdk: globals.fuchsiaSdk!,
operatingSystemUtils: globals.os,
- terminal: globals.terminal,
customDevicesConfig: globals.customDevicesConfig,
),
DevtoolsLauncher: () => DevtoolsServerLauncher(
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 9eeef33..9e6017e 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -9,13 +9,10 @@
import 'application_package.dart';
import 'artifacts.dart';
-import 'base/common.dart';
import 'base/context.dart';
import 'base/dds.dart';
import 'base/file_system.dart';
import 'base/logger.dart';
-import 'base/terminal.dart';
-import 'base/user_messages.dart' hide userMessages;
import 'base/utils.dart';
import 'build_info.dart';
import 'devfs.dart';
@@ -83,15 +80,9 @@
abstract class DeviceManager {
DeviceManager({
required Logger logger,
- required Terminal terminal,
- required UserMessages userMessages,
- }) : _logger = logger,
- _terminal = terminal,
- _userMessages = userMessages;
+ }) : _logger = logger;
final Logger _logger;
- final Terminal _terminal;
- final UserMessages _userMessages;
/// Constructing DeviceManagers is cheap; they only do expensive work if some
/// of their methods are called.
@@ -219,7 +210,12 @@
];
}
- /// Find and return a list of devices based on the current project and environment.
+ /// Find and return all target [Device]s based upon currently connected
+ /// devices, the current project, and criteria entered by the user on
+ /// the command line.
+ ///
+ /// If no device can be found that meets specified criteria,
+ /// then print an error message and return null.
///
/// Returns a list of devices specified by the user.
///
@@ -233,9 +229,13 @@
/// device connected, then filter out unsupported devices and prioritize
/// ephemeral devices.
///
- /// * If [flutterProject] is null, then assume the project supports all
- /// device types.
- Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout }) async {
+ /// * If [promptUserToChooseDevice] is true, and there are more than one
+ /// device after the aforementioned filters, and the user is connected to a
+ /// terminal, then show a prompt asking the user to choose one.
+ Future<List<Device>> findTargetDevices(
+ FlutterProject? flutterProject, {
+ Duration? timeout,
+ }) async {
if (timeout != null) {
// Reset the cache with the specified timeout.
await refreshAllConnectedDevices(timeout: timeout);
@@ -244,97 +244,56 @@
List<Device> devices = (await getDevices())
.where((Device device) => device.isSupported()).toList();
- // Always remove web and fuchsia devices from `--all`. This setting
- // currently requires devices to share a frontend_server and resident
- // runner instance. Both web and fuchsia require differently configured
- // compilers, and web requires an entirely different resident runner.
if (hasSpecifiedAllDevices) {
+ // User has specified `--device all`.
+ //
+ // Always remove web and fuchsia devices from `--all`. This setting
+ // currently requires devices to share a frontend_server and resident
+ // runner instance. Both web and fuchsia require differently configured
+ // compilers, and web requires an entirely different resident runner.
devices = <Device>[
for (final Device device in devices)
if (await device.targetPlatform != TargetPlatform.fuchsia_arm64 &&
await device.targetPlatform != TargetPlatform.fuchsia_x64 &&
- await device.targetPlatform != TargetPlatform.web_javascript)
+ await device.targetPlatform != TargetPlatform.web_javascript &&
+ isDeviceSupportedForProject(device, flutterProject))
device,
];
- }
+ } else if (!hasSpecifiedDeviceId) {
+ // User did not specify the device.
- // If there is no specified device, the remove all devices which are not
- // supported by the current application. For example, if there was no
- // 'android' folder then don't attempt to launch with an Android device.
- if (devices.length > 1 && !hasSpecifiedDeviceId) {
+ // Remove all devices which are not supported by the current application.
+ // For example, if there was no 'android' folder then don't attempt to
+ // launch with an Android device.
devices = <Device>[
for (final Device device in devices)
if (isDeviceSupportedForProject(device, flutterProject))
device,
];
- } else if (devices.length == 1 &&
- !hasSpecifiedDeviceId &&
- !isDeviceSupportedForProject(devices.single, flutterProject)) {
- // If there is only a single device but it is not supported, then return
- // early.
- return <Device>[];
- }
- // If there are still multiple devices and the user did not specify to run
- // all, then attempt to prioritize ephemeral devices. For example, if the
- // user only typed 'flutter run' and both an Android device and desktop
- // device are available, choose the Android device.
- if (devices.length > 1 && !hasSpecifiedAllDevices) {
- // Note: ephemeral is nullable for device types where this is not well
- // defined.
- if (devices.any((Device device) => device.ephemeral == true)) {
- // if there is only one ephemeral device, get it
- final List<Device> ephemeralDevices = devices
- .where((Device device) => device.ephemeral == true)
- .toList();
+ if (devices.length > 1) {
+ // If there are still multiple devices and the user did not specify to run
+ // all, then attempt to prioritize ephemeral devices. For example, if the
+ // user only typed 'flutter run' and both an Android device and desktop
+ // device are available, choose the Android device.
- if (ephemeralDevices.length == 1) {
- devices = ephemeralDevices;
- }
- }
- // If it was not able to prioritize a device. For example, if the user
- // has two active Android devices running, then we request the user to
- // choose one. If the user has two nonEphemeral devices running, we also
- // request input to choose one.
- if (devices.length > 1 && _terminal.stdinHasTerminal) {
- _logger.printStatus(_userMessages.flutterMultipleDevicesFound);
- await Device.printDevices(devices, _logger);
- final Device chosenDevice = await _chooseOneOfAvailableDevices(devices);
- specifiedDeviceId = chosenDevice.id;
- devices = <Device>[chosenDevice];
+ // Note: ephemeral is nullable for device types where this is not well
+ // defined.
+ final List<Device> ephemeralDevices = <Device>[
+ for (final Device device in devices)
+ if (device.ephemeral == true)
+ device,
+ ];
+
+ if (ephemeralDevices.length == 1) {
+ devices = ephemeralDevices;
+ }
}
}
+
return devices;
}
- Future<Device> _chooseOneOfAvailableDevices(List<Device> devices) async {
- _displayDeviceOptions(devices);
- final String userInput = await _readUserInput(devices.length);
- if (userInput.toLowerCase() == 'q') {
- throwToolExit('');
- }
- return devices[int.parse(userInput) - 1];
- }
-
- void _displayDeviceOptions(List<Device> devices) {
- int count = 1;
- for (final Device device in devices) {
- _logger.printStatus(_userMessages.flutterChooseDevice(count, device.name, device.id));
- count++;
- }
- }
-
- Future<String> _readUserInput(int deviceCount) async {
- _terminal.usesTerminalUi = true;
- final String result = await _terminal.promptForCharInput(
- <String>[ for (int i = 0; i < deviceCount; i++) '${i + 1}', 'q', 'Q'],
- displayAcceptedCharacters: false,
- logger: _logger,
- prompt: _userMessages.flutterChooseOne,
- );
- return result;
- }
-
/// Returns whether the device is supported for the project.
///
/// This exists to allow the check to be overridden for google3 clients. If
diff --git a/packages/flutter_tools/lib/src/flutter_device_manager.dart b/packages/flutter_tools/lib/src/flutter_device_manager.dart
index f30a0da..4741119 100644
--- a/packages/flutter_tools/lib/src/flutter_device_manager.dart
+++ b/packages/flutter_tools/lib/src/flutter_device_manager.dart
@@ -11,6 +11,7 @@
import 'base/file_system.dart';
import 'base/os.dart';
import 'base/platform.dart';
+import 'base/user_messages.dart';
import 'custom_devices/custom_device.dart';
import 'custom_devices/custom_devices_config.dart';
import 'device.dart';
@@ -51,10 +52,9 @@
required Artifacts artifacts,
required MacOSWorkflow macOSWorkflow,
required FuchsiaSdk fuchsiaSdk,
- required super.userMessages,
+ required UserMessages userMessages,
required OperatingSystemUtils operatingSystemUtils,
required WindowsWorkflow windowsWorkflow,
- required super.terminal,
required CustomDevicesConfig customDevicesConfig,
}) : deviceDiscoverers = <DeviceDiscovery>[
AndroidDevices(
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 41eee55..b6c628b 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -1413,52 +1413,105 @@
timeout: deviceDiscoveryTimeout,
);
- if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
- globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
- final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
- if (allDevices.isNotEmpty) {
- globals.printStatus('');
- globals.printStatus('The following devices were found:');
- await Device.printDevices(allDevices, globals.logger);
- }
- return null;
- } else if (devices.isEmpty) {
- if (deviceManager.hasSpecifiedAllDevices) {
- globals.printStatus(userMessages.flutterNoDevicesFound);
- } else {
- globals.printStatus(userMessages.flutterNoSupportedDevices);
- }
- final List<Device> unsupportedDevices = await deviceManager.getDevices();
- if (unsupportedDevices.isNotEmpty) {
- final StringBuffer result = StringBuffer();
- result.writeln(userMessages.flutterFoundButUnsupportedDevices);
- result.writeAll(
- (await Device.descriptions(unsupportedDevices))
- .map((String desc) => desc)
- .toList(),
- '\n',
- );
- result.writeln();
- result.writeln(userMessages.flutterMissPlatformProjects(
- Device.devicesPlatformTypes(unsupportedDevices),
- ));
- globals.printStatus(result.toString());
- }
- return null;
- } else if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices) {
+ if (devices.isEmpty) {
if (deviceManager.hasSpecifiedDeviceId) {
- globals.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
+ globals.logger.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
+ final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
+ if (allDevices.isNotEmpty) {
+ globals.logger.printStatus('');
+ globals.logger.printStatus('The following devices were found:');
+ await Device.printDevices(allDevices, globals.logger);
+ }
+ return null;
+ } else if (deviceManager.hasSpecifiedAllDevices) {
+ globals.logger.printStatus(userMessages.flutterNoDevicesFound);
+ await _printUnsupportedDevice(deviceManager);
+ return null;
} else {
- globals.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
- devices = await deviceManager.getAllConnectedDevices();
+ globals.logger.printStatus(userMessages.flutterNoSupportedDevices);
+ await _printUnsupportedDevice(deviceManager);
+ return null;
}
- globals.printStatus('');
- await Device.printDevices(devices, globals.logger);
- return null;
+ } else if (devices.length > 1) {
+ if (deviceManager.hasSpecifiedDeviceId) {
+ globals.logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
+ return null;
+ } else if (!deviceManager.hasSpecifiedAllDevices) {
+ if (globals.terminal.stdinHasTerminal) {
+ // If DeviceManager was not able to prioritize a device. For example, if the user
+ // has two active Android devices running, then we request the user to
+ // choose one. If the user has two nonEphemeral devices running, we also
+ // request input to choose one.
+ globals.logger.printStatus(userMessages.flutterMultipleDevicesFound);
+ await Device.printDevices(devices, globals.logger);
+ final Device chosenDevice = await _chooseOneOfAvailableDevices(devices);
+
+ // Update the [DeviceManager.specifiedDeviceId] so that we will not be prompted again.
+ deviceManager.specifiedDeviceId = chosenDevice.id;
+
+ devices = <Device>[chosenDevice];
+ } else {
+ // Show an error message asking the user to specify `-d all` if they
+ // want to run on multiple devices.
+ final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
+ globals.logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
+ globals.logger.printStatus('');
+ await Device.printDevices(allDevices, globals.logger);
+ return null;
+ }
+ }
}
+
return devices;
}
+ Future<void> _printUnsupportedDevice(DeviceManager deviceManager) async {
+ final List<Device> unsupportedDevices = await deviceManager.getDevices();
+ if (unsupportedDevices.isNotEmpty) {
+ final StringBuffer result = StringBuffer();
+ result.writeln(userMessages.flutterFoundButUnsupportedDevices);
+ result.writeAll(
+ (await Device.descriptions(unsupportedDevices))
+ .map((String desc) => desc)
+ .toList(),
+ '\n',
+ );
+ result.writeln();
+ result.writeln(userMessages.flutterMissPlatformProjects(
+ Device.devicesPlatformTypes(unsupportedDevices),
+ ));
+ globals.logger.printStatus(result.toString());
+ }
+ }
+
+ Future<Device> _chooseOneOfAvailableDevices(List<Device> devices) async {
+ _displayDeviceOptions(devices);
+ final String userInput = await _readUserInput(devices.length);
+ if (userInput.toLowerCase() == 'q') {
+ throwToolExit('');
+ }
+ return devices[int.parse(userInput) - 1];
+ }
+
+ void _displayDeviceOptions(List<Device> devices) {
+ int count = 1;
+ for (final Device device in devices) {
+ globals.logger.printStatus(userMessages.flutterChooseDevice(count, device.name, device.id));
+ count++;
+ }
+ }
+
+ Future<String> _readUserInput(int deviceCount) async {
+ globals.terminal.usesTerminalUi = true;
+ final String result = await globals.terminal.promptForCharInput(
+ <String>[ for (int i = 0; i < deviceCount; i++) '${i + 1}', 'q', 'Q'],
+ displayAcceptedCharacters: false,
+ logger: globals.logger,
+ prompt: userMessages.flutterChooseOne,
+ );
+ return result;
+ }
+
/// Find and return the target [Device] based upon currently connected
/// devices and criteria entered by the user on the command line.
/// If a device cannot be found that meets specified criteria,
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
index 0610be0..4c7482b 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/attach_test.dart
@@ -46,6 +46,7 @@
group('attach', () {
late StreamLogger logger;
late FileSystem testFileSystem;
+ late TestDeviceManager testDeviceManager;
setUp(() {
Cache.disableLocking();
@@ -57,6 +58,7 @@
);
testFileSystem.directory('lib').createSync();
testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync();
+ testDeviceManager = TestDeviceManager(logger: BufferLogger.test());
});
group('with one device and no specified target file', () {
@@ -92,7 +94,7 @@
},
);
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') {
@@ -113,6 +115,7 @@
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
logger: logger,
@@ -126,7 +129,7 @@
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader;
};
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') {
@@ -147,6 +150,7 @@
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('Fails with tool exit on bad Observatory uri', () async {
@@ -156,12 +160,13 @@
fakeLogReader.dispose();
return fakeLogReader;
};
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
expect(() => createTestCommandRunner(AttachCommand()).run(<String>['attach']), throwsToolExit());
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('accepts filesystem parameters', () async {
@@ -170,7 +175,7 @@
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader;
};
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
const String filesystemScheme = 'foo';
const String filesystemRoot = '/build-output/';
@@ -221,10 +226,11 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('exits when ipv6 is specified and debug-port is not', () async {
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final AttachCommand command = AttachCommand();
await expectLater(
@@ -237,6 +243,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
},);
testUsingContext('exits when observatory-port is specified and debug-port is not', () async {
@@ -245,7 +252,7 @@
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader;
};
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final AttachCommand command = AttachCommand();
await expectLater(
@@ -258,6 +265,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
},);
});
@@ -276,7 +284,7 @@
});
testUsingContext('succeeds in ipv4 mode', () async {
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
@@ -298,10 +306,11 @@
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('succeeds in ipv6 mode', () async {
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
@@ -324,10 +333,11 @@
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('skips in ipv4 mode with a provided observatory port', () async {
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
@@ -359,10 +369,11 @@
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('skips in ipv6 mode with a provided observatory port', () async {
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
@@ -395,6 +406,7 @@
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger,
+ DeviceManager: () => testDeviceManager,
});
});
@@ -408,11 +420,12 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('fails when targeted device is not Android with --device-user', () async {
final FakeIOSDevice device = FakeIOSDevice();
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
expect(createTestCommandRunner(AttachCommand()).run(<String>[
'attach',
'--device-user',
@@ -421,12 +434,15 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('exits when multiple devices connected', () async {
final AttachCommand command = AttachCommand();
- testDeviceManager.addDevice(FakeAndroidDevice(id: 'xx1'));
- testDeviceManager.addDevice(FakeAndroidDevice(id: 'yy2'));
+ testDeviceManager.devices = <Device>[
+ FakeAndroidDevice(id: 'xx1'),
+ FakeAndroidDevice(id: 'yy2'),
+ ];
await expectLater(
createTestCommandRunner(command).run(<String>['attach']),
throwsToolExit(),
@@ -438,6 +454,8 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
+ AnsiTerminal: () => FakeTerminal(stdinHasTerminal: false),
});
testUsingContext('Catches service disappeared error', () async {
@@ -457,7 +475,7 @@
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kServiceDisappeared, '');
};
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
testFileSystem.file('lib/main.dart').createSync();
final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory);
@@ -467,6 +485,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
});
testUsingContext('Does not catch generic RPC error', () async {
@@ -487,7 +506,7 @@
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kInvalidParams, '');
};
- testDeviceManager.addDevice(device);
+ testDeviceManager.devices = <Device>[device];
testFileSystem.file('lib/main.dart').createSync();
final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory);
@@ -497,6 +516,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(),
+ DeviceManager: () => testDeviceManager,
});
});
}
@@ -791,6 +811,9 @@
@override
Category get category => Category.mobile;
+
+ @override
+ bool get ephemeral => true;
}
// Unfortunately Device, despite not being immutable, has an `operator ==`.
@@ -840,6 +863,12 @@
@override
final PlatformType platformType = PlatformType.ios;
+
+ @override
+ bool isSupported() => true;
+
+ @override
+ bool isSupportedForProject(FlutterProject project) => true;
}
class FakeMDnsClient extends Fake implements MDnsClient {
@@ -887,3 +916,24 @@
@override
void stop() {}
}
+
+class TestDeviceManager extends DeviceManager {
+ TestDeviceManager({required this.logger}) : super(logger: logger);
+ List<Device> devices = <Device>[];
+
+ final BufferLogger logger;
+
+ @override
+ List<DeviceDiscovery> get deviceDiscoverers {
+ final FakePollingDeviceDiscovery discoverer = FakePollingDeviceDiscovery();
+ devices.forEach(discoverer.addDevice);
+ return <DeviceDiscovery>[discoverer];
+ }
+}
+
+class FakeTerminal extends Fake implements AnsiTerminal {
+ FakeTerminal({this.stdinHasTerminal = true});
+
+ @override
+ final bool stdinHasTerminal;
+}
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart
index 46bfb7f..cc0e3d4 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/devices_test.dart
@@ -6,8 +6,6 @@
import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/artifacts.dart';
-import 'package:flutter_tools/src/base/terminal.dart';
-import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/devices.dart';
import 'package:flutter_tools/src/device.dart';
@@ -133,7 +131,7 @@
}
class _FakeDeviceManager extends DeviceManager {
- _FakeDeviceManager() : super(logger: testLogger, terminal: Terminal.test(), userMessages: userMessages);
+ _FakeDeviceManager() : super(logger: testLogger);
@override
Future<List<Device>> getAllConnectedDevices() =>
@@ -153,7 +151,7 @@
}
class NoDevicesManager extends DeviceManager {
- NoDevicesManager() : super(logger: testLogger, terminal: Terminal.test(), userMessages: userMessages);
+ NoDevicesManager() : super(logger: testLogger);
@override
Future<List<Device>> getAllConnectedDevices() async => <Device>[];
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
index 966d552..224d18c 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/drive_test.dart
@@ -328,7 +328,7 @@
Future<List<Device>> getDevices() async => devices;
@override
- Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, {Duration? timeout}) async => devices;
+ Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, {Duration? timeout, bool promptUserToChooseDevice = true}) async => devices;
}
class FailingFakeFlutterDriverFactory extends Fake implements FlutterDriverFactory {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index 8ad2c87..ecdac20 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -44,11 +44,11 @@
});
group('run', () {
- late FakeDeviceManager mockDeviceManager;
+ late TestDeviceManager testDeviceManager;
late FileSystem fileSystem;
setUp(() {
- mockDeviceManager = FakeDeviceManager();
+ testDeviceManager = TestDeviceManager(logger: BufferLogger.test());
fileSystem = MemoryFileSystem.test();
});
@@ -166,9 +166,7 @@
testUsingContext('exits with a user message when no supported devices attached', () async {
final RunCommand command = RunCommand();
- mockDeviceManager
- ..devices = <Device>[]
- ..targetDevices = <Device>[];
+ testDeviceManager.devices = <Device>[];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
@@ -184,7 +182,7 @@
containsIgnoringWhitespace(userMessages.flutterNoSupportedDevices),
);
}, overrides: <Type, Generator>{
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
@@ -193,9 +191,9 @@
testUsingContext('exits and lists available devices when specified device not found', () async {
final RunCommand command = RunCommand();
final FakeDevice device = FakeDevice(isLocalEmulator: true);
- mockDeviceManager
+ testDeviceManager
..devices = <Device>[device]
- ..hasSpecifiedDeviceId = true;
+ ..specifiedDeviceId = 'invalid-device-id';
await expectLater(
() => createTestCommandRunner(command).run(<String>[
@@ -211,7 +209,7 @@
expect(testLogger.statusText, contains('The following devices were found:'));
expect(testLogger.statusText, contains('FakeDevice (mobile) • fake_device • ios • (simulator)'));
}, overrides: <Type, Generator>{
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
@@ -220,9 +218,7 @@
testUsingContext('fails when targeted device is not Android with --device-user', () async {
final FakeDevice device = FakeDevice(isLocalEmulator: true);
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates();
await expectLater(createTestCommandRunner(command).run(<String>[
@@ -234,7 +230,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
@@ -242,9 +238,7 @@
testUsingContext('succeeds when targeted device is an Android device with --device-user', () async {
final FakeDevice device = FakeDevice(isLocalEmulator: true, platformType: PlatformType.android);
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates();
await createTestCommandRunner(command).run(<String>[
@@ -257,7 +251,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
@@ -285,9 +279,7 @@
processManager: FakeProcessManager.any(),
);
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
final RunCommand command = RunCommand();
await expectLater(createTestCommandRunner(command).run(<String>[
@@ -297,7 +289,7 @@
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
@@ -327,9 +319,7 @@
processManager: FakeProcessManager.any(),
);
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
final RunCommand command = RunCommand();
await expectLater(createTestCommandRunner(command).run(<String>[
@@ -339,17 +329,20 @@
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
testUsingContext('shows unsupported devices when no supported devices are found', () async {
final RunCommand command = RunCommand();
- final FakeDevice mockDevice = FakeDevice(targetPlatform: TargetPlatform.android_arm, isLocalEmulator: true, sdkNameAndVersion: 'api-14');
- mockDeviceManager
- ..devices = <Device>[mockDevice]
- ..targetDevices = <Device>[];
+ final FakeDevice mockDevice = FakeDevice(
+ targetPlatform: TargetPlatform.android_arm,
+ isLocalEmulator: true,
+ sdkNameAndVersion: 'api-14',
+ isSupported: false,
+ );
+ testDeviceManager.devices = <Device>[mockDevice];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
@@ -377,7 +370,7 @@
),
);
}, overrides: <Type, Generator>{
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
@@ -389,13 +382,7 @@
sdkNameAndVersion: 'iOS 13',
)..startAppSuccess = false;
- mockDeviceManager
- ..devices = <Device>[
- mockDevice,
- ]
- ..targetDevices = <Device>[
- mockDevice,
- ];
+ testDeviceManager.devices = <Device>[mockDevice];
// Causes swift to be detected in the analytics.
fs.currentDirectory.childDirectory('ios').childFile('AppDelegate.swift').createSync(recursive: true);
@@ -412,7 +399,7 @@
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage,
@@ -423,13 +410,7 @@
final FakeDevice mockDevice = FakeDevice(sdkNameAndVersion: 'iOS 13')
..startAppSuccess = false;
- mockDeviceManager
- ..devices = <Device>[
- mockDevice,
- ]
- ..targetDevices = <Device>[
- mockDevice,
- ];
+ testDeviceManager.devices = <Device>[mockDevice];
// Causes swift to be detected in the analytics.
fs.currentDirectory.childDirectory('ios').childFile('AppDelegate.swift').createSync(recursive: true);
@@ -451,7 +432,7 @@
AnsiTerminal: () => fakeTerminal,
Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(),
@@ -462,9 +443,7 @@
testUsingContext('enables multidex by default', () async {
final DaemonCapturingRunCommand command = DaemonCapturingRunCommand();
final FakeDevice device = FakeDevice();
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
@@ -480,7 +459,7 @@
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage,
@@ -491,9 +470,7 @@
testUsingContext('can disable multidex with --no-multidex', () async {
final DaemonCapturingRunCommand command = DaemonCapturingRunCommand();
final FakeDevice device = FakeDevice();
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
@@ -510,7 +487,7 @@
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage,
@@ -521,9 +498,7 @@
testUsingContext('can pass --device-user', () async {
final DaemonCapturingRunCommand command = DaemonCapturingRunCommand();
final FakeDevice device = FakeDevice(platformType: PlatformType.android);
- mockDeviceManager
- ..devices = <Device>[device]
- ..targetDevices = <Device>[device];
+ testDeviceManager.devices = <Device>[device];
await expectLater(
() => createTestCommandRunner(command).run(<String>[
@@ -541,7 +516,7 @@
}, overrides: <Type, Generator>{
Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage,
@@ -621,21 +596,21 @@
});
testUsingContext('should only request artifacts corresponding to connected devices', () async {
- mockDeviceManager.devices = <Device>[FakeDevice(targetPlatform: TargetPlatform.android_arm)];
+ testDeviceManager.devices = <Device>[FakeDevice(targetPlatform: TargetPlatform.android_arm)];
expect(await RunCommand().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.androidGenSnapshot,
}));
- mockDeviceManager.devices = <Device>[FakeDevice()];
+ testDeviceManager.devices = <Device>[FakeDevice()];
expect(await RunCommand().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.iOS,
}));
- mockDeviceManager.devices = <Device>[
+ testDeviceManager.devices = <Device>[
FakeDevice(),
FakeDevice(targetPlatform: TargetPlatform.android_arm),
];
@@ -646,7 +621,7 @@
DevelopmentArtifact.androidGenSnapshot,
}));
- mockDeviceManager.devices = <Device>[
+ testDeviceManager.devices = <Device>[
FakeDevice(targetPlatform: TargetPlatform.web_javascript),
];
@@ -655,7 +630,7 @@
DevelopmentArtifact.web,
}));
}, overrides: <Type, Generator>{
- DeviceManager: () => mockDeviceManager,
+ DeviceManager: () => testDeviceManager,
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(),
@@ -881,28 +856,11 @@
});
}
-class FakeDeviceManager extends Fake implements DeviceManager {
+class TestDeviceManager extends DeviceManager {
+ TestDeviceManager({required this.logger}) : super(logger: logger);
List<Device> devices = <Device>[];
- List<Device> targetDevices = <Device>[];
- @override
- String? specifiedDeviceId;
-
- @override
- bool hasSpecifiedAllDevices = false;
-
- @override
- bool hasSpecifiedDeviceId = false;
-
- @override
- Future<List<Device>> getDevices() async {
- return devices;
- }
-
- @override
- Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, {Duration? timeout}) async {
- return targetDevices;
- }
+ final Logger logger;
@override
List<DeviceDiscovery> get deviceDiscoverers {
@@ -910,9 +868,6 @@
devices.forEach(discoverer.addDevice);
return <DeviceDiscovery>[discoverer];
}
-
- @override
- Future<List<Device>> getAllConnectedDevices() async => devices;
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
@@ -929,10 +884,12 @@
TargetPlatform targetPlatform = TargetPlatform.ios,
String sdkNameAndVersion = '',
PlatformType platformType = PlatformType.ios,
+ bool isSupported = true,
}): _isLocalEmulator = isLocalEmulator,
_targetPlatform = targetPlatform,
_sdkNameAndVersion = sdkNameAndVersion,
- _platformType = platformType;
+ _platformType = platformType,
+ _isSupported = isSupported;
static const int kSuccess = 1;
static const int kFailure = -1;
@@ -940,6 +897,7 @@
final bool _isLocalEmulator;
final String _sdkNameAndVersion;
final PlatformType _platformType;
+ final bool _isSupported;
@override
Category get category => Category.mobile;
@@ -970,7 +928,7 @@
bool supported = true;
@override
- bool isSupportedForProject(FlutterProject flutterProject) => true;
+ bool isSupportedForProject(FlutterProject flutterProject) => _isSupported;
@override
bool isSupported() => supported;
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
index 8fa7008..22b737a 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/test_test.dart
@@ -10,8 +10,6 @@
import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart';
-import 'package:flutter_tools/src/base/terminal.dart';
-import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/test.dart';
import 'package:flutter_tools/src/device.dart';
@@ -851,7 +849,7 @@
}
class _FakeDeviceManager extends DeviceManager {
- _FakeDeviceManager(this._connectedDevices) : super(logger: testLogger, terminal: Terminal.test(), userMessages: userMessages);
+ _FakeDeviceManager(this._connectedDevices) : super(logger: testLogger);
final List<Device> _connectedDevices;
diff --git a/packages/flutter_tools/test/general.shard/device_test.dart b/packages/flutter_tools/test/general.shard/device_test.dart
index a21e341..e03ed0e 100644
--- a/packages/flutter_tools/test/general.shard/device_test.dart
+++ b/packages/flutter_tools/test/general.shard/device_test.dart
@@ -7,8 +7,6 @@
import 'package:fake_async/fake_async.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
-import 'package:flutter_tools/src/base/terminal.dart';
-import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/convert.dart';
@@ -30,7 +28,6 @@
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
expect(await deviceManager.getDevices(), devices);
@@ -56,7 +53,6 @@
LongPollingDeviceDiscovery(),
],
logger: logger,
- terminal: Terminal.test(),
);
Future<void> expectDevice(String id, List<Device> expected) async {
@@ -85,7 +81,6 @@
LongPollingDeviceDiscovery(),
],
logger: logger,
- terminal: Terminal.test(),
wellKnownId: 'windows',
);
@@ -114,7 +109,6 @@
ThrowingPollingDeviceDiscovery(),
],
logger: logger,
- terminal: Terminal.test(),
);
Future<void> expectDevice(String id, List<Device> expected) async {
@@ -133,7 +127,6 @@
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[device1],
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]);
@@ -147,7 +140,6 @@
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[device1],
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device1]);
@@ -199,95 +191,13 @@
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered.single, ephemeralOne);
});
- testWithoutContext('choose first non-ephemeral device', () async {
- final List<Device> devices = <Device>[
- nonEphemeralOne,
- nonEphemeralTwo,
- ];
- final FakeTerminal terminal = FakeTerminal()
- ..setPrompt(<String>['1', '2', 'q', 'Q'], '1');
-
- final DeviceManager deviceManager = TestDeviceManager(
- devices,
- logger: BufferLogger.test(),
- terminal: terminal,
- );
- final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
-
- expect(filtered, <Device>[
- nonEphemeralOne,
- ]);
- });
-
- testWithoutContext('choose second non-ephemeral device', () async {
- final List<Device> devices = <Device>[
- nonEphemeralOne,
- nonEphemeralTwo,
- ];
- final FakeTerminal terminal = FakeTerminal()
- ..setPrompt(<String>['1', '2', 'q', 'Q'], '2');
-
- final DeviceManager deviceManager = TestDeviceManager(
- devices,
- logger: BufferLogger.test(),
- terminal: terminal,
- );
- final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
-
- expect(filtered, <Device>[
- nonEphemeralTwo,
- ]);
- });
-
- testWithoutContext('choose first ephemeral device', () async {
- final List<Device> devices = <Device>[
- ephemeralOne,
- ephemeralTwo,
- ];
-
- final FakeTerminal terminal = FakeTerminal()
- ..setPrompt(<String>['1', '2', 'q', 'Q'], '1');
-
- final DeviceManager deviceManager = TestDeviceManager(
- devices,
- logger: BufferLogger.test(),
- terminal: terminal,
- );
- final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
-
- expect(filtered, <Device>[
- ephemeralOne,
- ]);
- });
-
- testWithoutContext('choose second ephemeral device', () async {
- final List<Device> devices = <Device>[
- ephemeralOne,
- ephemeralTwo,
- ];
- final FakeTerminal terminal = FakeTerminal()
- ..setPrompt(<String>['1', '2', 'q', 'Q'], '2');
-
- final DeviceManager deviceManager = TestDeviceManager(
- devices,
- logger: BufferLogger.test(),
- terminal: terminal,
- );
- final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
-
- expect(filtered, <Device>[
- ephemeralTwo,
- ]);
- });
-
- testWithoutContext('choose non-ephemeral device', () async {
+ testWithoutContext('returns all devices when multiple non ephemeral devices are found', () async {
final List<Device> devices = <Device>[
ephemeralOne,
ephemeralTwo,
@@ -295,40 +205,19 @@
nonEphemeralTwo,
];
- final FakeTerminal terminal = FakeTerminal()
- ..setPrompt(<String>['1', '2', '3', '4', 'q', 'Q'], '3');
-
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: terminal,
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[
- nonEphemeralOne,
- ]);
- });
-
- testWithoutContext('exit from choose one of available devices', () async {
- final List<Device> devices = <Device>[
ephemeralOne,
ephemeralTwo,
- ];
-
- final FakeTerminal terminal = FakeTerminal()
- ..setPrompt(<String>['1', '2', 'q', 'Q'], 'q');
-
- final DeviceManager deviceManager = TestDeviceManager(
- devices,
- logger: BufferLogger.test(),
- terminal: terminal,
- );
- await expectLater(
- () async => deviceManager.findTargetDevices(FakeFlutterProject()),
- throwsToolExit(),
- );
+ nonEphemeralOne,
+ nonEphemeralTwo,
+ ]);
});
testWithoutContext('Unsupported devices listed in all connected devices', () async {
@@ -340,7 +229,6 @@
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
final List<Device> filtered = await deviceManager.getAllConnectedDevices();
@@ -355,11 +243,9 @@
unsupported,
unsupportedForProject,
];
-
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
@@ -375,7 +261,6 @@
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(null);
@@ -390,7 +275,6 @@
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
deviceManager.specifiedDeviceId = 'all';
@@ -409,7 +293,6 @@
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
deviceManager.specifiedDeviceId = 'all';
@@ -421,6 +304,57 @@
]);
});
+ testWithoutContext('Returns device with the specified id', () async {
+ final List<Device> devices = <Device>[
+ nonEphemeralOne,
+ ];
+ final DeviceManager deviceManager = TestDeviceManager(
+ devices,
+ logger: BufferLogger.test(),
+ );
+ deviceManager.specifiedDeviceId = nonEphemeralOne.id;
+
+ final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
+
+ expect(filtered, <Device>[
+ nonEphemeralOne,
+ ]);
+ });
+
+ testWithoutContext('Returns multiple devices when multiple devices matches the specified id', () async {
+ final List<Device> devices = <Device>[
+ nonEphemeralOne,
+ nonEphemeralTwo,
+ ];
+ final DeviceManager deviceManager = TestDeviceManager(
+ devices,
+ logger: BufferLogger.test(),
+ );
+ deviceManager.specifiedDeviceId = 'nonEphemeral'; // This prefix matches both devices
+
+ final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
+
+ expect(filtered, <Device>[
+ nonEphemeralOne,
+ nonEphemeralTwo,
+ ]);
+ });
+
+ testWithoutContext('Returns empty when device of specified id is not found', () async {
+ final List<Device> devices = <Device>[
+ nonEphemeralOne,
+ ];
+ final DeviceManager deviceManager = TestDeviceManager(
+ devices,
+ logger: BufferLogger.test(),
+ );
+ deviceManager.specifiedDeviceId = nonEphemeralTwo.id;
+
+ final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
+
+ expect(filtered, <Device>[]);
+ });
+
testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
final List<Device> devices = <Device>[
unsupported,
@@ -429,7 +363,6 @@
final TestDeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
deviceManager.isAlwaysSupportedForProjectOverride = true;
@@ -453,7 +386,6 @@
deviceDiscovery,
],
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(
@@ -479,7 +411,6 @@
deviceDiscovery,
],
logger: BufferLogger.test(),
- terminal: Terminal.test(),
);
deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(
@@ -542,11 +473,10 @@
List<Device> allDevices, {
List<DeviceDiscovery>? deviceDiscoveryOverrides,
required super.logger,
- required super.terminal,
String? wellKnownId,
}) : _fakeDeviceDiscoverer = FakePollingDeviceDiscovery(),
_deviceDiscoverers = <DeviceDiscovery>[],
- super(userMessages: UserMessages()) {
+ super() {
if (wellKnownId != null) {
_fakeDeviceDiscoverer.wellKnownIds.add(wellKnownId);
}
@@ -650,31 +580,3 @@
@override
List<String> get wellKnownIds => <String>[];
}
-
-class FakeTerminal extends Fake implements Terminal {
- @override
- bool stdinHasTerminal = true;
-
- @override
- bool usesTerminalUi = true;
-
- void setPrompt(List<String> characters, String result) {
- _nextPrompt = characters;
- _nextResult = result;
- }
-
- List<String>? _nextPrompt;
- late String _nextResult;
-
- @override
- Future<String> promptForCharInput(
- List<String> acceptedCharacters, {
- Logger? logger,
- String? prompt,
- int? defaultChoiceIndex,
- bool displayAcceptedCharacters = true,
- }) async {
- expect(acceptedCharacters, _nextPrompt);
- return _nextResult;
- }
-}
diff --git a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
index 2d73435..765773e 100644
--- a/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
+++ b/packages/flutter_tools/test/general.shard/runner/flutter_command_test.dart
@@ -11,11 +11,15 @@
import 'package:flutter_tools/src/base/error_handling_io.dart';
import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart';
+import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/signals.dart';
+import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/base/time.dart';
+import 'package:flutter_tools/src/base/user_messages.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/pub.dart';
+import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/pre_run_validator.dart';
import 'package:flutter_tools/src/project.dart';
@@ -25,6 +29,7 @@
import '../../src/common.dart';
import '../../src/context.dart';
+import '../../src/fake_devices.dart';
import '../../src/test_flutter_command_runner.dart';
import 'utils.dart';
@@ -669,6 +674,138 @@
FileSystem: () => fileSystem,
ProcessManager: () => processManager,
});
+
+ group('findAllTargetDevices', () {
+ final FakeDevice device1 = FakeDevice('device1', 'device1');
+ final FakeDevice device2 = FakeDevice('device2', 'device2');
+ group('when specified device id', () {
+ testUsingContext('returns device when device is found', () async {
+ testDeviceManager.specifiedDeviceId = 'device-id';
+ testDeviceManager.addDevice(device1);
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, <Device>[device1]);
+ });
+
+ testUsingContext('show error when no device found', () async {
+ testDeviceManager.specifiedDeviceId = 'device-id';
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, null);
+ expect(testLogger.statusText, contains(UserMessages().flutterNoMatchingDevice('device-id')));
+ });
+
+ testUsingContext('show error when multiple devices found', () async {
+ testDeviceManager.specifiedDeviceId = 'device-id';
+ testDeviceManager.addDevice(device1);
+ testDeviceManager.addDevice(device2);
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, null);
+ expect(testLogger.statusText, contains(UserMessages().flutterFoundSpecifiedDevices(2, 'device-id')));
+ });
+ });
+
+ group('when specified all', () {
+ testUsingContext('can return one device', () async {
+ testDeviceManager.specifiedDeviceId = 'all';
+ testDeviceManager.addDevice(device1);
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, <Device>[device1]);
+ });
+
+ testUsingContext('can return multiple devices', () async {
+ testDeviceManager.specifiedDeviceId = 'all';
+ testDeviceManager.addDevice(device1);
+ testDeviceManager.addDevice(device2);
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, <Device>[device1, device2]);
+ });
+
+ testUsingContext('show error when no device found', () async {
+ testDeviceManager.specifiedDeviceId = 'all';
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, null);
+ expect(testLogger.statusText, contains(UserMessages().flutterNoDevicesFound));
+ });
+ });
+
+ group('when device not specified', () {
+ testUsingContext('returns one device when only one device connected', () async {
+ testDeviceManager.addDevice(device1);
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, <Device>[device1]);
+ });
+
+ testUsingContext('show error when no device found', () async {
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, null);
+ expect(testLogger.statusText, contains(UserMessages().flutterNoSupportedDevices));
+ });
+
+ testUsingContext('show error when multiple devices found and not connected to terminal', () async {
+ testDeviceManager.addDevice(device1);
+ testDeviceManager.addDevice(device2);
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+ expect(devices, null);
+ expect(testLogger.statusText, contains(UserMessages().flutterSpecifyDeviceWithAllOption));
+ }, overrides: <Type, Generator>{
+ AnsiTerminal: () => FakeTerminal(stdinHasTerminal: false),
+ });
+
+ // Prompt to choose device when multiple devices found and connected to terminal
+ group('show prompt', () {
+ late FakeTerminal terminal;
+ setUp(() {
+ terminal = FakeTerminal();
+ });
+
+ testUsingContext('choose first device', () async {
+ testDeviceManager.addDevice(device1);
+ testDeviceManager.addDevice(device2);
+ terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+
+ expect(devices, <Device>[device1]);
+ }, overrides: <Type, Generator>{
+ AnsiTerminal: () => terminal,
+ });
+
+ testUsingContext('choose second device', () async {
+ testDeviceManager.addDevice(device1);
+ testDeviceManager.addDevice(device2);
+ terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '2');
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+ final List<Device>? devices = await flutterCommand.findAllTargetDevices();
+
+ expect(devices, <Device>[device2]);
+ }, overrides: <Type, Generator>{
+ AnsiTerminal: () => terminal,
+ });
+
+ testUsingContext('exits without choosing device', () async {
+ testDeviceManager.addDevice(device1);
+ testDeviceManager.addDevice(device2);
+ terminal.setPrompt(<String>['1', '2', 'q', 'Q'], 'q');
+ final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
+
+ await expectLater(
+ flutterCommand.findAllTargetDevices(),
+ throwsToolExit(),
+ );
+ }, overrides: <Type, Generator>{
+ AnsiTerminal: () => terminal,
+ });
+ });
+ });
+ });
});
}
@@ -823,3 +960,33 @@
bool printProgress = true,
}) async { }
}
+
+class FakeTerminal extends Fake implements AnsiTerminal {
+ FakeTerminal({this.stdinHasTerminal = true});
+
+ @override
+ final bool stdinHasTerminal;
+
+ @override
+ bool usesTerminalUi = true;
+
+ void setPrompt(List<String> characters, String result) {
+ _nextPrompt = characters;
+ _nextResult = result;
+ }
+
+ List<String>? _nextPrompt;
+ late String _nextResult;
+
+ @override
+ Future<String> promptForCharInput(
+ List<String> acceptedCharacters, {
+ Logger? logger,
+ String? prompt,
+ int? defaultChoiceIndex,
+ bool displayAcceptedCharacters = true,
+ }) async {
+ expect(acceptedCharacters, _nextPrompt);
+ return _nextResult;
+ }
+}
diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart
index d292e99..8a2dd5d 100644
--- a/packages/flutter_tools/test/src/context.dart
+++ b/packages/flutter_tools/test/src/context.dart
@@ -241,7 +241,7 @@
}
@override
- Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout }) async {
+ Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout, bool promptUserToChooseDevice = true }) async {
return devices;
}
}