Move target devices logic to its own classes and file (#121903)
Move target devices logic to its own classes and file
This commit is contained in:
parent
2d1a5cdc95
commit
ee42a302ec
@ -62,9 +62,27 @@ class DevicesCommand extends FlutterCommand {
|
||||
exitCode: 1);
|
||||
}
|
||||
|
||||
final DevicesCommandOutput output = DevicesCommandOutput(
|
||||
deviceDiscoveryTimeout: deviceDiscoveryTimeout,
|
||||
);
|
||||
|
||||
await output.findAndOutputAllTargetDevices(
|
||||
machine: boolArgDeprecated('machine'),
|
||||
);
|
||||
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
}
|
||||
|
||||
class DevicesCommandOutput {
|
||||
DevicesCommandOutput({this.deviceDiscoveryTimeout});
|
||||
|
||||
final Duration? deviceDiscoveryTimeout;
|
||||
|
||||
Future<void> findAndOutputAllTargetDevices({required bool machine}) async {
|
||||
final List<Device> devices = await globals.deviceManager?.refreshAllDevices(timeout: deviceDiscoveryTimeout) ?? <Device>[];
|
||||
|
||||
if (boolArgDeprecated('machine')) {
|
||||
if (machine) {
|
||||
await printDevicesAsJson(devices);
|
||||
} else {
|
||||
if (devices.isEmpty) {
|
||||
@ -86,7 +104,6 @@ class DevicesCommand extends FlutterCommand {
|
||||
}
|
||||
await _printDiagnostics();
|
||||
}
|
||||
return FlutterCommandResult.success();
|
||||
}
|
||||
|
||||
Future<void> _printDiagnostics() async {
|
||||
|
@ -245,76 +245,11 @@ abstract class DeviceManager {
|
||||
];
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// * If the user specified '-d all', then return all connected devices which
|
||||
/// support the current project, except for fuchsia and web.
|
||||
///
|
||||
/// * If the user specified a device id, then do nothing as the list is already
|
||||
/// filtered by [getDevices].
|
||||
///
|
||||
/// * If the user did not specify a device id and there is more than one
|
||||
/// device connected, then filter out unsupported devices and prioritize
|
||||
/// ephemeral devices.
|
||||
///
|
||||
/// * If [promptUserToChooseDevice] is true, and there are more than one
|
||||
/// device after the aforementioned filters, and the user is connected to a
|
||||
/// terminal, then show a prompt asking the user to choose one.
|
||||
Future<List<Device>> findTargetDevices({
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
if (timeout != null) {
|
||||
// Reset the cache with the specified timeout.
|
||||
await refreshAllDevices(timeout: timeout);
|
||||
}
|
||||
|
||||
final List<Device> devices = await getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceSupportFilter(
|
||||
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
if (!hasSpecifiedDeviceId) {
|
||||
// User did not specify the device.
|
||||
|
||||
if (devices.length > 1) {
|
||||
// If there are still multiple devices and the user did not specify to run
|
||||
// all, then attempt to prioritize ephemeral devices. For example, if the
|
||||
// user only typed 'flutter run' and both an Android device and desktop
|
||||
// device are available, choose the Android device.
|
||||
|
||||
// Ephemeral is nullable for device types where this is not well
|
||||
// defined.
|
||||
final List<Device> ephemeralDevices = <Device>[
|
||||
for (final Device device in devices)
|
||||
if (device.ephemeral == true)
|
||||
device,
|
||||
];
|
||||
|
||||
if (ephemeralDevices.length == 1) {
|
||||
return ephemeralDevices;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
/// Determines how to filter devices.
|
||||
///
|
||||
/// By default, filters to only include devices that are supported by Flutter.
|
||||
///
|
||||
/// If the user has not specificied a device, filters to only include devices
|
||||
/// If the user has not specified a device, filters to only include devices
|
||||
/// that are supported by Flutter and supported by the project.
|
||||
///
|
||||
/// If the user has specified `--device all`, filters to only include devices
|
||||
@ -343,6 +278,27 @@ abstract class DeviceManager {
|
||||
return DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter();
|
||||
}
|
||||
}
|
||||
|
||||
/// If the user did not specify to run all or a specific device, then attempt
|
||||
/// to prioritize ephemeral devices.
|
||||
///
|
||||
/// If there is not exactly one ephemeral device return null.
|
||||
///
|
||||
/// For example, if the user only typed 'flutter run' and both an Android
|
||||
/// device and desktop device are available, choose the Android device.
|
||||
///
|
||||
/// Note: ephemeral is nullable for device types where this is not well
|
||||
/// defined.
|
||||
Device? getSingleEphemeralDevice(List<Device> devices){
|
||||
if (!hasSpecifiedDeviceId) {
|
||||
try {
|
||||
return devices.singleWhere((Device device) => device.ephemeral == true);
|
||||
} on StateError {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// A class for determining how to filter devices based on if they are supported.
|
||||
|
@ -31,6 +31,7 @@ import '../project.dart';
|
||||
import '../reporting/reporting.dart';
|
||||
import '../web/compile.dart';
|
||||
import 'flutter_command_runner.dart';
|
||||
import 'target_devices.dart';
|
||||
|
||||
export '../cache.dart' show DevelopmentArtifact;
|
||||
|
||||
@ -709,6 +710,11 @@ abstract class FlutterCommand extends Command<void> {
|
||||
return null;
|
||||
}();
|
||||
|
||||
late final TargetDevices _targetDevices = TargetDevices(
|
||||
deviceManager: globals.deviceManager!,
|
||||
logger: globals.logger,
|
||||
);
|
||||
|
||||
void addBuildModeFlags({
|
||||
required bool verboseHelp,
|
||||
bool defaultToRelease = true,
|
||||
@ -1530,113 +1536,10 @@ abstract class FlutterCommand extends Command<void> {
|
||||
Future<List<Device>?> findAllTargetDevices({
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
}) async {
|
||||
if (!globals.doctor!.canLaunchAnything) {
|
||||
globals.printError(userMessages.flutterNoDevelopmentDevice);
|
||||
return null;
|
||||
}
|
||||
final DeviceManager deviceManager = globals.deviceManager!;
|
||||
List<Device> devices = await deviceManager.findTargetDevices(
|
||||
return _targetDevices.findAllTargetDevices(
|
||||
deviceDiscoveryTimeout: deviceDiscoveryTimeout,
|
||||
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
|
||||
timeout: deviceDiscoveryTimeout,
|
||||
);
|
||||
|
||||
if (devices.isEmpty) {
|
||||
if (deviceManager.hasSpecifiedDeviceId) {
|
||||
globals.logger.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
|
||||
final List<Device> allDevices = await deviceManager.getAllDevices();
|
||||
if (allDevices.isNotEmpty) {
|
||||
globals.logger.printStatus('');
|
||||
globals.logger.printStatus('The following devices were found:');
|
||||
await Device.printDevices(allDevices, globals.logger);
|
||||
}
|
||||
return null;
|
||||
} else if (deviceManager.hasSpecifiedAllDevices) {
|
||||
globals.logger.printStatus(userMessages.flutterNoDevicesFound);
|
||||
await _printUnsupportedDevice(deviceManager);
|
||||
return null;
|
||||
} else {
|
||||
globals.logger.printStatus(userMessages.flutterNoSupportedDevices);
|
||||
await _printUnsupportedDevice(deviceManager);
|
||||
return null;
|
||||
}
|
||||
} else if (devices.length > 1) {
|
||||
if (deviceManager.hasSpecifiedDeviceId) {
|
||||
globals.logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
|
||||
return null;
|
||||
} else if (!deviceManager.hasSpecifiedAllDevices) {
|
||||
if (globals.terminal.stdinHasTerminal) {
|
||||
// If DeviceManager was not able to prioritize a device. For example, if the user
|
||||
// has two active Android devices running, then we request the user to
|
||||
// choose one. If the user has two nonEphemeral devices running, we also
|
||||
// request input to choose one.
|
||||
globals.logger.printStatus(userMessages.flutterMultipleDevicesFound);
|
||||
await Device.printDevices(devices, globals.logger);
|
||||
final Device chosenDevice = await _chooseOneOfAvailableDevices(devices);
|
||||
|
||||
// Update the [DeviceManager.specifiedDeviceId] so that we will not be prompted again.
|
||||
deviceManager.specifiedDeviceId = chosenDevice.id;
|
||||
|
||||
devices = <Device>[chosenDevice];
|
||||
} else {
|
||||
// Show an error message asking the user to specify `-d all` if they
|
||||
// want to run on multiple devices.
|
||||
final List<Device> allDevices = await deviceManager.getAllDevices();
|
||||
globals.logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
|
||||
globals.logger.printStatus('');
|
||||
await Device.printDevices(allDevices, globals.logger);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
Future<void> _printUnsupportedDevice(DeviceManager deviceManager) async {
|
||||
final List<Device> unsupportedDevices = await deviceManager.getDevices();
|
||||
if (unsupportedDevices.isNotEmpty) {
|
||||
final StringBuffer result = StringBuffer();
|
||||
result.writeln(userMessages.flutterFoundButUnsupportedDevices);
|
||||
result.writeAll(
|
||||
(await Device.descriptions(unsupportedDevices))
|
||||
.map((String desc) => desc)
|
||||
.toList(),
|
||||
'\n',
|
||||
);
|
||||
result.writeln();
|
||||
result.writeln(userMessages.flutterMissPlatformProjects(
|
||||
Device.devicesPlatformTypes(unsupportedDevices),
|
||||
));
|
||||
globals.logger.printStatus(result.toString());
|
||||
}
|
||||
}
|
||||
|
||||
Future<Device> _chooseOneOfAvailableDevices(List<Device> devices) async {
|
||||
_displayDeviceOptions(devices);
|
||||
final String userInput = await _readUserInput(devices.length);
|
||||
if (userInput.toLowerCase() == 'q') {
|
||||
throwToolExit('');
|
||||
}
|
||||
return devices[int.parse(userInput) - 1];
|
||||
}
|
||||
|
||||
void _displayDeviceOptions(List<Device> devices) {
|
||||
int count = 1;
|
||||
for (final Device device in devices) {
|
||||
globals.logger.printStatus(userMessages.flutterChooseDevice(count, device.name, device.id));
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _readUserInput(int deviceCount) async {
|
||||
globals.terminal.usesTerminalUi = true;
|
||||
final String result = await globals.terminal.promptForCharInput(
|
||||
<String>[ for (int i = 0; i < deviceCount; i++) '${i + 1}', 'q', 'Q'],
|
||||
displayAcceptedCharacters: false,
|
||||
logger: globals.logger,
|
||||
prompt: userMessages.flutterChooseOne,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Find and return the target [Device] based upon currently connected
|
||||
|
182
packages/flutter_tools/lib/src/runner/target_devices.dart
Normal file
182
packages/flutter_tools/lib/src/runner/target_devices.dart
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
import '../base/common.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/user_messages.dart';
|
||||
import '../device.dart';
|
||||
import '../globals.dart' as globals;
|
||||
|
||||
class TargetDevices {
|
||||
TargetDevices({
|
||||
required DeviceManager deviceManager,
|
||||
required Logger logger,
|
||||
}) : _deviceManager = deviceManager,
|
||||
_logger = logger;
|
||||
|
||||
final DeviceManager _deviceManager;
|
||||
final Logger _logger;
|
||||
|
||||
/// Find and return all target [Device]s based upon currently connected
|
||||
/// devices 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.
|
||||
Future<List<Device>?> findAllTargetDevices({
|
||||
Duration? deviceDiscoveryTimeout,
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
}) async {
|
||||
if (!globals.doctor!.canLaunchAnything) {
|
||||
_logger.printError(userMessages.flutterNoDevelopmentDevice);
|
||||
return null;
|
||||
}
|
||||
List<Device> devices = await getDevices(
|
||||
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
|
||||
timeout: deviceDiscoveryTimeout,
|
||||
);
|
||||
|
||||
if (devices.isEmpty) {
|
||||
if (_deviceManager.hasSpecifiedDeviceId) {
|
||||
_logger.printStatus(userMessages.flutterNoMatchingDevice(_deviceManager.specifiedDeviceId!));
|
||||
final List<Device> allDevices = await _deviceManager.getAllDevices();
|
||||
if (allDevices.isNotEmpty) {
|
||||
_logger.printStatus('');
|
||||
_logger.printStatus('The following devices were found:');
|
||||
await Device.printDevices(allDevices, _logger);
|
||||
}
|
||||
return null;
|
||||
} else if (_deviceManager.hasSpecifiedAllDevices) {
|
||||
_logger.printStatus(userMessages.flutterNoDevicesFound);
|
||||
await _printUnsupportedDevice(_deviceManager);
|
||||
return null;
|
||||
} else {
|
||||
_logger.printStatus(userMessages.flutterNoSupportedDevices);
|
||||
await _printUnsupportedDevice(_deviceManager);
|
||||
return null;
|
||||
}
|
||||
} else if (devices.length > 1) {
|
||||
if (_deviceManager.hasSpecifiedDeviceId) {
|
||||
_logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, _deviceManager.specifiedDeviceId!));
|
||||
return null;
|
||||
} else if (!_deviceManager.hasSpecifiedAllDevices) {
|
||||
if (globals.terminal.stdinHasTerminal) {
|
||||
// If DeviceManager was not able to prioritize a device. For example, if the user
|
||||
// has two active Android devices running, then we request the user to
|
||||
// choose one. If the user has two nonEphemeral devices running, we also
|
||||
// request input to choose one.
|
||||
_logger.printStatus(userMessages.flutterMultipleDevicesFound);
|
||||
await Device.printDevices(devices, _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.getAllDevices();
|
||||
_logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
|
||||
_logger.printStatus('');
|
||||
await Device.printDevices(allDevices, _logger);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
|
||||
Future<void> _printUnsupportedDevice(DeviceManager deviceManager) async {
|
||||
final List<Device> unsupportedDevices = await deviceManager.getDevices();
|
||||
if (unsupportedDevices.isNotEmpty) {
|
||||
final StringBuffer result = StringBuffer();
|
||||
result.writeln(userMessages.flutterFoundButUnsupportedDevices);
|
||||
result.writeAll(
|
||||
(await Device.descriptions(unsupportedDevices))
|
||||
.map((String desc) => desc)
|
||||
.toList(),
|
||||
'\n',
|
||||
);
|
||||
result.writeln();
|
||||
result.writeln(userMessages.flutterMissPlatformProjects(
|
||||
Device.devicesPlatformTypes(unsupportedDevices),
|
||||
));
|
||||
_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) {
|
||||
_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: _logger,
|
||||
prompt: userMessages.flutterChooseOne,
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// 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.
|
||||
///
|
||||
/// Returns a list of devices specified by the user.
|
||||
///
|
||||
/// * If the user specified '-d all', then return all connected devices which
|
||||
/// support the current project, except for fuchsia and web.
|
||||
///
|
||||
/// * If the user specified a device id, then do nothing as the list is already
|
||||
/// filtered by [_deviceManager.getDevices].
|
||||
///
|
||||
/// * If the user did not specify a device id and there is more than one
|
||||
/// device connected, then filter out unsupported devices and prioritize
|
||||
/// ephemeral devices.
|
||||
@visibleForTesting
|
||||
Future<List<Device>> getDevices({
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
Duration? timeout,
|
||||
}) async {
|
||||
if (timeout != null) {
|
||||
// Reset the cache with the specified timeout.
|
||||
await _deviceManager.refreshAllDevices(timeout: timeout);
|
||||
}
|
||||
|
||||
final List<Device> devices = await _deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: _deviceManager.deviceSupportFilter(
|
||||
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
// If there is more than one device, attempt to prioritize ephemeral devices.
|
||||
if (devices.length > 1) {
|
||||
final Device? ephemeralDevice = _deviceManager.getSingleEphemeralDevice(devices);
|
||||
if (ephemeralDevice != null) {
|
||||
return <Device>[ephemeralDevice];
|
||||
}
|
||||
}
|
||||
|
||||
return devices;
|
||||
}
|
||||
}
|
@ -581,25 +581,6 @@ class FakePub extends Fake implements Pub {
|
||||
}) async { }
|
||||
}
|
||||
|
||||
class FakeDeviceManager extends Fake implements DeviceManager {
|
||||
List<Device> devices = <Device>[];
|
||||
|
||||
@override
|
||||
String? specifiedDeviceId;
|
||||
|
||||
@override
|
||||
Future<List<Device>> getDevices({
|
||||
DeviceDiscoveryFilter? filter,
|
||||
}) async => devices;
|
||||
|
||||
@override
|
||||
Future<List<Device>> findTargetDevices({
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
Duration? timeout,
|
||||
bool promptUserToChooseDevice = true,
|
||||
}) async => devices;
|
||||
}
|
||||
|
||||
/// A [FlutterDriverFactory] that creates a [NeverEndingDriverService].
|
||||
class NeverEndingFlutterDriverFactory extends Fake implements FlutterDriverFactory {
|
||||
NeverEndingFlutterDriverFactory(this.callback);
|
||||
|
@ -205,22 +205,21 @@ void main() {
|
||||
ephemeralOne,
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
unsupported,
|
||||
unsupportedForProject,
|
||||
];
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
devices,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
|
||||
expect(filtered.single, ephemeralOne);
|
||||
final Device? ephemeralDevice = deviceManager.getSingleEphemeralDevice(devices);
|
||||
|
||||
expect(ephemeralDevice, ephemeralOne);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('returns all devices when multiple non ephemeral devices are found', () async {
|
||||
testUsingContext('returns null when multiple non ephemeral devices are found', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
ephemeralOne,
|
||||
ephemeralTwo,
|
||||
@ -233,19 +232,52 @@ void main() {
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
final Device? ephemeralDevice = deviceManager.getSingleEphemeralDevice(devices);
|
||||
|
||||
expect(filtered, <Device>[
|
||||
ephemeralOne,
|
||||
ephemeralTwo,
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
]);
|
||||
expect(ephemeralDevice, isNull);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Unsupported devices listed in all devices', () async {
|
||||
testUsingContext('return null when hasSpecifiedDeviceId is true', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
ephemeralOne,
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
];
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
devices,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
deviceManager.specifiedDeviceId = 'device';
|
||||
|
||||
final Device? ephemeralDevice = deviceManager.getSingleEphemeralDevice(devices);
|
||||
|
||||
expect(ephemeralDevice, isNull);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('returns null when no ephemeral devices are found', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
];
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
devices,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
|
||||
final Device? ephemeralDevice = deviceManager.getSingleEphemeralDevice(devices);
|
||||
|
||||
expect(ephemeralDevice, isNull);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testWithoutContext('Unsupported devices listed in all devices', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
unsupported,
|
||||
unsupportedForProject,
|
||||
@ -255,14 +287,12 @@ void main() {
|
||||
devices,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
final List<Device> filtered = await deviceManager.getAllDevices();
|
||||
final List<Device> filtered = await deviceManager.getDevices();
|
||||
|
||||
expect(filtered, <Device>[
|
||||
unsupported,
|
||||
unsupportedForProject,
|
||||
]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Removes unsupported devices', () async {
|
||||
@ -274,14 +304,16 @@ void main() {
|
||||
devices,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Retains devices unsupported by the project if FlutterProject is null', () async {
|
||||
testUsingContext('Retains devices unsupported by the project if includeDevicesUnsupportedByProject is true', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
unsupported,
|
||||
unsupportedForProject,
|
||||
@ -291,13 +323,15 @@ void main() {
|
||||
devices,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices(
|
||||
includeDevicesUnsupportedByProject: true,
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(
|
||||
includeDevicesUnsupportedByProject: true,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[unsupportedForProject]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Removes web and fuchsia from --all', () async {
|
||||
@ -311,13 +345,13 @@ void main() {
|
||||
);
|
||||
deviceManager.specifiedDeviceId = 'all';
|
||||
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices(
|
||||
includeDevicesUnsupportedByProject: true,
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Removes devices unsupported by the project from --all', () async {
|
||||
@ -333,19 +367,22 @@ void main() {
|
||||
);
|
||||
deviceManager.specifiedDeviceId = 'all';
|
||||
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Returns device with the specified id', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
];
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
devices,
|
||||
@ -353,13 +390,15 @@ void main() {
|
||||
);
|
||||
deviceManager.specifiedDeviceId = nonEphemeralOne.id;
|
||||
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[
|
||||
nonEphemeralOne,
|
||||
]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Returns multiple devices when multiple devices matches the specified id', () async {
|
||||
@ -373,14 +412,16 @@ void main() {
|
||||
);
|
||||
deviceManager.specifiedDeviceId = 'nonEphemeral'; // This prefix matches both devices
|
||||
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[
|
||||
nonEphemeralOne,
|
||||
nonEphemeralTwo,
|
||||
]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Returns empty when device of specified id is not found', () async {
|
||||
@ -393,11 +434,13 @@ void main() {
|
||||
);
|
||||
deviceManager.specifiedDeviceId = nonEphemeralTwo.id;
|
||||
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
final List<Device> filtered = await deviceManager.getDevices(
|
||||
filter: DeviceDiscoveryFilter(
|
||||
supportFilter: deviceManager.deviceSupportFilter(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testWithoutContext('uses DeviceDiscoverySupportFilter.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
|
||||
@ -439,8 +482,6 @@ void main() {
|
||||
final List<Device> filtered = await deviceManager.getDevices();
|
||||
|
||||
expect(filtered, <Device>[]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Return unconnected devices when filter allows', () async {
|
||||
@ -460,8 +501,6 @@ void main() {
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[unconnectedDevice]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Filter to only include wireless devices', () async {
|
||||
@ -482,8 +521,6 @@ void main() {
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[wirelessDevice]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('Filter to only include attached devices', () async {
|
||||
@ -504,59 +541,6 @@ void main() {
|
||||
);
|
||||
|
||||
expect(filtered, <Device>[ephemeralOne]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('does not refresh device cache without a timeout', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
ephemeralOne,
|
||||
];
|
||||
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery()
|
||||
..deviceValues = devices;
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
<Device>[],
|
||||
deviceDiscoveryOverrides: <DeviceDiscovery>[
|
||||
deviceDiscovery,
|
||||
],
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
deviceManager.specifiedDeviceId = ephemeralOne.id;
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices();
|
||||
|
||||
expect(filtered.single, ephemeralOne);
|
||||
expect(deviceDiscovery.devicesCalled, 1);
|
||||
expect(deviceDiscovery.discoverDevicesCalled, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
|
||||
testUsingContext('refreshes device cache with a timeout', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
ephemeralOne,
|
||||
];
|
||||
const Duration timeout = Duration(seconds: 2);
|
||||
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery()
|
||||
..deviceValues = devices;
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
<Device>[],
|
||||
deviceDiscoveryOverrides: <DeviceDiscovery>[
|
||||
deviceDiscovery,
|
||||
],
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
deviceManager.specifiedDeviceId = ephemeralOne.id;
|
||||
final List<Device> filtered = await deviceManager.findTargetDevices(
|
||||
timeout: timeout,
|
||||
);
|
||||
|
||||
expect(filtered.single, ephemeralOne);
|
||||
expect(deviceDiscovery.devicesCalled, 1);
|
||||
expect(deviceDiscovery.discoverDevicesCalled, 1);
|
||||
}, overrides: <Type, Generator>{
|
||||
FlutterProject: () => FakeFlutterProject(),
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -11,9 +11,7 @@ import 'package:flutter_tools/src/base/common.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/io.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/base/signals.dart';
|
||||
import 'package:flutter_tools/src/base/terminal.dart';
|
||||
import 'package:flutter_tools/src/base/time.dart';
|
||||
import 'package:flutter_tools/src/base/user_messages.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
@ -696,135 +694,31 @@ void main() {
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
group('findAllTargetDevices', () {
|
||||
group('findTargetDevice', () {
|
||||
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')));
|
||||
});
|
||||
testUsingContext('no device found', () async {
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
|
||||
final Device? device = await flutterCommand.findTargetDevice();
|
||||
expect(device, isNull);
|
||||
});
|
||||
|
||||
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));
|
||||
});
|
||||
testUsingContext('finds single device', () async {
|
||||
testDeviceManager.addDevice(device1);
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
|
||||
final Device? device = await flutterCommand.findTargetDevice();
|
||||
expect(device, device1);
|
||||
});
|
||||
|
||||
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,
|
||||
});
|
||||
});
|
||||
testUsingContext('finds multiple devices', () async {
|
||||
testDeviceManager.addDevice(device1);
|
||||
testDeviceManager.addDevice(device2);
|
||||
testDeviceManager.specifiedDeviceId = 'all';
|
||||
final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
|
||||
final Device? device = await flutterCommand.findTargetDevice();
|
||||
expect(device, isNull);
|
||||
expect(testLogger.statusText, contains(UserMessages().flutterSpecifyDevice));
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -980,33 +874,3 @@ class FakePub extends Fake implements Pub {
|
||||
PubOutputMode outputMode = PubOutputMode.all,
|
||||
}) 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;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,476 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter_tools/src/android/android_workflow.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/device.dart';
|
||||
import 'package:flutter_tools/src/doctor.dart';
|
||||
import 'package:flutter_tools/src/doctor_validator.dart';
|
||||
import 'package:flutter_tools/src/runner/target_devices.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
import '../../src/common.dart';
|
||||
import '../../src/context.dart';
|
||||
import '../../src/fake_devices.dart';
|
||||
|
||||
void main() {
|
||||
group('When cannot launch anything', () {
|
||||
late BufferLogger logger;
|
||||
late FakeDoctor doctor;
|
||||
final FakeDevice device1 = FakeDevice('device1', 'device1');
|
||||
|
||||
setUp(() {
|
||||
logger = BufferLogger.test();
|
||||
doctor = FakeDoctor(logger, canLaunchAnything: false);
|
||||
});
|
||||
|
||||
testUsingContext('does not search for devices', () async {
|
||||
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery()
|
||||
..deviceValues = <Device>[device1];
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
<Device>[],
|
||||
deviceDiscoveryOverrides: <DeviceDiscovery>[
|
||||
deviceDiscovery,
|
||||
],
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: deviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(logger.errorText, contains(UserMessages().flutterNoDevelopmentDevice));
|
||||
expect(devices, isNull);
|
||||
expect(deviceDiscovery.devicesCalled, 0);
|
||||
expect(deviceDiscovery.discoverDevicesCalled, 0);
|
||||
}, overrides: <Type, Generator>{
|
||||
Doctor: () => doctor,
|
||||
});
|
||||
});
|
||||
|
||||
group('Ensure refresh when deviceDiscoveryTimeout is provided', () {
|
||||
late BufferLogger logger;
|
||||
final FakeDevice device1 = FakeDevice('device1', 'device1');
|
||||
|
||||
setUp(() {
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
testUsingContext('does not refresh device cache without a timeout', () async {
|
||||
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery()
|
||||
..deviceValues = <Device>[device1];
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
<Device>[],
|
||||
deviceDiscoveryOverrides: <DeviceDiscovery>[
|
||||
deviceDiscovery,
|
||||
],
|
||||
logger: logger,
|
||||
);
|
||||
deviceManager.specifiedDeviceId = device1.id;
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: deviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices?.single, device1);
|
||||
expect(deviceDiscovery.devicesCalled, 1);
|
||||
expect(deviceDiscovery.discoverDevicesCalled, 0);
|
||||
});
|
||||
|
||||
testUsingContext('refreshes device cache with a timeout', () async {
|
||||
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery()
|
||||
..deviceValues = <Device>[device1];
|
||||
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
<Device>[],
|
||||
deviceDiscoveryOverrides: <DeviceDiscovery>[
|
||||
deviceDiscovery,
|
||||
],
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
deviceManager.specifiedDeviceId = device1.id;
|
||||
|
||||
const Duration timeout = Duration(seconds: 2);
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: deviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices(
|
||||
deviceDiscoveryTimeout: timeout,
|
||||
);
|
||||
|
||||
expect(devices?.single, device1);
|
||||
expect(deviceDiscovery.devicesCalled, 1);
|
||||
expect(deviceDiscovery.discoverDevicesCalled, 1);
|
||||
});
|
||||
});
|
||||
|
||||
group('findAllTargetDevices', () {
|
||||
late BufferLogger logger;
|
||||
final FakeDevice device1 = FakeDevice('device1', 'device1');
|
||||
final FakeDevice device2 = FakeDevice('device2', 'device2');
|
||||
|
||||
setUp(() {
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
group('when specified device id', () {
|
||||
testUsingContext('returns device when device is found', () async {
|
||||
testDeviceManager.specifiedDeviceId = 'device1';
|
||||
testDeviceManager.addDevice(device1);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, <Device>[device1]);
|
||||
});
|
||||
|
||||
testUsingContext('show error when no device found', () async {
|
||||
testDeviceManager.specifiedDeviceId = 'device-id';
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, null);
|
||||
expect(logger.statusText, contains(UserMessages().flutterNoMatchingDevice('device-id')));
|
||||
});
|
||||
|
||||
testUsingContext('show error when multiple devices found', () async {
|
||||
testDeviceManager.specifiedDeviceId = 'device';
|
||||
testDeviceManager.addDevice(device1);
|
||||
testDeviceManager.addDevice(device2);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, null);
|
||||
expect(logger.statusText, contains(UserMessages().flutterFoundSpecifiedDevices(2, 'device')));
|
||||
});
|
||||
});
|
||||
|
||||
group('when specified all', () {
|
||||
testUsingContext('can return one device', () async {
|
||||
testDeviceManager.specifiedDeviceId = 'all';
|
||||
testDeviceManager.addDevice(device1);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, <Device>[device1]);
|
||||
});
|
||||
|
||||
testUsingContext('can return multiple devices', () async {
|
||||
testDeviceManager.specifiedDeviceId = 'all';
|
||||
testDeviceManager.addDevice(device1);
|
||||
testDeviceManager.addDevice(device2);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, <Device>[device1, device2]);
|
||||
});
|
||||
|
||||
testUsingContext('show error when no device found', () async {
|
||||
testDeviceManager.specifiedDeviceId = 'all';
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, null);
|
||||
expect(logger.statusText, contains(UserMessages().flutterNoDevicesFound));
|
||||
});
|
||||
});
|
||||
|
||||
group('when device not specified', () {
|
||||
testUsingContext('returns one device when only one device connected', () async {
|
||||
testDeviceManager.addDevice(device1);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, <Device>[device1]);
|
||||
});
|
||||
|
||||
testUsingContext('show error when no device found', () async {
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, null);
|
||||
expect(logger.statusText, contains(UserMessages().flutterNoSupportedDevices));
|
||||
});
|
||||
|
||||
testUsingContext('show error when multiple devices found and not connected to terminal', () async {
|
||||
testDeviceManager.addDevice(device1);
|
||||
testDeviceManager.addDevice(device2);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.findAllTargetDevices();
|
||||
|
||||
expect(devices, null);
|
||||
expect(logger.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 TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.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 TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
final List<Device>? devices = await targetDevices.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 TargetDevices targetDevices = TargetDevices(
|
||||
deviceManager: testDeviceManager,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
await expectLater(
|
||||
targetDevices.findAllTargetDevices(),
|
||||
throwsToolExit(),
|
||||
);
|
||||
}, overrides: <Type, Generator>{
|
||||
AnsiTerminal: () => terminal,
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('Filter devices', () {
|
||||
late BufferLogger logger;
|
||||
final FakeDevice ephemeralOne = FakeDevice('ephemeralOne', 'ephemeralOne');
|
||||
final FakeDevice ephemeralTwo = FakeDevice('ephemeralTwo', 'ephemeralTwo');
|
||||
final FakeDevice nonEphemeralOne = FakeDevice('nonEphemeralOne', 'nonEphemeralOne', ephemeral: false);
|
||||
|
||||
setUp(() {
|
||||
logger = BufferLogger.test();
|
||||
});
|
||||
|
||||
testUsingContext('chooses ephemeral device', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
ephemeralOne,
|
||||
nonEphemeralOne,
|
||||
];
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
devices,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(deviceManager: deviceManager, logger: logger);
|
||||
final List<Device> filtered = await targetDevices.getDevices();
|
||||
|
||||
expect(filtered, <Device>[ephemeralOne]);
|
||||
});
|
||||
|
||||
testUsingContext('returns all devices when multiple non ephemeral devices are found', () async {
|
||||
final List<Device> devices = <Device>[
|
||||
ephemeralOne,
|
||||
ephemeralTwo,
|
||||
nonEphemeralOne,
|
||||
];
|
||||
final DeviceManager deviceManager = TestDeviceManager(
|
||||
devices,
|
||||
logger: logger,
|
||||
);
|
||||
|
||||
final TargetDevices targetDevices = TargetDevices(deviceManager: deviceManager, logger: logger);
|
||||
final List<Device> filtered = await targetDevices.getDevices();
|
||||
|
||||
expect(filtered, <Device>[
|
||||
ephemeralOne,
|
||||
ephemeralTwo,
|
||||
nonEphemeralOne,
|
||||
]);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class TestDeviceManager extends DeviceManager {
|
||||
TestDeviceManager(
|
||||
List<Device> allDevices, {
|
||||
List<DeviceDiscovery>? deviceDiscoveryOverrides,
|
||||
required super.logger,
|
||||
String? wellKnownId,
|
||||
FakePollingDeviceDiscovery? fakeDiscoverer,
|
||||
}) : _fakeDeviceDiscoverer = fakeDiscoverer ?? FakePollingDeviceDiscovery(),
|
||||
_deviceDiscoverers = <DeviceDiscovery>[],
|
||||
super() {
|
||||
if (wellKnownId != null) {
|
||||
_fakeDeviceDiscoverer.wellKnownIds.add(wellKnownId);
|
||||
}
|
||||
_deviceDiscoverers.add(_fakeDeviceDiscoverer);
|
||||
if (deviceDiscoveryOverrides != null) {
|
||||
_deviceDiscoverers.addAll(deviceDiscoveryOverrides);
|
||||
}
|
||||
resetDevices(allDevices);
|
||||
}
|
||||
@override
|
||||
List<DeviceDiscovery> get deviceDiscoverers => _deviceDiscoverers;
|
||||
final List<DeviceDiscovery> _deviceDiscoverers;
|
||||
final FakePollingDeviceDiscovery _fakeDeviceDiscoverer;
|
||||
|
||||
void resetDevices(List<Device> allDevices) {
|
||||
_fakeDeviceDiscoverer.setDevices(allDevices);
|
||||
}
|
||||
}
|
||||
|
||||
class MockDeviceDiscovery extends Fake implements DeviceDiscovery {
|
||||
int devicesCalled = 0;
|
||||
int discoverDevicesCalled = 0;
|
||||
|
||||
@override
|
||||
bool supportsPlatform = true;
|
||||
|
||||
List<Device> deviceValues = <Device>[];
|
||||
|
||||
@override
|
||||
Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async {
|
||||
devicesCalled += 1;
|
||||
return deviceValues;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Device>> discoverDevices({
|
||||
Duration? timeout,
|
||||
DeviceDiscoveryFilter? filter,
|
||||
}) async {
|
||||
discoverDevicesCalled += 1;
|
||||
return deviceValues;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String> get wellKnownIds => <String>[];
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
class FakeDoctor extends Doctor {
|
||||
FakeDoctor(
|
||||
Logger logger, {
|
||||
this.canLaunchAnything = true,
|
||||
}) : super(logger: logger);
|
||||
|
||||
// True for testing.
|
||||
@override
|
||||
bool get canListAnything => true;
|
||||
|
||||
// True for testing.
|
||||
@override
|
||||
bool canLaunchAnything;
|
||||
|
||||
@override
|
||||
/// Replaces the android workflow with a version that overrides licensesAccepted,
|
||||
/// to prevent individual tests from having to mock out the process for
|
||||
/// the Doctor.
|
||||
List<DoctorValidator> get validators {
|
||||
final List<DoctorValidator> superValidators = super.validators;
|
||||
return superValidators.map<DoctorValidator>((DoctorValidator v) {
|
||||
if (v is AndroidLicenseValidator) {
|
||||
return FakeAndroidLicenseValidator();
|
||||
}
|
||||
return v;
|
||||
}).toList();
|
||||
}
|
||||
}
|
@ -222,7 +222,9 @@ class FakeDeviceManager implements DeviceManager {
|
||||
String deviceId, {
|
||||
DeviceDiscoveryFilter? filter,
|
||||
}) async {
|
||||
return devices.where((Device device) => device.id == deviceId).toList();
|
||||
return devices.where((Device device) {
|
||||
return device.id == deviceId || device.id.startsWith(deviceId);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
@ -245,15 +247,6 @@ class FakeDeviceManager implements DeviceManager {
|
||||
@override
|
||||
List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[];
|
||||
|
||||
@override
|
||||
Future<List<Device>> findTargetDevices({
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
Duration? timeout,
|
||||
bool promptUserToChooseDevice = true,
|
||||
}) async {
|
||||
return devices;
|
||||
}
|
||||
|
||||
@override
|
||||
DeviceDiscoverySupportFilter deviceSupportFilter({
|
||||
bool includeDevicesUnsupportedByProject = false,
|
||||
@ -261,6 +254,9 @@ class FakeDeviceManager implements DeviceManager {
|
||||
}) {
|
||||
return TestDeviceDiscoverySupportFilter();
|
||||
}
|
||||
|
||||
@override
|
||||
Device? getSingleEphemeralDevice(List<Device> devices) => null;
|
||||
}
|
||||
|
||||
class TestDeviceDiscoverySupportFilter extends Fake implements DeviceDiscoverySupportFilter {
|
||||
|
Loading…
x
Reference in New Issue
Block a user