Unverified Commit 27248d4b authored by Victoria Ashworth's avatar Victoria Ashworth Committed by GitHub

Separate attached and wireless devices (#122615)

Separate attached and wireless devices
parent 5d10cc28
...@@ -89,6 +89,14 @@ class AndroidDevice extends Device { ...@@ -89,6 +89,14 @@ class AndroidDevice extends Device {
final String modelID; final String modelID;
final String? deviceCodeName; final String? deviceCodeName;
@override
// Wirelessly paired Android devices should have `adb-tls-connect` in the id.
// Source: https://android.googlesource.com/platform/packages/modules/adb/+/f4ba8d73079b99532069dbe888a58167b8723d6c/adb_mdns.h#30
DeviceConnectionInterface get connectionInterface =>
id.contains('adb-tls-connect')
? DeviceConnectionInterface.wireless
: DeviceConnectionInterface.attached;
late final Future<Map<String, String>> _properties = () async { late final Future<Map<String, String>> _properties = () async {
Map<String, String> properties = <String, String>{}; Map<String, String> properties = <String, String>{};
......
...@@ -265,7 +265,7 @@ class UserMessages { ...@@ -265,7 +265,7 @@ class UserMessages {
'for information about installing additional components.'; 'for information about installing additional components.';
String flutterNoMatchingDevice(String deviceId) => 'No supported devices found with name or id ' String flutterNoMatchingDevice(String deviceId) => 'No supported devices found with name or id '
"matching '$deviceId'."; "matching '$deviceId'.";
String get flutterNoDevicesFound => 'No devices found'; String get flutterNoDevicesFound => 'No devices found.';
String get flutterNoSupportedDevices => 'No supported devices connected.'; String get flutterNoSupportedDevices => 'No supported devices connected.';
String flutterMissPlatformProjects(List<String> unsupportedDevicesType) => String flutterMissPlatformProjects(List<String> unsupportedDevicesType) =>
'If you would like your app to run on ${unsupportedDevicesType.join(' or ')}, consider running `flutter create .` to generate projects for these platforms.'; 'If you would like your app to run on ${unsupportedDevicesType.join(' or ')}, consider running `flutter create .` to generate projects for these platforms.';
......
...@@ -24,7 +24,6 @@ import '../device.dart'; ...@@ -24,7 +24,6 @@ import '../device.dart';
import '../device_port_forwarder.dart'; import '../device_port_forwarder.dart';
import '../fuchsia/fuchsia_device.dart'; import '../fuchsia/fuchsia_device.dart';
import '../ios/devices.dart'; import '../ios/devices.dart';
import '../ios/iproxy.dart';
import '../ios/simulators.dart'; import '../ios/simulators.dart';
import '../macos/macos_ipad_device.dart'; import '../macos/macos_ipad_device.dart';
import '../mdns_discovery.dart'; import '../mdns_discovery.dart';
...@@ -287,7 +286,7 @@ known, it can be explicitly provided to attach via the command-line, e.g. ...@@ -287,7 +286,7 @@ known, it can be explicitly provided to attach via the command-line, e.g.
final String ipv6Loopback = InternetAddress.loopbackIPv6.address; final String ipv6Loopback = InternetAddress.loopbackIPv6.address;
final String ipv4Loopback = InternetAddress.loopbackIPv4.address; final String ipv4Loopback = InternetAddress.loopbackIPv4.address;
final String hostname = usesIpv6 ? ipv6Loopback : ipv4Loopback; final String hostname = usesIpv6 ? ipv6Loopback : ipv4Loopback;
final bool isNetworkDevice = (device is IOSDevice) && device.interfaceType == IOSDeviceConnectionInterface.network; final bool isNetworkDevice = (device is IOSDevice) && device.isWirelesslyConnected;
if ((debugPort == null && debugUri == null) || isNetworkDevice) { if ((debugPort == null && debugUri == null) || isNetworkDevice) {
if (device is FuchsiaDevice) { if (device is FuchsiaDevice) {
......
...@@ -79,13 +79,59 @@ class DevicesCommandOutput { ...@@ -79,13 +79,59 @@ class DevicesCommandOutput {
final Duration? deviceDiscoveryTimeout; final Duration? deviceDiscoveryTimeout;
Future<List<Device>> _getAttachedDevices(DeviceManager deviceManager) async {
return deviceManager.getAllDevices(
filter: DeviceDiscoveryFilter(
deviceConnectionInterface: DeviceConnectionInterface.attached,
),
);
}
Future<List<Device>> _getWirelessDevices(DeviceManager deviceManager) async {
return deviceManager.getAllDevices(
filter: DeviceDiscoveryFilter(
deviceConnectionInterface: DeviceConnectionInterface.wireless,
),
);
}
Future<void> findAndOutputAllTargetDevices({required bool machine}) async { Future<void> findAndOutputAllTargetDevices({required bool machine}) async {
final List<Device> devices = await globals.deviceManager?.refreshAllDevices(timeout: deviceDiscoveryTimeout) ?? <Device>[]; List<Device> attachedDevices = <Device>[];
List<Device> wirelessDevices = <Device>[];
final DeviceManager? deviceManager = globals.deviceManager;
if (deviceManager != null) {
// Refresh the cache and then get the attached and wireless devices from
// the cache.
await deviceManager.refreshAllDevices(timeout: deviceDiscoveryTimeout);
attachedDevices = await _getAttachedDevices(deviceManager);
wirelessDevices = await _getWirelessDevices(deviceManager);
}
final List<Device> allDevices = attachedDevices + wirelessDevices;
if (machine) { if (machine) {
await printDevicesAsJson(devices); await printDevicesAsJson(allDevices);
return;
}
if (allDevices.isEmpty) {
_printNoDevicesDetected();
} else { } else {
if (devices.isEmpty) { if (attachedDevices.isNotEmpty) {
globals.printStatus('${attachedDevices.length} connected ${pluralize('device', attachedDevices.length)}:\n');
await Device.printDevices(attachedDevices, globals.logger);
}
if (wirelessDevices.isNotEmpty) {
if (attachedDevices.isNotEmpty) {
globals.printStatus('');
}
globals.printStatus('${wirelessDevices.length} wirelessly connected ${pluralize('device', wirelessDevices.length)}:\n');
await Device.printDevices(wirelessDevices, globals.logger);
}
}
await _printDiagnostics();
}
void _printNoDevicesDetected() {
final StringBuffer status = StringBuffer('No devices detected.'); final StringBuffer status = StringBuffer('No devices detected.');
status.writeln(); status.writeln();
status.writeln(); status.writeln();
...@@ -98,12 +144,6 @@ class DevicesCommandOutput { ...@@ -98,12 +144,6 @@ class DevicesCommandOutput {
status.write('Visit https://flutter.dev/setup/ for troubleshooting tips.'); status.write('Visit https://flutter.dev/setup/ for troubleshooting tips.');
globals.printStatus(status.toString()); globals.printStatus(status.toString());
} else {
globals.printStatus('${devices.length} connected ${pluralize('device', devices.length)}:\n');
await Device.printDevices(devices, globals.logger);
}
await _printDiagnostics();
}
} }
Future<void> _printDiagnostics() async { Future<void> _printDiagnostics() async {
......
...@@ -23,7 +23,6 @@ import '../device.dart'; ...@@ -23,7 +23,6 @@ import '../device.dart';
import '../drive/drive_service.dart'; import '../drive/drive_service.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../ios/devices.dart'; import '../ios/devices.dart';
import '../ios/iproxy.dart';
import '../resident_runner.dart'; import '../resident_runner.dart';
import '../runner/flutter_command.dart' show FlutterCommandCategory, FlutterCommandResult, FlutterOptions; import '../runner/flutter_command.dart' show FlutterCommandCategory, FlutterCommandResult, FlutterOptions;
import '../web/web_device.dart'; import '../web/web_device.dart';
...@@ -220,7 +219,7 @@ class DriveCommand extends RunCommandBase { ...@@ -220,7 +219,7 @@ class DriveCommand extends RunCommandBase {
Future<bool> get disablePortPublication async { Future<bool> get disablePortPublication async {
final ArgResults? localArgResults = argResults; final ArgResults? localArgResults = argResults;
final Device? device = await targetedDevice; final Device? device = await targetedDevice;
final bool isNetworkDevice = device is IOSDevice && device.interfaceType == IOSDeviceConnectionInterface.network; final bool isNetworkDevice = device is IOSDevice && device.isWirelesslyConnected;
if (isNetworkDevice && localArgResults != null && !localArgResults.wasParsed('publish-port')) { if (isNetworkDevice && localArgResults != null && !localArgResults.wasParsed('publish-port')) {
_logger.printTrace('Network device is being used. Changing `publish-port` to be enabled.'); _logger.printTrace('Network device is being used. Changing `publish-port` to be enabled.');
return false; return false;
......
...@@ -20,7 +20,6 @@ import '../device.dart'; ...@@ -20,7 +20,6 @@ import '../device.dart';
import '../features.dart'; import '../features.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../ios/devices.dart'; import '../ios/devices.dart';
import '../ios/iproxy.dart';
import '../project.dart'; import '../project.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
import '../resident_runner.dart'; import '../resident_runner.dart';
...@@ -426,7 +425,7 @@ class RunCommand extends RunCommandBase { ...@@ -426,7 +425,7 @@ class RunCommand extends RunCommandBase {
final TargetPlatform platform = await device.targetPlatform; final TargetPlatform platform = await device.targetPlatform;
anyAndroidDevices = platform == TargetPlatform.android; anyAndroidDevices = platform == TargetPlatform.android;
anyIOSDevices = platform == TargetPlatform.ios; anyIOSDevices = platform == TargetPlatform.ios;
if (device is IOSDevice && device.interfaceType == IOSDeviceConnectionInterface.network) { if (device is IOSDevice && device.isWirelesslyConnected) {
anyIOSNetworkDevices = true; anyIOSNetworkDevices = true;
} }
deviceType = getNameForTargetPlatform(platform); deviceType = getNameForTargetPlatform(platform);
...@@ -440,7 +439,7 @@ class RunCommand extends RunCommandBase { ...@@ -440,7 +439,7 @@ class RunCommand extends RunCommandBase {
final TargetPlatform platform = await device.targetPlatform; final TargetPlatform platform = await device.targetPlatform;
anyAndroidDevices = anyAndroidDevices || (platform == TargetPlatform.android); anyAndroidDevices = anyAndroidDevices || (platform == TargetPlatform.android);
anyIOSDevices = anyIOSDevices || (platform == TargetPlatform.ios); anyIOSDevices = anyIOSDevices || (platform == TargetPlatform.ios);
if (device is IOSDevice && device.interfaceType == IOSDeviceConnectionInterface.network) { if (device is IOSDevice && device.isWirelesslyConnected) {
anyIOSNetworkDevices = true; anyIOSNetworkDevices = true;
} }
if (anyAndroidDevices && anyIOSDevices) { if (anyAndroidDevices && anyIOSDevices) {
......
...@@ -17,7 +17,6 @@ import 'base/utils.dart'; ...@@ -17,7 +17,6 @@ import 'base/utils.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'devfs.dart'; import 'devfs.dart';
import 'device_port_forwarder.dart'; import 'device_port_forwarder.dart';
import 'ios/iproxy.dart';
import 'project.dart'; import 'project.dart';
import 'vmservice.dart'; import 'vmservice.dart';
...@@ -1098,7 +1097,7 @@ class DebuggingOptions { ...@@ -1098,7 +1097,7 @@ class DebuggingOptions {
String? route, String? route,
Map<String, Object?> platformArgs, { Map<String, Object?> platformArgs, {
bool ipv6 = false, bool ipv6 = false,
IOSDeviceConnectionInterface interfaceType = IOSDeviceConnectionInterface.none DeviceConnectionInterface interfaceType = DeviceConnectionInterface.attached,
}) { }) {
final String dartVmFlags = computeDartVmFlags(this); final String dartVmFlags = computeDartVmFlags(this);
return <String>[ return <String>[
...@@ -1137,7 +1136,7 @@ class DebuggingOptions { ...@@ -1137,7 +1136,7 @@ class DebuggingOptions {
if (environmentType == EnvironmentType.simulator && hostVmServicePort != null) if (environmentType == EnvironmentType.simulator && hostVmServicePort != null)
'--vm-service-port=$hostVmServicePort', '--vm-service-port=$hostVmServicePort',
// Tell the VM service to listen on all interfaces, don't restrict to the loopback. // Tell the VM service to listen on all interfaces, don't restrict to the loopback.
if (interfaceType == IOSDeviceConnectionInterface.network) if (interfaceType == DeviceConnectionInterface.wireless)
'--vm-service-host=${ipv6 ? '::0' : '0.0.0.0'}', '--vm-service-host=${ipv6 ? '::0' : '0.0.0.0'}',
if (enableEmbedderApi) '--enable-embedder-api', if (enableEmbedderApi) '--enable-embedder-api',
]; ];
......
...@@ -151,7 +151,7 @@ class IOSDevice extends Device { ...@@ -151,7 +151,7 @@ class IOSDevice extends Device {
required FileSystem fileSystem, required FileSystem fileSystem,
required this.name, required this.name,
required this.cpuArchitecture, required this.cpuArchitecture,
required this.interfaceType, required this.connectionInterface,
String? sdkVersion, String? sdkVersion,
required Platform platform, required Platform platform,
required IOSDeploy iosDeploy, required IOSDeploy iosDeploy,
...@@ -199,7 +199,8 @@ class IOSDevice extends Device { ...@@ -199,7 +199,8 @@ class IOSDevice extends Device {
final DarwinArch cpuArchitecture; final DarwinArch cpuArchitecture;
final IOSDeviceConnectionInterface interfaceType; @override
final DeviceConnectionInterface connectionInterface;
final Map<IOSApp?, DeviceLogReader> _logReaders = <IOSApp?, DeviceLogReader>{}; final Map<IOSApp?, DeviceLogReader> _logReaders = <IOSApp?, DeviceLogReader>{};
...@@ -256,7 +257,7 @@ class IOSDevice extends Device { ...@@ -256,7 +257,7 @@ class IOSDevice extends Device {
bundlePath: bundle.path, bundlePath: bundle.path,
appDeltaDirectory: app.appDeltaDirectory, appDeltaDirectory: app.appDeltaDirectory,
launchArguments: <String>[], launchArguments: <String>[],
interfaceType: interfaceType, interfaceType: connectionInterface,
); );
} on ProcessException catch (e) { } on ProcessException catch (e) {
_logger.printError(e.message); _logger.printError(e.message);
...@@ -311,7 +312,7 @@ class IOSDevice extends Device { ...@@ -311,7 +312,7 @@ class IOSDevice extends Device {
@visibleForTesting Duration? discoveryTimeout, @visibleForTesting Duration? discoveryTimeout,
}) async { }) async {
String? packageId; String? packageId;
if (interfaceType == IOSDeviceConnectionInterface.network && if (isWirelesslyConnected &&
debuggingOptions.debuggingEnabled && debuggingOptions.debuggingEnabled &&
debuggingOptions.disablePortPublication) { debuggingOptions.disablePortPublication) {
throwToolExit('Cannot start app on wirelessly tethered iOS device. Try running again with the --publish-port flag'); throwToolExit('Cannot start app on wirelessly tethered iOS device. Try running again with the --publish-port flag');
...@@ -351,7 +352,7 @@ class IOSDevice extends Device { ...@@ -351,7 +352,7 @@ class IOSDevice extends Device {
route, route,
platformArgs, platformArgs,
ipv6: ipv6, ipv6: ipv6,
interfaceType: interfaceType, interfaceType: connectionInterface,
); );
Status startAppStatus = _logger.startProgress( Status startAppStatus = _logger.startProgress(
'Installing and launching...', 'Installing and launching...',
...@@ -371,7 +372,7 @@ class IOSDevice extends Device { ...@@ -371,7 +372,7 @@ class IOSDevice extends Device {
bundlePath: bundle.path, bundlePath: bundle.path,
appDeltaDirectory: package.appDeltaDirectory, appDeltaDirectory: package.appDeltaDirectory,
launchArguments: launchArguments, launchArguments: launchArguments,
interfaceType: interfaceType, interfaceType: connectionInterface,
uninstallFirst: debuggingOptions.uninstallFirst, uninstallFirst: debuggingOptions.uninstallFirst,
); );
if (deviceLogReader is IOSDeviceLogReader) { if (deviceLogReader is IOSDeviceLogReader) {
...@@ -381,7 +382,7 @@ class IOSDevice extends Device { ...@@ -381,7 +382,7 @@ class IOSDevice extends Device {
// Don't port foward if debugging with a network device. // Don't port foward if debugging with a network device.
vmServiceDiscovery = ProtocolDiscovery.vmService( vmServiceDiscovery = ProtocolDiscovery.vmService(
deviceLogReader, deviceLogReader,
portForwarder: interfaceType == IOSDeviceConnectionInterface.network ? null : portForwarder, portForwarder: isWirelesslyConnected ? null : portForwarder,
hostPort: debuggingOptions.hostVmServicePort, hostPort: debuggingOptions.hostVmServicePort,
devicePort: debuggingOptions.deviceVmServicePort, devicePort: debuggingOptions.deviceVmServicePort,
ipv6: ipv6, ipv6: ipv6,
...@@ -394,7 +395,7 @@ class IOSDevice extends Device { ...@@ -394,7 +395,7 @@ class IOSDevice extends Device {
bundlePath: bundle.path, bundlePath: bundle.path,
appDeltaDirectory: package.appDeltaDirectory, appDeltaDirectory: package.appDeltaDirectory,
launchArguments: launchArguments, launchArguments: launchArguments,
interfaceType: interfaceType, interfaceType: connectionInterface,
uninstallFirst: debuggingOptions.uninstallFirst, uninstallFirst: debuggingOptions.uninstallFirst,
); );
} else { } else {
...@@ -414,13 +415,13 @@ class IOSDevice extends Device { ...@@ -414,13 +415,13 @@ class IOSDevice extends Device {
_logger.printTrace('Application launched on the device. Waiting for Dart VM Service url.'); _logger.printTrace('Application launched on the device. Waiting for Dart VM Service url.');
final int defaultTimeout = interfaceType == IOSDeviceConnectionInterface.network ? 45 : 30; final int defaultTimeout = isWirelesslyConnected ? 45 : 30;
final Timer timer = Timer(discoveryTimeout ?? Duration(seconds: defaultTimeout), () { final Timer timer = Timer(discoveryTimeout ?? Duration(seconds: defaultTimeout), () {
_logger.printError('The Dart VM Service was not discovered after $defaultTimeout seconds. This is taking much longer than expected...'); _logger.printError('The Dart VM Service was not discovered after $defaultTimeout seconds. This is taking much longer than expected...');
// If debugging with a wireless device and the timeout is reached, remind the // If debugging with a wireless device and the timeout is reached, remind the
// user to allow local network permissions. // user to allow local network permissions.
if (interfaceType == IOSDeviceConnectionInterface.network) { if (isWirelesslyConnected) {
_logger.printError( _logger.printError(
'\nClick "Allow" to the prompt asking if you would like to find and connect devices on your local network. ' '\nClick "Allow" to the prompt asking if you would like to find and connect devices on your local network. '
'This is required for wireless debugging. If you selected "Don\'t Allow", ' 'This is required for wireless debugging. If you selected "Don\'t Allow", '
...@@ -433,7 +434,7 @@ class IOSDevice extends Device { ...@@ -433,7 +434,7 @@ class IOSDevice extends Device {
}); });
Uri? localUri; Uri? localUri;
if (interfaceType == IOSDeviceConnectionInterface.network) { if (isWirelesslyConnected) {
// Wait for Dart VM Service to start up. // Wait for Dart VM Service to start up.
final Uri? serviceURL = await vmServiceDiscovery?.uri; final Uri? serviceURL = await vmServiceDiscovery?.uri;
if (serviceURL == null) { if (serviceURL == null) {
...@@ -538,7 +539,7 @@ class IOSDevice extends Device { ...@@ -538,7 +539,7 @@ class IOSDevice extends Device {
@override @override
Future<void> takeScreenshot(File outputFile) async { Future<void> takeScreenshot(File outputFile) async {
await _iMobileDevice.takeScreenshot(outputFile, id, interfaceType); await _iMobileDevice.takeScreenshot(outputFile, id, connectionInterface);
} }
@override @override
......
...@@ -15,8 +15,8 @@ import '../base/platform.dart'; ...@@ -15,8 +15,8 @@ import '../base/platform.dart';
import '../base/process.dart'; import '../base/process.dart';
import '../cache.dart'; import '../cache.dart';
import '../convert.dart'; import '../convert.dart';
import '../device.dart';
import 'code_signing.dart'; import 'code_signing.dart';
import 'iproxy.dart';
// Error message patterns from ios-deploy output // Error message patterns from ios-deploy output
const String noProvisioningProfileErrorOne = 'Error 0xe8008015'; const String noProvisioningProfileErrorOne = 'Error 0xe8008015';
...@@ -88,7 +88,7 @@ class IOSDeploy { ...@@ -88,7 +88,7 @@ class IOSDeploy {
required String deviceId, required String deviceId,
required String bundlePath, required String bundlePath,
required List<String>launchArguments, required List<String>launchArguments,
required IOSDeviceConnectionInterface interfaceType, required DeviceConnectionInterface interfaceType,
Directory? appDeltaDirectory, Directory? appDeltaDirectory,
}) async { }) async {
appDeltaDirectory?.createSync(recursive: true); appDeltaDirectory?.createSync(recursive: true);
...@@ -102,7 +102,7 @@ class IOSDeploy { ...@@ -102,7 +102,7 @@ class IOSDeploy {
'--app_deltas', '--app_deltas',
appDeltaDirectory.path, appDeltaDirectory.path,
], ],
if (interfaceType != IOSDeviceConnectionInterface.network) if (interfaceType != DeviceConnectionInterface.wireless)
'--no-wifi', '--no-wifi',
if (launchArguments.isNotEmpty) ...<String>[ if (launchArguments.isNotEmpty) ...<String>[
'--args', '--args',
...@@ -126,7 +126,7 @@ class IOSDeploy { ...@@ -126,7 +126,7 @@ class IOSDeploy {
required String deviceId, required String deviceId,
required String bundlePath, required String bundlePath,
required List<String> launchArguments, required List<String> launchArguments,
required IOSDeviceConnectionInterface interfaceType, required DeviceConnectionInterface interfaceType,
Directory? appDeltaDirectory, Directory? appDeltaDirectory,
required bool uninstallFirst, required bool uninstallFirst,
}) { }) {
...@@ -149,7 +149,7 @@ class IOSDeploy { ...@@ -149,7 +149,7 @@ class IOSDeploy {
if (uninstallFirst) if (uninstallFirst)
'--uninstall', '--uninstall',
'--debug', '--debug',
if (interfaceType != IOSDeviceConnectionInterface.network) if (interfaceType != DeviceConnectionInterface.wireless)
'--no-wifi', '--no-wifi',
if (launchArguments.isNotEmpty) ...<String>[ if (launchArguments.isNotEmpty) ...<String>[
'--args', '--args',
...@@ -171,7 +171,7 @@ class IOSDeploy { ...@@ -171,7 +171,7 @@ class IOSDeploy {
required String deviceId, required String deviceId,
required String bundlePath, required String bundlePath,
required List<String> launchArguments, required List<String> launchArguments,
required IOSDeviceConnectionInterface interfaceType, required DeviceConnectionInterface interfaceType,
required bool uninstallFirst, required bool uninstallFirst,
Directory? appDeltaDirectory, Directory? appDeltaDirectory,
}) async { }) async {
...@@ -186,7 +186,7 @@ class IOSDeploy { ...@@ -186,7 +186,7 @@ class IOSDeploy {
'--app_deltas', '--app_deltas',
appDeltaDirectory.path, appDeltaDirectory.path,
], ],
if (interfaceType != IOSDeviceConnectionInterface.network) if (interfaceType != DeviceConnectionInterface.wireless)
'--no-wifi', '--no-wifi',
if (uninstallFirst) if (uninstallFirst)
'--uninstall', '--uninstall',
......
...@@ -8,12 +8,6 @@ import '../base/io.dart'; ...@@ -8,12 +8,6 @@ import '../base/io.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/process.dart'; import '../base/process.dart';
enum IOSDeviceConnectionInterface {
none,
usb,
network,
}
/// Wraps iproxy command line tool port forwarding. /// Wraps iproxy command line tool port forwarding.
/// ///
/// See https://github.com/libimobiledevice/libusbmuxd. /// See https://github.com/libimobiledevice/libusbmuxd.
......
...@@ -16,6 +16,7 @@ import '../base/project_migrator.dart'; ...@@ -16,6 +16,7 @@ import '../base/project_migrator.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../device.dart';
import '../flutter_manifest.dart'; import '../flutter_manifest.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../macos/cocoapod_utils.dart'; import '../macos/cocoapod_utils.dart';
...@@ -27,7 +28,6 @@ import '../project.dart'; ...@@ -27,7 +28,6 @@ import '../project.dart';
import '../reporting/reporting.dart'; import '../reporting/reporting.dart';
import 'application_package.dart'; import 'application_package.dart';
import 'code_signing.dart'; import 'code_signing.dart';
import 'iproxy.dart';
import 'migrations/host_app_info_plist_migration.dart'; import 'migrations/host_app_info_plist_migration.dart';
import 'migrations/ios_deployment_target_migration.dart'; import 'migrations/ios_deployment_target_migration.dart';
import 'migrations/project_base_configuration_migration.dart'; import 'migrations/project_base_configuration_migration.dart';
...@@ -87,7 +87,7 @@ class IMobileDevice { ...@@ -87,7 +87,7 @@ class IMobileDevice {
Future<void> takeScreenshot( Future<void> takeScreenshot(
File outputFile, File outputFile,
String deviceID, String deviceID,
IOSDeviceConnectionInterface interfaceType, DeviceConnectionInterface interfaceType,
) { ) {
return _processUtils.run( return _processUtils.run(
<String>[ <String>[
...@@ -95,7 +95,7 @@ class IMobileDevice { ...@@ -95,7 +95,7 @@ class IMobileDevice {
outputFile.path, outputFile.path,
'--udid', '--udid',
deviceID, deviceID,
if (interfaceType == IOSDeviceConnectionInterface.network) if (interfaceType == DeviceConnectionInterface.wireless)
'--network', '--network',
], ],
throwOnError: true, throwOnError: true,
......
...@@ -14,6 +14,7 @@ import '../base/process.dart'; ...@@ -14,6 +14,7 @@ import '../base/process.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../cache.dart'; import '../cache.dart';
import '../convert.dart'; import '../convert.dart';
import '../device.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
import '../ios/devices.dart'; import '../ios/devices.dart';
import '../ios/ios_deploy.dart'; import '../ios/ios_deploy.dart';
...@@ -303,8 +304,6 @@ class XCDevice { ...@@ -303,8 +304,6 @@ class XCDevice {
} }
} }
final IOSDeviceConnectionInterface interface = _interfaceType(device);
String? sdkVersion = _sdkVersion(device); String? sdkVersion = _sdkVersion(device);
if (sdkVersion != null) { if (sdkVersion != null) {
...@@ -318,7 +317,7 @@ class XCDevice { ...@@ -318,7 +317,7 @@ class XCDevice {
identifier, identifier,
name: name, name: name,
cpuArchitecture: _cpuArchitecture(device), cpuArchitecture: _cpuArchitecture(device),
interfaceType: interface, connectionInterface: _interfaceType(device),
sdkVersion: sdkVersion, sdkVersion: sdkVersion,
iProxy: _iProxy, iProxy: _iProxy,
fileSystem: globals.fs, fileSystem: globals.fs,
...@@ -356,19 +355,16 @@ class XCDevice { ...@@ -356,19 +355,16 @@ class XCDevice {
return code is int ? code : null; return code is int ? code : null;
} }
static IOSDeviceConnectionInterface _interfaceType(Map<String, Object?> deviceProperties) { static DeviceConnectionInterface _interfaceType(Map<String, Object?> deviceProperties) {
// Interface can be "usb", "network", or "none" for simulators // Interface can be "usb" or "network". It can also be missing
// and unknown future interfaces. // (e.g. simulators do not have an interface property).
// If the interface is "network", use `DeviceConnectionInterface.wireless`,
// otherwise use `DeviceConnectionInterface.attached.
final Object? interface = deviceProperties['interface']; final Object? interface = deviceProperties['interface'];
if (interface is String) { if (interface is String && interface.toLowerCase() == 'network') {
if (interface.toLowerCase() == 'network') { return DeviceConnectionInterface.wireless;
return IOSDeviceConnectionInterface.network;
} else {
return IOSDeviceConnectionInterface.usb;
} }
} return DeviceConnectionInterface.attached;
return IOSDeviceConnectionInterface.none;
} }
static String? _sdkVersion(Map<String, Object?> deviceProperties) { static String? _sdkVersion(Map<String, Object?> deviceProperties) {
......
...@@ -2,14 +2,18 @@ ...@@ -2,14 +2,18 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
import 'package:meta/meta.dart';
import '../base/common.dart'; import '../base/common.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../base/user_messages.dart'; import '../base/user_messages.dart';
import '../device.dart'; import '../device.dart';
import '../globals.dart' as globals; import '../globals.dart' as globals;
const String _wirelesslyConnectedDevicesMessage = 'Wirelessly connected devices:';
/// This class handles functionality of finding and selecting target devices.
///
/// Target devices are devices that are supported and selectable to run
/// a flutter application on.
class TargetDevices { class TargetDevices {
TargetDevices({ TargetDevices({
required DeviceManager deviceManager, required DeviceManager deviceManager,
...@@ -20,10 +24,64 @@ class TargetDevices { ...@@ -20,10 +24,64 @@ class TargetDevices {
final DeviceManager _deviceManager; final DeviceManager _deviceManager;
final Logger _logger; final Logger _logger;
/// Find and return all target [Device]s based upon currently connected Future<List<Device>> _getAttachedDevices({
/// devices and criteria entered by the user on the command line. DeviceDiscoverySupportFilter? supportFilter,
/// If no device can be found that meets specified criteria, }) async {
/// then print an error message and return null. return _deviceManager.getDevices(
filter: DeviceDiscoveryFilter(
deviceConnectionInterface: DeviceConnectionInterface.attached,
supportFilter: supportFilter,
),
);
}
Future<List<Device>> _getWirelessDevices({
DeviceDiscoverySupportFilter? supportFilter,
}) async {
return _deviceManager.getDevices(
filter: DeviceDiscoveryFilter(
deviceConnectionInterface: DeviceConnectionInterface.wireless,
supportFilter: supportFilter,
),
);
}
Future<List<Device>> _getDeviceById({
bool includeDevicesUnsupportedByProject = false,
}) async {
return _deviceManager.getDevices(
filter: DeviceDiscoveryFilter(
supportFilter: _deviceManager.deviceSupportFilter(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
),
),
);
}
DeviceDiscoverySupportFilter _defaultSupportFilter(
bool includeDevicesUnsupportedByProject,
) {
return _deviceManager.deviceSupportFilter(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
);
}
/// Find and return all target [Device]s based upon criteria entered by the
/// user on the command line.
///
/// When the user has specified `all` devices, return all devices meeting criteria.
///
/// When the user has specified a device id/name, attempt to find an exact or
/// partial match. If an exact match or a single partial match is found,
/// return it immediately.
///
/// When multiple devices are found and there is a terminal attached to
/// stdin, allow the user to select which device to use. When a terminal
/// with stdin is not available, print a list of available devices and
/// return null.
///
/// When no devices meet user specifications, print a list of unsupported
/// devices and return null.
Future<List<Device>?> findAllTargetDevices({ Future<List<Device>?> findAllTargetDevices({
Duration? deviceDiscoveryTimeout, Duration? deviceDiscoveryTimeout,
bool includeDevicesUnsupportedByProject = false, bool includeDevicesUnsupportedByProject = false,
...@@ -32,67 +90,175 @@ class TargetDevices { ...@@ -32,67 +90,175 @@ class TargetDevices {
_logger.printError(userMessages.flutterNoDevelopmentDevice); _logger.printError(userMessages.flutterNoDevelopmentDevice);
return null; return null;
} }
List<Device> devices = await getDevices(
if (deviceDiscoveryTimeout != null) {
// Reset the cache with the specified timeout.
await _deviceManager.refreshAllDevices(timeout: deviceDiscoveryTimeout);
}
if (_deviceManager.hasSpecifiedDeviceId) {
// Must check for device match separately from `_getAttachedDevices` and
// `_getWirelessDevices` because if an exact match is found in one
// and a partial match is found in another, there is no way to distinguish
// between them.
final List<Device> devices = await _getDeviceById(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject, includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
timeout: deviceDiscoveryTimeout,
); );
if (devices.length == 1) {
return devices;
}
}
final List<Device> attachedDevices = await _getAttachedDevices(
supportFilter: _defaultSupportFilter(includeDevicesUnsupportedByProject),
);
final List<Device> wirelessDevices = await _getWirelessDevices(
supportFilter: _defaultSupportFilter(includeDevicesUnsupportedByProject),
);
final List<Device> allDevices = attachedDevices + wirelessDevices;
if (allDevices.isEmpty) {
return _handleNoDevices();
} else if (_deviceManager.hasSpecifiedAllDevices) {
return allDevices;
} else if (allDevices.length > 1) {
return _handleMultipleDevices(attachedDevices, wirelessDevices);
}
return allDevices;
}
/// When no supported devices are found, display a message and list of
/// unsupported devices found.
Future<List<Device>?> _handleNoDevices() async {
// Get connected devices from cache, including unsupported ones.
final List<Device> unsupportedDevices = await _deviceManager.getAllDevices();
if (devices.isEmpty) {
if (_deviceManager.hasSpecifiedDeviceId) { if (_deviceManager.hasSpecifiedDeviceId) {
_logger.printStatus(userMessages.flutterNoMatchingDevice(_deviceManager.specifiedDeviceId!)); _logger.printStatus(
final List<Device> allDevices = await _deviceManager.getAllDevices(); userMessages.flutterNoMatchingDevice(_deviceManager.specifiedDeviceId!),
if (allDevices.isNotEmpty) { );
if (unsupportedDevices.isNotEmpty) {
_logger.printStatus(''); _logger.printStatus('');
_logger.printStatus('The following devices were found:'); _logger.printStatus('The following devices were found:');
await Device.printDevices(allDevices, _logger); await Device.printDevices(unsupportedDevices, _logger);
} }
return null; 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(_deviceManager.hasSpecifiedAllDevices
_logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, _deviceManager.specifiedDeviceId!)); ? userMessages.flutterNoDevicesFound
: userMessages.flutterNoSupportedDevices);
await _printUnsupportedDevice(unsupportedDevices);
return null; 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. /// Determine which device to use when multiple found.
_deviceManager.specifiedDeviceId = chosenDevice.id; ///
/// If user has not specified a device id/name, attempt to prioritize
/// ephemeral devices. If a single ephemeral device is found, return it
/// immediately.
///
/// Otherwise, prompt the user to select a device if there is a terminal
/// with stdin. If there is not a terminal, display the list of devices with
/// instructions to use a device selection flag.
Future<List<Device>?> _handleMultipleDevices(
List<Device> attachedDevices,
List<Device> wirelessDevices,
) async {
final List<Device> allDevices = attachedDevices + wirelessDevices;
final Device? ephemeralDevice = _deviceManager.getSingleEphemeralDevice(allDevices);
if (ephemeralDevice != null) {
return <Device>[ephemeralDevice];
}
if (globals.terminal.stdinHasTerminal) {
return _selectFromMultipleDevices(attachedDevices, wirelessDevices);
} else {
return _printMultipleDevices(attachedDevices, wirelessDevices);
}
}
devices = <Device>[chosenDevice]; /// Display a list of found devices. When the user has not specified the
/// device id/name, display devices unsupported by the project as well and
/// give instructions to use a device selection flag.
Future<List<Device>?> _printMultipleDevices(
List<Device> attachedDevices,
List<Device> wirelessDevices,
) async {
List<Device> supportedAttachedDevices = attachedDevices;
List<Device> supportedWirelessDevices = wirelessDevices;
if (_deviceManager.hasSpecifiedDeviceId) {
final int allDeviceLength = supportedAttachedDevices.length + supportedWirelessDevices.length;
_logger.printStatus(userMessages.flutterFoundSpecifiedDevices(
allDeviceLength,
_deviceManager.specifiedDeviceId!,
));
} else { } else {
// Show an error message asking the user to specify `-d all` if they // Get connected devices from cache, including ones unsupported for the
// want to run on multiple devices. // project but still supported by Flutter.
final List<Device> allDevices = await _deviceManager.getAllDevices(); supportedAttachedDevices = await _getAttachedDevices(
supportFilter: DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter(),
);
supportedWirelessDevices = await _getWirelessDevices(
supportFilter: DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter(),
);
_logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption); _logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
_logger.printStatus(''); _logger.printStatus('');
await Device.printDevices(allDevices, _logger); }
await Device.printDevices(supportedAttachedDevices, _logger);
if (supportedWirelessDevices.isNotEmpty) {
if (_deviceManager.hasSpecifiedDeviceId || supportedAttachedDevices.isNotEmpty) {
_logger.printStatus('');
}
_logger.printStatus(_wirelesslyConnectedDevicesMessage);
await Device.printDevices(supportedWirelessDevices, _logger);
}
return null; return null;
} }
/// Display a list of selectable devices, prompt the user to choose one, and
/// wait for the user to select a valid option.
Future<List<Device>?> _selectFromMultipleDevices(
List<Device> attachedDevices,
List<Device> wirelessDevices,
) async {
final List<Device> allDevices = attachedDevices + wirelessDevices;
if (_deviceManager.hasSpecifiedDeviceId) {
_logger.printStatus(userMessages.flutterFoundSpecifiedDevices(
allDevices.length,
_deviceManager.specifiedDeviceId!,
));
} else {
_logger.printStatus(userMessages.flutterMultipleDevicesFound);
} }
await Device.printDevices(attachedDevices, _logger);
if (wirelessDevices.isNotEmpty) {
_logger.printStatus('');
_logger.printStatus(_wirelesslyConnectedDevicesMessage);
await Device.printDevices(wirelessDevices, _logger);
_logger.printStatus('');
} }
return devices; final Device chosenDevice = await _chooseOneOfAvailableDevices(allDevices);
// Update the [DeviceManager.specifiedDeviceId] so that the user will not be prompted again.
_deviceManager.specifiedDeviceId = chosenDevice.id;
return <Device>[chosenDevice];
} }
Future<void> _printUnsupportedDevice(DeviceManager deviceManager) async { Future<void> _printUnsupportedDevice(List<Device> unsupportedDevices) async {
final List<Device> unsupportedDevices = await deviceManager.getDevices();
if (unsupportedDevices.isNotEmpty) { if (unsupportedDevices.isNotEmpty) {
final StringBuffer result = StringBuffer(); final StringBuffer result = StringBuffer();
result.writeln();
result.writeln(userMessages.flutterFoundButUnsupportedDevices); result.writeln(userMessages.flutterFoundButUnsupportedDevices);
result.writeAll( result.writeAll(
(await Device.descriptions(unsupportedDevices)) (await Device.descriptions(unsupportedDevices))
...@@ -104,7 +270,7 @@ class TargetDevices { ...@@ -104,7 +270,7 @@ class TargetDevices {
result.writeln(userMessages.flutterMissPlatformProjects( result.writeln(userMessages.flutterMissPlatformProjects(
Device.devicesPlatformTypes(unsupportedDevices), Device.devicesPlatformTypes(unsupportedDevices),
)); ));
_logger.printStatus(result.toString()); _logger.printStatus(result.toString(), newline: false);
} }
} }
...@@ -135,48 +301,4 @@ class TargetDevices { ...@@ -135,48 +301,4 @@ class TargetDevices {
); );
return result; 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;
}
} }
...@@ -23,7 +23,6 @@ import 'package:flutter_tools/src/device.dart'; ...@@ -23,7 +23,6 @@ import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/device_port_forwarder.dart'; import 'package:flutter_tools/src/device_port_forwarder.dart';
import 'package:flutter_tools/src/ios/application_package.dart'; import 'package:flutter_tools/src/ios/application_package.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/macos/macos_ipad_device.dart'; import 'package:flutter_tools/src/macos/macos_ipad_device.dart';
import 'package:flutter_tools/src/mdns_discovery.dart'; import 'package:flutter_tools/src/mdns_discovery.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
...@@ -241,7 +240,7 @@ void main() { ...@@ -241,7 +240,7 @@ void main() {
logReader: fakeLogReader, logReader: fakeLogReader,
portForwarder: portForwarder, portForwarder: portForwarder,
majorSdkVersion: 16, majorSdkVersion: 16,
interfaceType: IOSDeviceConnectionInterface.network, connectionInterface: DeviceConnectionInterface.wireless,
); );
testDeviceManager.devices = <Device>[device]; testDeviceManager.devices = <Device>[device];
final FakeHotRunner hotRunner = FakeHotRunner(); final FakeHotRunner hotRunner = FakeHotRunner();
...@@ -313,7 +312,7 @@ void main() { ...@@ -313,7 +312,7 @@ void main() {
logReader: fakeLogReader, logReader: fakeLogReader,
portForwarder: portForwarder, portForwarder: portForwarder,
majorSdkVersion: 16, majorSdkVersion: 16,
interfaceType: IOSDeviceConnectionInterface.network, connectionInterface: DeviceConnectionInterface.wireless,
); );
testDeviceManager.devices = <Device>[device]; testDeviceManager.devices = <Device>[device];
final FakeHotRunner hotRunner = FakeHotRunner(); final FakeHotRunner hotRunner = FakeHotRunner();
...@@ -389,7 +388,7 @@ void main() { ...@@ -389,7 +388,7 @@ void main() {
logReader: fakeLogReader, logReader: fakeLogReader,
portForwarder: portForwarder, portForwarder: portForwarder,
majorSdkVersion: 16, majorSdkVersion: 16,
interfaceType: IOSDeviceConnectionInterface.network, connectionInterface: DeviceConnectionInterface.wireless,
); );
testDeviceManager.devices = <Device>[device]; testDeviceManager.devices = <Device>[device];
final FakeHotRunner hotRunner = FakeHotRunner(); final FakeHotRunner hotRunner = FakeHotRunner();
...@@ -1237,6 +1236,10 @@ class FakeAndroidDevice extends Fake implements AndroidDevice { ...@@ -1237,6 +1236,10 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
@override @override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm; Future<TargetPlatform> get targetPlatform async => TargetPlatform.android_arm;
@override
DeviceConnectionInterface get connectionInterface =>
DeviceConnectionInterface.attached;
@override @override
bool isSupported() => true; bool isSupported() => true;
...@@ -1291,7 +1294,7 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -1291,7 +1294,7 @@ class FakeIOSDevice extends Fake implements IOSDevice {
DevicePortForwarder? portForwarder, DevicePortForwarder? portForwarder,
DeviceLogReader? logReader, DeviceLogReader? logReader,
this.onGetLogReader, this.onGetLogReader,
this.interfaceType = IOSDeviceConnectionInterface.none, this.connectionInterface = DeviceConnectionInterface.attached,
this.majorSdkVersion = 0, this.majorSdkVersion = 0,
}) : _portForwarder = portForwarder, _logReader = logReader; }) : _portForwarder = portForwarder, _logReader = logReader;
...@@ -1300,7 +1303,11 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -1300,7 +1303,11 @@ class FakeIOSDevice extends Fake implements IOSDevice {
int majorSdkVersion; int majorSdkVersion;
@override @override
final IOSDeviceConnectionInterface interfaceType; final DeviceConnectionInterface connectionInterface;
@override
bool get isWirelesslyConnected =>
connectionInterface == DeviceConnectionInterface.wireless;
@override @override
DevicePortForwarder get portForwarder => _portForwarder!; DevicePortForwarder get portForwarder => _portForwarder!;
......
...@@ -6,12 +6,12 @@ import 'dart:convert'; ...@@ -6,12 +6,12 @@ import 'dart:convert';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_sdk.dart';
import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/artifacts.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/commands/devices.dart'; import 'package:flutter_tools/src/commands/devices.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fake_devices.dart'; import '../../src/fake_devices.dart';
import '../../src/test_flutter_command_runner.dart'; import '../../src/test_flutter_command_runner.dart';
...@@ -23,9 +23,11 @@ void main() { ...@@ -23,9 +23,11 @@ void main() {
}); });
late Cache cache; late Cache cache;
late Platform platform;
setUp(() { setUp(() {
cache = Cache.test(processManager: FakeProcessManager.any()); cache = Cache.test(processManager: FakeProcessManager.any());
platform = FakePlatform();
}); });
testUsingContext('returns 0 when called', () async { testUsingContext('returns 0 when called', () async {
...@@ -39,7 +41,16 @@ void main() { ...@@ -39,7 +41,16 @@ void main() {
testUsingContext('no error when no connected devices', () async { testUsingContext('no error when no connected devices', () async {
final DevicesCommand command = DevicesCommand(); final DevicesCommand command = DevicesCommand();
await createTestCommandRunner(command).run(<String>['devices']); await createTestCommandRunner(command).run(<String>['devices']);
expect(testLogger.statusText, containsIgnoringWhitespace('No devices detected')); expect(
testLogger.statusText,
equals('''
No devices detected.
Run "flutter emulators" to list and start any available device emulators.
If you expected your device to be detected, please run "flutter doctor" to diagnose potential issues. You may also try increasing the time to wait for connected devices with the --device-timeout flag. Visit https://flutter.dev/setup/ for troubleshooting tips.
'''),
);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AndroidSdk: () => null, AndroidSdk: () => null,
DeviceManager: () => NoDevicesManager(), DeviceManager: () => NoDevicesManager(),
...@@ -48,16 +59,27 @@ void main() { ...@@ -48,16 +59,27 @@ void main() {
Artifacts: () => Artifacts.test(), Artifacts: () => Artifacts.test(),
}); });
group('when includes both attached and wireless devices', () {
List<FakeDeviceJsonData>? deviceList;
setUp(() {
deviceList = <FakeDeviceJsonData>[
fakeDevices[0],
fakeDevices[1],
fakeDevices[2],
];
});
testUsingContext("get devices' platform types", () async { testUsingContext("get devices' platform types", () async {
final List<String> platformTypes = Device.devicesPlatformTypes( final List<String> platformTypes = Device.devicesPlatformTypes(
await globals.deviceManager!.getAllDevices(), await globals.deviceManager!.getAllDevices(),
); );
expect(platformTypes, <String>['android', 'web']); expect(platformTypes, <String>['android', 'web']);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(), DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Cache: () => cache, Cache: () => cache,
Artifacts: () => Artifacts.test(), Artifacts: () => Artifacts.test(),
Platform: () => platform,
}); });
testUsingContext('Outputs parsable JSON with --machine flag', () async { testUsingContext('Outputs parsable JSON with --machine flag', () async {
...@@ -65,77 +87,115 @@ void main() { ...@@ -65,77 +87,115 @@ void main() {
await createTestCommandRunner(command).run(<String>['devices', '--machine']); await createTestCommandRunner(command).run(<String>['devices', '--machine']);
expect( expect(
json.decode(testLogger.statusText), json.decode(testLogger.statusText),
<Map<String,Object>>[ <Map<String, Object>>[
<String, Object>{ fakeDevices[0].json,
'name': 'ephemeral', fakeDevices[1].json,
'id': 'ephemeral', fakeDevices[2].json,
'isSupported': true,
'targetPlatform': 'android-arm',
'emulator': true,
'sdk': 'Test SDK (1.2.3)',
'capabilities': <String, Object>{
'hotReload': true,
'hotRestart': true,
'screenshot': false,
'fastStart': false,
'flutterExit': true,
'hardwareRendering': true,
'startPaused': true,
},
},
<String,Object>{
'name': 'webby',
'id': 'webby',
'isSupported': true,
'targetPlatform': 'web-javascript',
'emulator': true,
'sdk': 'Web SDK (1.2.4)',
'capabilities': <String, Object>{
'hotReload': true,
'hotRestart': true,
'screenshot': false,
'fastStart': false,
'flutterExit': true,
'hardwareRendering': true,
'startPaused': true,
},
},
], ],
); );
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(), DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Cache: () => cache, Cache: () => cache,
Artifacts: () => Artifacts.test(), Artifacts: () => Artifacts.test(),
Platform: () => platform,
}); });
testUsingContext('available devices and diagnostics', () async { testUsingContext('available devices and diagnostics', () async {
final DevicesCommand command = DevicesCommand(); final DevicesCommand command = DevicesCommand();
await createTestCommandRunner(command).run(<String>['devices']); await createTestCommandRunner(command).run(<String>['devices']);
expect( expect(testLogger.statusText, '''
testLogger.statusText,
'''
2 connected devices: 2 connected devices:
ephemeral (mobile) • ephemeral • android-arm • Test SDK (1.2.3) (emulator) ephemeral (mobile) • ephemeral • android-arm • Test SDK (1.2.3) (emulator)
webby (mobile) • webby • web-javascript • Web SDK (1.2.4) (emulator) webby (mobile) • webby • web-javascript • Web SDK (1.2.4) (emulator)
1 wirelessly connected device:
wireless android (mobile) • wireless-android • android-arm • Test SDK (1.2.3) (emulator)
• Cannot connect to device ABC • Cannot connect to device ABC
''' ''');
); }, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
});
});
group('when includes only attached devices', () {
List<FakeDeviceJsonData>? deviceList;
setUp(() {
deviceList = <FakeDeviceJsonData>[
fakeDevices[0],
fakeDevices[1],
];
});
testUsingContext('available devices and diagnostics', () async {
final DevicesCommand command = DevicesCommand();
await createTestCommandRunner(command).run(<String>['devices']);
expect(testLogger.statusText, '''
2 connected devices:
ephemeral (mobile) • ephemeral • android-arm • Test SDK (1.2.3) (emulator)
webby (mobile) • webby • web-javascript • Web SDK (1.2.4) (emulator)
• Cannot connect to device ABC
''');
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(), DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(), ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
});
});
group('when includes only wireless devices', () {
List<FakeDeviceJsonData>? deviceList;
setUp(() {
deviceList = <FakeDeviceJsonData>[
fakeDevices[2],
];
});
testUsingContext('available devices and diagnostics', () async {
final DevicesCommand command = DevicesCommand();
await createTestCommandRunner(command).run(<String>['devices']);
expect(testLogger.statusText, '''
1 wirelessly connected device:
wireless android (mobile) • wireless-android • android-arm • Test SDK (1.2.3) (emulator)
• Cannot connect to device ABC
''');
}, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(),
Platform: () => platform,
});
}); });
}); });
} }
class _FakeDeviceManager extends DeviceManager { class _FakeDeviceManager extends DeviceManager {
_FakeDeviceManager() : super(logger: testLogger); _FakeDeviceManager({
List<FakeDeviceJsonData>? devices,
}) : fakeDevices = devices ?? <FakeDeviceJsonData>[],
super(logger: testLogger);
List<FakeDeviceJsonData> fakeDevices = <FakeDeviceJsonData>[];
@override @override
Future<List<Device>> getAllDevices({DeviceDiscoveryFilter? filter}) => Future<List<Device>> getAllDevices({DeviceDiscoveryFilter? filter}) async {
Future<List<Device>>.value(fakeDevices.map((FakeDeviceJsonData d) => d.dev).toList()); final List<Device> devices = <Device>[];
for (final FakeDeviceJsonData deviceJson in fakeDevices) {
if (filter?.deviceConnectionInterface == null ||
deviceJson.dev.connectionInterface == filter?.deviceConnectionInterface) {
devices.add(deviceJson.dev);
}
}
return devices;
}
@override @override
Future<List<Device>> refreshAllDevices({ Future<List<Device>> refreshAllDevices({
......
...@@ -22,7 +22,6 @@ import 'package:flutter_tools/src/dart/pub.dart'; ...@@ -22,7 +22,6 @@ import 'package:flutter_tools/src/dart/pub.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/drive/drive_service.dart'; import 'package:flutter_tools/src/drive/drive_service.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:package_config/package_config.dart'; import 'package:package_config/package_config.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
...@@ -68,7 +67,7 @@ void main() { ...@@ -68,7 +67,7 @@ void main() {
final Device screenshotDevice = ThrowingScreenshotDevice() final Device screenshotDevice = ThrowingScreenshotDevice()
..supportsScreenshot = false; ..supportsScreenshot = false;
fakeDeviceManager.devices = <Device>[screenshotDevice]; fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(() => createTestCommandRunner(command).run( await expectLater(() => createTestCommandRunner(command).run(
<String>[ <String>[
...@@ -104,7 +103,7 @@ void main() { ...@@ -104,7 +103,7 @@ void main() {
fileSystem.directory('drive_screenshots').createSync(); fileSystem.directory('drive_screenshots').createSync();
final Device screenshotDevice = ThrowingScreenshotDevice(); final Device screenshotDevice = ThrowingScreenshotDevice();
fakeDeviceManager.devices = <Device>[screenshotDevice]; fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(() => createTestCommandRunner(command).run( await expectLater(() => createTestCommandRunner(command).run(
<String>[ <String>[
...@@ -142,7 +141,7 @@ void main() { ...@@ -142,7 +141,7 @@ void main() {
fileSystem.directory('drive_screenshots').createSync(); fileSystem.directory('drive_screenshots').createSync();
final Device screenshotDevice = ScreenshotDevice(); final Device screenshotDevice = ScreenshotDevice();
fakeDeviceManager.devices = <Device>[screenshotDevice]; fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(() => createTestCommandRunner(command).run( await expectLater(() => createTestCommandRunner(command).run(
<String>[ <String>[
...@@ -184,7 +183,7 @@ void main() { ...@@ -184,7 +183,7 @@ void main() {
fileSystem.file('drive_screenshots').createSync(); fileSystem.file('drive_screenshots').createSync();
final Device screenshotDevice = ThrowingScreenshotDevice(); final Device screenshotDevice = ThrowingScreenshotDevice();
fakeDeviceManager.devices = <Device>[screenshotDevice]; fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
await expectLater(() => createTestCommandRunner(command).run( await expectLater(() => createTestCommandRunner(command).run(
<String>[ <String>[
...@@ -222,7 +221,7 @@ void main() { ...@@ -222,7 +221,7 @@ void main() {
fileSystem.directory('drive_screenshots').createSync(); fileSystem.directory('drive_screenshots').createSync();
final ScreenshotDevice screenshotDevice = ScreenshotDevice(); final ScreenshotDevice screenshotDevice = ScreenshotDevice();
fakeDeviceManager.devices = <Device>[screenshotDevice]; fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
expect(screenshotDevice.screenshots, isEmpty); expect(screenshotDevice.screenshots, isEmpty);
bool caughtToolExit = false; bool caughtToolExit = false;
...@@ -293,7 +292,7 @@ void main() { ...@@ -293,7 +292,7 @@ void main() {
fileSystem.directory('drive_screenshots').createSync(); fileSystem.directory('drive_screenshots').createSync();
final ScreenshotDevice screenshotDevice = ScreenshotDevice(); final ScreenshotDevice screenshotDevice = ScreenshotDevice();
fakeDeviceManager.devices = <Device>[screenshotDevice]; fakeDeviceManager.attachedDevices = <Device>[screenshotDevice];
expect(screenshotDevice.screenshots, isEmpty); expect(screenshotDevice.screenshots, isEmpty);
...@@ -423,8 +422,8 @@ void main() { ...@@ -423,8 +422,8 @@ void main() {
fileSystem.file('pubspec.yaml').createSync(); fileSystem.file('pubspec.yaml').createSync();
final Device networkDevice = FakeIosDevice() final Device networkDevice = FakeIosDevice()
..interfaceType = IOSDeviceConnectionInterface.network; ..connectionInterface = DeviceConnectionInterface.wireless;
fakeDeviceManager.devices = <Device>[networkDevice]; fakeDeviceManager.wirelessDevices = <Device>[networkDevice];
await expectLater(() => createTestCommandRunner(command).run(<String>[ await expectLater(() => createTestCommandRunner(command).run(<String>[
'drive', 'drive',
...@@ -456,8 +455,8 @@ void main() { ...@@ -456,8 +455,8 @@ void main() {
]), throwsToolExit()); ]), throwsToolExit());
final Device usbDevice = FakeIosDevice() final Device usbDevice = FakeIosDevice()
..interfaceType = IOSDeviceConnectionInterface.usb; ..connectionInterface = DeviceConnectionInterface.attached;
fakeDeviceManager.devices = <Device>[usbDevice]; fakeDeviceManager.attachedDevices = <Device>[usbDevice];
final DebuggingOptions options = await command.createDebuggingOptions(false); final DebuggingOptions options = await command.createDebuggingOptions(false);
expect(options.disablePortPublication, true); expect(options.disablePortPublication, true);
...@@ -481,8 +480,8 @@ void main() { ...@@ -481,8 +480,8 @@ void main() {
fileSystem.file('pubspec.yaml').createSync(); fileSystem.file('pubspec.yaml').createSync();
final Device networkDevice = FakeIosDevice() final Device networkDevice = FakeIosDevice()
..interfaceType = IOSDeviceConnectionInterface.network; ..connectionInterface = DeviceConnectionInterface.wireless;
fakeDeviceManager.devices = <Device>[networkDevice]; fakeDeviceManager.wirelessDevices = <Device>[networkDevice];
await expectLater(() => createTestCommandRunner(command).run(<String>[ await expectLater(() => createTestCommandRunner(command).run(<String>[
'drive', 'drive',
...@@ -661,7 +660,11 @@ class FakeProcessSignal extends Fake implements io.ProcessSignal { ...@@ -661,7 +660,11 @@ class FakeProcessSignal extends Fake implements io.ProcessSignal {
// ignore: avoid_implementing_value_types // ignore: avoid_implementing_value_types
class FakeIosDevice extends Fake implements IOSDevice { class FakeIosDevice extends Fake implements IOSDevice {
@override @override
IOSDeviceConnectionInterface interfaceType = IOSDeviceConnectionInterface.usb; DeviceConnectionInterface connectionInterface = DeviceConnectionInterface.attached;
@override
bool get isWirelesslyConnected =>
connectionInterface == DeviceConnectionInterface.wireless;
@override @override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
......
...@@ -36,7 +36,7 @@ void main() { ...@@ -36,7 +36,7 @@ void main() {
command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk());
final FakeAndroidDevice device = FakeAndroidDevice(); final FakeAndroidDevice device = FakeAndroidDevice();
testDeviceManager.addDevice(device); testDeviceManager.addAttachedDevice(device);
await createTestCommandRunner(command).run(<String>['install']); await createTestCommandRunner(command).run(<String>['install']);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
...@@ -50,7 +50,7 @@ void main() { ...@@ -50,7 +50,7 @@ void main() {
command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk());
final FakeIOSDevice device = FakeIOSDevice(); final FakeIOSDevice device = FakeIOSDevice();
testDeviceManager.addDevice(device); testDeviceManager.addAttachedDevice(device);
expect(() async => createTestCommandRunner(command).run(<String>['install', '--device-user', '10']), expect(() async => createTestCommandRunner(command).run(<String>['install', '--device-user', '10']),
throwsToolExit(message: '--device-user is only supported for Android')); throwsToolExit(message: '--device-user is only supported for Android'));
...@@ -65,7 +65,7 @@ void main() { ...@@ -65,7 +65,7 @@ void main() {
command.applicationPackages = FakeApplicationPackageFactory(FakeIOSApp()); command.applicationPackages = FakeApplicationPackageFactory(FakeIOSApp());
final FakeIOSDevice device = FakeIOSDevice(); final FakeIOSDevice device = FakeIOSDevice();
testDeviceManager.addDevice(device); testDeviceManager.addAttachedDevice(device);
await createTestCommandRunner(command).run(<String>['install']); await createTestCommandRunner(command).run(<String>['install']);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
...@@ -79,7 +79,7 @@ void main() { ...@@ -79,7 +79,7 @@ void main() {
command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk());
final FakeAndroidDevice device = FakeAndroidDevice(); final FakeAndroidDevice device = FakeAndroidDevice();
testDeviceManager.addDevice(device); testDeviceManager.addAttachedDevice(device);
expect(() async => createTestCommandRunner(command).run(<String>['install', '--use-application-binary', 'bogus']), expect(() async => createTestCommandRunner(command).run(<String>['install', '--use-application-binary', 'bogus']),
throwsToolExit(message: 'Prebuilt binary bogus does not exist')); throwsToolExit(message: 'Prebuilt binary bogus does not exist'));
...@@ -94,7 +94,7 @@ void main() { ...@@ -94,7 +94,7 @@ void main() {
command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk()); command.applicationPackages = FakeApplicationPackageFactory(FakeAndroidApk());
final FakeAndroidDevice device = FakeAndroidDevice(); final FakeAndroidDevice device = FakeAndroidDevice();
testDeviceManager.addDevice(device); testDeviceManager.addAttachedDevice(device);
fileSystem.file('binary').createSync(recursive: true); fileSystem.file('binary').createSync(recursive: true);
await createTestCommandRunner(command).run(<String>['install', '--use-application-binary', 'binary']); await createTestCommandRunner(command).run(<String>['install', '--use-application-binary', 'binary']);
...@@ -111,7 +111,7 @@ void main() { ...@@ -111,7 +111,7 @@ void main() {
command.applicationPackages = fakeAppFactory; command.applicationPackages = fakeAppFactory;
final FakeIOSDevice device = FakeIOSDevice(); final FakeIOSDevice device = FakeIOSDevice();
testDeviceManager.addDevice(device); testDeviceManager.addAttachedDevice(device);
await createTestCommandRunner(command).run(<String>['install', '--flavor', flavor]); await createTestCommandRunner(command).run(<String>['install', '--flavor', flavor]);
expect(fakeAppFactory.buildInfo, isNotNull); expect(fakeAppFactory.buildInfo, isNotNull);
...@@ -183,4 +183,7 @@ class FakeAndroidDevice extends Fake implements AndroidDevice { ...@@ -183,4 +183,7 @@ class FakeAndroidDevice extends Fake implements AndroidDevice {
@override @override
String get name => 'Android'; String get name => 'Android';
@override
bool get ephemeral => true;
} }
...@@ -26,7 +26,6 @@ import 'package:flutter_tools/src/devfs.dart'; ...@@ -26,7 +26,6 @@ import 'package:flutter_tools/src/devfs.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/globals.dart' as globals; import 'package:flutter_tools/src/globals.dart' as globals;
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart'; import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/resident_runner.dart'; import 'package:flutter_tools/src/resident_runner.dart';
...@@ -711,7 +710,7 @@ void main() { ...@@ -711,7 +710,7 @@ void main() {
testUsingContext('with only iOS usb device', () async { testUsingContext('with only iOS usb device', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.usb, sdkNameAndVersion: 'iOS 16.2'), FakeIOSDevice(sdkNameAndVersion: 'iOS 16.2'),
]; ];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices); final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CommandRunner<void> runner = createTestCommandRunner(command); final CommandRunner<void> runner = createTestCommandRunner(command);
...@@ -752,7 +751,10 @@ void main() { ...@@ -752,7 +751,10 @@ void main() {
testUsingContext('with only iOS network device', () async { testUsingContext('with only iOS network device', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.network, sdkNameAndVersion: 'iOS 16.2'), FakeIOSDevice(
connectionInterface: DeviceConnectionInterface.wireless,
sdkNameAndVersion: 'iOS 16.2',
),
]; ];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices); final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CommandRunner<void> runner = createTestCommandRunner(command); final CommandRunner<void> runner = createTestCommandRunner(command);
...@@ -793,8 +795,11 @@ void main() { ...@@ -793,8 +795,11 @@ void main() {
testUsingContext('with both iOS usb and network devices', () async { testUsingContext('with both iOS usb and network devices', () async {
final List<Device> devices = <Device>[ final List<Device> devices = <Device>[
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.network, sdkNameAndVersion: 'iOS 16.2'), FakeIOSDevice(
FakeIOSDevice(interfaceType: IOSDeviceConnectionInterface.usb, sdkNameAndVersion: 'iOS 16.2'), connectionInterface: DeviceConnectionInterface.wireless,
sdkNameAndVersion: 'iOS 16.2',
),
FakeIOSDevice(sdkNameAndVersion: 'iOS 16.2'),
]; ];
final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices); final TestRunCommandForUsageValues command = TestRunCommandForUsageValues(devices: devices);
final CommandRunner<void> runner = createTestCommandRunner(command); final CommandRunner<void> runner = createTestCommandRunner(command);
...@@ -1126,6 +1131,10 @@ class FakeDevice extends Fake implements Device { ...@@ -1126,6 +1131,10 @@ class FakeDevice extends Fake implements Device {
@override @override
bool get isConnected => true; bool get isConnected => true;
@override
DeviceConnectionInterface get connectionInterface =>
DeviceConnectionInterface.attached;
bool supported = true; bool supported = true;
@override @override
...@@ -1209,7 +1218,7 @@ class FakeDevice extends Fake implements Device { ...@@ -1209,7 +1218,7 @@ class FakeDevice extends Fake implements Device {
// ignore: avoid_implementing_value_types // ignore: avoid_implementing_value_types
class FakeIOSDevice extends Fake implements IOSDevice { class FakeIOSDevice extends Fake implements IOSDevice {
FakeIOSDevice({ FakeIOSDevice({
this.interfaceType = IOSDeviceConnectionInterface.none, this.connectionInterface = DeviceConnectionInterface.attached,
bool isLocalEmulator = false, bool isLocalEmulator = false,
String sdkNameAndVersion = '', String sdkNameAndVersion = '',
}): _isLocalEmulator = isLocalEmulator, }): _isLocalEmulator = isLocalEmulator,
...@@ -1225,7 +1234,11 @@ class FakeIOSDevice extends Fake implements IOSDevice { ...@@ -1225,7 +1234,11 @@ class FakeIOSDevice extends Fake implements IOSDevice {
Future<String> get sdkNameAndVersion => Future<String>.value(_sdkNameAndVersion); Future<String> get sdkNameAndVersion => Future<String>.value(_sdkNameAndVersion);
@override @override
final IOSDeviceConnectionInterface interfaceType; final DeviceConnectionInterface connectionInterface;
@override
bool get isWirelesslyConnected =>
connectionInterface == DeviceConnectionInterface.wireless;
@override @override
Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios; Future<TargetPlatform> get targetPlatform async => TargetPlatform.ios;
......
...@@ -922,8 +922,15 @@ class _FakeDeviceManager extends DeviceManager { ...@@ -922,8 +922,15 @@ class _FakeDeviceManager extends DeviceManager {
@override @override
Future<List<Device>> getAllDevices({ Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter, DeviceDiscoveryFilter? filter,
}) async => _connectedDevices; }) async => filteredDevices(filter);
@override @override
List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[]; List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[];
List<Device> filteredDevices(DeviceDiscoveryFilter? filter) {
if (filter?.deviceConnectionInterface == DeviceConnectionInterface.wireless) {
return <Device>[];
}
return _connectedDevices;
}
} }
...@@ -87,6 +87,13 @@ class FakeDeviceManager extends Fake implements DeviceManager { ...@@ -87,6 +87,13 @@ class FakeDeviceManager extends Fake implements DeviceManager {
@override @override
String? specifiedDeviceId; String? specifiedDeviceId;
@override
Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter,
}) async {
return devices;
}
@override @override
Future<List<Device>> refreshAllDevices({ Future<List<Device>> refreshAllDevices({
Duration? timeout, Duration? timeout,
......
...@@ -122,7 +122,7 @@ void main() { ...@@ -122,7 +122,7 @@ void main() {
expect(androidDevices.supportsPlatform, false); expect(androidDevices.supportsPlatform, false);
}); });
testWithoutContext('AndroidDevices can parse output for physical devices', () async { testWithoutContext('AndroidDevices can parse output for physical attached devices', () async {
final AndroidDevices androidDevices = AndroidDevices( final AndroidDevices androidDevices = AndroidDevices(
userMessages: UserMessages(), userMessages: UserMessages(),
androidWorkflow: androidWorkflow, androidWorkflow: androidWorkflow,
...@@ -147,6 +147,35 @@ List of devices attached ...@@ -147,6 +147,35 @@ List of devices attached
expect(devices, hasLength(1)); expect(devices, hasLength(1));
expect(devices.first.name, 'Nexus 7'); expect(devices.first.name, 'Nexus 7');
expect(devices.first.category, Category.mobile); expect(devices.first.category, Category.mobile);
expect(devices.first.connectionInterface, DeviceConnectionInterface.attached);
});
testWithoutContext('AndroidDevices can parse output for physical wireless devices', () async {
final AndroidDevices androidDevices = AndroidDevices(
userMessages: UserMessages(),
androidWorkflow: androidWorkflow,
androidSdk: FakeAndroidSdk(),
logger: BufferLogger.test(),
processManager: FakeProcessManager.list(<FakeCommand>[
const FakeCommand(
command: <String>['adb', 'devices', '-l'],
stdout: '''
List of devices attached
05a02bac._adb-tls-connect._tcp. device product:razor model:Nexus_7 device:flo
''',
),
]),
platform: FakePlatform(),
fileSystem: MemoryFileSystem.test(),
);
final List<Device> devices = await androidDevices.pollingGetDevices();
expect(devices, hasLength(1));
expect(devices.first.name, 'Nexus 7');
expect(devices.first.category, Category.mobile);
expect(devices.first.connectionInterface, DeviceConnectionInterface.wireless);
}); });
testWithoutContext('AndroidDevices can parse output for emulators and short listings', () async { testWithoutContext('AndroidDevices can parse output for emulators and short listings', () async {
......
...@@ -11,7 +11,6 @@ import 'package:flutter_tools/src/base/utils.dart'; ...@@ -11,7 +11,6 @@ import 'package:flutter_tools/src/base/utils.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/convert.dart'; import 'package:flutter_tools/src/convert.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
...@@ -832,7 +831,7 @@ void main() { ...@@ -832,7 +831,7 @@ void main() {
EnvironmentType.physical, EnvironmentType.physical,
null, null,
<String, Object?>{}, <String, Object?>{},
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
); );
expect( expect(
...@@ -856,7 +855,7 @@ void main() { ...@@ -856,7 +855,7 @@ void main() {
null, null,
<String, Object?>{}, <String, Object?>{},
ipv6: true, ipv6: true,
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
); );
expect( expect(
......
...@@ -74,7 +74,7 @@ void main() { ...@@ -74,7 +74,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
sdkVersion: '13.3', sdkVersion: '13.3',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
expect(device.isSupported(), isTrue); expect(device.isSupported(), isTrue);
}); });
...@@ -90,7 +90,7 @@ void main() { ...@@ -90,7 +90,7 @@ void main() {
iMobileDevice: iMobileDevice, iMobileDevice: iMobileDevice,
name: 'iPhone 1', name: 'iPhone 1',
cpuArchitecture: DarwinArch.armv7, cpuArchitecture: DarwinArch.armv7,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
expect(device.isSupported(), isFalse); expect(device.isSupported(), isFalse);
}); });
...@@ -107,7 +107,7 @@ void main() { ...@@ -107,7 +107,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
sdkVersion: '1.0.0', sdkVersion: '1.0.0',
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
).majorSdkVersion, 1); ).majorSdkVersion, 1);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -120,7 +120,7 @@ void main() { ...@@ -120,7 +120,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
sdkVersion: '13.1.1', sdkVersion: '13.1.1',
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
).majorSdkVersion, 13); ).majorSdkVersion, 13);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -133,7 +133,7 @@ void main() { ...@@ -133,7 +133,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
sdkVersion: '10', sdkVersion: '10',
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
).majorSdkVersion, 10); ).majorSdkVersion, 10);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -146,7 +146,7 @@ void main() { ...@@ -146,7 +146,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
sdkVersion: '0', sdkVersion: '0',
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
).majorSdkVersion, 0); ).majorSdkVersion, 0);
expect(IOSDevice( expect(IOSDevice(
'device-123', 'device-123',
...@@ -159,7 +159,7 @@ void main() { ...@@ -159,7 +159,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
sdkVersion: 'bogus', sdkVersion: 'bogus',
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
).majorSdkVersion, 0); ).majorSdkVersion, 0);
}); });
...@@ -175,7 +175,7 @@ void main() { ...@@ -175,7 +175,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
sdkVersion: '13.3 17C54', sdkVersion: '13.3 17C54',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
expect(await device.sdkNameAndVersion,'iOS 13.3 17C54'); expect(await device.sdkNameAndVersion,'iOS 13.3 17C54');
...@@ -193,7 +193,7 @@ void main() { ...@@ -193,7 +193,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
sdkVersion: '13.3', sdkVersion: '13.3',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
expect(device.supportsRuntimeMode(BuildMode.debug), true); expect(device.supportsRuntimeMode(BuildMode.debug), true);
...@@ -217,7 +217,7 @@ void main() { ...@@ -217,7 +217,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
sdkVersion: '13.3', sdkVersion: '13.3',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
}, },
throwsAssertionError, throwsAssertionError,
...@@ -307,7 +307,7 @@ void main() { ...@@ -307,7 +307,7 @@ void main() {
name: 'iPhone 1', name: 'iPhone 1',
sdkVersion: '13.3', sdkVersion: '13.3',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
logReader1 = createLogReader(device, appPackage1, process1); logReader1 = createLogReader(device, appPackage1, process1);
logReader2 = createLogReader(device, appPackage2, process2); logReader2 = createLogReader(device, appPackage2, process2);
...@@ -368,7 +368,7 @@ void main() { ...@@ -368,7 +368,7 @@ void main() {
logger: logger, logger: logger,
platform: macPlatform, platform: macPlatform,
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
device2 = IOSDevice( device2 = IOSDevice(
...@@ -382,7 +382,7 @@ void main() { ...@@ -382,7 +382,7 @@ void main() {
logger: logger, logger: logger,
platform: macPlatform, platform: macPlatform,
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
}); });
......
...@@ -12,8 +12,8 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -12,8 +12,8 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart'; import 'package:flutter_tools/src/ios/ios_deploy.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/fake_process_manager.dart'; import '../../src/fake_process_manager.dart';
...@@ -73,7 +73,7 @@ void main () { ...@@ -73,7 +73,7 @@ void main () {
bundlePath: '/', bundlePath: '/',
appDeltaDirectory: appDeltaDirectory, appDeltaDirectory: appDeltaDirectory,
launchArguments: <String>['--enable-dart-profiling'], launchArguments: <String>['--enable-dart-profiling'],
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
uninstallFirst: true, uninstallFirst: true,
); );
......
...@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -10,6 +10,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/application_package.dart'; import 'package:flutter_tools/src/ios/application_package.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart'; import 'package:flutter_tools/src/ios/ios_deploy.dart';
...@@ -62,7 +63,7 @@ void main() { ...@@ -62,7 +63,7 @@ void main() {
final IOSDevice device = setUpIOSDevice( final IOSDevice device = setUpIOSDevice(
processManager: processManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
interfaceType: IOSDeviceConnectionInterface.usb, interfaceType: DeviceConnectionInterface.attached,
artifacts: artifacts, artifacts: artifacts,
); );
final bool wasInstalled = await device.installApp(iosApp); final bool wasInstalled = await device.installApp(iosApp);
...@@ -95,7 +96,7 @@ void main() { ...@@ -95,7 +96,7 @@ void main() {
final IOSDevice device = setUpIOSDevice( final IOSDevice device = setUpIOSDevice(
processManager: processManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
artifacts: artifacts, artifacts: artifacts,
); );
final bool wasInstalled = await device.installApp(iosApp); final bool wasInstalled = await device.installApp(iosApp);
...@@ -319,7 +320,7 @@ IOSDevice setUpIOSDevice({ ...@@ -319,7 +320,7 @@ IOSDevice setUpIOSDevice({
required ProcessManager processManager, required ProcessManager processManager,
FileSystem? fileSystem, FileSystem? fileSystem,
Logger? logger, Logger? logger,
IOSDeviceConnectionInterface? interfaceType, DeviceConnectionInterface? interfaceType,
Artifacts? artifacts, Artifacts? artifacts,
}) { }) {
logger ??= BufferLogger.test(); logger ??= BufferLogger.test();
...@@ -357,6 +358,6 @@ IOSDevice setUpIOSDevice({ ...@@ -357,6 +358,6 @@ IOSDevice setUpIOSDevice({
cache: cache, cache: cache,
), ),
iProxy: IProxy.test(logger: logger, processManager: processManager), iProxy: IProxy.test(logger: logger, processManager: processManager),
interfaceType: interfaceType ?? IOSDeviceConnectionInterface.usb, connectionInterface: interfaceType ?? DeviceConnectionInterface.attached,
); );
} }
...@@ -9,6 +9,7 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -9,6 +9,7 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/ios_deploy.dart'; import 'package:flutter_tools/src/ios/ios_deploy.dart';
import 'package:flutter_tools/src/ios/iproxy.dart'; import 'package:flutter_tools/src/ios/iproxy.dart';
...@@ -98,6 +99,6 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) { ...@@ -98,6 +99,6 @@ IOSDevice setUpIOSDevice(FileSystem fileSystem) {
sdkVersion: '13.3', sdkVersion: '13.3',
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
iProxy: IProxy.test(logger: logger, processManager: processManager), iProxy: IProxy.test(logger: logger, processManager: processManager),
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
} }
...@@ -337,7 +337,7 @@ IOSDevice setUpIOSDevice({ ...@@ -337,7 +337,7 @@ IOSDevice setUpIOSDevice({
cache: cache, cache: cache,
), ),
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
} }
......
...@@ -250,7 +250,7 @@ void main() { ...@@ -250,7 +250,7 @@ void main() {
processManager: processManager, processManager: processManager,
fileSystem: fileSystem, fileSystem: fileSystem,
logger: logger, logger: logger,
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
); );
final IOSApp iosApp = PrebuiltIOSApp( final IOSApp iosApp = PrebuiltIOSApp(
projectBundleId: 'app', projectBundleId: 'app',
...@@ -560,7 +560,7 @@ IOSDevice setUpIOSDevice({ ...@@ -560,7 +560,7 @@ IOSDevice setUpIOSDevice({
Logger? logger, Logger? logger,
ProcessManager? processManager, ProcessManager? processManager,
IOSDeploy? iosDeploy, IOSDeploy? iosDeploy,
IOSDeviceConnectionInterface interfaceType = IOSDeviceConnectionInterface.usb, DeviceConnectionInterface interfaceType = DeviceConnectionInterface.attached,
}) { }) {
final Artifacts artifacts = Artifacts.test(); final Artifacts artifacts = Artifacts.test();
final FakePlatform macPlatform = FakePlatform( final FakePlatform macPlatform = FakePlatform(
...@@ -598,7 +598,7 @@ IOSDevice setUpIOSDevice({ ...@@ -598,7 +598,7 @@ IOSDevice setUpIOSDevice({
cache: cache, cache: cache,
), ),
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: interfaceType, connectionInterface: interfaceType,
); );
} }
......
...@@ -10,8 +10,8 @@ import 'package:flutter_tools/src/base/logger.dart'; ...@@ -10,8 +10,8 @@ import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/process.dart'; import 'package:flutter_tools/src/base/process.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/code_signing.dart'; import 'package:flutter_tools/src/ios/code_signing.dart';
import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/ios/mac.dart'; import 'package:flutter_tools/src/ios/mac.dart';
import 'package:flutter_tools/src/ios/xcresult.dart'; import 'package:flutter_tools/src/ios/xcresult.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
...@@ -77,7 +77,7 @@ void main() { ...@@ -77,7 +77,7 @@ void main() {
expect(() async => iMobileDevice.takeScreenshot( expect(() async => iMobileDevice.takeScreenshot(
outputFile, outputFile,
'1234', '1234',
IOSDeviceConnectionInterface.usb, DeviceConnectionInterface.attached,
), throwsA(anything)); ), throwsA(anything));
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
...@@ -100,7 +100,7 @@ void main() { ...@@ -100,7 +100,7 @@ void main() {
await iMobileDevice.takeScreenshot( await iMobileDevice.takeScreenshot(
outputFile, outputFile,
'1234', '1234',
IOSDeviceConnectionInterface.usb, DeviceConnectionInterface.attached,
); );
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
...@@ -123,7 +123,7 @@ void main() { ...@@ -123,7 +123,7 @@ void main() {
await iMobileDevice.takeScreenshot( await iMobileDevice.takeScreenshot(
outputFile, outputFile,
'1234', '1234',
IOSDeviceConnectionInterface.network, DeviceConnectionInterface.wireless,
); );
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });
......
...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart'; ...@@ -11,6 +11,7 @@ import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/version.dart'; import 'package:flutter_tools/src/base/version.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/cache.dart';
import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/ios/devices.dart'; import 'package:flutter_tools/src/ios/devices.dart';
import 'package:flutter_tools/src/ios/iproxy.dart'; import 'package:flutter_tools/src/ios/iproxy.dart';
import 'package:flutter_tools/src/ios/xcodeproj.dart'; import 'package:flutter_tools/src/ios/xcodeproj.dart';
...@@ -480,22 +481,31 @@ void main() { ...@@ -480,22 +481,31 @@ void main() {
)); ));
final List<IOSDevice> devices = await xcdevice.getAvailableIOSDevices(); final List<IOSDevice> devices = await xcdevice.getAvailableIOSDevices();
expect(devices, hasLength(4)); expect(devices, hasLength(4));
expect(devices[0].id, '00008027-00192736010F802E'); expect(devices[0].id, '00008027-00192736010F802E');
expect(devices[0].name, 'An iPhone (Space Gray)'); expect(devices[0].name, 'An iPhone (Space Gray)');
expect(await devices[0].sdkNameAndVersion, 'iOS 13.3 17C54'); expect(await devices[0].sdkNameAndVersion, 'iOS 13.3 17C54');
expect(devices[0].cpuArchitecture, DarwinArch.arm64); expect(devices[0].cpuArchitecture, DarwinArch.arm64);
expect(devices[0].connectionInterface, DeviceConnectionInterface.attached);
expect(devices[1].id, '98206e7a4afd4aedaff06e687594e089dede3c44'); expect(devices[1].id, '98206e7a4afd4aedaff06e687594e089dede3c44');
expect(devices[1].name, 'iPad 1'); expect(devices[1].name, 'iPad 1');
expect(await devices[1].sdkNameAndVersion, 'iOS 10.1 14C54'); expect(await devices[1].sdkNameAndVersion, 'iOS 10.1 14C54');
expect(devices[1].cpuArchitecture, DarwinArch.armv7); expect(devices[1].cpuArchitecture, DarwinArch.armv7);
expect(devices[1].connectionInterface, DeviceConnectionInterface.attached);
expect(devices[2].id, '234234234234234234345445687594e089dede3c44'); expect(devices[2].id, '234234234234234234345445687594e089dede3c44');
expect(devices[2].name, 'A networked iPad'); expect(devices[2].name, 'A networked iPad');
expect(await devices[2].sdkNameAndVersion, 'iOS 10.1 14C54'); expect(await devices[2].sdkNameAndVersion, 'iOS 10.1 14C54');
expect(devices[2].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture. expect(devices[2].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture.
expect(devices[2].connectionInterface, DeviceConnectionInterface.wireless);
expect(devices[3].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4'); expect(devices[3].id, 'f577a7903cc54959be2e34bc4f7f80b7009efcf4');
expect(devices[3].name, 'iPad 2'); expect(devices[3].name, 'iPad 2');
expect(await devices[3].sdkNameAndVersion, 'iOS 10.1 14C54'); expect(await devices[3].sdkNameAndVersion, 'iOS 10.1 14C54');
expect(devices[3].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture. expect(devices[3].cpuArchitecture, DarwinArch.arm64); // Defaults to arm64 for unknown architecture.
expect(devices[3].connectionInterface, DeviceConnectionInterface.attached);
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Platform: () => macPlatform, Platform: () => macPlatform,
......
...@@ -705,15 +705,15 @@ void main() { ...@@ -705,15 +705,15 @@ void main() {
}); });
testUsingContext('finds single device', () async { testUsingContext('finds single device', () async {
testDeviceManager.addDevice(device1); testDeviceManager.addAttachedDevice(device1);
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(); final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
final Device? device = await flutterCommand.findTargetDevice(); final Device? device = await flutterCommand.findTargetDevice();
expect(device, device1); expect(device, device1);
}); });
testUsingContext('finds multiple devices', () async { testUsingContext('finds multiple devices', () async {
testDeviceManager.addDevice(device1); testDeviceManager.addAttachedDevice(device1);
testDeviceManager.addDevice(device2); testDeviceManager.addAttachedDevice(device2);
testDeviceManager.specifiedDeviceId = 'all'; testDeviceManager.specifiedDeviceId = 'all';
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(); final DummyFlutterCommand flutterCommand = DummyFlutterCommand();
final Device? device = await flutterCommand.findTargetDevice(); final Device? device = await flutterCommand.findTargetDevice();
......
...@@ -4,42 +4,60 @@ ...@@ -4,42 +4,60 @@
import 'dart:async'; 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/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart'; import 'package:flutter_tools/src/base/terminal.dart';
import 'package:flutter_tools/src/base/user_messages.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/doctor.dart'; import 'package:flutter_tools/src/doctor.dart';
import 'package:flutter_tools/src/doctor_validator.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/runner/target_devices.dart'; import 'package:flutter_tools/src/runner/target_devices.dart';
import 'package:test/fake.dart'; import 'package:test/fake.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/fake_devices.dart';
void main() { void main() {
group('When cannot launch anything', () { group('findAllTargetDevices', () {
late Platform platform;
final FakeDevice attachedAndroidDevice1 = FakeDevice(deviceName: 'target-device-1');
final FakeDevice attachedAndroidDevice2 = FakeDevice(deviceName: 'target-device-2');
final FakeDevice attachedUnsupportedAndroidDevice = FakeDevice(deviceName: 'target-device-3', deviceSupported: false);
final FakeDevice attachedUnsupportedForProjectAndroidDevice = FakeDevice(deviceName: 'target-device-4', deviceSupportForProject: false);
final FakeDevice wirelessAndroidDevice1 = FakeDevice.wireless(deviceName: 'target-device-5');
final FakeDevice wirelessAndroidDevice2 = FakeDevice.wireless(deviceName: 'target-device-6');
final FakeDevice wirelessUnsupportedAndroidDevice = FakeDevice.wireless(deviceName: 'target-device-7', deviceSupported: false);
final FakeDevice wirelessUnsupportedForProjectAndroidDevice = FakeDevice.wireless(deviceName: 'target-device-8', deviceSupportForProject: false);
final FakeDevice nonEphemeralDevice = FakeDevice(deviceName: 'target-device-9', ephemeral: false);
final FakeDevice fuchsiaDevice = FakeDevice.fuchsia(deviceName: 'target-device-10');
final FakeDevice exactMatchAndroidDevice = FakeDevice(deviceName: 'target-device');
final FakeDevice exactMatchWirelessAndroidDevice = FakeDevice.wireless(deviceName: 'target-device');
final FakeDevice exactMatchattachedUnsupportedAndroidDevice = FakeDevice(deviceName: 'target-device', deviceSupported: false);
final FakeDevice exactMatchUnsupportedByProjectDevice = FakeDevice(deviceName: 'target-device', deviceSupportForProject: false);
setUp(() {
platform = FakePlatform();
});
group('when cannot launch anything', () {
late BufferLogger logger; late BufferLogger logger;
late FakeDoctor doctor; late FakeDoctor doctor;
final FakeDevice device1 = FakeDevice('device1', 'device1');
setUp(() { setUp(() {
logger = BufferLogger.test(); logger = BufferLogger.test();
doctor = FakeDoctor(logger, canLaunchAnything: false); doctor = FakeDoctor(canLaunchAnything: false);
}); });
testUsingContext('does not search for devices', () async { testUsingContext('does not search for devices', () async {
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery() final TestDeviceManager deviceManager = TestDeviceManager(
..deviceValues = <Device>[device1];
final DeviceManager deviceManager = TestDeviceManager(
<Device>[],
deviceDiscoveryOverrides: <DeviceDiscovery>[
deviceDiscovery,
],
logger: logger, logger: logger,
platform: platform,
); );
deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager, deviceManager: deviceManager,
...@@ -47,373 +65,1017 @@ void main() { ...@@ -47,373 +65,1017 @@ void main() {
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.errorText, contains(UserMessages().flutterNoDevelopmentDevice)); expect(logger.errorText, equals('''
Unable to locate a development device; please run 'flutter doctor' for information about installing additional components.
'''));
expect(devices, isNull); expect(devices, isNull);
expect(deviceDiscovery.devicesCalled, 0); expect(deviceManager.androidDiscoverer.devicesCalled, 0);
expect(deviceDiscovery.discoverDevicesCalled, 0); expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Doctor: () => doctor, Doctor: () => doctor,
}); });
}); });
group('Ensure refresh when deviceDiscoveryTimeout is provided', () { testUsingContext('ensure refresh when deviceDiscoveryTimeout is provided', () async {
final BufferLogger logger = BufferLogger.test();
final TestDeviceManager deviceManager = TestDeviceManager(
logger: logger,
platform: platform,
);
deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];
deviceManager.androidDiscoverer.refreshDeviceList = <Device>[attachedAndroidDevice1, wirelessAndroidDevice1];
deviceManager.hasSpecifiedAllDevices = true;
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices(
deviceDiscoveryTimeout: const Duration(seconds: 2),
);
expect(logger.statusText, equals(''));
expect(devices, <Device>[attachedAndroidDevice1, wirelessAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 1);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
});
group('finds no devices', () {
late BufferLogger logger; late BufferLogger logger;
final FakeDevice device1 = FakeDevice('device1', 'device1'); late TestDeviceManager deviceManager;
setUp(() { setUp(() {
logger = BufferLogger.test(); logger = BufferLogger.test();
deviceManager = TestDeviceManager(
logger: logger,
platform: platform,
);
});
group('when device not specified', () {
testUsingContext('when no devices', () async {
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
No supported devices connected.
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('does not refresh device cache without a timeout', () async { testUsingContext('when device is unsupported by flutter or project', () async {
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery() deviceManager.androidDiscoverer.deviceList = <Device>[
..deviceValues = <Device>[device1]; attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
];
final DeviceManager deviceManager = TestDeviceManager( final TargetDevices targetDevices = TargetDevices(
<Device>[], deviceManager: deviceManager,
deviceDiscoveryOverrides: <DeviceDiscovery>[
deviceDiscovery,
],
logger: logger, logger: logger,
); );
deviceManager.specifiedDeviceId = device1.id; final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
No supported devices connected.
The following devices were found, but are not supported by this project:
target-device-3 (mobile) • xxx • android • Android 10 (unsupported)
target-device-4 (mobile) • xxx • android • Android 10
If you would like your app to run on android, consider running `flutter create .` to generate projects for these platforms.
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
});
});
group('with hasSpecifiedDeviceId', () {
setUp(() {
deviceManager.specifiedDeviceId = 'target-device';
});
testUsingContext('when no devices', () async {
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices?.single, device1); expect(logger.statusText, equals('''
expect(deviceDiscovery.devicesCalled, 1); No supported devices found with name or id matching 'target-device'.
expect(deviceDiscovery.discoverDevicesCalled, 0); '''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 4);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('refreshes device cache with a timeout', () async { testUsingContext('when no devices match', () async {
final MockDeviceDiscovery deviceDiscovery = MockDeviceDiscovery() final FakeDevice device1 = FakeDevice(deviceName: 'no-match-1');
..deviceValues = <Device>[device1]; final FakeDevice device2 = FakeDevice.wireless(deviceName: 'no-match-2');
deviceManager.androidDiscoverer.deviceList = <Device>[device1, device2];
final DeviceManager deviceManager = TestDeviceManager( final TargetDevices targetDevices = TargetDevices(
<Device>[], deviceManager: deviceManager,
deviceDiscoveryOverrides: <DeviceDiscovery>[ logger: logger,
deviceDiscovery,
],
logger: BufferLogger.test(),
); );
deviceManager.specifiedDeviceId = device1.id; final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.
The following devices were found:
no-match-1 (mobile) • xxx • android • Android 10
no-match-2 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 4);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
});
testUsingContext('when matching device is unsupported by flutter', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchattachedUnsupportedAndroidDevice];
const Duration timeout = Duration(seconds: 2);
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices( final List<Device>? devices = await targetDevices.findAllTargetDevices();
deviceDiscoveryTimeout: timeout,
expect(logger.statusText, equals('''
No supported devices found with name or id matching 'target-device'.
The following devices were found:
target-device (mobile) • xxx • android • Android 10 (unsupported)
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 4);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
});
});
group('with hasSpecifiedAllDevices', () {
setUp(() {
deviceManager.hasSpecifiedAllDevices = true;
});
testUsingContext('when no devices', () async {
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices?.single, device1); expect(logger.statusText, equals('''
expect(deviceDiscovery.devicesCalled, 1); No devices found.
expect(deviceDiscovery.discoverDevicesCalled, 1); '''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('when devices are either unsupported by flutter or project or all', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[
attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
];
deviceManager.otherDiscoverer.deviceList = <Device>[fuchsiaDevice];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
No devices found.
The following devices were found, but are not supported by this project:
target-device-3 (mobile) • xxx • android • Android 10 (unsupported)
target-device-4 (mobile) • xxx • android • Android 10
target-device-10 (mobile) • xxx • fuchsia-arm64 • tester
If you would like your app to run on android or fuchsia, consider running `flutter create .` to generate projects for these platforms.
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
group('findAllTargetDevices', () { });
});
group('finds single device', () {
late BufferLogger logger; late BufferLogger logger;
final FakeDevice device1 = FakeDevice('device1', 'device1'); late TestDeviceManager deviceManager;
final FakeDevice device2 = FakeDevice('device2', 'device2');
setUp(() { setUp(() {
logger = BufferLogger.test(); logger = BufferLogger.test();
deviceManager = TestDeviceManager(
logger: logger,
platform: platform,
);
}); });
group('when specified device id', () { group('when device not specified', () {
testUsingContext('returns device when device is found', () async { testUsingContext('when single attached device', () async {
testDeviceManager.specifiedDeviceId = 'device1'; deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];
testDeviceManager.addDevice(device1);
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, <Device>[device1]); expect(logger.statusText, equals(''));
expect(devices, <Device>[attachedAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('show error when no device found', () async { testUsingContext('when single wireless device', () async {
testDeviceManager.specifiedDeviceId = 'device-id'; deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, null); expect(logger.statusText, equals(''));
expect(logger.statusText, contains(UserMessages().flutterNoMatchingDevice('device-id'))); expect(devices, <Device>[wirelessAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('show error when multiple devices found', () async { testUsingContext('when multiple but only one ephemeral', () async {
testDeviceManager.specifiedDeviceId = 'device'; deviceManager.androidDiscoverer.deviceList = <Device>[nonEphemeralDevice, wirelessAndroidDevice1];
testDeviceManager.addDevice(device1);
testDeviceManager.addDevice(device2);
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, null); expect(logger.statusText, equals(''));
expect(logger.statusText, contains(UserMessages().flutterFoundSpecifiedDevices(2, 'device'))); expect(devices, <Device>[wirelessAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
}); });
group('when specified all', () { group('with hasSpecifiedDeviceId', () {
testUsingContext('can return one device', () async { setUp(() {
testDeviceManager.specifiedDeviceId = 'all'; deviceManager.specifiedDeviceId = 'target-device';
testDeviceManager.addDevice(device1); });
testUsingContext('when multiple matches but first is unsupported by flutter', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[
exactMatchattachedUnsupportedAndroidDevice,
exactMatchAndroidDevice,
];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, <Device>[device1]); expect(logger.statusText, equals(''));
expect(devices, <Device>[exactMatchAndroidDevice]);
expect(deviceManager.androidDiscoverer.devicesCalled, 1);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('can return multiple devices', () async { testUsingContext('when matching device is unsupported by project', () async {
testDeviceManager.specifiedDeviceId = 'all'; deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchUnsupportedByProjectDevice];
testDeviceManager.addDevice(device1);
testDeviceManager.addDevice(device2);
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, <Device>[device1, device2]); expect(logger.statusText, equals(''));
expect(devices, <Device>[exactMatchUnsupportedByProjectDevice]);
expect(deviceManager.androidDiscoverer.devicesCalled, 1);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('show error when no device found', () async { testUsingContext('when matching attached device', () async {
testDeviceManager.specifiedDeviceId = 'all'; deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchAndroidDevice];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, null); expect(logger.statusText, equals(''));
expect(logger.statusText, contains(UserMessages().flutterNoDevicesFound)); expect(devices, <Device>[exactMatchAndroidDevice]);
}); expect(deviceManager.androidDiscoverer.devicesCalled, 1);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
group('when device not specified', () { testUsingContext('when matching wireless device', () async {
testUsingContext('returns one device when only one device connected', () async { deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchWirelessAndroidDevice];
testDeviceManager.addDevice(device1);
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, <Device>[device1]); expect(logger.statusText, equals(''));
expect(devices, <Device>[exactMatchWirelessAndroidDevice]);
expect(deviceManager.androidDiscoverer.devicesCalled, 1);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
testUsingContext('show error when no device found', () async { testUsingContext('when exact match attached device and partial match wireless device', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[exactMatchAndroidDevice, wirelessAndroidDevice1];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, null); expect(logger.statusText, equals(''));
expect(logger.statusText, contains(UserMessages().flutterNoSupportedDevices)); expect(devices, <Device>[exactMatchAndroidDevice]);
expect(deviceManager.androidDiscoverer.devicesCalled, 1);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
});
});
group('with hasSpecifiedAllDevices', () {
setUp(() {
deviceManager.hasSpecifiedAllDevices = true;
}); });
testUsingContext('show error when multiple devices found and not connected to terminal', () async { testUsingContext('when only one device', () async {
testDeviceManager.addDevice(device1); deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1];
testDeviceManager.addDevice(device2);
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, null); expect(logger.statusText, equals(''));
expect(logger.statusText, contains(UserMessages().flutterSpecifyDeviceWithAllOption)); expect(devices, <Device>[attachedAndroidDevice1]);
}, overrides: <Type, Generator>{ expect(deviceManager.androidDiscoverer.devicesCalled, 2);
AnsiTerminal: () => FakeTerminal(stdinHasTerminal: false), expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
});
});
});
group('Finds multiple devices', () {
late BufferLogger logger;
late TestDeviceManager deviceManager;
setUp(() {
logger = BufferLogger.test();
deviceManager = TestDeviceManager(
logger: logger,
platform: platform,
);
}); });
// Prompt to choose device when multiple devices found and connected to terminal group('when device not specified', () {
group('show prompt', () { group('with stdinHasTerminal', () {
late FakeTerminal terminal; late FakeTerminal terminal;
setUp(() { setUp(() {
terminal = FakeTerminal(); terminal = FakeTerminal();
}); });
testUsingContext('choose first device', () async { testUsingContext('including attached, wireless, unsupported devices', () async {
testDeviceManager.addDevice(device1); deviceManager.androidDiscoverer.deviceList = <Device>[
testDeviceManager.addDevice(device2); attachedAndroidDevice1,
attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
wirelessAndroidDevice1,
wirelessUnsupportedAndroidDevice,
wirelessUnsupportedForProjectAndroidDevice,
];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '2');
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Multiple devices found:
target-device-1 (mobile) • xxx • android • Android 10
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
[1]: target-device-1 (xxx)
[2]: target-device-5 (xxx)
'''));
expect(devices, <Device>[wirelessAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
testUsingContext('including only attached devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1'); terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Multiple devices found:
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
'''));
expect(devices, <Device>[attachedAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
testUsingContext('including only wireless devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, <Device>[device1]); expect(logger.statusText, equals('''
Multiple devices found:
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10
[1]: target-device-5 (xxx)
[2]: target-device-6 (xxx)
'''));
expect(devices, <Device>[wirelessAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 2);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AnsiTerminal: () => terminal, AnsiTerminal: () => terminal,
}); });
testUsingContext('choose second device', () async { });
testDeviceManager.addDevice(device1);
testDeviceManager.addDevice(device2); group('without stdinHasTerminal', () {
terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '2'); late FakeTerminal terminal;
setUp(() {
terminal = FakeTerminal(stdinHasTerminal: false);
});
testUsingContext('including attached, wireless, unsupported devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[
attachedAndroidDevice1,
attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
wirelessAndroidDevice1,
wirelessUnsupportedAndroidDevice,
wirelessUnsupportedForProjectAndroidDevice,
];
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices(); final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(devices, <Device>[device2]); expect(logger.statusText, equals('''
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.
target-device-1 (mobile) • xxx • android • Android 10
target-device-4 (mobile) • xxx • android • Android 10
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-8 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 4);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AnsiTerminal: () => terminal, AnsiTerminal: () => terminal,
}); });
testUsingContext('exits without choosing device', () async { testUsingContext('including only attached devices', () async {
testDeviceManager.addDevice(device1); deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];
testDeviceManager.addDevice(device2);
terminal.setPrompt(<String>['1', '2', 'q', 'Q'], 'q');
final TargetDevices targetDevices = TargetDevices( final TargetDevices targetDevices = TargetDevices(
deviceManager: testDeviceManager, deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 4);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
testUsingContext('including only wireless devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];
await expectLater( final TargetDevices targetDevices = TargetDevices(
targetDevices.findAllTargetDevices(), deviceManager: deviceManager,
throwsToolExit(), logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
More than one device connected; please specify a device with the '-d <deviceId>' flag, or use '-d all' to act on all devices.
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 4);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
AnsiTerminal: () => terminal, AnsiTerminal: () => terminal,
}); });
}); });
}); });
group('with hasSpecifiedDeviceId', () {
setUp(() {
deviceManager.specifiedDeviceId = 'target-device';
}); });
group('Filter devices', () { group('with stdinHasTerminal', () {
late BufferLogger logger; late FakeTerminal terminal;
final FakeDevice ephemeralOne = FakeDevice('ephemeralOne', 'ephemeralOne');
final FakeDevice ephemeralTwo = FakeDevice('ephemeralTwo', 'ephemeralTwo');
final FakeDevice nonEphemeralOne = FakeDevice('nonEphemeralOne', 'nonEphemeralOne', ephemeral: false);
setUp(() { setUp(() {
logger = BufferLogger.test(); terminal = FakeTerminal();
}); });
testUsingContext('chooses ephemeral device', () async { testUsingContext('including attached, wireless, unsupported devices', () async {
final List<Device> devices = <Device>[ deviceManager.androidDiscoverer.deviceList = <Device>[
ephemeralOne, attachedAndroidDevice1,
nonEphemeralOne, attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
wirelessAndroidDevice1,
wirelessUnsupportedAndroidDevice,
wirelessUnsupportedForProjectAndroidDevice,
]; ];
final DeviceManager deviceManager = TestDeviceManager(
devices, terminal.setPrompt(<String>['1', '2', '3', '4', 'q', 'Q'], '2');
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 4 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-4 (mobile) • xxx • android • Android 10
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-8 (mobile) • xxx • android • Android 10
[1]: target-device-1 (xxx)
[2]: target-device-4 (xxx)
[3]: target-device-5 (xxx)
[4]: target-device-8 (xxx)
'''));
expect(devices, <Device>[attachedUnsupportedForProjectAndroidDevice]);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
final TargetDevices targetDevices = TargetDevices(deviceManager: deviceManager, logger: logger); testUsingContext('including only attached devices', () async {
final List<Device> filtered = await targetDevices.getDevices(); deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];
expect(filtered, <Device>[ephemeralOne]); terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
[1]: target-device-1 (xxx)
[2]: target-device-2 (xxx)
'''));
expect(devices, <Device>[attachedAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
}); });
testUsingContext('returns all devices when multiple non ephemeral devices are found', () async { testUsingContext('including only wireless devices', () async {
final List<Device> devices = <Device>[ deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];
ephemeralOne,
ephemeralTwo, terminal.setPrompt(<String>['1', '2', 'q', 'Q'], '1');
nonEphemeralOne, final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10
[1]: target-device-5 (xxx)
[2]: target-device-6 (xxx)
'''));
expect(devices, <Device>[wirelessAndroidDevice1]);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
});
group('without stdinHasTerminal', () {
late FakeTerminal terminal;
setUp(() {
terminal = FakeTerminal(stdinHasTerminal: false);
});
testUsingContext('including only one ephemeral', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[nonEphemeralDevice, attachedAndroidDevice1];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
target-device-9 (mobile) • xxx • android • Android 10
target-device-1 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
testUsingContext('including matching attached, wireless, unsupported devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[
attachedAndroidDevice1,
attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
wirelessAndroidDevice1,
wirelessUnsupportedAndroidDevice,
wirelessUnsupportedForProjectAndroidDevice,
]; ];
final DeviceManager deviceManager = TestDeviceManager(
devices, final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 4 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-4 (mobile) • xxx • android • Android 10
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-8 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
testUsingContext('including only attached devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[attachedAndroidDevice1, attachedAndroidDevice2];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
target-device-1 (mobile) • xxx • android • Android 10
target-device-2 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
testUsingContext('including only wireless devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[wirelessAndroidDevice1, wirelessAndroidDevice2];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger, logger: logger,
); );
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(logger.statusText, equals('''
Found 2 devices with name or id matching target-device:
Wirelessly connected devices:
target-device-5 (mobile) • xxx • android • Android 10
target-device-6 (mobile) • xxx • android • Android 10
'''));
expect(devices, isNull);
expect(deviceManager.androidDiscoverer.devicesCalled, 3);
expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}, overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
});
});
});
final TargetDevices targetDevices = TargetDevices(deviceManager: deviceManager, logger: logger); group('with hasSpecifiedAllDevices', () {
final List<Device> filtered = await targetDevices.getDevices(); setUp(() {
deviceManager.hasSpecifiedAllDevices = true;
});
testUsingContext('including attached, wireless, unsupported devices', () async {
deviceManager.androidDiscoverer.deviceList = <Device>[
attachedAndroidDevice1,
attachedUnsupportedAndroidDevice,
attachedUnsupportedForProjectAndroidDevice,
wirelessAndroidDevice1,
wirelessUnsupportedAndroidDevice,
wirelessUnsupportedForProjectAndroidDevice,
];
deviceManager.otherDiscoverer.deviceList = <Device>[fuchsiaDevice];
final TargetDevices targetDevices = TargetDevices(
deviceManager: deviceManager,
logger: logger,
);
final List<Device>? devices = await targetDevices.findAllTargetDevices();
expect(filtered, <Device>[ expect(logger.statusText, equals(''));
ephemeralOne, expect(devices, <Device>[attachedAndroidDevice1, wirelessAndroidDevice1]);
ephemeralTwo, expect(deviceManager.androidDiscoverer.devicesCalled, 2);
nonEphemeralOne, expect(deviceManager.androidDiscoverer.discoverDevicesCalled, 0);
]); expect(deviceManager.androidDiscoverer.numberOfTimesPolled, 1);
}); });
}); });
});
});
} }
class TestDeviceManager extends DeviceManager { class TestDeviceManager extends DeviceManager {
TestDeviceManager( TestDeviceManager({
List<Device> allDevices, { required this.logger,
List<DeviceDiscovery>? deviceDiscoveryOverrides, required this.platform,
required super.logger, }) : super(logger: logger);
String? wellKnownId,
FakePollingDeviceDiscovery? fakeDiscoverer, final Logger logger;
}) : _fakeDeviceDiscoverer = fakeDiscoverer ?? FakePollingDeviceDiscovery(), final Platform platform;
_deviceDiscoverers = <DeviceDiscovery>[],
super() { @override
if (wellKnownId != null) { String? specifiedDeviceId;
_fakeDeviceDiscoverer.wellKnownIds.add(wellKnownId);
}
_deviceDiscoverers.add(_fakeDeviceDiscoverer);
if (deviceDiscoveryOverrides != null) {
_deviceDiscoverers.addAll(deviceDiscoveryOverrides);
}
resetDevices(allDevices);
}
@override @override
List<DeviceDiscovery> get deviceDiscoverers => _deviceDiscoverers; bool hasSpecifiedAllDevices = false;
final List<DeviceDiscovery> _deviceDiscoverers;
final FakePollingDeviceDiscovery _fakeDeviceDiscoverer;
void resetDevices(List<Device> allDevices) { final TestPollingDeviceDiscovery androidDiscoverer = TestPollingDeviceDiscovery(
_fakeDeviceDiscoverer.setDevices(allDevices); 'android',
);
final TestPollingDeviceDiscovery otherDiscoverer = TestPollingDeviceDiscovery(
'other',
);
final TestPollingDeviceDiscovery iosDiscoverer = TestPollingDeviceDiscovery(
'ios',
);
@override
List<DeviceDiscovery> get deviceDiscoverers {
return <DeviceDiscovery>[
androidDiscoverer,
otherDiscoverer,
iosDiscoverer,
];
} }
} }
class MockDeviceDiscovery extends Fake implements DeviceDiscovery { class TestPollingDeviceDiscovery extends PollingDeviceDiscovery {
TestPollingDeviceDiscovery(super.name);
List<Device> deviceList = <Device>[];
List<Device> refreshDeviceList = <Device>[];
int devicesCalled = 0; int devicesCalled = 0;
int discoverDevicesCalled = 0; int discoverDevicesCalled = 0;
int numberOfTimesPolled = 0;
@override @override
bool supportsPlatform = true; bool get supportsPlatform => true;
List<Device> deviceValues = <Device>[]; @override
List<String> get wellKnownIds => const <String>[];
@override
Future<List<Device>> pollingGetDevices({Duration? timeout}) async {
numberOfTimesPolled++;
return deviceList;
}
@override @override
Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async { Future<List<Device>> devices({DeviceDiscoveryFilter? filter}) async {
devicesCalled += 1; devicesCalled += 1;
return deviceValues; return super.devices(filter: filter);
} }
@override @override
Future<List<Device>> discoverDevices({ Future<List<Device>> discoverDevices({
Duration? timeout, Duration? timeout,
DeviceDiscoveryFilter? filter, DeviceDiscoveryFilter? filter,
}) async { }) {
discoverDevicesCalled += 1; discoverDevicesCalled++;
return deviceValues; if (refreshDeviceList.isNotEmpty) {
deviceList = refreshDeviceList;
}
return super.discoverDevices(timeout: timeout, filter: filter);
} }
@override @override
List<String> get wellKnownIds => <String>[]; bool get canListAnything => true;
}
// Unfortunately Device, despite not being immutable, has an `operator ==`.
// Until we fix that, we have to also ignore related lints here.
// ignore: avoid_implementing_value_types
class FakeDevice extends Fake implements Device {
FakeDevice({
String? deviceId,
String? deviceName,
bool deviceSupported = true,
bool deviceSupportForProject = true,
this.ephemeral = true,
this.isConnected = true,
this.connectionInterface = DeviceConnectionInterface.attached,
this.platformType = PlatformType.android,
TargetPlatform deviceTargetPlatform = TargetPlatform.android,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
_isSupported = deviceSupported,
_isSupportedForProject = deviceSupportForProject,
_targetPlatform = deviceTargetPlatform;
FakeDevice.wireless({
String? deviceId,
String? deviceName,
bool deviceSupported = true,
bool deviceSupportForProject = true,
this.ephemeral = true,
this.isConnected = true,
this.connectionInterface = DeviceConnectionInterface.wireless,
this.platformType = PlatformType.android,
TargetPlatform deviceTargetPlatform = TargetPlatform.android,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
_isSupported = deviceSupported,
_isSupportedForProject = deviceSupportForProject,
_targetPlatform = deviceTargetPlatform;
FakeDevice.fuchsia({
String? deviceId,
String? deviceName,
bool deviceSupported = true,
bool deviceSupportForProject = true,
this.ephemeral = true,
this.isConnected = true,
this.connectionInterface = DeviceConnectionInterface.attached,
this.platformType = PlatformType.fuchsia,
TargetPlatform deviceTargetPlatform = TargetPlatform.fuchsia_arm64,
}) : id = deviceId ?? 'xxx',
name = deviceName ?? 'test',
_isSupported = deviceSupported,
_isSupportedForProject = deviceSupportForProject,
_targetPlatform = deviceTargetPlatform,
_sdkNameAndVersion = 'tester';
final bool _isSupported;
final bool _isSupportedForProject;
final TargetPlatform _targetPlatform;
String _sdkNameAndVersion = 'Android 10';
@override
String name;
@override
final bool ephemeral;
@override
String id;
@override
bool isSupported() => _isSupported;
@override
bool isSupportedForProject(FlutterProject project) => _isSupportedForProject;
@override
DeviceConnectionInterface connectionInterface;
@override
bool isConnected;
@override
Future<TargetPlatform> get targetPlatform async => _targetPlatform;
@override
final PlatformType? platformType;
@override
Future<String> get sdkNameAndVersion async => _sdkNameAndVersion;
@override
Future<bool> get isLocalEmulator async => false;
@override
Category? get category => Category.mobile;
@override
Future<String> get targetPlatformDisplayName async =>
getNameForTargetPlatform(await targetPlatform);
} }
class FakeTerminal extends Fake implements AnsiTerminal { class FakeTerminal extends Fake implements AnsiTerminal {
...@@ -446,31 +1108,11 @@ class FakeTerminal extends Fake implements AnsiTerminal { ...@@ -446,31 +1108,11 @@ class FakeTerminal extends Fake implements AnsiTerminal {
} }
} }
class FakeDoctor extends Doctor { class FakeDoctor extends Fake implements Doctor {
FakeDoctor( FakeDoctor({
Logger logger, {
this.canLaunchAnything = true, this.canLaunchAnything = true,
}) : super(logger: logger); });
// True for testing.
@override
bool get canListAnything => true;
// True for testing.
@override @override
bool canLaunchAnything; 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();
}
} }
...@@ -181,7 +181,8 @@ void _printBufferedErrors(AppContext testContext) { ...@@ -181,7 +181,8 @@ void _printBufferedErrors(AppContext testContext) {
} }
class FakeDeviceManager implements DeviceManager { class FakeDeviceManager implements DeviceManager {
List<Device> devices = <Device>[]; List<Device> attachedDevices = <Device>[];
List<Device> wirelessDevices = <Device>[];
String? _specifiedDeviceId; String? _specifiedDeviceId;
...@@ -209,20 +210,20 @@ class FakeDeviceManager implements DeviceManager { ...@@ -209,20 +210,20 @@ class FakeDeviceManager implements DeviceManager {
@override @override
Future<List<Device>> getAllDevices({ Future<List<Device>> getAllDevices({
DeviceDiscoveryFilter? filter, DeviceDiscoveryFilter? filter,
}) async => devices; }) async => filteredDevices(filter);
@override @override
Future<List<Device>> refreshAllDevices({ Future<List<Device>> refreshAllDevices({
Duration? timeout, Duration? timeout,
DeviceDiscoveryFilter? filter, DeviceDiscoveryFilter? filter,
}) async => devices; }) async => filteredDevices(filter);
@override @override
Future<List<Device>> getDevicesById( Future<List<Device>> getDevicesById(
String deviceId, { String deviceId, {
DeviceDiscoveryFilter? filter, DeviceDiscoveryFilter? filter,
}) async { }) async {
return devices.where((Device device) { return filteredDevices(filter).where((Device device) {
return device.id == deviceId || device.id.startsWith(deviceId); return device.id == deviceId || device.id.startsWith(deviceId);
}).toList(); }).toList();
} }
...@@ -236,7 +237,8 @@ class FakeDeviceManager implements DeviceManager { ...@@ -236,7 +237,8 @@ class FakeDeviceManager implements DeviceManager {
: getAllDevices(filter: filter); : getAllDevices(filter: filter);
} }
void addDevice(Device device) => devices.add(device); void addAttachedDevice(Device device) => attachedDevices.add(device);
void addWirelessDevice(Device device) => wirelessDevices.add(device);
@override @override
bool get canListAnything => true; bool get canListAnything => true;
...@@ -257,6 +259,16 @@ class FakeDeviceManager implements DeviceManager { ...@@ -257,6 +259,16 @@ class FakeDeviceManager implements DeviceManager {
@override @override
Device? getSingleEphemeralDevice(List<Device> devices) => null; Device? getSingleEphemeralDevice(List<Device> devices) => null;
List<Device> filteredDevices(DeviceDiscoveryFilter? filter) {
if (filter?.deviceConnectionInterface == DeviceConnectionInterface.attached) {
return attachedDevices;
}
if (filter?.deviceConnectionInterface == DeviceConnectionInterface.wireless) {
return wirelessDevices;
}
return attachedDevices + wirelessDevices;
}
} }
class TestDeviceDiscoverySupportFilter extends Fake implements DeviceDiscoverySupportFilter { class TestDeviceDiscoverySupportFilter extends Fake implements DeviceDiscoverySupportFilter {
......
...@@ -54,6 +54,31 @@ List<FakeDeviceJsonData> fakeDevices = <FakeDeviceJsonData>[ ...@@ -54,6 +54,31 @@ List<FakeDeviceJsonData> fakeDevices = <FakeDeviceJsonData>[
}, },
}, },
), ),
FakeDeviceJsonData(
FakeDevice(
'wireless android',
'wireless-android',
type: PlatformType.android,
connectionInterface: DeviceConnectionInterface.wireless,
),
<String, Object>{
'name': 'wireless android',
'id': 'wireless-android',
'isSupported': true,
'targetPlatform': 'android-arm',
'emulator': true,
'sdk': 'Test SDK (1.2.3)',
'capabilities': <String, Object>{
'hotReload': true,
'hotRestart': true,
'screenshot': false,
'fastStart': false,
'flutterExit': true,
'hardwareRendering': true,
'startPaused': true,
},
}
),
]; ];
/// Fake device to test `devices` command. /// Fake device to test `devices` command.
......
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