Unverified Commit cc26a1aa authored by Victoria Ashworth's avatar Victoria Ashworth Committed by GitHub

Update device filtering and introduce isConnected and connectionInterface (#121359)

Update device filtering and introduce isConnected and connectionInterface
parent c590086b
......@@ -883,7 +883,7 @@ class DeviceDomain extends Domain {
Future<List<Map<String, Object?>>> getDevices([ Map<String, Object?>? args ]) async {
return <Map<String, Object?>>[
for (final PollingDeviceDiscovery discoverer in _discoverers)
for (final Device device in await discoverer.devices)
for (final Device device in await discoverer.devices())
await _deviceToMap(device),
];
}
......@@ -1069,7 +1069,7 @@ class DeviceDomain extends Domain {
/// Return the device matching the deviceId field in the args.
Future<Device?> _getDevice(String? deviceId) async {
for (final PollingDeviceDiscovery discoverer in _discoverers) {
final List<Device> devices = await discoverer.devices;
final List<Device> devices = await discoverer.devices();
Device? device;
for (final Device localDevice in devices) {
if (localDevice.id == deviceId) {
......
......@@ -62,7 +62,7 @@ class DevicesCommand extends FlutterCommand {
exitCode: 1);
}
final List<Device> devices = await globals.deviceManager?.refreshAllConnectedDevices(timeout: deviceDiscoveryTimeout) ?? <Device>[];
final List<Device> devices = await globals.deviceManager?.refreshAllDevices(timeout: deviceDiscoveryTimeout) ?? <Device>[];
if (boolArgDeprecated('machine')) {
await printDevicesAsJson(devices);
......
......@@ -209,7 +209,9 @@ class DriveCommand extends RunCommandBase {
String? get applicationBinaryPath => stringArgDeprecated(FlutterOptions.kUseApplicationBinary);
Future<Device?> get targetedDevice async {
return findTargetDevice(includeUnsupportedDevices: applicationBinaryPath == null);
return findTargetDevice(
includeDevicesUnsupportedByProject: applicationBinaryPath == null,
);
}
// Network devices need `publish-port` to be enabled because it requires mDNS.
......
......@@ -36,7 +36,7 @@ class LogsCommand extends FlutterCommand {
@override
Future<FlutterCommandResult> verifyThenRunCommand(String? commandPath) async {
device = await findTargetDevice(includeUnsupportedDevices: true);
device = await findTargetDevice(includeDevicesUnsupportedByProject: true);
if (device == null) {
throwToolExit(null);
}
......
......@@ -110,7 +110,19 @@ abstract class DeviceManager {
/// specifiedDeviceId = 'all'.
bool get hasSpecifiedAllDevices => _specifiedDeviceId == 'all';
Future<List<Device>> getDevicesById(String deviceId) async {
/// Get devices filtered by [filter] that match the given device id/name.
///
/// If [filter] is not provided, a default filter that requires devices to be
/// connected will be used.
///
/// If an exact match is found, return it immediately. Otherwise wait for all
/// discoverers to complete and return any partial matches.
Future<List<Device>> getDevicesById(
String deviceId, {
DeviceDiscoveryFilter? filter,
}) async {
filter ??= DeviceDiscoveryFilter();
final String lowerDeviceId = deviceId.toLowerCase();
bool exactlyMatchesDeviceId(Device device) =>
device.id.toLowerCase() == lowerDeviceId ||
......@@ -135,7 +147,7 @@ abstract class DeviceManager {
for (final DeviceDiscovery discoverer in _platformDiscoverers)
if (!hasWellKnownId || discoverer.wellKnownIds.contains(specifiedDeviceId))
discoverer
.devices
.devices(filter: filter)
.then((List<Device> devices) {
for (final Device device in devices) {
if (exactlyMatchesDeviceId(device)) {
......@@ -165,34 +177,56 @@ abstract class DeviceManager {
return prefixMatches;
}
/// Returns the list of connected devices, filtered by any user-specified device id.
Future<List<Device>> getDevices() {
/// Returns a list of devices filtered by the user-specified device
/// id/name (if applicable) and [filter].
///
/// If [filter] is not provided, a default filter that requires devices to be
/// connected will be used.
Future<List<Device>> getDevices({
DeviceDiscoveryFilter? filter,
}) {
filter ??= DeviceDiscoveryFilter();
final String? id = specifiedDeviceId;
if (id == null) {
return getAllConnectedDevices();
return getAllDevices(filter: filter);
}
return getDevicesById(id);
return getDevicesById(id, filter: filter);
}
Iterable<DeviceDiscovery> get _platformDiscoverers {
return deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform);
}
/// Returns the list of all connected devices.
Future<List<Device>> getAllConnectedDevices() async {
/// Returns a list of devices filtered by [filter].
///
/// If [filter] is not provided, a default filter that requires devices to be
/// connected will be used.
Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter,
}) async {
filter ??= DeviceDiscoveryFilter();
final List<List<Device>> devices = await Future.wait<List<Device>>(<Future<List<Device>>>[
for (final DeviceDiscovery discoverer in _platformDiscoverers)
discoverer.devices,
discoverer.devices(filter: filter),
]);
return devices.expand<Device>((List<Device> deviceList) => deviceList).toList();
}
/// Returns the list of all connected devices. Discards existing cache of devices.
Future<List<Device>> refreshAllConnectedDevices({ Duration? timeout }) async {
/// Returns a list of devices filtered by [filter]. Discards existing cache of devices.
///
/// If [filter] is not provided, a default filter that requires devices to be
/// connected will be used.
///
/// Search for devices to populate the cache for no longer than [timeout].
Future<List<Device>> refreshAllDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) async {
filter ??= DeviceDiscoveryFilter();
final List<List<Device>> devices = await Future.wait<List<Device>>(<Future<List<Device>>>[
for (final DeviceDiscovery discoverer in _platformDiscoverers)
discoverer.discoverDevices(timeout: timeout),
discoverer.discoverDevices(filter: filter, timeout: timeout),
]);
return devices.expand<Device>((List<Device> deviceList) => deviceList).toList();
......@@ -233,45 +267,26 @@ abstract class DeviceManager {
/// * If [promptUserToChooseDevice] is true, and there are more than one
/// device after the aforementioned filters, and the user is connected to a
/// terminal, then show a prompt asking the user to choose one.
Future<List<Device>> findTargetDevices(
FlutterProject? flutterProject, {
Future<List<Device>> findTargetDevices({
bool includeDevicesUnsupportedByProject = false,
Duration? timeout,
}) async {
if (timeout != null) {
// Reset the cache with the specified timeout.
await refreshAllConnectedDevices(timeout: timeout);
await refreshAllDevices(timeout: timeout);
}
List<Device> devices = (await getDevices())
.where((Device device) => device.isSupported()).toList();
final List<Device> devices = await getDevices(
filter: DeviceDiscoveryFilter(
supportFilter: deviceSupportFilter(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
),
),
);
if (hasSpecifiedAllDevices) {
// User has specified `--device all`.
//
// Always remove web and fuchsia devices from `--all`. This setting
// currently requires devices to share a frontend_server and resident
// runner instance. Both web and fuchsia require differently configured
// compilers, and web requires an entirely different resident runner.
devices = <Device>[
for (final Device device in devices)
if (await device.targetPlatform != TargetPlatform.fuchsia_arm64 &&
await device.targetPlatform != TargetPlatform.fuchsia_x64 &&
await device.targetPlatform != TargetPlatform.web_javascript &&
isDeviceSupportedForProject(device, flutterProject))
device,
];
} else if (!hasSpecifiedDeviceId) {
if (!hasSpecifiedDeviceId) {
// User did not specify the device.
// Remove all devices which are not supported by the current application.
// For example, if there was no 'android' folder then don't attempt to
// launch with an Android device.
devices = <Device>[
for (final Device device in devices)
if (isDeviceSupportedForProject(device, flutterProject))
device,
];
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
......@@ -287,7 +302,7 @@ abstract class DeviceManager {
];
if (ephemeralDevices.length == 1) {
devices = ephemeralDevices;
return ephemeralDevices;
}
}
}
......@@ -295,18 +310,166 @@ abstract class DeviceManager {
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
/// that are supported by Flutter and supported by the project.
///
/// If the user has specified `--device all`, filters to only include devices
/// that are supported by Flutter, supported by the project, and supported for `all`.
///
/// If [includeDevicesUnsupportedByProject] is true, all devices will be
/// considered supported by the project, regardless of user specifications.
///
/// This also exists to allow the check to be overridden for google3 clients.
DeviceDiscoverySupportFilter deviceSupportFilter({
bool includeDevicesUnsupportedByProject = false,
}) {
FlutterProject? flutterProject;
if (includeDevicesUnsupportedByProject == false) {
flutterProject = FlutterProject.current();
}
if (hasSpecifiedAllDevices) {
return DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutterOrProjectOrAll(
flutterProject: flutterProject,
);
} else if (!hasSpecifiedDeviceId) {
return DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutterOrProject(
flutterProject: flutterProject,
);
} else {
return DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter();
}
}
}
/// A class for determining how to filter devices based on if they are supported.
class DeviceDiscoverySupportFilter {
/// Filter devices to only include those supported by Flutter.
DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter()
: _excludeDevicesNotSupportedByProject = false,
_excludeDevicesNotSupportedByAll = false,
_flutterProject = null;
/// Filter devices to only include those supported by Flutter and the
/// provided [flutterProject].
///
/// If [flutterProject] is null, all devices will be considered supported by
/// the project.
DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutterOrProject({
required FlutterProject? flutterProject,
}) : _flutterProject = flutterProject,
_excludeDevicesNotSupportedByProject = true,
_excludeDevicesNotSupportedByAll = false;
/// Filter devices to only include those supported by Flutter, the provided
/// [flutterProject], and `--device all`.
///
/// If [flutterProject] is null, all devices will be considered supported by
/// the project.
DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutterOrProjectOrAll({
required FlutterProject? flutterProject,
}) : _flutterProject = flutterProject,
_excludeDevicesNotSupportedByProject = true,
_excludeDevicesNotSupportedByAll = true;
final FlutterProject? _flutterProject;
final bool _excludeDevicesNotSupportedByProject;
final bool _excludeDevicesNotSupportedByAll;
Future<bool> matchesRequirements(Device device) async {
final bool meetsSupportByFlutterRequirement = device.isSupported();
final bool meetsSupportForProjectRequirement = !_excludeDevicesNotSupportedByProject || isDeviceSupportedForProject(device);
final bool meetsSupportForAllRequirement = !_excludeDevicesNotSupportedByAll || await isDeviceSupportedForAll(device);
return meetsSupportByFlutterRequirement &&
meetsSupportForProjectRequirement &&
meetsSupportForAllRequirement;
}
/// 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.
Future<bool> isDeviceSupportedForAll(Device device) async {
final TargetPlatform devicePlatform = await device.targetPlatform;
return device.isSupported() &&
devicePlatform != TargetPlatform.fuchsia_arm64 &&
devicePlatform != TargetPlatform.fuchsia_x64 &&
devicePlatform != TargetPlatform.web_javascript &&
isDeviceSupportedForProject(device);
}
/// Returns whether the device is supported for the project.
///
/// This exists to allow the check to be overridden for google3 clients. If
/// [flutterProject] is null then return true.
bool isDeviceSupportedForProject(Device device, FlutterProject? flutterProject) {
if (flutterProject == null) {
/// A device can be supported by Flutter but not supported for the project
/// (e.g. when the user has removed the iOS directory from their project).
///
/// This also exists to allow the check to be overridden for google3 clients. If
/// [_flutterProject] is null then return true.
bool isDeviceSupportedForProject(Device device) {
if (!device.isSupported()) {
return false;
}
if (_flutterProject == null) {
return true;
}
return device.isSupportedForProject(flutterProject);
return device.isSupportedForProject(_flutterProject!);
}
}
/// A class for filtering devices.
///
/// If [excludeDisconnected] is true, only devices detected as connected will be included.
///
/// If [supportFilter] is provided, only devices matching the requirements will be included.
///
/// If [deviceConnectionInterface] is provided, only devices matching the DeviceConnectionInterface will be included.
class DeviceDiscoveryFilter {
DeviceDiscoveryFilter({
this.excludeDisconnected = true,
this.supportFilter,
this.deviceConnectionInterface,
});
final bool excludeDisconnected;
final DeviceDiscoverySupportFilter? supportFilter;
final DeviceConnectionInterface? deviceConnectionInterface;
Future<bool> matchesRequirements(Device device) async {
final DeviceDiscoverySupportFilter? localSupportFilter = supportFilter;
final bool meetsConnectionRequirement = !excludeDisconnected || device.isConnected;
final bool meetsSupportRequirements = localSupportFilter == null || (await localSupportFilter.matchesRequirements(device));
final bool meetsConnectionInterfaceRequirement = matchesDeviceConnectionInterface(device, deviceConnectionInterface);
return meetsConnectionRequirement &&
meetsSupportRequirements &&
meetsConnectionInterfaceRequirement;
}
Future<List<Device>> filterDevices(List<Device> devices) async {
devices = <Device>[
for (final Device device in devices)
if (await matchesRequirements(device)) device,
];
return devices;
}
bool matchesDeviceConnectionInterface(
Device device,
DeviceConnectionInterface? deviceConnectionInterface,
) {
if (deviceConnectionInterface == null) {
return true;
}
return device.connectionInterface == deviceConnectionInterface;
}
}
/// An abstract class to discover and enumerate a specific type of devices.
abstract class DeviceDiscovery {
......@@ -317,10 +480,13 @@ abstract class DeviceDiscovery {
bool get canListAnything;
/// Return all connected devices, cached on subsequent calls.
Future<List<Device>> get devices;
Future<List<Device>> devices({DeviceDiscoveryFilter? filter});
/// Return all connected devices. Discards existing cache of devices.
Future<List<Device>> discoverDevices({ Duration? timeout });
Future<List<Device>> discoverDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
});
/// Gets a list of diagnostic messages pertaining to issues with any connected
/// devices (will be an empty list if there are no issues).
......@@ -352,7 +518,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
Timer? _timer;
Future<List<Device>> pollingGetDevices({ Duration? timeout });
Future<List<Device>> pollingGetDevices({Duration? timeout});
void startPolling() {
if (_timer == null) {
......@@ -380,19 +546,50 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
_timer = null;
}
/// Get devices from cache filtered by [filter].
///
/// If the cache is empty, populate the cache.
@override
Future<List<Device>> get devices {
return _populateDevices();
Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) {
return _populateDevices(filter: filter);
}
/// Empty the cache and repopulate it before getting devices from cache filtered by [filter].
///
/// Search for devices to populate the cache for no longer than [timeout].
@override
Future<List<Device>> discoverDevices({ Duration? timeout }) {
deviceNotifier = null;
return _populateDevices(timeout: timeout);
Future<List<Device>> discoverDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) {
return _populateDevices(timeout: timeout, filter: filter, resetCache: true);
}
Future<List<Device>> _populateDevices({ Duration? timeout }) async {
deviceNotifier ??= ItemListNotifier<Device>.from(await pollingGetDevices(timeout: timeout));
/// Get devices from cache filtered by [filter].
///
/// If the cache is empty or [resetCache] is true, populate the cache.
///
/// Search for devices to populate the cache for no longer than [timeout].
Future<List<Device>> _populateDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
bool resetCache = false,
}) async {
if (deviceNotifier == null || resetCache) {
final List<Device> devices = await pollingGetDevices(timeout: timeout);
// If the cache was populated while the polling was ongoing, do not
// overwrite the cache unless it's explicitly refreshing the cache.
if (resetCache) {
deviceNotifier = ItemListNotifier<Device>.from(devices);
} else {
deviceNotifier ??= ItemListNotifier<Device>.from(devices);
}
}
// If a filter is provided, filter cache to only return devices matching.
if (filter != null) {
return filter.filterDevices(deviceNotifier!.items);
}
return deviceNotifier!.items;
}
......@@ -412,6 +609,12 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
String toString() => '$name device discovery';
}
/// How a device is connected.
enum DeviceConnectionInterface {
attached,
wireless,
}
/// A device is a physical hardware that can run a Flutter application.
///
/// This may correspond to a connected iOS or Android device, or represent
......@@ -434,6 +637,14 @@ abstract class Device {
/// Whether this is an ephemeral device.
final bool ephemeral;
bool get isConnected => true;
DeviceConnectionInterface get connectionInterface =>
DeviceConnectionInterface.attached;
bool get isWirelesslyConnected =>
connectionInterface == DeviceConnectionInterface.wireless;
String get name;
bool get supportsStartPaused => true;
......
......@@ -688,7 +688,7 @@ class DeviceValidator extends DoctorValidator {
@override
Future<ValidationResult> validate() async {
final List<Device> devices = await _deviceManager.getAllConnectedDevices();
final List<Device> devices = await _deviceManager.getAllDevices();
List<ValidationMessage> installedMessages = <ValidationMessage>[];
if (devices.isNotEmpty) {
installedMessages = (await Device.descriptions(devices))
......
......@@ -57,11 +57,14 @@ class ProxiedDevices extends DeviceDiscovery {
List<Device>? _devices;
@override
Future<List<Device>> get devices async =>
_devices ?? await discoverDevices();
Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async =>
_devices ?? await discoverDevices(filter: filter);
@override
Future<List<Device>> discoverDevices({Duration? timeout}) async {
Future<List<Device>> discoverDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter
}) async {
final List<Map<String, Object?>> discoveredDevices = _cast<List<dynamic>>(await connection.sendRequest('device.discoverDevices')).cast<Map<String, Object?>>();
final List<ProxiedDevice> devices = <ProxiedDevice>[
for (final Map<String, Object?> device in discoveredDevices)
......
......@@ -1491,7 +1491,7 @@ abstract class FlutterCommand extends Command<void> {
/// If no device can be found that meets specified criteria,
/// then print an error message and return null.
Future<List<Device>?> findAllTargetDevices({
bool includeUnsupportedDevices = false,
bool includeDevicesUnsupportedByProject = false,
}) async {
if (!globals.doctor!.canLaunchAnything) {
globals.printError(userMessages.flutterNoDevelopmentDevice);
......@@ -1499,14 +1499,14 @@ abstract class FlutterCommand extends Command<void> {
}
final DeviceManager deviceManager = globals.deviceManager!;
List<Device> devices = await deviceManager.findTargetDevices(
includeUnsupportedDevices ? null : FlutterProject.current(),
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
timeout: deviceDiscoveryTimeout,
);
if (devices.isEmpty) {
if (deviceManager.hasSpecifiedDeviceId) {
globals.logger.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
final List<Device> allDevices = await deviceManager.getAllDevices();
if (allDevices.isNotEmpty) {
globals.logger.printStatus('');
globals.logger.printStatus('The following devices were found:');
......@@ -1543,7 +1543,7 @@ abstract class FlutterCommand extends Command<void> {
} 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();
final List<Device> allDevices = await deviceManager.getAllDevices();
globals.logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
globals.logger.printStatus('');
await Device.printDevices(allDevices, globals.logger);
......@@ -1607,18 +1607,20 @@ abstract class FlutterCommand extends Command<void> {
/// If a device cannot be found that meets specified criteria,
/// then print an error message and return null.
///
/// If [includeUnsupportedDevices] is true, the tool does not filter
/// If [includeDevicesUnsupportedByProject] is true, the tool does not filter
/// the list by the current project support list.
Future<Device?> findTargetDevice({
bool includeUnsupportedDevices = false,
bool includeDevicesUnsupportedByProject = false,
}) async {
List<Device>? deviceList = await findAllTargetDevices(includeUnsupportedDevices: includeUnsupportedDevices);
List<Device>? deviceList = await findAllTargetDevices(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
);
if (deviceList == null) {
return null;
}
if (deviceList.length > 1) {
globals.printStatus(userMessages.flutterSpecifyDevice);
deviceList = await globals.deviceManager!.getAllConnectedDevices();
deviceList = await globals.deviceManager!.getAllDevices();
globals.printStatus('');
await Device.printDevices(deviceList, globals.logger);
return null;
......
......@@ -1240,6 +1240,9 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
@override
bool isSupported() => true;
@override
bool get isConnected => true;
@override
bool get supportsHotRestart => true;
......@@ -1340,6 +1343,9 @@ class FakeIOSDevice extends Fake implements IOSDevice {
@override
bool isSupportedForProject(FlutterProject project) => true;
@override
bool get isConnected => true;
}
class FakeMDnsClient extends Fake implements MDnsClient {
......
......@@ -50,7 +50,7 @@ void main() {
testUsingContext("get devices' platform types", () async {
final List<String> platformTypes = Device.devicesPlatformTypes(
await globals.deviceManager!.getAllConnectedDevices(),
await globals.deviceManager!.getAllDevices(),
);
expect(platformTypes, <String>['android', 'web']);
}, overrides: <Type, Generator>{
......@@ -134,12 +134,14 @@ class _FakeDeviceManager extends DeviceManager {
_FakeDeviceManager() : super(logger: testLogger);
@override
Future<List<Device>> getAllConnectedDevices() =>
Future<List<Device>> getAllDevices({DeviceDiscoveryFilter? filter}) =>
Future<List<Device>>.value(fakeDevices.map((FakeDeviceJsonData d) => d.dev).toList());
@override
Future<List<Device>> refreshAllConnectedDevices({Duration? timeout}) =>
getAllConnectedDevices();
Future<List<Device>> refreshAllDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) => getAllDevices(filter: filter);
@override
Future<List<String>> getDeviceDiagnostics() => Future<List<String>>.value(
......@@ -154,11 +156,16 @@ class NoDevicesManager extends DeviceManager {
NoDevicesManager() : super(logger: testLogger);
@override
Future<List<Device>> getAllConnectedDevices() async => <Device>[];
Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter,
}) async => <Device>[];
@override
Future<List<Device>> refreshAllConnectedDevices({Duration? timeout}) =>
getAllConnectedDevices();
Future<List<Device>> refreshAllDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) =>
getAllDevices();
@override
List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[];
......
......@@ -1192,7 +1192,9 @@ class FakeDeviceManager extends Fake implements DeviceManager {
List<Device> devices = <Device>[];
@override
Future<List<Device>> getAllConnectedDevices() async => devices;
Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter,
}) async => devices;
@override
Future<List<String>> getDeviceDiagnostics() async => diagnostics;
......
......@@ -587,10 +587,16 @@ class FakeDeviceManager extends Fake implements DeviceManager {
String? specifiedDeviceId;
@override
Future<List<Device>> getDevices() async => devices;
Future<List<Device>> getDevices({
DeviceDiscoveryFilter? filter,
}) async => devices;
@override
Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, {Duration? timeout, bool promptUserToChooseDevice = true}) async => devices;
Future<List<Device>> findTargetDevices({
bool includeDevicesUnsupportedByProject = false,
Duration? timeout,
bool promptUserToChooseDevice = true,
}) async => devices;
}
/// A [FlutterDriverFactory] that creates a [NeverEndingDriverService].
......
......@@ -98,7 +98,7 @@ void main() {
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
final List<Device> devices = await proxiedDevices.devices;
final List<Device> devices = await proxiedDevices.devices();
expect(devices, hasLength(1));
final Device device = devices[0];
final bool supportsRuntimeMode = await device.supportsRuntimeMode(BuildMode.release);
......@@ -121,7 +121,7 @@ void main() {
final FakeDeviceLogReader fakeLogReader = FakeDeviceLogReader();
fakeDevice.logReader = fakeLogReader;
final List<Device> devices = await proxiedDevices.devices;
final List<Device> devices = await proxiedDevices.devices();
expect(devices, hasLength(1));
final Device device = devices[0];
final DeviceLogReader logReader = await device.getLogReader();
......@@ -153,7 +153,7 @@ void main() {
dummyApplicationBinary.writeAsStringSync('dummy content');
prebuiltApplicationPackage.applicationPackage = dummyApplicationBinary;
final List<Device> devices = await proxiedDevices.devices;
final List<Device> devices = await proxiedDevices.devices();
expect(devices, hasLength(1));
final Device device = devices[0];
......@@ -200,7 +200,7 @@ void main() {
final ProxiedDevices proxiedDevices = ProxiedDevices(clientDaemonConnection, logger: bufferLogger);
final List<Device> devices = await proxiedDevices.devices;
final List<Device> devices = await proxiedDevices.devices();
expect(devices, hasLength(1));
final Device device = devices[0];
......
......@@ -1054,6 +1054,9 @@ class FakeDevice extends Fake implements Device {
@override
bool get supportsFastStart => false;
@override
bool get isConnected => true;
bool supported = true;
@override
......
......@@ -920,7 +920,9 @@ class _FakeDeviceManager extends DeviceManager {
final List<Device> _connectedDevices;
@override
Future<List<Device>> getAllConnectedDevices() async => _connectedDevices;
Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter,
}) async => _connectedDevices;
@override
List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[];
......
......@@ -88,7 +88,10 @@ class FakeDeviceManager extends Fake implements DeviceManager {
String? specifiedDeviceId;
@override
Future<List<Device>> refreshAllConnectedDevices({Duration? timeout}) async {
Future<List<Device>> refreshAllDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) async {
return devices;
}
}
......@@ -166,7 +166,7 @@ void main() {
directory: dir,
logger: BufferLogger.test()
)
).devices, <Device>[]);
).devices(), <Device>[]);
});
testWithoutContext('CustomDevice: no devices listed if custom devices feature flag disabled', () async {
......@@ -184,7 +184,7 @@ void main() {
directory: dir,
logger: BufferLogger.test()
)
).devices, <Device>[]);
).devices(), <Device>[]);
});
testWithoutContext('CustomDevices.devices', () async {
......@@ -208,7 +208,7 @@ void main() {
directory: dir,
logger: BufferLogger.test()
)
).devices,
).devices(),
hasLength(1)
);
});
......
......@@ -16,6 +16,7 @@ import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart';
import '../src/common.dart';
import '../src/context.dart';
import '../src/fake_devices.dart';
void main() {
......@@ -123,30 +124,47 @@ void main() {
expect(logger.traceText, contains('Ignored error discovering Nexus'));
});
testWithoutContext('getAllConnectedDevices caches', () async {
testWithoutContext('getDeviceById two exact matches, matches on first', () async {
final FakeDevice device1 = FakeDevice('Nexus 5', '0553790d0a4e726f');
final FakeDevice device2 = FakeDevice('Nexus 5', '01abfc49119c410e');
final List<Device> devices = <Device>[device1, device2];
final BufferLogger logger = BufferLogger.test();
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: logger,
);
Future<void> expectDevice(String id, List<Device> expected) async {
expect(await deviceManager.getDevicesById(id), expected);
}
await expectDevice('Nexus 5', <Device>[device1]);
});
testWithoutContext('getAllDevices caches', () async {
final FakeDevice device1 = FakeDevice('Nexus 5', '0553790d0a4e726f');
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[device1],
logger: BufferLogger.test(),
);
expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]);
expect(await deviceManager.getAllDevices(), <Device>[device1]);
final FakeDevice device2 = FakeDevice('Nexus 5X', '01abfc49119c410e');
deviceManager.resetDevices(<Device>[device2]);
expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]);
expect(await deviceManager.getAllDevices(), <Device>[device1]);
});
testWithoutContext('refreshAllConnectedDevices does not cache', () async {
testWithoutContext('refreshAllDevices does not cache', () async {
final FakeDevice device1 = FakeDevice('Nexus 5', '0553790d0a4e726f');
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[device1],
logger: BufferLogger.test(),
);
expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device1]);
expect(await deviceManager.refreshAllDevices(), <Device>[device1]);
final FakeDevice device2 = FakeDevice('Nexus 5X', '01abfc49119c410e');
deviceManager.resetDevices(<Device>[device2]);
expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device2]);
expect(await deviceManager.refreshAllDevices(), <Device>[device2]);
});
});
......@@ -179,8 +197,10 @@ void main() {
..targetPlatform = Future<TargetPlatform>.value(TargetPlatform.web_javascript);
final FakeDevice fuchsiaDevice = FakeDevice('fuchsiay', 'fuchsiay')
..targetPlatform = Future<TargetPlatform>.value(TargetPlatform.fuchsia_x64);
final FakeDevice unconnectedDevice = FakeDevice('ephemeralTwo', 'ephemeralTwo', isConnected: false);
final FakeDevice wirelessDevice = FakeDevice('ephemeralTwo', 'ephemeralTwo', connectionInterface: DeviceConnectionInterface.wireless);
testWithoutContext('chooses ephemeral device', () async {
testUsingContext('chooses ephemeral device', () async {
final List<Device> devices = <Device>[
ephemeralOne,
nonEphemeralOne,
......@@ -193,12 +213,14 @@ void main() {
devices,
logger: BufferLogger.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered.single, ephemeralOne);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('returns all devices when multiple non ephemeral devices are found', () async {
testUsingContext('returns all devices when multiple non ephemeral devices are found', () async {
final List<Device> devices = <Device>[
ephemeralOne,
ephemeralTwo,
......@@ -211,7 +233,7 @@ void main() {
logger: BufferLogger.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered, <Device>[
ephemeralOne,
......@@ -219,9 +241,11 @@ void main() {
nonEphemeralOne,
nonEphemeralTwo,
]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Unsupported devices listed in all connected devices', () async {
testUsingContext('Unsupported devices listed in all devices', () async {
final List<Device> devices = <Device>[
unsupported,
unsupportedForProject,
......@@ -231,15 +255,17 @@ void main() {
devices,
logger: BufferLogger.test(),
);
final List<Device> filtered = await deviceManager.getAllConnectedDevices();
final List<Device> filtered = await deviceManager.getAllDevices();
expect(filtered, <Device>[
unsupported,
unsupportedForProject,
]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Removes a unsupported devices', () async {
testUsingContext('Removes unsupported devices', () async {
final List<Device> devices = <Device>[
unsupported,
unsupportedForProject,
......@@ -248,12 +274,14 @@ void main() {
devices,
logger: BufferLogger.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered, <Device>[]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Retains devices unsupported by the project if FlutterProject is null', () async {
testUsingContext('Retains devices unsupported by the project if FlutterProject is null', () async {
final List<Device> devices = <Device>[
unsupported,
unsupportedForProject,
......@@ -263,12 +291,16 @@ void main() {
devices,
logger: BufferLogger.test(),
);
final List<Device> filtered = await deviceManager.findTargetDevices(null);
final List<Device> filtered = await deviceManager.findTargetDevices(
includeDevicesUnsupportedByProject: true,
);
expect(filtered, <Device>[unsupportedForProject]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Removes web and fuchsia from --all', () async {
testUsingContext('Removes web and fuchsia from --all', () async {
final List<Device> devices = <Device>[
webDevice,
fuchsiaDevice,
......@@ -279,12 +311,16 @@ void main() {
);
deviceManager.specifiedDeviceId = 'all';
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices(
includeDevicesUnsupportedByProject: true,
);
expect(filtered, <Device>[]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Removes devices unsupported by the project from --all', () async {
testUsingContext('Removes devices unsupported by the project from --all', () async {
final List<Device> devices = <Device>[
nonEphemeralOne,
nonEphemeralTwo,
......@@ -297,15 +333,17 @@ void main() {
);
deviceManager.specifiedDeviceId = 'all';
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered, <Device>[
nonEphemeralOne,
nonEphemeralTwo,
]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Returns device with the specified id', () async {
testUsingContext('Returns device with the specified id', () async {
final List<Device> devices = <Device>[
nonEphemeralOne,
];
......@@ -315,14 +353,16 @@ void main() {
);
deviceManager.specifiedDeviceId = nonEphemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered, <Device>[
nonEphemeralOne,
]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Returns multiple devices when multiple devices matches the specified id', () async {
testUsingContext('Returns multiple devices when multiple devices matches the specified id', () async {
final List<Device> devices = <Device>[
nonEphemeralOne,
nonEphemeralTwo,
......@@ -333,15 +373,17 @@ void main() {
);
deviceManager.specifiedDeviceId = 'nonEphemeral'; // This prefix matches both devices
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered, <Device>[
nonEphemeralOne,
nonEphemeralTwo,
]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('Returns empty when device of specified id is not found', () async {
testUsingContext('Returns empty when device of specified id is not found', () async {
final List<Device> devices = <Device>[
nonEphemeralOne,
];
......@@ -351,12 +393,14 @@ void main() {
);
deviceManager.specifiedDeviceId = nonEphemeralTwo.id;
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered, <Device>[]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
testWithoutContext('uses DeviceDiscoverySupportFilter.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
final List<Device> devices = <Device>[
unsupported,
unsupportedForProject,
......@@ -365,16 +409,106 @@ void main() {
devices,
logger: BufferLogger.test(),
);
deviceManager.isAlwaysSupportedForProjectOverride = true;
final TestDeviceDiscoverySupportFilter supportFilter =
TestDeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutterOrProject(
flutterProject: FakeFlutterProject(),
);
supportFilter.isAlwaysSupportedForProjectOverride = true;
final DeviceDiscoveryFilter filter = DeviceDiscoveryFilter(
supportFilter: supportFilter,
);
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
final List<Device> filtered = await deviceManager.getDevices(
filter: filter,
);
expect(filtered, <Device>[
unsupportedForProject,
]);
});
testWithoutContext('does not refresh device cache without a timeout', () async {
testUsingContext('Unconnencted devices filtered out by default', () async {
final List<Device> devices = <Device>[
unconnectedDevice,
];
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
);
final List<Device> filtered = await deviceManager.getDevices();
expect(filtered, <Device>[]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testUsingContext('Return unconnected devices when filter allows', () async {
final List<Device> devices = <Device>[
unconnectedDevice,
];
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
);
final DeviceDiscoveryFilter filter = DeviceDiscoveryFilter(
excludeDisconnected: false,
);
final List<Device> filtered = await deviceManager.getDevices(
filter: filter,
);
expect(filtered, <Device>[unconnectedDevice]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testUsingContext('Filter to only include wireless devices', () async {
final List<Device> devices = <Device>[
ephemeralOne,
wirelessDevice,
];
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
);
final DeviceDiscoveryFilter filter = DeviceDiscoveryFilter(
deviceConnectionInterface: DeviceConnectionInterface.wireless,
);
final List<Device> filtered = await deviceManager.getDevices(
filter: filter,
);
expect(filtered, <Device>[wirelessDevice]);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testUsingContext('Filter to only include attached devices', () async {
final List<Device> devices = <Device>[
ephemeralOne,
wirelessDevice,
];
final DeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
);
final DeviceDiscoveryFilter filter = DeviceDiscoveryFilter(
deviceConnectionInterface: DeviceConnectionInterface.attached,
);
final List<Device> filtered = await deviceManager.getDevices(
filter: filter,
);
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,
];
......@@ -389,16 +523,16 @@ void main() {
logger: BufferLogger.test(),
);
deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(
FakeFlutterProject(),
);
final List<Device> filtered = await deviceManager.findTargetDevices();
expect(filtered.single, ephemeralOne);
expect(deviceDiscovery.devicesCalled, 1);
expect(deviceDiscovery.discoverDevicesCalled, 0);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
testWithoutContext('refreshes device cache with a timeout', () async {
testUsingContext('refreshes device cache with a timeout', () async {
final List<Device> devices = <Device>[
ephemeralOne,
];
......@@ -415,13 +549,161 @@ void main() {
);
deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(
FakeFlutterProject(),
timeout: timeout,
);
expect(filtered.single, ephemeralOne);
expect(deviceDiscovery.devicesCalled, 1);
expect(deviceDiscovery.discoverDevicesCalled, 1);
}, overrides: <Type, Generator>{
FlutterProject: () => FakeFlutterProject(),
});
});
group('Simultaneous device discovery', () {
testWithoutContext('Run getAllDevices and refreshAllDevices at same time with refreshAllDevices finishing last', () async {
FakeAsync().run((FakeAsync time) {
final FakeDevice device1 = FakeDevice('Nexus 5', '0553790d0a4e726f');
final FakeDevice device2 = FakeDevice('Nexus 5X', '01abfc49119c410e');
const Duration timeToGetInitialDevices = Duration(seconds: 1);
const Duration timeToRefreshDevices = Duration(seconds: 5);
final List<Device> initialDevices = <Device>[device2];
final List<Device> refreshDevices = <Device>[device1];
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[],
logger: BufferLogger.test(),
fakeDiscoverer: FakePollingDeviceDiscoveryWithTimeout(
<List<Device>>[
initialDevices,
refreshDevices,
],
timeout: timeToGetInitialDevices,
),
);
// Expect that the cache is set by getOrSetCache process (1 second timeout)
// and then later updated by refreshCache process (5 second timeout).
// Ending with devices from the refreshCache process.
final Future<List<Device>> refreshCache = deviceManager.refreshAllDevices(
timeout: timeToRefreshDevices,
);
final Future<List<Device>> getOrSetCache = deviceManager.getAllDevices();
// After 1 second, the getAllDevices should be done
time.elapse(const Duration(seconds: 1));
expect(getOrSetCache, completion(<Device>[device2]));
// double check values in cache are as expected
Future<List<Device>> getFromCache = deviceManager.getAllDevices();
expect(getFromCache, completion(<Device>[device2]));
// After 5 seconds, getOrSetCache should be done
time.elapse(const Duration(seconds: 5));
expect(refreshCache, completion(<Device>[device1]));
// double check values in cache are as expected
getFromCache = deviceManager.getAllDevices();
expect(getFromCache, completion(<Device>[device1]));
time.flushMicrotasks();
});
});
testWithoutContext('Run getAllDevices and refreshAllDevices at same time with refreshAllDevices finishing first', () async {
fakeAsync((FakeAsync async) {
final FakeDevice device1 = FakeDevice('Nexus 5', '0553790d0a4e726f');
final FakeDevice device2 = FakeDevice('Nexus 5X', '01abfc49119c410e');
const Duration timeToGetInitialDevices = Duration(seconds: 5);
const Duration timeToRefreshDevices = Duration(seconds: 1);
final List<Device> initialDevices = <Device>[device2];
final List<Device> refreshDevices = <Device>[device1];
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[],
logger: BufferLogger.test(),
fakeDiscoverer: FakePollingDeviceDiscoveryWithTimeout(
<List<Device>>[
initialDevices,
refreshDevices,
],
timeout: timeToGetInitialDevices,
),
);
// Expect that the cache is set by refreshCache process (1 second timeout).
// Then later when getOrSetCache finishes (5 second timeout), it does not update the cache.
// Ending with devices from the refreshCache process.
final Future<List<Device>> refreshCache = deviceManager.refreshAllDevices(
timeout: timeToRefreshDevices,
);
final Future<List<Device>> getOrSetCache = deviceManager.getAllDevices();
// After 1 second, the refreshCache should be done
async.elapse(const Duration(seconds: 1));
expect(refreshCache, completion(<Device>[device2]));
// double check values in cache are as expected
Future<List<Device>> getFromCache = deviceManager.getAllDevices();
expect(getFromCache, completion(<Device>[device2]));
// After 5 seconds, getOrSetCache should be done
async.elapse(const Duration(seconds: 5));
expect(getOrSetCache, completion(<Device>[device2]));
// double check values in cache are as expected
getFromCache = deviceManager.getAllDevices();
expect(getFromCache, completion(<Device>[device2]));
async.flushMicrotasks();
});
});
testWithoutContext('refreshAllDevices twice', () async {
fakeAsync((FakeAsync async) {
final FakeDevice device1 = FakeDevice('Nexus 5', '0553790d0a4e726f');
final FakeDevice device2 = FakeDevice('Nexus 5X', '01abfc49119c410e');
const Duration timeToFirstRefresh = Duration(seconds: 1);
const Duration timeToSecondRefresh = Duration(seconds: 5);
final List<Device> firstRefreshDevices = <Device>[device2];
final List<Device> secondRefreshDevices = <Device>[device1];
final TestDeviceManager deviceManager = TestDeviceManager(
<Device>[],
logger: BufferLogger.test(),
fakeDiscoverer: FakePollingDeviceDiscoveryWithTimeout(
<List<Device>>[
firstRefreshDevices,
secondRefreshDevices,
],
),
);
// Expect that the cache is updated by each refresh in order of completion.
final Future<List<Device>> firstRefresh = deviceManager.refreshAllDevices(
timeout: timeToFirstRefresh,
);
final Future<List<Device>> secondRefresh = deviceManager.refreshAllDevices(
timeout: timeToSecondRefresh,
);
// After 1 second, the firstRefresh should be done
async.elapse(const Duration(seconds: 1));
expect(firstRefresh, completion(<Device>[device2]));
// double check values in cache are as expected
Future<List<Device>> getFromCache = deviceManager.getAllDevices();
expect(getFromCache, completion(<Device>[device2]));
// After 5 seconds, secondRefresh should be done
async.elapse(const Duration(seconds: 5));
expect(secondRefresh, completion(<Device>[device1]));
// double check values in cache are as expected
getFromCache = deviceManager.getAllDevices();
expect(getFromCache, completion(<Device>[device1]));
async.flushMicrotasks();
});
});
});
......@@ -744,7 +1026,8 @@ class TestDeviceManager extends DeviceManager {
List<DeviceDiscovery>? deviceDiscoveryOverrides,
required super.logger,
String? wellKnownId,
}) : _fakeDeviceDiscoverer = FakePollingDeviceDiscovery(),
FakePollingDeviceDiscovery? fakeDiscoverer,
}) : _fakeDeviceDiscoverer = fakeDiscoverer ?? FakePollingDeviceDiscovery(),
_deviceDiscoverers = <DeviceDiscovery>[],
super() {
if (wellKnownId != null) {
......@@ -764,16 +1047,6 @@ class TestDeviceManager extends DeviceManager {
void resetDevices(List<Device> allDevices) {
_fakeDeviceDiscoverer.setDevices(allDevices);
}
bool? isAlwaysSupportedForProjectOverride;
@override
bool isDeviceSupportedForProject(Device device, FlutterProject? flutterProject) {
if (isAlwaysSupportedForProjectOverride != null) {
return isAlwaysSupportedForProjectOverride!;
}
return super.isDeviceSupportedForProject(device, flutterProject);
}
}
class MockDeviceDiscovery extends Fake implements DeviceDiscovery {
......@@ -786,13 +1059,16 @@ class MockDeviceDiscovery extends Fake implements DeviceDiscovery {
List<Device> deviceValues = <Device>[];
@override
Future<List<Device>> get devices async {
Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async {
devicesCalled += 1;
return deviceValues;
}
@override
Future<List<Device>> discoverDevices({Duration? timeout}) async {
Future<List<Device>> discoverDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) async {
discoverDevicesCalled += 1;
return deviceValues;
}
......@@ -801,6 +1077,43 @@ class MockDeviceDiscovery extends Fake implements DeviceDiscovery {
List<String> get wellKnownIds => <String>[];
}
class TestDeviceDiscoverySupportFilter extends DeviceDiscoverySupportFilter {
TestDeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutterOrProject({
required super.flutterProject,
}) : super.excludeDevicesUnsupportedByFlutterOrProject();
bool? isAlwaysSupportedForProjectOverride;
@override
bool isDeviceSupportedForProject(Device device) {
if (isAlwaysSupportedForProjectOverride != null) {
return isAlwaysSupportedForProjectOverride!;
}
return super.isDeviceSupportedForProject(device);
}
}
class FakePollingDeviceDiscoveryWithTimeout extends FakePollingDeviceDiscovery {
FakePollingDeviceDiscoveryWithTimeout(
this._devices, {
Duration? timeout,
}): defaultTimeout = timeout ?? const Duration(seconds: 2);
final List<List<Device>> _devices;
int index = 0;
Duration defaultTimeout;
@override
Future<List<Device>> pollingGetDevices({ Duration? timeout }) async {
timeout ??= defaultTimeout;
await Future<void>.delayed(timeout);
final List<Device> results = _devices[index];
index += 1;
return results;
}
}
class FakeFlutterProject extends Fake implements FlutterProject { }
class LongPollingDeviceDiscovery extends PollingDeviceDiscovery {
......
......@@ -67,7 +67,7 @@ void main() {
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).devices, <Device>[]);
).devices(), <Device>[]);
});
testWithoutContext('LinuxDevice: no devices listed if Linux feature flag disabled', () async {
......@@ -78,7 +78,7 @@ void main() {
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).devices, <Device>[]);
).devices(), <Device>[]);
});
testWithoutContext('LinuxDevice: devices', () async {
......@@ -89,7 +89,7 @@ void main() {
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
operatingSystemUtils: FakeOperatingSystemUtils(),
).devices, hasLength(1));
).devices(), hasLength(1));
});
testWithoutContext('LinuxDevice has well known id "linux"', () async {
......
......@@ -93,7 +93,7 @@ void main() {
featureFlags: TestFeatureFlags(isMacOSEnabled: true),
platform: linux,
),
).devices, isEmpty);
).devices(), isEmpty);
});
testWithoutContext('No devices listed if platform is supported and feature is disabled', () async {
......@@ -109,7 +109,7 @@ void main() {
),
);
expect(await macOSDevices.devices, isEmpty);
expect(await macOSDevices.devices(), isEmpty);
});
testWithoutContext('devices listed if platform is supported and feature is enabled', () async {
......@@ -125,7 +125,7 @@ void main() {
),
);
expect(await macOSDevices.devices, hasLength(1));
expect(await macOSDevices.devices(), hasLength(1));
});
testWithoutContext('has a well known device id macos', () async {
......
......@@ -50,7 +50,7 @@ void main() {
);
expect(discoverer.supportsPlatform, isTrue);
final List<Device> devices = await discoverer.devices;
final List<Device> devices = await discoverer.devices();
expect(devices, isEmpty);
});
......@@ -66,7 +66,7 @@ void main() {
);
expect(discoverer.supportsPlatform, isTrue);
final List<Device> devices = await discoverer.devices;
final List<Device> devices = await discoverer.devices();
expect(devices, isEmpty);
});
......@@ -82,7 +82,7 @@ void main() {
);
expect(discoverer.supportsPlatform, isTrue);
final List<Device> devices = await discoverer.devices;
final List<Device> devices = await discoverer.devices();
expect(devices, isEmpty);
});
......@@ -98,7 +98,7 @@ void main() {
);
expect(discoverer.supportsPlatform, isTrue);
List<Device> devices = await discoverer.devices;
List<Device> devices = await discoverer.devices();
expect(devices, hasLength(1));
final Device device = devices.single;
......
......@@ -47,7 +47,7 @@ void main() {
testWithoutContext('no device', () async {
final FlutterTesterDevices discoverer = setUpFlutterTesterDevices();
final List<Device> devices = await discoverer.devices;
final List<Device> devices = await discoverer.devices();
expect(devices, isEmpty);
});
......@@ -55,7 +55,7 @@ void main() {
FlutterTesterDevices.showFlutterTesterDevice = true;
final FlutterTesterDevices discoverer = setUpFlutterTesterDevices();
final List<Device> devices = await discoverer.devices;
final List<Device> devices = await discoverer.devices();
expect(devices, hasLength(1));
final Device device = devices.single;
......
......@@ -48,7 +48,7 @@ void main() {
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
).devices, <Device>[]);
).devices(), <Device>[]);
});
testWithoutContext('WindowsDevices lists a devices if the workflow is supported', () async {
......@@ -61,7 +61,7 @@ void main() {
logger: BufferLogger.test(),
processManager: FakeProcessManager.any(),
fileSystem: MemoryFileSystem.test(),
).devices, hasLength(1));
).devices(), hasLength(1));
});
testWithoutContext('isSupportedForProject is true with editable host app', () async {
......
......@@ -207,21 +207,31 @@ class FakeDeviceManager implements DeviceManager {
}
@override
Future<List<Device>> getAllConnectedDevices() async => devices;
Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter,
}) async => devices;
@override
Future<List<Device>> refreshAllConnectedDevices({ Duration? timeout }) async => devices;
Future<List<Device>> refreshAllDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) async => devices;
@override
Future<List<Device>> getDevicesById(String deviceId) async {
Future<List<Device>> getDevicesById(
String deviceId, {
DeviceDiscoveryFilter? filter,
}) async {
return devices.where((Device device) => device.id == deviceId).toList();
}
@override
Future<List<Device>> getDevices() {
Future<List<Device>> getDevices({
DeviceDiscoveryFilter? filter,
}) {
return hasSpecifiedDeviceId
? getDevicesById(specifiedDeviceId!)
: getAllConnectedDevices();
? getDevicesById(specifiedDeviceId!, filter: filter)
: getAllDevices(filter: filter);
}
void addDevice(Device device) => devices.add(device);
......@@ -236,16 +246,27 @@ class FakeDeviceManager implements DeviceManager {
List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[];
@override
bool isDeviceSupportedForProject(Device device, FlutterProject? flutterProject) {
return device.isSupportedForProject(flutterProject!);
Future<List<Device>> findTargetDevices({
bool includeDevicesUnsupportedByProject = false,
Duration? timeout,
bool promptUserToChooseDevice = true,
}) async {
return devices;
}
@override
Future<List<Device>> findTargetDevices(FlutterProject? flutterProject, { Duration? timeout, bool promptUserToChooseDevice = true }) async {
return devices;
DeviceDiscoverySupportFilter deviceSupportFilter({
bool includeDevicesUnsupportedByProject = false,
FlutterProject? flutterProject,
}) {
return TestDeviceDiscoverySupportFilter();
}
}
class TestDeviceDiscoverySupportFilter extends Fake implements DeviceDiscoverySupportFilter {
TestDeviceDiscoverySupportFilter();
}
class FakeAndroidLicenseValidator extends Fake implements AndroidLicenseValidator {
@override
Future<LicensesAccepted> get licensesAccepted async => LicensesAccepted.all;
......
......@@ -62,6 +62,8 @@ class FakeDevice extends Device {
bool ephemeral = true,
bool isSupported = true,
bool isSupportedForProject = true,
this.isConnected = true,
this.connectionInterface = DeviceConnectionInterface.attached,
PlatformType type = PlatformType.web,
LaunchResult? launchResult,
}) : _isSupported = isSupported,
......@@ -118,6 +120,12 @@ class FakeDevice extends Device {
@override
bool isSupported() => _isSupported;
@override
bool isConnected;
@override
DeviceConnectionInterface connectionInterface;
@override
Future<bool> isLocalEmulator = Future<bool>.value(true);
......@@ -174,7 +182,10 @@ class FakePollingDeviceDiscovery extends PollingDeviceDiscovery {
bool discoverDevicesCalled = false;
@override
Future<List<Device>> discoverDevices({Duration? timeout}) {
Future<List<Device>> discoverDevices({
Duration? timeout,
DeviceDiscoveryFilter? filter,
}) {
discoverDevicesCalled = true;
return super.discoverDevices(timeout: timeout);
}
......
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