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,
......
...@@ -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 {
......
...@@ -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;
......
...@@ -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