Unverified Commit 2a73ce9b authored by Lau Ching Jun's avatar Lau Ching Jun Committed by GitHub

Refactor `DeviceManager.findTargetDevices()` and...

Refactor `DeviceManager.findTargetDevices()` and `FlutterCommand.findAllTargetDevices()`, and add a flag to not show prompt. (#112223)
parent eefd5d4c
...@@ -205,7 +205,6 @@ Future<T> runInContext<T>( ...@@ -205,7 +205,6 @@ Future<T> runInContext<T>(
), ),
fuchsiaSdk: globals.fuchsiaSdk!, fuchsiaSdk: globals.fuchsiaSdk!,
operatingSystemUtils: globals.os, operatingSystemUtils: globals.os,
terminal: globals.terminal,
customDevicesConfig: globals.customDevicesConfig, customDevicesConfig: globals.customDevicesConfig,
), ),
DevtoolsLauncher: () => DevtoolsServerLauncher( DevtoolsLauncher: () => DevtoolsServerLauncher(
......
...@@ -9,13 +9,10 @@ import 'package:meta/meta.dart'; ...@@ -9,13 +9,10 @@ import 'package:meta/meta.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'artifacts.dart'; import 'artifacts.dart';
import 'base/common.dart';
import 'base/context.dart'; import 'base/context.dart';
import 'base/dds.dart'; import 'base/dds.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'base/terminal.dart';
import 'base/user_messages.dart' hide userMessages;
import 'base/utils.dart'; import 'base/utils.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'devfs.dart'; import 'devfs.dart';
...@@ -83,15 +80,9 @@ class PlatformType { ...@@ -83,15 +80,9 @@ class PlatformType {
abstract class DeviceManager { abstract class DeviceManager {
DeviceManager({ DeviceManager({
required Logger logger, required Logger logger,
required Terminal terminal, }) : _logger = logger;
required UserMessages userMessages,
}) : _logger = logger,
_terminal = terminal,
_userMessages = userMessages;
final Logger _logger; final Logger _logger;
final Terminal _terminal;
final UserMessages _userMessages;
/// Constructing DeviceManagers is cheap; they only do expensive work if some /// Constructing DeviceManagers is cheap; they only do expensive work if some
/// of their methods are called. /// of their methods are called.
...@@ -219,7 +210,12 @@ abstract class DeviceManager { ...@@ -219,7 +210,12 @@ abstract class DeviceManager {
]; ];
} }
/// 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. /// Returns a list of devices specified by the user.
/// ///
...@@ -233,9 +229,13 @@ abstract class DeviceManager { ...@@ -233,9 +229,13 @@ abstract class DeviceManager {
/// device connected, then filter out unsupported devices and prioritize /// device connected, then filter out unsupported devices and prioritize
/// ephemeral devices. /// ephemeral devices.
/// ///
/// * If [flutterProject] is null, then assume the project supports all /// * If [promptUserToChooseDevice] is true, and there are more than one
/// device types. /// device after the aforementioned filters, and the user is connected to a
Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout }) async { /// terminal, then show a prompt asking the user to choose one.
Future<List<Device>> findTargetDevices(
FlutterProject? flutterProject, {
Duration? timeout,
}) async {
if (timeout != null) { if (timeout != null) {
// Reset the cache with the specified timeout. // Reset the cache with the specified timeout.
await refreshAllConnectedDevices(timeout: timeout); await refreshAllConnectedDevices(timeout: timeout);
...@@ -244,95 +244,54 @@ abstract class DeviceManager { ...@@ -244,95 +244,54 @@ abstract class DeviceManager {
List<Device> devices = (await getDevices()) List<Device> devices = (await getDevices())
.where((Device device) => device.isSupported()).toList(); .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) { 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>[ devices = <Device>[
for (final Device device in devices) for (final Device device in devices)
if (await device.targetPlatform != TargetPlatform.fuchsia_arm64 && if (await device.targetPlatform != TargetPlatform.fuchsia_arm64 &&
await device.targetPlatform != TargetPlatform.fuchsia_x64 && await device.targetPlatform != TargetPlatform.fuchsia_x64 &&
await device.targetPlatform != TargetPlatform.web_javascript) await device.targetPlatform != TargetPlatform.web_javascript &&
isDeviceSupportedForProject(device, flutterProject))
device, device,
]; ];
} } else if (!hasSpecifiedDeviceId) {
// User did not specify the device.
// If there is no specified device, the remove all devices which are not // Remove all devices which are not supported by the current application.
// supported by the current application. For example, if there was no // For example, if there was no 'android' folder then don't attempt to
// 'android' folder then don't attempt to launch with an Android device. // launch with an Android device.
if (devices.length > 1 && !hasSpecifiedDeviceId) {
devices = <Device>[ devices = <Device>[
for (final Device device in devices) for (final Device device in devices)
if (isDeviceSupportedForProject(device, flutterProject)) if (isDeviceSupportedForProject(device, flutterProject))
device, 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 if (devices.length > 1) {
// all, then attempt to prioritize ephemeral devices. For example, if the // If there are still multiple devices and the user did not specify to run
// user only typed 'flutter run' and both an Android device and desktop // all, then attempt to prioritize ephemeral devices. For example, if the
// device are available, choose the Android device. // user only typed 'flutter run' and both an Android device and desktop
if (devices.length > 1 && !hasSpecifiedAllDevices) { // device are available, choose the Android device.
// Note: ephemeral is nullable for device types where this is not well
// defined. // Note: ephemeral is nullable for device types where this is not well
if (devices.any((Device device) => device.ephemeral == true)) { // defined.
// if there is only one ephemeral device, get it final List<Device> ephemeralDevices = <Device>[
final List<Device> ephemeralDevices = devices for (final Device device in devices)
.where((Device device) => device.ephemeral == true) if (device.ephemeral == true)
.toList(); device,
];
if (ephemeralDevices.length == 1) {
devices = ephemeralDevices; 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];
}
}
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 { return devices;
_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. /// Returns whether the device is supported for the project.
......
...@@ -11,6 +11,7 @@ import 'artifacts.dart'; ...@@ -11,6 +11,7 @@ import 'artifacts.dart';
import 'base/file_system.dart'; import 'base/file_system.dart';
import 'base/os.dart'; import 'base/os.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'base/user_messages.dart';
import 'custom_devices/custom_device.dart'; import 'custom_devices/custom_device.dart';
import 'custom_devices/custom_devices_config.dart'; import 'custom_devices/custom_devices_config.dart';
import 'device.dart'; import 'device.dart';
...@@ -51,10 +52,9 @@ class FlutterDeviceManager extends DeviceManager { ...@@ -51,10 +52,9 @@ class FlutterDeviceManager extends DeviceManager {
required Artifacts artifacts, required Artifacts artifacts,
required MacOSWorkflow macOSWorkflow, required MacOSWorkflow macOSWorkflow,
required FuchsiaSdk fuchsiaSdk, required FuchsiaSdk fuchsiaSdk,
required super.userMessages, required UserMessages userMessages,
required OperatingSystemUtils operatingSystemUtils, required OperatingSystemUtils operatingSystemUtils,
required WindowsWorkflow windowsWorkflow, required WindowsWorkflow windowsWorkflow,
required super.terminal,
required CustomDevicesConfig customDevicesConfig, required CustomDevicesConfig customDevicesConfig,
}) : deviceDiscoverers = <DeviceDiscovery>[ }) : deviceDiscoverers = <DeviceDiscovery>[
AndroidDevices( AndroidDevices(
......
...@@ -1413,52 +1413,105 @@ abstract class FlutterCommand extends Command<void> { ...@@ -1413,52 +1413,105 @@ abstract class FlutterCommand extends Command<void> {
timeout: deviceDiscoveryTimeout, timeout: deviceDiscoveryTimeout,
); );
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) { if (devices.isEmpty) {
globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!)); if (deviceManager.hasSpecifiedDeviceId) {
final List<Device> allDevices = await deviceManager.getAllConnectedDevices(); globals.logger.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
if (allDevices.isNotEmpty) { final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
globals.printStatus(''); if (allDevices.isNotEmpty) {
globals.printStatus('The following devices were found:'); globals.logger.printStatus('');
await Device.printDevices(allDevices, globals.logger); globals.logger.printStatus('The following devices were found:');
} await Device.printDevices(allDevices, globals.logger);
return null; }
} else if (devices.isEmpty) { return null;
if (deviceManager.hasSpecifiedAllDevices) { } else if (deviceManager.hasSpecifiedAllDevices) {
globals.printStatus(userMessages.flutterNoDevicesFound); globals.logger.printStatus(userMessages.flutterNoDevicesFound);
await _printUnsupportedDevice(deviceManager);
return null;
} else { } else {
globals.printStatus(userMessages.flutterNoSupportedDevices); globals.logger.printStatus(userMessages.flutterNoSupportedDevices);
} await _printUnsupportedDevice(deviceManager);
final List<Device> unsupportedDevices = await deviceManager.getDevices(); return null;
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) {
} else if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices) {
if (deviceManager.hasSpecifiedDeviceId) { if (deviceManager.hasSpecifiedDeviceId) {
globals.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!)); globals.logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
} else { return null;
globals.printStatus(userMessages.flutterSpecifyDeviceWithAllOption); } else if (!deviceManager.hasSpecifiedAllDevices) {
devices = await deviceManager.getAllConnectedDevices(); 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;
}
} }
globals.printStatus('');
await Device.printDevices(devices, globals.logger);
return null;
} }
return devices; 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 /// Find and return the target [Device] based upon currently connected
/// devices and criteria entered by the user on the command line. /// devices and criteria entered by the user on the command line.
/// If a device cannot be found that meets specified criteria, /// If a device cannot be found that meets specified criteria,
......
...@@ -46,6 +46,7 @@ void main() { ...@@ -46,6 +46,7 @@ void main() {
group('attach', () { group('attach', () {
late StreamLogger logger; late StreamLogger logger;
late FileSystem testFileSystem; late FileSystem testFileSystem;
late TestDeviceManager testDeviceManager;
setUp(() { setUp(() {
Cache.disableLocking(); Cache.disableLocking();
...@@ -57,6 +58,7 @@ void main() { ...@@ -57,6 +58,7 @@ void main() {
); );
testFileSystem.directory('lib').createSync(); testFileSystem.directory('lib').createSync();
testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync(); testFileSystem.file(testFileSystem.path.join('lib', 'main.dart')).createSync();
testDeviceManager = TestDeviceManager(logger: BufferLogger.test());
}); });
group('with one device and no specified target file', () { group('with one device and no specified target file', () {
...@@ -92,7 +94,7 @@ void main() { ...@@ -92,7 +94,7 @@ void main() {
}, },
); );
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) { final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') { if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') {
...@@ -113,6 +115,7 @@ void main() { ...@@ -113,6 +115,7 @@ void main() {
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery( MDnsObservatoryDiscovery: () => MDnsObservatoryDiscovery(
mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}), mdnsClient: FakeMDnsClient(<PtrResourceRecord>[], <String, List<SrvResourceRecord>>{}),
logger: logger, logger: logger,
...@@ -126,7 +129,7 @@ void main() { ...@@ -126,7 +129,7 @@ void main() {
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort'); fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader; return fakeLogReader;
}; };
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) { final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') { if (message == '[verbose] Observatory URL on device: http://127.0.0.1:$devicePort') {
...@@ -147,6 +150,7 @@ void main() { ...@@ -147,6 +150,7 @@ void main() {
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('Fails with tool exit on bad Observatory uri', () async { testUsingContext('Fails with tool exit on bad Observatory uri', () async {
...@@ -156,12 +160,13 @@ void main() { ...@@ -156,12 +160,13 @@ void main() {
fakeLogReader.dispose(); fakeLogReader.dispose();
return fakeLogReader; return fakeLogReader;
}; };
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
expect(() => createTestCommandRunner(AttachCommand()).run(<String>['attach']), throwsToolExit()); expect(() => createTestCommandRunner(AttachCommand()).run(<String>['attach']), throwsToolExit());
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('accepts filesystem parameters', () async { testUsingContext('accepts filesystem parameters', () async {
...@@ -170,7 +175,7 @@ void main() { ...@@ -170,7 +175,7 @@ void main() {
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort'); fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader; return fakeLogReader;
}; };
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
const String filesystemScheme = 'foo'; const String filesystemScheme = 'foo';
const String filesystemRoot = '/build-output/'; const String filesystemRoot = '/build-output/';
...@@ -221,10 +226,11 @@ void main() { ...@@ -221,10 +226,11 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('exits when ipv6 is specified and debug-port is not', () async { testUsingContext('exits when ipv6 is specified and debug-port is not', () async {
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
final AttachCommand command = AttachCommand(); final AttachCommand command = AttachCommand();
await expectLater( await expectLater(
...@@ -237,6 +243,7 @@ void main() { ...@@ -237,6 +243,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
},); },);
testUsingContext('exits when observatory-port is specified and debug-port is not', () async { testUsingContext('exits when observatory-port is specified and debug-port is not', () async {
...@@ -245,7 +252,7 @@ void main() { ...@@ -245,7 +252,7 @@ void main() {
fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort'); fakeLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:$devicePort');
return fakeLogReader; return fakeLogReader;
}; };
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
final AttachCommand command = AttachCommand(); final AttachCommand command = AttachCommand();
await expectLater( await expectLater(
...@@ -258,6 +265,7 @@ void main() { ...@@ -258,6 +265,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
},); },);
}); });
...@@ -276,7 +284,7 @@ void main() { ...@@ -276,7 +284,7 @@ void main() {
}); });
testUsingContext('succeeds in ipv4 mode', () async { testUsingContext('succeeds in ipv4 mode', () async {
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) { final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
...@@ -298,10 +306,11 @@ void main() { ...@@ -298,10 +306,11 @@ void main() {
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('succeeds in ipv6 mode', () async { testUsingContext('succeeds in ipv6 mode', () async {
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
final Completer<void> completer = Completer<void>(); final Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) { final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
...@@ -324,10 +333,11 @@ void main() { ...@@ -324,10 +333,11 @@ void main() {
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('skips in ipv4 mode with a provided observatory port', () async { 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 Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) { final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
...@@ -359,10 +369,11 @@ void main() { ...@@ -359,10 +369,11 @@ void main() {
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('skips in ipv6 mode with a provided observatory port', () async { 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 Completer<void> completer = Completer<void>();
final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) { final StreamSubscription<String> loggerSubscription = logger.stream.listen((String message) {
...@@ -395,6 +406,7 @@ void main() { ...@@ -395,6 +406,7 @@ void main() {
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Logger: () => logger, Logger: () => logger,
DeviceManager: () => testDeviceManager,
}); });
}); });
...@@ -408,11 +420,12 @@ void main() { ...@@ -408,11 +420,12 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('fails when targeted device is not Android with --device-user', () async { testUsingContext('fails when targeted device is not Android with --device-user', () async {
final FakeIOSDevice device = FakeIOSDevice(); final FakeIOSDevice device = FakeIOSDevice();
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
expect(createTestCommandRunner(AttachCommand()).run(<String>[ expect(createTestCommandRunner(AttachCommand()).run(<String>[
'attach', 'attach',
'--device-user', '--device-user',
...@@ -421,12 +434,15 @@ void main() { ...@@ -421,12 +434,15 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('exits when multiple devices connected', () async { testUsingContext('exits when multiple devices connected', () async {
final AttachCommand command = AttachCommand(); final AttachCommand command = AttachCommand();
testDeviceManager.addDevice(FakeAndroidDevice(id: 'xx1')); testDeviceManager.devices = <Device>[
testDeviceManager.addDevice(FakeAndroidDevice(id: 'yy2')); FakeAndroidDevice(id: 'xx1'),
FakeAndroidDevice(id: 'yy2'),
];
await expectLater( await expectLater(
createTestCommandRunner(command).run(<String>['attach']), createTestCommandRunner(command).run(<String>['attach']),
throwsToolExit(), throwsToolExit(),
...@@ -438,6 +454,8 @@ void main() { ...@@ -438,6 +454,8 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
AnsiTerminal: () => FakeTerminal(stdinHasTerminal: false),
}); });
testUsingContext('Catches service disappeared error', () async { testUsingContext('Catches service disappeared error', () async {
...@@ -457,7 +475,7 @@ void main() { ...@@ -457,7 +475,7 @@ void main() {
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kServiceDisappeared, ''); throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kServiceDisappeared, '');
}; };
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
testFileSystem.file('lib/main.dart').createSync(); testFileSystem.file('lib/main.dart').createSync();
final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory); final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory);
...@@ -467,6 +485,7 @@ void main() { ...@@ -467,6 +485,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
}); });
testUsingContext('Does not catch generic RPC error', () async { testUsingContext('Does not catch generic RPC error', () async {
...@@ -487,7 +506,7 @@ void main() { ...@@ -487,7 +506,7 @@ void main() {
throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kInvalidParams, ''); throw vm_service.RPCError('flutter._listViews', RPCErrorCodes.kInvalidParams, '');
}; };
testDeviceManager.addDevice(device); testDeviceManager.devices = <Device>[device];
testFileSystem.file('lib/main.dart').createSync(); testFileSystem.file('lib/main.dart').createSync();
final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory); final AttachCommand command = AttachCommand(hotRunnerFactory: hotRunnerFactory);
...@@ -497,6 +516,7 @@ void main() { ...@@ -497,6 +516,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => testFileSystem, FileSystem: () => testFileSystem,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => testDeviceManager,
}); });
}); });
} }
...@@ -791,6 +811,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice { ...@@ -791,6 +811,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
@override @override
Category get category => Category.mobile; Category get category => Category.mobile;
@override
bool get ephemeral => true;
} }
// Unfortunately Device, despite not being immutable, has an `operator ==`. // Unfortunately Device, despite not being immutable, has an `operator ==`.
...@@ -840,6 +863,12 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -840,6 +863,12 @@ class FakeIOSDevice extends Fake implements IOSDevice {
@override @override
final PlatformType platformType = PlatformType.ios; final PlatformType platformType = PlatformType.ios;
@override
bool isSupported() => true;
@override
bool isSupportedForProject(FlutterProject project) => true;
} }
class FakeMDnsClient extends Fake implements MDnsClient { class FakeMDnsClient extends Fake implements MDnsClient {
...@@ -887,3 +916,24 @@ class FakeMDnsClient extends Fake implements MDnsClient { ...@@ -887,3 +916,24 @@ class FakeMDnsClient extends Fake implements MDnsClient {
@override @override
void stop() {} 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;
}
...@@ -6,8 +6,6 @@ import 'dart:convert'; ...@@ -6,8 +6,6 @@ import 'dart:convert';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/artifacts.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/cache.dart';
import 'package:flutter_tools/src/commands/devices.dart'; import 'package:flutter_tools/src/commands/devices.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
...@@ -133,7 +131,7 @@ webby (mobile) • webby • web-javascript • Web SDK (1.2.4) (emulato ...@@ -133,7 +131,7 @@ webby (mobile) • webby • web-javascript • Web SDK (1.2.4) (emulato
} }
class _FakeDeviceManager extends DeviceManager { class _FakeDeviceManager extends DeviceManager {
_FakeDeviceManager() : super(logger: testLogger, terminal: Terminal.test(), userMessages: userMessages); _FakeDeviceManager() : super(logger: testLogger);
@override @override
Future<List<Device>> getAllConnectedDevices() => Future<List<Device>> getAllConnectedDevices() =>
...@@ -153,7 +151,7 @@ class _FakeDeviceManager extends DeviceManager { ...@@ -153,7 +151,7 @@ class _FakeDeviceManager extends DeviceManager {
} }
class NoDevicesManager extends DeviceManager { class NoDevicesManager extends DeviceManager {
NoDevicesManager() : super(logger: testLogger, terminal: Terminal.test(), userMessages: userMessages); NoDevicesManager() : super(logger: testLogger);
@override @override
Future<List<Device>> getAllConnectedDevices() async => <Device>[]; Future<List<Device>> getAllConnectedDevices() async => <Device>[];
......
...@@ -328,7 +328,7 @@ class FakeDeviceManager extends Fake implements DeviceManager { ...@@ -328,7 +328,7 @@ class FakeDeviceManager extends Fake implements DeviceManager {
Future<List<Device>> getDevices() async => devices; Future<List<Device>> getDevices() async => devices;
@override @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 { class FailingFakeFlutterDriverFactory extends Fake implements FlutterDriverFactory {
......
...@@ -44,11 +44,11 @@ void main() { ...@@ -44,11 +44,11 @@ void main() {
}); });
group('run', () { group('run', () {
late FakeDeviceManager mockDeviceManager; late TestDeviceManager testDeviceManager;
late FileSystem fileSystem; late FileSystem fileSystem;
setUp(() { setUp(() {
mockDeviceManager = FakeDeviceManager(); testDeviceManager = TestDeviceManager(logger: BufferLogger.test());
fileSystem = MemoryFileSystem.test(); fileSystem = MemoryFileSystem.test();
}); });
...@@ -166,9 +166,7 @@ void main() { ...@@ -166,9 +166,7 @@ void main() {
testUsingContext('exits with a user message when no supported devices attached', () async { testUsingContext('exits with a user message when no supported devices attached', () async {
final RunCommand command = RunCommand(); final RunCommand command = RunCommand();
mockDeviceManager testDeviceManager.devices = <Device>[];
..devices = <Device>[]
..targetDevices = <Device>[];
await expectLater( await expectLater(
() => createTestCommandRunner(command).run(<String>[ () => createTestCommandRunner(command).run(<String>[
...@@ -184,7 +182,7 @@ void main() { ...@@ -184,7 +182,7 @@ void main() {
containsIgnoringWhitespace(userMessages.flutterNoSupportedDevices), containsIgnoringWhitespace(userMessages.flutterNoSupportedDevices),
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
...@@ -193,9 +191,9 @@ void main() { ...@@ -193,9 +191,9 @@ void main() {
testUsingContext('exits and lists available devices when specified device not found', () async { testUsingContext('exits and lists available devices when specified device not found', () async {
final RunCommand command = RunCommand(); final RunCommand command = RunCommand();
final FakeDevice device = FakeDevice(isLocalEmulator: true); final FakeDevice device = FakeDevice(isLocalEmulator: true);
mockDeviceManager testDeviceManager
..devices = <Device>[device] ..devices = <Device>[device]
..hasSpecifiedDeviceId = true; ..specifiedDeviceId = 'invalid-device-id';
await expectLater( await expectLater(
() => createTestCommandRunner(command).run(<String>[ () => createTestCommandRunner(command).run(<String>[
...@@ -211,7 +209,7 @@ void main() { ...@@ -211,7 +209,7 @@ void main() {
expect(testLogger.statusText, contains('The following devices were found:')); expect(testLogger.statusText, contains('The following devices were found:'));
expect(testLogger.statusText, contains('FakeDevice (mobile) • fake_device • ios • (simulator)')); expect(testLogger.statusText, contains('FakeDevice (mobile) • fake_device • ios • (simulator)'));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
...@@ -220,9 +218,7 @@ void main() { ...@@ -220,9 +218,7 @@ void main() {
testUsingContext('fails when targeted device is not Android with --device-user', () async { testUsingContext('fails when targeted device is not Android with --device-user', () async {
final FakeDevice device = FakeDevice(isLocalEmulator: true); final FakeDevice device = FakeDevice(isLocalEmulator: true);
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates(); final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates();
await expectLater(createTestCommandRunner(command).run(<String>[ await expectLater(createTestCommandRunner(command).run(<String>[
...@@ -234,7 +230,7 @@ void main() { ...@@ -234,7 +230,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
}); });
...@@ -242,9 +238,7 @@ void main() { ...@@ -242,9 +238,7 @@ void main() {
testUsingContext('succeeds when targeted device is an Android device with --device-user', () async { testUsingContext('succeeds when targeted device is an Android device with --device-user', () async {
final FakeDevice device = FakeDevice(isLocalEmulator: true, platformType: PlatformType.android); final FakeDevice device = FakeDevice(isLocalEmulator: true, platformType: PlatformType.android);
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates(); final TestRunCommandThatOnlyValidates command = TestRunCommandThatOnlyValidates();
await createTestCommandRunner(command).run(<String>[ await createTestCommandRunner(command).run(<String>[
...@@ -257,7 +251,7 @@ void main() { ...@@ -257,7 +251,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
}); });
...@@ -285,9 +279,7 @@ void main() { ...@@ -285,9 +279,7 @@ void main() {
processManager: FakeProcessManager.any(), processManager: FakeProcessManager.any(),
); );
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
final RunCommand command = RunCommand(); final RunCommand command = RunCommand();
await expectLater(createTestCommandRunner(command).run(<String>[ await expectLater(createTestCommandRunner(command).run(<String>[
...@@ -297,7 +289,7 @@ void main() { ...@@ -297,7 +289,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
}); });
...@@ -327,9 +319,7 @@ void main() { ...@@ -327,9 +319,7 @@ void main() {
processManager: FakeProcessManager.any(), processManager: FakeProcessManager.any(),
); );
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
final RunCommand command = RunCommand(); final RunCommand command = RunCommand();
await expectLater(createTestCommandRunner(command).run(<String>[ await expectLater(createTestCommandRunner(command).run(<String>[
...@@ -339,17 +329,20 @@ void main() { ...@@ -339,17 +329,20 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
}); });
testUsingContext('shows unsupported devices when no supported devices are found', () async { testUsingContext('shows unsupported devices when no supported devices are found', () async {
final RunCommand command = RunCommand(); final RunCommand command = RunCommand();
final FakeDevice mockDevice = FakeDevice(targetPlatform: TargetPlatform.android_arm, isLocalEmulator: true, sdkNameAndVersion: 'api-14'); final FakeDevice mockDevice = FakeDevice(
mockDeviceManager targetPlatform: TargetPlatform.android_arm,
..devices = <Device>[mockDevice] isLocalEmulator: true,
..targetDevices = <Device>[]; sdkNameAndVersion: 'api-14',
isSupported: false,
);
testDeviceManager.devices = <Device>[mockDevice];
await expectLater( await expectLater(
() => createTestCommandRunner(command).run(<String>[ () => createTestCommandRunner(command).run(<String>[
...@@ -377,7 +370,7 @@ void main() { ...@@ -377,7 +370,7 @@ void main() {
), ),
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
...@@ -389,13 +382,7 @@ void main() { ...@@ -389,13 +382,7 @@ void main() {
sdkNameAndVersion: 'iOS 13', sdkNameAndVersion: 'iOS 13',
)..startAppSuccess = false; )..startAppSuccess = false;
mockDeviceManager testDeviceManager.devices = <Device>[mockDevice];
..devices = <Device>[
mockDevice,
]
..targetDevices = <Device>[
mockDevice,
];
// Causes swift to be detected in the analytics. // Causes swift to be detected in the analytics.
fs.currentDirectory.childDirectory('ios').childFile('AppDelegate.swift').createSync(recursive: true); fs.currentDirectory.childDirectory('ios').childFile('AppDelegate.swift').createSync(recursive: true);
...@@ -412,7 +399,7 @@ void main() { ...@@ -412,7 +399,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Artifacts: () => artifacts, Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage, Usage: () => usage,
...@@ -423,13 +410,7 @@ void main() { ...@@ -423,13 +410,7 @@ void main() {
final FakeDevice mockDevice = FakeDevice(sdkNameAndVersion: 'iOS 13') final FakeDevice mockDevice = FakeDevice(sdkNameAndVersion: 'iOS 13')
..startAppSuccess = false; ..startAppSuccess = false;
mockDeviceManager testDeviceManager.devices = <Device>[mockDevice];
..devices = <Device>[
mockDevice,
]
..targetDevices = <Device>[
mockDevice,
];
// Causes swift to be detected in the analytics. // Causes swift to be detected in the analytics.
fs.currentDirectory.childDirectory('ios').childFile('AppDelegate.swift').createSync(recursive: true); fs.currentDirectory.childDirectory('ios').childFile('AppDelegate.swift').createSync(recursive: true);
...@@ -451,7 +432,7 @@ void main() { ...@@ -451,7 +432,7 @@ void main() {
AnsiTerminal: () => fakeTerminal, AnsiTerminal: () => fakeTerminal,
Artifacts: () => artifacts, Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Stdio: () => FakeStdio(), Stdio: () => FakeStdio(),
...@@ -462,9 +443,7 @@ void main() { ...@@ -462,9 +443,7 @@ void main() {
testUsingContext('enables multidex by default', () async { testUsingContext('enables multidex by default', () async {
final DaemonCapturingRunCommand command = DaemonCapturingRunCommand(); final DaemonCapturingRunCommand command = DaemonCapturingRunCommand();
final FakeDevice device = FakeDevice(); final FakeDevice device = FakeDevice();
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
await expectLater( await expectLater(
() => createTestCommandRunner(command).run(<String>[ () => createTestCommandRunner(command).run(<String>[
...@@ -480,7 +459,7 @@ void main() { ...@@ -480,7 +459,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Artifacts: () => artifacts, Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage, Usage: () => usage,
...@@ -491,9 +470,7 @@ void main() { ...@@ -491,9 +470,7 @@ void main() {
testUsingContext('can disable multidex with --no-multidex', () async { testUsingContext('can disable multidex with --no-multidex', () async {
final DaemonCapturingRunCommand command = DaemonCapturingRunCommand(); final DaemonCapturingRunCommand command = DaemonCapturingRunCommand();
final FakeDevice device = FakeDevice(); final FakeDevice device = FakeDevice();
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
await expectLater( await expectLater(
() => createTestCommandRunner(command).run(<String>[ () => createTestCommandRunner(command).run(<String>[
...@@ -510,7 +487,7 @@ void main() { ...@@ -510,7 +487,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Artifacts: () => artifacts, Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage, Usage: () => usage,
...@@ -521,9 +498,7 @@ void main() { ...@@ -521,9 +498,7 @@ void main() {
testUsingContext('can pass --device-user', () async { testUsingContext('can pass --device-user', () async {
final DaemonCapturingRunCommand command = DaemonCapturingRunCommand(); final DaemonCapturingRunCommand command = DaemonCapturingRunCommand();
final FakeDevice device = FakeDevice(platformType: PlatformType.android); final FakeDevice device = FakeDevice(platformType: PlatformType.android);
mockDeviceManager testDeviceManager.devices = <Device>[device];
..devices = <Device>[device]
..targetDevices = <Device>[device];
await expectLater( await expectLater(
() => createTestCommandRunner(command).run(<String>[ () => createTestCommandRunner(command).run(<String>[
...@@ -541,7 +516,7 @@ void main() { ...@@ -541,7 +516,7 @@ void main() {
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Artifacts: () => artifacts, Artifacts: () => artifacts,
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
FileSystem: () => fs, FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Usage: () => usage, Usage: () => usage,
...@@ -621,21 +596,21 @@ void main() { ...@@ -621,21 +596,21 @@ void main() {
}); });
testUsingContext('should only request artifacts corresponding to connected devices', () async { 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>{ expect(await RunCommand().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidGenSnapshot,
})); }));
mockDeviceManager.devices = <Device>[FakeDevice()]; testDeviceManager.devices = <Device>[FakeDevice()];
expect(await RunCommand().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{ expect(await RunCommand().requiredArtifacts, unorderedEquals(<DevelopmentArtifact>{
DevelopmentArtifact.universal, DevelopmentArtifact.universal,
DevelopmentArtifact.iOS, DevelopmentArtifact.iOS,
})); }));
mockDeviceManager.devices = <Device>[ testDeviceManager.devices = <Device>[
FakeDevice(), FakeDevice(),
FakeDevice(targetPlatform: TargetPlatform.android_arm), FakeDevice(targetPlatform: TargetPlatform.android_arm),
]; ];
...@@ -646,7 +621,7 @@ void main() { ...@@ -646,7 +621,7 @@ void main() {
DevelopmentArtifact.androidGenSnapshot, DevelopmentArtifact.androidGenSnapshot,
})); }));
mockDeviceManager.devices = <Device>[ testDeviceManager.devices = <Device>[
FakeDevice(targetPlatform: TargetPlatform.web_javascript), FakeDevice(targetPlatform: TargetPlatform.web_javascript),
]; ];
...@@ -655,7 +630,7 @@ void main() { ...@@ -655,7 +630,7 @@ void main() {
DevelopmentArtifact.web, DevelopmentArtifact.web,
})); }));
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => mockDeviceManager, DeviceManager: () => testDeviceManager,
Cache: () => Cache.test(processManager: FakeProcessManager.any()), Cache: () => Cache.test(processManager: FakeProcessManager.any()),
FileSystem: () => MemoryFileSystem.test(), FileSystem: () => MemoryFileSystem.test(),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
...@@ -881,28 +856,11 @@ void main() { ...@@ -881,28 +856,11 @@ void main() {
}); });
} }
class FakeDeviceManager extends Fake implements DeviceManager { class TestDeviceManager extends DeviceManager {
TestDeviceManager({required this.logger}) : super(logger: logger);
List<Device> devices = <Device>[]; List<Device> devices = <Device>[];
List<Device> targetDevices = <Device>[];
@override
String? specifiedDeviceId;
@override final Logger logger;
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;
}
@override @override
List<DeviceDiscovery> get deviceDiscoverers { List<DeviceDiscovery> get deviceDiscoverers {
...@@ -910,9 +868,6 @@ class FakeDeviceManager extends Fake implements DeviceManager { ...@@ -910,9 +868,6 @@ class FakeDeviceManager extends Fake implements DeviceManager {
devices.forEach(discoverer.addDevice); devices.forEach(discoverer.addDevice);
return <DeviceDiscovery>[discoverer]; return <DeviceDiscovery>[discoverer];
} }
@override
Future<List<Device>> getAllConnectedDevices() async => devices;
} }
class FakeAndroidSdk extends Fake implements AndroidSdk { class FakeAndroidSdk extends Fake implements AndroidSdk {
...@@ -929,10 +884,12 @@ class FakeDevice extends Fake implements Device { ...@@ -929,10 +884,12 @@ class FakeDevice extends Fake implements Device {
TargetPlatform targetPlatform = TargetPlatform.ios, TargetPlatform targetPlatform = TargetPlatform.ios,
String sdkNameAndVersion = '', String sdkNameAndVersion = '',
PlatformType platformType = PlatformType.ios, PlatformType platformType = PlatformType.ios,
bool isSupported = true,
}): _isLocalEmulator = isLocalEmulator, }): _isLocalEmulator = isLocalEmulator,
_targetPlatform = targetPlatform, _targetPlatform = targetPlatform,
_sdkNameAndVersion = sdkNameAndVersion, _sdkNameAndVersion = sdkNameAndVersion,
_platformType = platformType; _platformType = platformType,
_isSupported = isSupported;
static const int kSuccess = 1; static const int kSuccess = 1;
static const int kFailure = -1; static const int kFailure = -1;
...@@ -940,6 +897,7 @@ class FakeDevice extends Fake implements Device { ...@@ -940,6 +897,7 @@ class FakeDevice extends Fake implements Device {
final bool _isLocalEmulator; final bool _isLocalEmulator;
final String _sdkNameAndVersion; final String _sdkNameAndVersion;
final PlatformType _platformType; final PlatformType _platformType;
final bool _isSupported;
@override @override
Category get category => Category.mobile; Category get category => Category.mobile;
...@@ -970,7 +928,7 @@ class FakeDevice extends Fake implements Device { ...@@ -970,7 +928,7 @@ class FakeDevice extends Fake implements Device {
bool supported = true; bool supported = true;
@override @override
bool isSupportedForProject(FlutterProject flutterProject) => true; bool isSupportedForProject(FlutterProject flutterProject) => _isSupported;
@override @override
bool isSupported() => supported; bool isSupported() => supported;
......
...@@ -10,8 +10,6 @@ import 'package:file/memory.dart'; ...@@ -10,8 +10,6 @@ import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.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/cache.dart';
import 'package:flutter_tools/src/commands/test.dart'; import 'package:flutter_tools/src/commands/test.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
...@@ -851,7 +849,7 @@ class FakePackageTest implements TestWrapper { ...@@ -851,7 +849,7 @@ class FakePackageTest implements TestWrapper {
} }
class _FakeDeviceManager extends DeviceManager { 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; final List<Device> _connectedDevices;
......
...@@ -7,8 +7,6 @@ import 'dart:async'; ...@@ -7,8 +7,6 @@ import 'dart:async';
import 'package:fake_async/fake_async.dart'; import 'package:fake_async/fake_async.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.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/base/utils.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
...@@ -30,7 +28,6 @@ void main() { ...@@ -30,7 +28,6 @@ void main() {
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
expect(await deviceManager.getDevices(), devices); expect(await deviceManager.getDevices(), devices);
...@@ -56,7 +53,6 @@ void main() { ...@@ -56,7 +53,6 @@ void main() {
LongPollingDeviceDiscovery(), LongPollingDeviceDiscovery(),
], ],
logger: logger, logger: logger,
terminal: Terminal.test(),
); );
Future<void> expectDevice(String id, List<Device> expected) async { Future<void> expectDevice(String id, List<Device> expected) async {
...@@ -85,7 +81,6 @@ void main() { ...@@ -85,7 +81,6 @@ void main() {
LongPollingDeviceDiscovery(), LongPollingDeviceDiscovery(),
], ],
logger: logger, logger: logger,
terminal: Terminal.test(),
wellKnownId: 'windows', wellKnownId: 'windows',
); );
...@@ -114,7 +109,6 @@ void main() { ...@@ -114,7 +109,6 @@ void main() {
ThrowingPollingDeviceDiscovery(), ThrowingPollingDeviceDiscovery(),
], ],
logger: logger, logger: logger,
terminal: Terminal.test(),
); );
Future<void> expectDevice(String id, List<Device> expected) async { Future<void> expectDevice(String id, List<Device> expected) async {
...@@ -133,7 +127,6 @@ void main() { ...@@ -133,7 +127,6 @@ void main() {
final TestDeviceManager deviceManager = TestDeviceManager( final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[device1], <Device>[device1],
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]); expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]);
...@@ -147,7 +140,6 @@ void main() { ...@@ -147,7 +140,6 @@ void main() {
final TestDeviceManager deviceManager = TestDeviceManager( final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[device1], <Device>[device1],
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device1]); expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device1]);
...@@ -199,226 +191,168 @@ void main() { ...@@ -199,226 +191,168 @@ void main() {
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered.single, ephemeralOne); expect(filtered.single, ephemeralOne);
}); });
testWithoutContext('choose first non-ephemeral device', () async { testWithoutContext('returns all devices when multiple non ephemeral devices are found', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
ephemeralOne,
ephemeralTwo,
nonEphemeralOne, nonEphemeralOne,
nonEphemeralTwo, nonEphemeralTwo,
]; ];
final FakeTerminal terminal = FakeTerminal()
..setPrompt(<String>['1', '2', 'q', 'Q'], '1');
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: terminal,
); );
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[ expect(filtered, <Device>[
ephemeralOne,
ephemeralTwo,
nonEphemeralOne, nonEphemeralOne,
nonEphemeralTwo,
]); ]);
}); });
testWithoutContext('choose second non-ephemeral device', () async { testWithoutContext('Unsupported devices listed in all connected devices', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
nonEphemeralOne, unsupported,
nonEphemeralTwo, unsupportedForProject,
]; ];
final FakeTerminal terminal = FakeTerminal()
..setPrompt(<String>['1', '2', 'q', 'Q'], '2');
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: terminal,
); );
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.getAllConnectedDevices();
expect(filtered, <Device>[ expect(filtered, <Device>[
nonEphemeralTwo, unsupported,
unsupportedForProject,
]); ]);
}); });
testWithoutContext('choose first ephemeral device', () async { testWithoutContext('Removes a unsupported devices', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
ephemeralOne, unsupported,
ephemeralTwo, unsupportedForProject,
]; ];
final FakeTerminal terminal = FakeTerminal()
..setPrompt(<String>['1', '2', 'q', 'Q'], '1');
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: terminal,
); );
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[ expect(filtered, <Device>[]);
ephemeralOne,
]);
}); });
testWithoutContext('choose second ephemeral device', () async { testWithoutContext('Retains devices unsupported by the project if FlutterProject is null', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
ephemeralOne, unsupported,
ephemeralTwo, unsupportedForProject,
]; ];
final FakeTerminal terminal = FakeTerminal()
..setPrompt(<String>['1', '2', 'q', 'Q'], '2');
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: terminal,
); );
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(null);
expect(filtered, <Device>[ expect(filtered, <Device>[unsupportedForProject]);
ephemeralTwo,
]);
}); });
testWithoutContext('choose non-ephemeral device', () async { testWithoutContext('Removes web and fuchsia from --all', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
ephemeralOne, webDevice,
ephemeralTwo, fuchsiaDevice,
nonEphemeralOne,
nonEphemeralTwo,
]; ];
final FakeTerminal terminal = FakeTerminal()
..setPrompt(<String>['1', '2', '3', '4', 'q', 'Q'], '3');
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: terminal,
); );
deviceManager.specifiedDeviceId = 'all';
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[ 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(),
);
}); });
testWithoutContext('Unsupported devices listed in all connected devices', () async { testWithoutContext('Removes devices unsupported by the project from --all', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
nonEphemeralOne,
nonEphemeralTwo,
unsupported, unsupported,
unsupportedForProject, unsupportedForProject,
]; ];
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
final List<Device> filtered = await deviceManager.getAllConnectedDevices(); deviceManager.specifiedDeviceId = 'all';
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[ expect(filtered, <Device>[
unsupported, nonEphemeralOne,
unsupportedForProject, nonEphemeralTwo,
]); ]);
}); });
testWithoutContext('Removes a unsupported devices', () async { testWithoutContext('Returns device with the specified id', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
unsupported, nonEphemeralOne,
unsupportedForProject,
]; ];
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); deviceManager.specifiedDeviceId = nonEphemeralOne.id;
expect(filtered, <Device>[]);
});
testWithoutContext('Retains devices unsupported by the project if FlutterProject is null', () async { final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> devices = <Device>[
unsupported,
unsupportedForProject,
];
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
terminal: Terminal.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(null);
expect(filtered, <Device>[unsupportedForProject]); expect(filtered, <Device>[
nonEphemeralOne,
]);
}); });
testWithoutContext('Removes web and fuchsia from --all', () async { testWithoutContext('Returns multiple devices when multiple devices matches the specified id', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
webDevice, nonEphemeralOne,
fuchsiaDevice, nonEphemeralTwo,
]; ];
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
deviceManager.specifiedDeviceId = 'all'; deviceManager.specifiedDeviceId = 'nonEphemeral'; // This prefix matches both devices
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[]); expect(filtered, <Device>[
nonEphemeralOne,
nonEphemeralTwo,
]);
}); });
testWithoutContext('Removes devices unsupported by the project from --all', () async { testWithoutContext('Returns empty when device of specified id is not found', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
nonEphemeralOne, nonEphemeralOne,
nonEphemeralTwo,
unsupported,
unsupportedForProject,
]; ];
final DeviceManager deviceManager = TestDeviceManager( final DeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
deviceManager.specifiedDeviceId = 'all'; deviceManager.specifiedDeviceId = nonEphemeralTwo.id;
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject()); final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[ expect(filtered, <Device>[]);
nonEphemeralOne,
nonEphemeralTwo,
]);
}); });
testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async { testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
...@@ -429,7 +363,6 @@ void main() { ...@@ -429,7 +363,6 @@ void main() {
final TestDeviceManager deviceManager = TestDeviceManager( final TestDeviceManager deviceManager = TestDeviceManager(
devices, devices,
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
deviceManager.isAlwaysSupportedForProjectOverride = true; deviceManager.isAlwaysSupportedForProjectOverride = true;
...@@ -453,7 +386,6 @@ void main() { ...@@ -453,7 +386,6 @@ void main() {
deviceDiscovery, deviceDiscovery,
], ],
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
deviceManager.specifiedDeviceId = ephemeralOne.id; deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices( final List<Device> filtered = await deviceManager.findTargetDevices(
...@@ -479,7 +411,6 @@ void main() { ...@@ -479,7 +411,6 @@ void main() {
deviceDiscovery, deviceDiscovery,
], ],
logger: BufferLogger.test(), logger: BufferLogger.test(),
terminal: Terminal.test(),
); );
deviceManager.specifiedDeviceId = ephemeralOne.id; deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices( final List<Device> filtered = await deviceManager.findTargetDevices(
...@@ -542,11 +473,10 @@ class TestDeviceManager extends DeviceManager { ...@@ -542,11 +473,10 @@ class TestDeviceManager extends DeviceManager {
List<Device> allDevices, { List<Device> allDevices, {
List<DeviceDiscovery>? deviceDiscoveryOverrides, List<DeviceDiscovery>? deviceDiscoveryOverrides,
required super.logger, required super.logger,
required super.terminal,
String? wellKnownId, String? wellKnownId,
}) : _fakeDeviceDiscoverer = FakePollingDeviceDiscovery(), }) : _fakeDeviceDiscoverer = FakePollingDeviceDiscovery(),
_deviceDiscoverers = <DeviceDiscovery>[], _deviceDiscoverers = <DeviceDiscovery>[],
super(userMessages: UserMessages()) { super() {
if (wellKnownId != null) { if (wellKnownId != null) {
_fakeDeviceDiscoverer.wellKnownIds.add(wellKnownId); _fakeDeviceDiscoverer.wellKnownIds.add(wellKnownId);
} }
...@@ -650,31 +580,3 @@ class ThrowingPollingDeviceDiscovery extends PollingDeviceDiscovery { ...@@ -650,31 +580,3 @@ class ThrowingPollingDeviceDiscovery extends PollingDeviceDiscovery {
@override @override
List<String> get wellKnownIds => <String>[]; 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;
}
}
...@@ -11,11 +11,15 @@ import 'package:flutter_tools/src/base/common.dart'; ...@@ -11,11 +11,15 @@ import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/error_handling_io.dart'; 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/file_system.dart';
import 'package:flutter_tools/src/base/io.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/signals.dart';
import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/base/time.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/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/dart/pub.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/globals.dart' as globals;
import 'package:flutter_tools/src/pre_run_validator.dart'; import 'package:flutter_tools/src/pre_run_validator.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
...@@ -25,6 +29,7 @@ import 'package:test/fake.dart'; ...@@ -25,6 +29,7 @@ import 'package:test/fake.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fake_devices.dart';
import '../../src/test_flutter_command_runner.dart'; import '../../src/test_flutter_command_runner.dart';
import 'utils.dart'; import 'utils.dart';
...@@ -669,6 +674,138 @@ void main() { ...@@ -669,6 +674,138 @@ void main() {
FileSystem: () => fileSystem, FileSystem: () => fileSystem,
ProcessManager: () => processManager, 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 @@ class FakePub extends Fake implements Pub { ...@@ -823,3 +960,33 @@ class FakePub extends Fake implements Pub {
bool printProgress = true, bool printProgress = true,
}) async { } }) 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;
}
}
...@@ -241,7 +241,7 @@ class FakeDeviceManager implements DeviceManager { ...@@ -241,7 +241,7 @@ class FakeDeviceManager implements DeviceManager {
} }
@override @override
Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout }) async { Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout, bool promptUserToChooseDevice = true }) async {
return devices; return devices;
} }
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment