Separate attached and wireless devices (#122615)

Separate attached and wireless devices
This commit is contained in:
Victoria Ashworth 2023-03-15 11:35:05 -05:00 committed by GitHub
parent 5d10cc28ca
commit 27248d4b64
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 1637 additions and 661 deletions

View File

@ -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>{};

View File

@ -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.';

View File

@ -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.
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) {

View File

@ -79,31 +79,71 @@ 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);
} else { return;
if (devices.isEmpty) {
final StringBuffer status = StringBuffer('No devices detected.');
status.writeln();
status.writeln();
status.writeln('Run "flutter emulators" to list and start any available device emulators.');
status.writeln();
status.write('If you expected your device to be detected, please run "flutter doctor" to diagnose potential issues. ');
if (deviceDiscoveryTimeout == null) {
status.write('You may also try increasing the time to wait for connected devices with the --${FlutterOptions.kDeviceTimeout} flag. ');
}
status.write('Visit https://flutter.dev/setup/ for troubleshooting tips.');
globals.printStatus(status.toString());
} else {
globals.printStatus('${devices.length} connected ${pluralize('device', devices.length)}:\n');
await Device.printDevices(devices, globals.logger);
}
await _printDiagnostics();
} }
if (allDevices.isEmpty) {
_printNoDevicesDetected();
} else {
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.');
status.writeln();
status.writeln();
status.writeln('Run "flutter emulators" to list and start any available device emulators.');
status.writeln();
status.write('If you expected your device to be detected, please run "flutter doctor" to diagnose potential issues. ');
if (deviceDiscoveryTimeout == null) {
status.write('You may also try increasing the time to wait for connected devices with the --${FlutterOptions.kDeviceTimeout} flag. ');
}
status.write('Visit https://flutter.dev/setup/ for troubleshooting tips.');
globals.printStatus(status.toString());
} }
Future<void> _printDiagnostics() async { Future<void> _printDiagnostics() async {

View File

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

View File

@ -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 {
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 {
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) {

View File

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

View File

@ -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 {
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 {
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 {
@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 {
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 {
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 {
// 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 {
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 {
_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 {
}); });
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 {
@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

View File

@ -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 {
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 {
'--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 {
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 {
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 {
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 {
'--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',

View File

@ -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.

View File

@ -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';
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 {
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 {
outputFile.path, outputFile.path,
'--udid', '--udid',
deviceID, deviceID,
if (interfaceType == IOSDeviceConnectionInterface.network) if (interfaceType == DeviceConnectionInterface.wireless)
'--network', '--network',
], ],
throwOnError: true, throwOnError: true,

View File

@ -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 {
} }
} }
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 {
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 {
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) {

View File

@ -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 {
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 {
_logger.printError(userMessages.flutterNoDevelopmentDevice); _logger.printError(userMessages.flutterNoDevelopmentDevice);
return null; return null;
} }
List<Device> devices = await getDevices(
includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
timeout: deviceDiscoveryTimeout,
);
if (devices.isEmpty) { if (deviceDiscoveryTimeout != null) {
if (_deviceManager.hasSpecifiedDeviceId) { // Reset the cache with the specified timeout.
_logger.printStatus(userMessages.flutterNoMatchingDevice(_deviceManager.specifiedDeviceId!)); await _deviceManager.refreshAllDevices(timeout: deviceDiscoveryTimeout);
final List<Device> allDevices = await _deviceManager.getAllDevices(); }
if (allDevices.isNotEmpty) {
_logger.printStatus('');
_logger.printStatus('The following devices were found:');
await Device.printDevices(allDevices, _logger);
}
return null;
} else if (_deviceManager.hasSpecifiedAllDevices) {
_logger.printStatus(userMessages.flutterNoDevicesFound);
await _printUnsupportedDevice(_deviceManager);
return null;
} else {
_logger.printStatus(userMessages.flutterNoSupportedDevices);
await _printUnsupportedDevice(_deviceManager);
return null;
}
} else if (devices.length > 1) {
if (_deviceManager.hasSpecifiedDeviceId) {
_logger.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, _deviceManager.specifiedDeviceId!));
return null;
} else if (!_deviceManager.hasSpecifiedAllDevices) {
if (globals.terminal.stdinHasTerminal) {
// If DeviceManager was not able to prioritize a device. For example, if the user
// has two active Android devices running, then we request the user to
// choose one. If the user has two nonEphemeral devices running, we also
// request input to choose one.
_logger.printStatus(userMessages.flutterMultipleDevicesFound);
await Device.printDevices(devices, _logger);
final Device chosenDevice = await _chooseOneOfAvailableDevices(devices);
// Update the [DeviceManager.specifiedDeviceId] so that we will not be prompted again. if (_deviceManager.hasSpecifiedDeviceId) {
_deviceManager.specifiedDeviceId = chosenDevice.id; // Must check for device match separately from `_getAttachedDevices` and
// `_getWirelessDevices` because if an exact match is found in one
devices = <Device>[chosenDevice]; // and a partial match is found in another, there is no way to distinguish
} else { // between them.
// Show an error message asking the user to specify `-d all` if they final List<Device> devices = await _getDeviceById(
// want to run on multiple devices. includeDevicesUnsupportedByProject: includeDevicesUnsupportedByProject,
final List<Device> allDevices = await _deviceManager.getAllDevices(); );
_logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption); if (devices.length == 1) {
_logger.printStatus(''); return devices;
await Device.printDevices(allDevices, _logger);
return null;
}
} }
} }
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;
} }
Future<void> _printUnsupportedDevice(DeviceManager deviceManager) async { /// When no supported devices are found, display a message and list of
final List<Device> unsupportedDevices = await deviceManager.getDevices(); /// unsupported devices found.
Future<List<Device>?> _handleNoDevices() async {
// Get connected devices from cache, including unsupported ones.
final List<Device> unsupportedDevices = await _deviceManager.getAllDevices();
if (_deviceManager.hasSpecifiedDeviceId) {
_logger.printStatus(
userMessages.flutterNoMatchingDevice(_deviceManager.specifiedDeviceId!),
);
if (unsupportedDevices.isNotEmpty) {
_logger.printStatus('');
_logger.printStatus('The following devices were found:');
await Device.printDevices(unsupportedDevices, _logger);
}
return null;
}
_logger.printStatus(_deviceManager.hasSpecifiedAllDevices
? userMessages.flutterNoDevicesFound
: userMessages.flutterNoSupportedDevices);
await _printUnsupportedDevice(unsupportedDevices);
return null;
}
/// Determine which device to use when multiple found.
///
/// 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);
}
}
/// 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 {
// Get connected devices from cache, including ones unsupported for the
// project but still supported by Flutter.
supportedAttachedDevices = await _getAttachedDevices(
supportFilter: DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter(),
);
supportedWirelessDevices = await _getWirelessDevices(
supportFilter: DeviceDiscoverySupportFilter.excludeDevicesUnsupportedByFlutter(),
);
_logger.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
_logger.printStatus('');
}
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;
}
/// 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('');
}
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(List<Device> unsupportedDevices) async {
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 {
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 {
); );
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;
}
} }

View File

@ -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() {
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() {
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() {
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 {
@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 {
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 {
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!;

View File

@ -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() {
}); });
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() {
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,94 +59,143 @@ void main() {
Artifacts: () => Artifacts.test(), Artifacts: () => Artifacts.test(),
}); });
testUsingContext("get devices' platform types", () async { group('when includes both attached and wireless devices', () {
final List<String> platformTypes = Device.devicesPlatformTypes( List<FakeDeviceJsonData>? deviceList;
await globals.deviceManager!.getAllDevices(), setUp(() {
); deviceList = <FakeDeviceJsonData>[
expect(platformTypes, <String>['android', 'web']); fakeDevices[0],
}, overrides: <Type, Generator>{ fakeDevices[1],
DeviceManager: () => _FakeDeviceManager(), fakeDevices[2],
ProcessManager: () => FakeProcessManager.any(), ];
Cache: () => cache, });
Artifacts: () => Artifacts.test(),
testUsingContext("get devices' platform types", () async {
final List<String> platformTypes = Device.devicesPlatformTypes(
await globals.deviceManager!.getAllDevices(),
);
expect(platformTypes, <String>['android', 'web']);
}, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(),
Cache: () => cache,
Artifacts: () => Artifacts.test(),
Platform: () => platform,
});
testUsingContext('Outputs parsable JSON with --machine flag', () async {
final DevicesCommand command = DevicesCommand();
await createTestCommandRunner(command).run(<String>['devices', '--machine']);
expect(
json.decode(testLogger.statusText),
<Map<String, Object>>[
fakeDevices[0].json,
fakeDevices[1].json,
fakeDevices[2].json,
],
);
}, overrides: <Type, Generator>{
DeviceManager: () => _FakeDeviceManager(devices: deviceList),
ProcessManager: () => FakeProcessManager.any(),
Cache: () => cache,
Artifacts: () => Artifacts.test(),
Platform: () => platform,
});
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)
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,
});
}); });
testUsingContext('Outputs parsable JSON with --machine flag', () async { group('when includes only attached devices', () {
final DevicesCommand command = DevicesCommand(); List<FakeDeviceJsonData>? deviceList;
await createTestCommandRunner(command).run(<String>['devices', '--machine']); setUp(() {
expect( deviceList = <FakeDeviceJsonData>[
json.decode(testLogger.statusText), fakeDevices[0],
<Map<String,Object>>[ fakeDevices[1],
<String, Object>{ ];
'name': 'ephemeral', });
'id': 'ephemeral',
'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>{
DeviceManager: () => _FakeDeviceManager(),
ProcessManager: () => FakeProcessManager.any(),
Cache: () => cache,
Artifacts: () => Artifacts.test(),
});
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)
Cannot connect to device ABC Cannot connect to device ABC
''' ''');
); }, overrides: <Type, Generator>{
}, overrides: <Type, Generator>{ DeviceManager: () => _FakeDeviceManager(devices: deviceList),
DeviceManager: () => _FakeDeviceManager(), 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({

View File

@ -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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
]), 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() {
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 {
// 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;

View File

@ -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() {
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() {
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() {
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() {
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() {
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 {
@override @override
String get name => 'Android'; String get name => 'Android';
@override
bool get ephemeral => true;
} }

View File

@ -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() {
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() {
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() {
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 {
@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 {
// 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 {
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;

View File

@ -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;
}
} }

View File

@ -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,

View File

@ -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
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 {

View File

@ -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() {
EnvironmentType.physical, EnvironmentType.physical,
null, null,
<String, Object?>{}, <String, Object?>{},
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
); );
expect( expect(
@ -856,7 +855,7 @@ void main() {
null, null,
<String, Object?>{}, <String, Object?>{},
ipv6: true, ipv6: true,
interfaceType: IOSDeviceConnectionInterface.network, interfaceType: DeviceConnectionInterface.wireless,
); );
expect( expect(

View File

@ -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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
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() {
logger: logger, logger: logger,
platform: macPlatform, platform: macPlatform,
fileSystem: MemoryFileSystem.test(), fileSystem: MemoryFileSystem.test(),
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
}); });

View File

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

View File

@ -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() {
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() {
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({
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({
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,
); );
} }

View File

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

View File

@ -337,7 +337,7 @@ IOSDevice setUpIOSDevice({
cache: cache, cache: cache,
), ),
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: IOSDeviceConnectionInterface.usb, connectionInterface: DeviceConnectionInterface.attached,
); );
} }

View File

@ -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({
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({
cache: cache, cache: cache,
), ),
cpuArchitecture: DarwinArch.arm64, cpuArchitecture: DarwinArch.arm64,
interfaceType: interfaceType, connectionInterface: interfaceType,
); );
} }

View File

@ -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() {
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() {
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() {
await iMobileDevice.takeScreenshot( await iMobileDevice.takeScreenshot(
outputFile, outputFile,
'1234', '1234',
IOSDeviceConnectionInterface.network, DeviceConnectionInterface.wireless,
); );
expect(fakeProcessManager, hasNoRemainingExpectations); expect(fakeProcessManager, hasNoRemainingExpectations);
}); });

View File

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

View File

@ -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();

View File

@ -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 {
@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 {
: 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 {
@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 {

View File

@ -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.