Unverified Commit 2f216cee authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Add timeout flag to devices command, pipe through discovery (#51678)

parent 0a09b782
...@@ -62,7 +62,7 @@ class AndroidDevices extends PollingDeviceDiscovery { ...@@ -62,7 +62,7 @@ class AndroidDevices extends PollingDeviceDiscovery {
bool get canListAnything => androidWorkflow.canListDevices; bool get canListAnything => androidWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async => getAdbDevices(); Future<List<Device>> pollingGetDevices({ Duration timeout }) async => getAdbDevices();
@override @override
Future<List<String>> getDiagnostics() async => getAdbDeviceDiagnostics(); Future<List<String>> getDiagnostics() async => getAdbDeviceDiagnostics();
......
...@@ -12,12 +12,36 @@ import '../globals.dart' as globals; ...@@ -12,12 +12,36 @@ import '../globals.dart' as globals;
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
class DevicesCommand extends FlutterCommand { class DevicesCommand extends FlutterCommand {
DevicesCommand() {
argParser.addOption(
'timeout',
abbr: 't',
defaultsTo: null,
help: 'Time in seconds to wait for devices to attach. Longer timeouts may be necessary for networked devices.'
);
}
@override @override
final String name = 'devices'; final String name = 'devices';
@override @override
final String description = 'List all connected devices.'; final String description = 'List all connected devices.';
Duration get timeout {
if (argResults['timeout'] == null) {
return null;
}
if (_timeout == null) {
final int timeoutSeconds = int.tryParse(stringArg('timeout'));
if (timeoutSeconds == null) {
throwToolExit( 'Could not parse -t/--timeout argument. It must be an integer.');
}
_timeout = Duration(seconds: timeoutSeconds);
}
return _timeout;
}
Duration _timeout;
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (!doctor.canListAnything) { if (!doctor.canListAnything) {
...@@ -27,14 +51,21 @@ class DevicesCommand extends FlutterCommand { ...@@ -27,14 +51,21 @@ class DevicesCommand extends FlutterCommand {
exitCode: 1); exitCode: 1);
} }
final List<Device> devices = await deviceManager.getAllConnectedDevices(); final List<Device> devices = await deviceManager.refreshAllConnectedDevices(timeout: timeout);
if (devices.isEmpty) { if (devices.isEmpty) {
globals.printStatus( final StringBuffer status = StringBuffer('No devices detected.');
'No devices detected.\n\n' status.writeln();
"Run 'flutter emulators' to list and start any available device emulators.\n\n" status.writeln();
'Or, if you expected your device to be detected, please run "flutter doctor" to diagnose ' status.writeln('Run "flutter emulators" to list and start any available device emulators.');
'potential issues, or visit https://flutter.dev/setup/ for troubleshooting tips.'); status.writeln();
status.write('If you expected your device to be detected, please run "flutter doctor" to diagnose potential issues. ');
if (timeout == null) {
status.write('You may also try increasing the time to wait for connected devices with the --timeout flag. ');
}
status.write('Visit https://flutter.dev/setup/ for troubleshooting tips.');
globals.printStatus(status.toString());
final List<String> diagnostics = await deviceManager.getDeviceDiagnostics(); final List<String> diagnostics = await deviceManager.getDeviceDiagnostics();
if (diagnostics.isNotEmpty) { if (diagnostics.isNotEmpty) {
globals.printStatus(''); globals.printStatus('');
......
...@@ -124,7 +124,7 @@ class DeviceManager { ...@@ -124,7 +124,7 @@ class DeviceManager {
return devices.where(startsWithDeviceId).toList(); return devices.where(startsWithDeviceId).toList();
} }
/// Return the list of connected devices, filtered by any user-specified device id. /// Returns the list of connected devices, filtered by any user-specified device id.
Future<List<Device>> getDevices() { Future<List<Device>> getDevices() {
return hasSpecifiedDeviceId return hasSpecifiedDeviceId
? getDevicesById(specifiedDeviceId) ? getDevicesById(specifiedDeviceId)
...@@ -135,7 +135,7 @@ class DeviceManager { ...@@ -135,7 +135,7 @@ class DeviceManager {
return deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform); return deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform);
} }
/// Return the list of all connected devices. /// Returns the list of all connected devices.
Future<List<Device>> getAllConnectedDevices() async { Future<List<Device>> getAllConnectedDevices() async {
final List<List<Device>> devices = await Future.wait<List<Device>>(<Future<List<Device>>>[ final List<List<Device>> devices = await Future.wait<List<Device>>(<Future<List<Device>>>[
for (final DeviceDiscovery discoverer in _platformDiscoverers) for (final DeviceDiscovery discoverer in _platformDiscoverers)
...@@ -145,6 +145,16 @@ class DeviceManager { ...@@ -145,6 +145,16 @@ class DeviceManager {
return devices.expand<Device>((List<Device> deviceList) => deviceList).toList(); return devices.expand<Device>((List<Device> deviceList) => deviceList).toList();
} }
/// Returns the list of all connected devices. Discards existing cache of devices.
Future<List<Device>> refreshAllConnectedDevices({ Duration timeout }) async {
final List<List<Device>> devices = await Future.wait<List<Device>>(<Future<List<Device>>>[
for (final DeviceDiscovery discoverer in _platformDiscoverers)
discoverer.discoverDevices(timeout: timeout),
]);
return devices.expand<Device>((List<Device> deviceList) => deviceList).toList();
}
/// Whether we're capable of listing any devices given the current environment configuration. /// Whether we're capable of listing any devices given the current environment configuration.
bool get canListAnything { bool get canListAnything {
return _platformDiscoverers.any((DeviceDiscovery discoverer) => discoverer.canListAnything); return _platformDiscoverers.any((DeviceDiscovery discoverer) => discoverer.canListAnything);
...@@ -237,8 +247,12 @@ abstract class DeviceDiscovery { ...@@ -237,8 +247,12 @@ abstract class DeviceDiscovery {
/// current environment configuration. /// current environment configuration.
bool get canListAnything; bool get canListAnything;
/// Return all connected devices, cached on subsequent calls.
Future<List<Device>> get devices; Future<List<Device>> get devices;
/// Return all connected devices. Discards existing cache of devices.
Future<List<Device>> discoverDevices({ Duration timeout });
/// Gets a list of diagnostic messages pertaining to issues with any connected /// Gets a list of diagnostic messages pertaining to issues with any connected
/// devices (will be an empty list if there are no issues). /// devices (will be an empty list if there are no issues).
Future<List<String>> getDiagnostics() => Future<List<String>>.value(<String>[]); Future<List<String>> getDiagnostics() => Future<List<String>>.value(<String>[]);
...@@ -256,7 +270,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -256,7 +270,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
ItemListNotifier<Device> _items; ItemListNotifier<Device> _items;
Timer _timer; Timer _timer;
Future<List<Device>> pollingGetDevices(); Future<List<Device>> pollingGetDevices({ Duration timeout });
void startPolling() { void startPolling() {
if (_timer == null) { if (_timer == null) {
...@@ -268,7 +282,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -268,7 +282,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
Timer _initTimer() { Timer _initTimer() {
return Timer(_pollingInterval, () async { return Timer(_pollingInterval, () async {
try { try {
final List<Device> devices = await pollingGetDevices().timeout(_pollingTimeout); final List<Device> devices = await pollingGetDevices(timeout: _pollingTimeout);
_items.updateWithNewList(devices); _items.updateWithNewList(devices);
} on TimeoutException { } on TimeoutException {
globals.printTrace('Device poll timed out. Will retry.'); globals.printTrace('Device poll timed out. Will retry.');
...@@ -284,7 +298,17 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ...@@ -284,7 +298,17 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery {
@override @override
Future<List<Device>> get devices async { Future<List<Device>> get devices async {
_items ??= ItemListNotifier<Device>.from(await pollingGetDevices()); return _populateDevices();
}
@override
Future<List<Device>> discoverDevices({ Duration timeout }) async {
_items = null;
return _populateDevices(timeout: timeout);
}
Future<List<Device>> _populateDevices({ Duration timeout }) async {
_items ??= ItemListNotifier<Device>.from(await pollingGetDevices(timeout: timeout));
return _items.items; return _items.items;
} }
......
...@@ -22,7 +22,7 @@ class FuchsiaDevFinder { ...@@ -22,7 +22,7 @@ class FuchsiaDevFinder {
/// Returns a list of attached devices as a list of strings with entries /// Returns a list of attached devices as a list of strings with entries
/// formatted as follows: /// formatted as follows:
/// 192.168.42.172 scare-cable-skip-joy /// 192.168.42.172 scare-cable-skip-joy
Future<List<String>> list() async { Future<List<String>> list({ Duration timeout }) async {
if (fuchsiaArtifacts.devFinder == null || if (fuchsiaArtifacts.devFinder == null ||
!fuchsiaArtifacts.devFinder.existsSync()) { !fuchsiaArtifacts.devFinder.existsSync()) {
throwToolExit('Fuchsia device-finder tool not found.'); throwToolExit('Fuchsia device-finder tool not found.');
...@@ -31,6 +31,8 @@ class FuchsiaDevFinder { ...@@ -31,6 +31,8 @@ class FuchsiaDevFinder {
fuchsiaArtifacts.devFinder.path, fuchsiaArtifacts.devFinder.path,
'list', 'list',
'-full', '-full',
if (timeout != null)
...<String>['-timeout', '${timeout.inMilliseconds}ms']
]; ];
final RunResult result = await processUtils.run(command); final RunResult result = await processUtils.run(command);
if (result.exitCode != 0) { if (result.exitCode != 0) {
......
...@@ -145,11 +145,11 @@ class FuchsiaDevices extends PollingDeviceDiscovery { ...@@ -145,11 +145,11 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
bool get canListAnything => fuchsiaWorkflow.canListDevices; bool get canListAnything => fuchsiaWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
if (!fuchsiaWorkflow.canListDevices) { if (!fuchsiaWorkflow.canListDevices) {
return <Device>[]; return <Device>[];
} }
final String text = await fuchsiaSdk.listDevices(); final String text = await fuchsiaSdk.listDevices(timeout: timeout);
if (text == null || text.isEmpty) { if (text == null || text.isEmpty) {
return <Device>[]; return <Device>[];
} }
......
...@@ -47,12 +47,12 @@ class FuchsiaSdk { ...@@ -47,12 +47,12 @@ class FuchsiaSdk {
/// Example output: /// Example output:
/// $ device-finder list -full /// $ device-finder list -full
/// > 192.168.42.56 paper-pulp-bush-angel /// > 192.168.42.56 paper-pulp-bush-angel
Future<String> listDevices() async { Future<String> listDevices({ Duration timeout }) async {
if (fuchsiaArtifacts.devFinder == null || if (fuchsiaArtifacts.devFinder == null ||
!fuchsiaArtifacts.devFinder.existsSync()) { !fuchsiaArtifacts.devFinder.existsSync()) {
return null; return null;
} }
final List<String> devices = await fuchsiaDevFinder.list(); final List<String> devices = await fuchsiaDevFinder.list(timeout: timeout);
if (devices == null) { if (devices == null) {
return null; return null;
} }
......
...@@ -38,7 +38,10 @@ class IOSDevices extends PollingDeviceDiscovery { ...@@ -38,7 +38,10 @@ class IOSDevices extends PollingDeviceDiscovery {
bool get canListAnything => globals.iosWorkflow.canListDevices; bool get canListAnything => globals.iosWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() => IOSDevice.getAttachedDevices(globals.platform, globals.xcdevice); Future<List<Device>> pollingGetDevices({ Duration timeout }) {
return IOSDevice.getAttachedDevices(
globals.platform, globals.xcdevice, timeout: timeout);
}
@override @override
Future<List<String>> getDiagnostics() => IOSDevice.getDiagnostics(globals.platform, globals.xcdevice); Future<List<String>> getDiagnostics() => IOSDevice.getDiagnostics(globals.platform, globals.xcdevice);
...@@ -109,12 +112,12 @@ class IOSDevice extends Device { ...@@ -109,12 +112,12 @@ class IOSDevice extends Device {
@override @override
bool get supportsStartPaused => false; bool get supportsStartPaused => false;
static Future<List<IOSDevice>> getAttachedDevices(Platform platform, XCDevice xcdevice) async { static Future<List<IOSDevice>> getAttachedDevices(Platform platform, XCDevice xcdevice, { Duration timeout }) async {
if (!platform.isMacOS) { if (!platform.isMacOS) {
throw UnsupportedError('Control of iOS devices or simulators only supported on macOS.'); throw UnsupportedError('Control of iOS devices or simulators only supported on macOS.');
} }
return await xcdevice.getAvailableTetheredIOSDevices(); return await xcdevice.getAvailableTetheredIOSDevices(timeout: timeout);
} }
static Future<List<String>> getDiagnostics(Platform platform, XCDevice xcdevice) async { static Future<List<String>> getDiagnostics(Platform platform, XCDevice xcdevice) async {
......
...@@ -43,7 +43,7 @@ class IOSSimulators extends PollingDeviceDiscovery { ...@@ -43,7 +43,7 @@ class IOSSimulators extends PollingDeviceDiscovery {
bool get canListAnything => globals.iosWorkflow.canListDevices; bool get canListAnything => globals.iosWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async => _iosSimulatorUtils.getAttachedDevices(); Future<List<Device>> pollingGetDevices({ Duration timeout }) async => _iosSimulatorUtils.getAttachedDevices();
} }
class IOSSimulatorUtils { class IOSSimulatorUtils {
......
...@@ -76,7 +76,7 @@ class LinuxDevices extends PollingDeviceDiscovery { ...@@ -76,7 +76,7 @@ class LinuxDevices extends PollingDeviceDiscovery {
bool get canListAnything => _linuxWorkflow.canListDevices; bool get canListAnything => _linuxWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
if (!canListAnything) { if (!canListAnything) {
return const <Device>[]; return const <Device>[];
} }
......
...@@ -78,7 +78,7 @@ class MacOSDevices extends PollingDeviceDiscovery { ...@@ -78,7 +78,7 @@ class MacOSDevices extends PollingDeviceDiscovery {
bool get canListAnything => macOSWorkflow.canListDevices; bool get canListAnything => macOSWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
if (!canListAnything) { if (!canListAnything) {
return const <Device>[]; return const <Device>[];
} }
......
...@@ -228,7 +228,10 @@ class XCDevice { ...@@ -228,7 +228,10 @@ class XCDevice {
return _xcdevicePath; return _xcdevicePath;
} }
Future<List<dynamic>> _getAllDevices({bool useCache = false}) async { Future<List<dynamic>> _getAllDevices({
bool useCache = false,
@required Duration timeout
}) async {
if (!isInstalled) { if (!isInstalled) {
_logger.printTrace("Xcode not found. Run 'flutter doctor' for more information."); _logger.printTrace("Xcode not found. Run 'flutter doctor' for more information.");
return null; return null;
...@@ -244,7 +247,7 @@ class XCDevice { ...@@ -244,7 +247,7 @@ class XCDevice {
'xcdevice', 'xcdevice',
'list', 'list',
'--timeout', '--timeout',
'1', timeout.inSeconds.toString(),
], ],
throwOnError: true, throwOnError: true,
); );
...@@ -265,9 +268,9 @@ class XCDevice { ...@@ -265,9 +268,9 @@ class XCDevice {
List<dynamic> _cachedListResults; List<dynamic> _cachedListResults;
/// List of devices available over USB. /// [timeout] defaults to 1 second.
Future<List<IOSDevice>> getAvailableTetheredIOSDevices() async { Future<List<IOSDevice>> getAvailableTetheredIOSDevices({ Duration timeout }) async {
final List<dynamic> allAvailableDevices = await _getAllDevices(); final List<dynamic> allAvailableDevices = await _getAllDevices(timeout: timeout ?? const Duration(seconds: 1));
if (allAvailableDevices == null) { if (allAvailableDevices == null) {
return const <IOSDevice>[]; return const <IOSDevice>[];
...@@ -501,7 +504,10 @@ class XCDevice { ...@@ -501,7 +504,10 @@ class XCDevice {
/// List of all devices reporting errors. /// List of all devices reporting errors.
Future<List<String>> getDiagnostics() async { Future<List<String>> getDiagnostics() async {
final List<dynamic> allAvailableDevices = await _getAllDevices(useCache: true); final List<dynamic> allAvailableDevices = await _getAllDevices(
useCache: true,
timeout: const Duration(seconds: 1)
);
if (allAvailableDevices == null) { if (allAvailableDevices == null) {
return const <String>[]; return const <String>[];
......
...@@ -246,7 +246,7 @@ class FlutterTesterDevices extends PollingDeviceDiscovery { ...@@ -246,7 +246,7 @@ class FlutterTesterDevices extends PollingDeviceDiscovery {
bool get supportsPlatform => true; bool get supportsPlatform => true;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
return showFlutterTesterDevice ? <Device>[_testerDevice] : <Device>[]; return showFlutterTesterDevice ? <Device>[_testerDevice] : <Device>[];
} }
} }
......
...@@ -185,7 +185,7 @@ class WebDevices extends PollingDeviceDiscovery { ...@@ -185,7 +185,7 @@ class WebDevices extends PollingDeviceDiscovery {
bool get canListAnything => featureFlags.isWebEnabled; bool get canListAnything => featureFlags.isWebEnabled;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
return <Device>[ return <Device>[
if (_chromeIsAvailable) if (_chromeIsAvailable)
_webDevice, _webDevice,
......
...@@ -66,7 +66,7 @@ class WindowsDevices extends PollingDeviceDiscovery { ...@@ -66,7 +66,7 @@ class WindowsDevices extends PollingDeviceDiscovery {
bool get canListAnything => windowsWorkflow.canListDevices; bool get canListAnything => windowsWorkflow.canListDevices;
@override @override
Future<List<Device>> pollingGetDevices() async { Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
if (!canListAnything) { if (!canListAnything) {
return const <Device>[]; return const <Device>[];
} }
......
...@@ -12,6 +12,7 @@ import 'package:mockito/mockito.dart'; ...@@ -12,6 +12,7 @@ import 'package:mockito/mockito.dart';
import '../src/common.dart'; import '../src/common.dart';
import '../src/context.dart'; import '../src/context.dart';
import '../src/mocks.dart';
void main() { void main() {
group('DeviceManager', () { group('DeviceManager', () {
...@@ -39,6 +40,26 @@ void main() { ...@@ -39,6 +40,26 @@ void main() {
await expectDevice('0553790', <Device>[device1]); await expectDevice('0553790', <Device>[device1]);
await expectDevice('Nexus', <Device>[device1, device2]); await expectDevice('Nexus', <Device>[device1, device2]);
}); });
testUsingContext('getAllConnectedDevices caches', () async {
final _MockDevice device1 = _MockDevice('Nexus 5', '0553790d0a4e726f');
final TestDeviceManager deviceManager = TestDeviceManager(<Device>[device1]);
expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]);
final _MockDevice device2 = _MockDevice('Nexus 5X', '01abfc49119c410e');
deviceManager.resetDevices(<Device>[device2]);
expect(await deviceManager.getAllConnectedDevices(), <Device>[device1]);
});
testUsingContext('refreshAllConnectedDevices does not cache', () async {
final _MockDevice device1 = _MockDevice('Nexus 5', '0553790d0a4e726f');
final TestDeviceManager deviceManager = TestDeviceManager(<Device>[device1]);
expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device1]);
final _MockDevice device2 = _MockDevice('Nexus 5X', '01abfc49119c410e');
deviceManager.resetDevices(<Device>[device2]);
expect(await deviceManager.refreshAllConnectedDevices(), <Device>[device2]);
});
}); });
group('Filter devices', () { group('Filter devices', () {
...@@ -164,13 +185,19 @@ void main() { ...@@ -164,13 +185,19 @@ void main() {
} }
class TestDeviceManager extends DeviceManager { class TestDeviceManager extends DeviceManager {
TestDeviceManager(this.allDevices); TestDeviceManager(List<Device> allDevices) {
_deviceDiscoverer = MockPollingDeviceDiscovery();
resetDevices(allDevices);
}
@override
List<DeviceDiscovery> get deviceDiscoverers => <DeviceDiscovery>[_deviceDiscoverer];
MockPollingDeviceDiscovery _deviceDiscoverer;
final List<Device> allDevices; void resetDevices(List<Device> allDevices) {
bool isAlwaysSupportedOverride; _deviceDiscoverer.setDevices(allDevices);
}
@override bool isAlwaysSupportedOverride;
Future<List<Device>> getAllConnectedDevices() async => allDevices;
@override @override
bool isDeviceSupportedForProject(Device device, FlutterProject flutterProject) { bool isDeviceSupportedForProject(Device device, FlutterProject flutterProject) {
......
...@@ -1344,7 +1344,7 @@ class FailingKernelCompiler implements FuchsiaKernelCompiler { ...@@ -1344,7 +1344,7 @@ class FailingKernelCompiler implements FuchsiaKernelCompiler {
class FakeFuchsiaDevFinder implements FuchsiaDevFinder { class FakeFuchsiaDevFinder implements FuchsiaDevFinder {
@override @override
Future<List<String>> list() async { Future<List<String>> list({ Duration timeout }) async {
return <String>['192.168.42.172 scare-cable-skip-joy']; return <String>['192.168.42.172 scare-cable-skip-joy'];
} }
...@@ -1356,7 +1356,7 @@ class FakeFuchsiaDevFinder implements FuchsiaDevFinder { ...@@ -1356,7 +1356,7 @@ class FakeFuchsiaDevFinder implements FuchsiaDevFinder {
class FailingDevFinder implements FuchsiaDevFinder { class FailingDevFinder implements FuchsiaDevFinder {
@override @override
Future<List<String>> list() async { Future<List<String>> list({ Duration timeout }) async {
return null; return null;
} }
......
...@@ -5,7 +5,6 @@ ...@@ -5,7 +5,6 @@
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/linux/application_package.dart'; import 'package:flutter_tools/src/linux/application_package.dart';
import 'package:flutter_tools/src/linux/linux_device.dart'; import 'package:flutter_tools/src/linux/linux_device.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
...@@ -17,14 +16,17 @@ import 'package:platform/platform.dart'; ...@@ -17,14 +16,17 @@ import 'package:platform/platform.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/testbed.dart';
void main() { void main() {
final LinuxDevice device = LinuxDevice(); final LinuxDevice device = LinuxDevice();
final MockPlatform notLinux = MockPlatform(); final MockPlatform notLinux = MockPlatform();
when(notLinux.isLinux).thenReturn(false); when(notLinux.isLinux).thenReturn(false);
testUsingContext('LinuxDevice defaults', () async { final MockPlatform mockLinuxPlatform = MockPlatform();
when(mockLinuxPlatform.isLinux).thenReturn(true);
testWithoutContext('LinuxDevice defaults', () async {
final PrebuiltLinuxApp linuxApp = PrebuiltLinuxApp(executable: 'foo'); final PrebuiltLinuxApp linuxApp = PrebuiltLinuxApp(executable: 'foo');
expect(await device.targetPlatform, TargetPlatform.linux_x64); expect(await device.targetPlatform, TargetPlatform.linux_x64);
expect(device.name, 'Linux'); expect(device.name, 'Linux');
...@@ -36,13 +38,36 @@ void main() { ...@@ -36,13 +38,36 @@ void main() {
expect(device.category, Category.desktop); expect(device.category, Category.desktop);
}); });
testUsingContext('LinuxDevice: no devices listed if platform unsupported', () async { testWithoutContext('LinuxDevice: no devices listed if platform unsupported', () async {
expect(await LinuxDevices( expect(await LinuxDevices(
platform: notLinux, platform: notLinux,
featureFlags: featureFlags, featureFlags: TestFeatureFlags(isLinuxEnabled: true),
).devices, <Device>[]);
});
testWithoutContext('LinuxDevice: no devices listed if Linux feature flag disabled', () async {
expect(await LinuxDevices(
platform: mockLinuxPlatform,
featureFlags: TestFeatureFlags(isLinuxEnabled: false),
).devices, <Device>[]); ).devices, <Device>[]);
}); });
testWithoutContext('LinuxDevice: devices', () async {
expect(await LinuxDevices(
platform: mockLinuxPlatform,
featureFlags: TestFeatureFlags(isLinuxEnabled: true),
).devices, hasLength(1));
});
testWithoutContext('LinuxDevice: discoverDevices', () async {
// Timeout ignored.
final List<Device> devices = await LinuxDevices(
platform: mockLinuxPlatform,
featureFlags: TestFeatureFlags(isLinuxEnabled: true),
).discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
});
testUsingContext('LinuxDevice.isSupportedForProject is true with editable host app', () async { testUsingContext('LinuxDevice.isSupportedForProject is true with editable host app', () async {
globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
......
...@@ -13,20 +13,27 @@ import 'package:flutter_tools/src/base/file_system.dart'; ...@@ -13,20 +13,27 @@ import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/device.dart'; import 'package:flutter_tools/src/device.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/macos/application_package.dart'; import 'package:flutter_tools/src/macos/application_package.dart';
import 'package:flutter_tools/src/macos/macos_device.dart'; import 'package:flutter_tools/src/macos/macos_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/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/testbed.dart';
void main() { void main() {
group(MacOSDevice, () { group(MacOSDevice, () {
final MockPlatform notMac = MockPlatform();
final MacOSDevice device = MacOSDevice(); final MacOSDevice device = MacOSDevice();
final MockProcessManager mockProcessManager = MockProcessManager(); final MockProcessManager mockProcessManager = MockProcessManager();
final MockPlatform notMac = MockPlatform();
when(notMac.isMacOS).thenReturn(false); when(notMac.isMacOS).thenReturn(false);
when(notMac.environment).thenReturn(const <String, String>{}); when(notMac.environment).thenReturn(const <String, String>{});
final MockPlatform mockMacPlatform = MockPlatform();
when(mockMacPlatform.isMacOS).thenReturn(true);
when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async {
return ProcessResult(0, 1, '', ''); return ProcessResult(0, 1, '', '');
}); });
...@@ -48,6 +55,22 @@ void main() { ...@@ -48,6 +55,22 @@ void main() {
Platform: () => notMac, Platform: () => notMac,
}); });
testUsingContext('devices', () async {
expect(await MacOSDevices().devices, hasLength(1));
}, overrides: <Type, Generator>{
Platform: () => mockMacPlatform,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
});
testUsingContext('discoverDevices', () async {
// Timeout ignored.
final List<Device> devices = await MacOSDevices().discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
}, overrides: <Type, Generator>{
Platform: () => mockMacPlatform,
FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true),
});
testUsingContext('isSupportedForProject is true with editable host app', () async { testUsingContext('isSupportedForProject is true with editable host app', () async {
globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
......
...@@ -359,6 +359,18 @@ void main() { ...@@ -359,6 +359,18 @@ void main() {
Platform: () => macPlatform, Platform: () => macPlatform,
}); });
testWithoutContext('uses timeout', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
when(processManager.runSync(<String>['xcrun', '--find', 'xcdevice']))
.thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', ''));
when(processManager.run(any))
.thenAnswer((_) => Future<ProcessResult>.value(ProcessResult(1, 0, '[]', '')));
await xcdevice.getAvailableTetheredIOSDevices(timeout: const Duration(seconds: 20));
verify(processManager.run(<String>['xcrun', 'xcdevice', 'list', '--timeout', '20'])).called(1);
});
testUsingContext('ignores "Preparing debugger support for iPhone" error', () async { testUsingContext('ignores "Preparing debugger support for iPhone" error', () async {
when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true);
......
...@@ -64,6 +64,15 @@ void main() { ...@@ -64,6 +64,15 @@ void main() {
expect(device, isA<FlutterTesterDevice>()); expect(device, isA<FlutterTesterDevice>());
expect(device.id, 'flutter-tester'); expect(device.id, 'flutter-tester');
}); });
testUsingContext('discoverDevices', () async {
FlutterTesterDevices.showFlutterTesterDevice = true;
final FlutterTesterDevices discoverer = FlutterTesterDevices();
// Timeout ignored.
final List<Device> devices = await discoverer.discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
});
}); });
group('FlutterTesterDevice', () { group('FlutterTesterDevice', () {
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/base/file_system.dart'; import 'package:flutter_tools/src/base/file_system.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
import 'package:flutter_tools/src/features.dart';
import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/windows/application_package.dart'; import 'package:flutter_tools/src/windows/application_package.dart';
import 'package:flutter_tools/src/windows/windows_device.dart'; import 'package:flutter_tools/src/windows/windows_device.dart';
...@@ -15,15 +16,19 @@ import 'package:platform/platform.dart'; ...@@ -15,15 +16,19 @@ import 'package:platform/platform.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
import '../../src/testbed.dart';
void main() { void main() {
group(WindowsDevice, () { group(WindowsDevice, () {
final WindowsDevice device = WindowsDevice(); final WindowsDevice device = WindowsDevice();
final MockPlatform notWindows = MockPlatform();
final MockPlatform notWindows = MockPlatform();
when(notWindows.isWindows).thenReturn(false); when(notWindows.isWindows).thenReturn(false);
when(notWindows.environment).thenReturn(const <String, String>{}); when(notWindows.environment).thenReturn(const <String, String>{});
final MockPlatform mockWindowsPlatform = MockPlatform();
when(mockWindowsPlatform.isWindows).thenReturn(true);
testUsingContext('defaults', () async { testUsingContext('defaults', () async {
final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo'); final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo');
expect(await device.targetPlatform, TargetPlatform.windows_x64); expect(await device.targetPlatform, TargetPlatform.windows_x64);
...@@ -41,6 +46,22 @@ void main() { ...@@ -41,6 +46,22 @@ void main() {
Platform: () => notWindows, Platform: () => notWindows,
}); });
testUsingContext('WindowsDevices: devices', () async {
expect(await WindowsDevices().devices, hasLength(1));
}, overrides: <Type, Generator>{
Platform: () => mockWindowsPlatform,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
});
testUsingContext('WindowsDevices: discoverDevices', () async {
// Timeout ignored.
final List<Device> devices = await WindowsDevices().discoverDevices(timeout: const Duration(seconds: 10));
expect(devices, hasLength(1));
}, overrides: <Type, Generator>{
Platform: () => mockWindowsPlatform,
FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true),
});
testUsingContext('isSupportedForProject is true with editable host app', () async { testUsingContext('isSupportedForProject is true with editable host app', () async {
globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('pubspec.yaml').createSync();
globals.fs.file('.packages').createSync(); globals.fs.file('.packages').createSync();
......
...@@ -218,6 +218,9 @@ class FakeDeviceManager implements DeviceManager { ...@@ -218,6 +218,9 @@ class FakeDeviceManager implements DeviceManager {
@override @override
Future<List<Device>> getAllConnectedDevices() async => devices; Future<List<Device>> getAllConnectedDevices() async => devices;
@override
Future<List<Device>> refreshAllConnectedDevices({ Duration timeout }) async => devices;
@override @override
Future<List<Device>> getDevicesById(String deviceId) async { Future<List<Device>> getDevicesById(String deviceId) async {
return devices.where((Device device) => device.id == deviceId).toList(); return devices.where((Device device) => device.id == deviceId).toList();
......
...@@ -526,7 +526,12 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { ...@@ -526,7 +526,12 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
final StreamController<Device> _onRemovedController = StreamController<Device>.broadcast(); final StreamController<Device> _onRemovedController = StreamController<Device>.broadcast();
@override @override
Future<List<Device>> pollingGetDevices() async => _devices; Future<List<Device>> pollingGetDevices({ Duration timeout }) async {
lastPollingTimeout = timeout;
return _devices;
}
Duration lastPollingTimeout;
@override @override
bool get supportsPlatform => true; bool get supportsPlatform => true;
...@@ -534,14 +539,22 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { ...@@ -534,14 +539,22 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery {
@override @override
bool get canListAnything => true; bool get canListAnything => true;
void addDevice(MockAndroidDevice device) { void addDevice(Device device) {
_devices.add(device); _devices.add(device);
_onAddedController.add(device); _onAddedController.add(device);
} }
@override void _removeDevice(Device device) {
Future<List<Device>> get devices async => _devices; _devices.remove(device);
_onRemovedController.add(device);
}
void setDevices(List<Device> devices) {
while(_devices.isNotEmpty) {
_removeDevice(_devices.first);
}
devices.forEach(addDevice);
}
@override @override
Stream<Device> get onAdded => _onAddedController.stream; Stream<Device> get onAdded => _onAddedController.stream;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment