From 2f216ceee599ddb31371dbe5da6fec7ec6e68513 Mon Sep 17 00:00:00 2001 From: Jenn Magder Date: Mon, 16 Mar 2020 14:15:00 -0700 Subject: [PATCH] Add timeout flag to devices command, pipe through discovery (#51678) --- .../lib/src/android/android_device.dart | 2 +- .../lib/src/commands/devices.dart | 43 ++++++++++++++++--- packages/flutter_tools/lib/src/device.dart | 34 ++++++++++++--- .../lib/src/fuchsia/fuchsia_dev_finder.dart | 4 +- .../lib/src/fuchsia/fuchsia_device.dart | 4 +- .../lib/src/fuchsia/fuchsia_sdk.dart | 4 +- .../flutter_tools/lib/src/ios/devices.dart | 9 ++-- .../flutter_tools/lib/src/ios/simulators.dart | 2 +- .../lib/src/linux/linux_device.dart | 2 +- .../lib/src/macos/macos_device.dart | 2 +- .../flutter_tools/lib/src/macos/xcode.dart | 18 +++++--- .../lib/src/tester/flutter_tester.dart | 2 +- .../flutter_tools/lib/src/web/web_device.dart | 2 +- .../lib/src/windows/windows_device.dart | 2 +- .../test/general.shard/device_test.dart | 39 ++++++++++++++--- .../fuchsia/fuchsia_device_test.dart | 4 +- .../linux/linux_device_test.dart | 35 ++++++++++++--- .../macos/macos_device_test.dart | 25 ++++++++++- .../test/general.shard/macos/xcode_test.dart | 12 ++++++ .../tester/flutter_tester_test.dart | 9 ++++ .../windows/windows_device_test.dart | 23 +++++++++- packages/flutter_tools/test/src/context.dart | 3 ++ packages/flutter_tools/test/src/mocks.dart | 23 +++++++--- 23 files changed, 251 insertions(+), 52 deletions(-) diff --git a/packages/flutter_tools/lib/src/android/android_device.dart b/packages/flutter_tools/lib/src/android/android_device.dart index fb9ef2b315..e5e30632d1 100644 --- a/packages/flutter_tools/lib/src/android/android_device.dart +++ b/packages/flutter_tools/lib/src/android/android_device.dart @@ -62,7 +62,7 @@ class AndroidDevices extends PollingDeviceDiscovery { bool get canListAnything => androidWorkflow.canListDevices; @override - Future> pollingGetDevices() async => getAdbDevices(); + Future> pollingGetDevices({ Duration timeout }) async => getAdbDevices(); @override Future> getDiagnostics() async => getAdbDeviceDiagnostics(); diff --git a/packages/flutter_tools/lib/src/commands/devices.dart b/packages/flutter_tools/lib/src/commands/devices.dart index c2b52085b6..47d2870384 100644 --- a/packages/flutter_tools/lib/src/commands/devices.dart +++ b/packages/flutter_tools/lib/src/commands/devices.dart @@ -12,12 +12,36 @@ import '../globals.dart' as globals; import '../runner/flutter_command.dart'; 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 final String name = 'devices'; @override 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 Future runCommand() async { if (!doctor.canListAnything) { @@ -27,14 +51,21 @@ class DevicesCommand extends FlutterCommand { exitCode: 1); } - final List devices = await deviceManager.getAllConnectedDevices(); + final List devices = await deviceManager.refreshAllConnectedDevices(timeout: timeout); if (devices.isEmpty) { - globals.printStatus( - 'No devices detected.\n\n' - "Run 'flutter emulators' to list and start any available device emulators.\n\n" - 'Or, if you expected your device to be detected, please run "flutter doctor" to diagnose ' - 'potential issues, or visit https://flutter.dev/setup/ for troubleshooting tips.'); + 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 (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 diagnostics = await deviceManager.getDeviceDiagnostics(); if (diagnostics.isNotEmpty) { globals.printStatus(''); diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart index f0bb7ea77f..8e4bb60114 100644 --- a/packages/flutter_tools/lib/src/device.dart +++ b/packages/flutter_tools/lib/src/device.dart @@ -124,7 +124,7 @@ class DeviceManager { 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> getDevices() { return hasSpecifiedDeviceId ? getDevicesById(specifiedDeviceId) @@ -135,7 +135,7 @@ class DeviceManager { return deviceDiscoverers.where((DeviceDiscovery discoverer) => discoverer.supportsPlatform); } - /// Return the list of all connected devices. + /// Returns the list of all connected devices. Future> getAllConnectedDevices() async { final List> devices = await Future.wait>(>>[ for (final DeviceDiscovery discoverer in _platformDiscoverers) @@ -145,6 +145,16 @@ class DeviceManager { return devices.expand((List deviceList) => deviceList).toList(); } + /// Returns the list of all connected devices. Discards existing cache of devices. + Future> refreshAllConnectedDevices({ Duration timeout }) async { + final List> devices = await Future.wait>(>>[ + for (final DeviceDiscovery discoverer in _platformDiscoverers) + discoverer.discoverDevices(timeout: timeout), + ]); + + return devices.expand((List deviceList) => deviceList).toList(); + } + /// Whether we're capable of listing any devices given the current environment configuration. bool get canListAnything { return _platformDiscoverers.any((DeviceDiscovery discoverer) => discoverer.canListAnything); @@ -237,8 +247,12 @@ abstract class DeviceDiscovery { /// current environment configuration. bool get canListAnything; + /// Return all connected devices, cached on subsequent calls. Future> get devices; + /// Return all connected devices. Discards existing cache of devices. + Future> discoverDevices({ Duration timeout }); + /// Gets a list of diagnostic messages pertaining to issues with any connected /// devices (will be an empty list if there are no issues). Future> getDiagnostics() => Future>.value([]); @@ -256,7 +270,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { ItemListNotifier _items; Timer _timer; - Future> pollingGetDevices(); + Future> pollingGetDevices({ Duration timeout }); void startPolling() { if (_timer == null) { @@ -268,7 +282,7 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { Timer _initTimer() { return Timer(_pollingInterval, () async { try { - final List devices = await pollingGetDevices().timeout(_pollingTimeout); + final List devices = await pollingGetDevices(timeout: _pollingTimeout); _items.updateWithNewList(devices); } on TimeoutException { globals.printTrace('Device poll timed out. Will retry.'); @@ -284,7 +298,17 @@ abstract class PollingDeviceDiscovery extends DeviceDiscovery { @override Future> get devices async { - _items ??= ItemListNotifier.from(await pollingGetDevices()); + return _populateDevices(); + } + + @override + Future> discoverDevices({ Duration timeout }) async { + _items = null; + return _populateDevices(timeout: timeout); + } + + Future> _populateDevices({ Duration timeout }) async { + _items ??= ItemListNotifier.from(await pollingGetDevices(timeout: timeout)); return _items.items; } diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart index 3cec094cce..c439e3c040 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_dev_finder.dart @@ -22,7 +22,7 @@ class FuchsiaDevFinder { /// Returns a list of attached devices as a list of strings with entries /// formatted as follows: /// 192.168.42.172 scare-cable-skip-joy - Future> list() async { + Future> list({ Duration timeout }) async { if (fuchsiaArtifacts.devFinder == null || !fuchsiaArtifacts.devFinder.existsSync()) { throwToolExit('Fuchsia device-finder tool not found.'); @@ -31,6 +31,8 @@ class FuchsiaDevFinder { fuchsiaArtifacts.devFinder.path, 'list', '-full', + if (timeout != null) + ...['-timeout', '${timeout.inMilliseconds}ms'] ]; final RunResult result = await processUtils.run(command); if (result.exitCode != 0) { diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart index 9dfb52febb..b0f0a464d4 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart @@ -145,11 +145,11 @@ class FuchsiaDevices extends PollingDeviceDiscovery { bool get canListAnything => fuchsiaWorkflow.canListDevices; @override - Future> pollingGetDevices() async { + Future> pollingGetDevices({ Duration timeout }) async { if (!fuchsiaWorkflow.canListDevices) { return []; } - final String text = await fuchsiaSdk.listDevices(); + final String text = await fuchsiaSdk.listDevices(timeout: timeout); if (text == null || text.isEmpty) { return []; } diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart index 59fc738977..34bb45d0c4 100644 --- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart +++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart @@ -47,12 +47,12 @@ class FuchsiaSdk { /// Example output: /// $ device-finder list -full /// > 192.168.42.56 paper-pulp-bush-angel - Future listDevices() async { + Future listDevices({ Duration timeout }) async { if (fuchsiaArtifacts.devFinder == null || !fuchsiaArtifacts.devFinder.existsSync()) { return null; } - final List devices = await fuchsiaDevFinder.list(); + final List devices = await fuchsiaDevFinder.list(timeout: timeout); if (devices == null) { return null; } diff --git a/packages/flutter_tools/lib/src/ios/devices.dart b/packages/flutter_tools/lib/src/ios/devices.dart index cfb128c03d..a30c130662 100644 --- a/packages/flutter_tools/lib/src/ios/devices.dart +++ b/packages/flutter_tools/lib/src/ios/devices.dart @@ -38,7 +38,10 @@ class IOSDevices extends PollingDeviceDiscovery { bool get canListAnything => globals.iosWorkflow.canListDevices; @override - Future> pollingGetDevices() => IOSDevice.getAttachedDevices(globals.platform, globals.xcdevice); + Future> pollingGetDevices({ Duration timeout }) { + return IOSDevice.getAttachedDevices( + globals.platform, globals.xcdevice, timeout: timeout); + } @override Future> getDiagnostics() => IOSDevice.getDiagnostics(globals.platform, globals.xcdevice); @@ -109,12 +112,12 @@ class IOSDevice extends Device { @override bool get supportsStartPaused => false; - static Future> getAttachedDevices(Platform platform, XCDevice xcdevice) async { + static Future> getAttachedDevices(Platform platform, XCDevice xcdevice, { Duration timeout }) async { if (!platform.isMacOS) { throw UnsupportedError('Control of iOS devices or simulators only supported on macOS.'); } - return await xcdevice.getAvailableTetheredIOSDevices(); + return await xcdevice.getAvailableTetheredIOSDevices(timeout: timeout); } static Future> getDiagnostics(Platform platform, XCDevice xcdevice) async { diff --git a/packages/flutter_tools/lib/src/ios/simulators.dart b/packages/flutter_tools/lib/src/ios/simulators.dart index 90edbfa19f..d151eca8f9 100644 --- a/packages/flutter_tools/lib/src/ios/simulators.dart +++ b/packages/flutter_tools/lib/src/ios/simulators.dart @@ -43,7 +43,7 @@ class IOSSimulators extends PollingDeviceDiscovery { bool get canListAnything => globals.iosWorkflow.canListDevices; @override - Future> pollingGetDevices() async => _iosSimulatorUtils.getAttachedDevices(); + Future> pollingGetDevices({ Duration timeout }) async => _iosSimulatorUtils.getAttachedDevices(); } class IOSSimulatorUtils { diff --git a/packages/flutter_tools/lib/src/linux/linux_device.dart b/packages/flutter_tools/lib/src/linux/linux_device.dart index 06c3e1cf9f..c66b6c9217 100644 --- a/packages/flutter_tools/lib/src/linux/linux_device.dart +++ b/packages/flutter_tools/lib/src/linux/linux_device.dart @@ -76,7 +76,7 @@ class LinuxDevices extends PollingDeviceDiscovery { bool get canListAnything => _linuxWorkflow.canListDevices; @override - Future> pollingGetDevices() async { + Future> pollingGetDevices({ Duration timeout }) async { if (!canListAnything) { return const []; } diff --git a/packages/flutter_tools/lib/src/macos/macos_device.dart b/packages/flutter_tools/lib/src/macos/macos_device.dart index d0448d217a..8f790812b2 100644 --- a/packages/flutter_tools/lib/src/macos/macos_device.dart +++ b/packages/flutter_tools/lib/src/macos/macos_device.dart @@ -78,7 +78,7 @@ class MacOSDevices extends PollingDeviceDiscovery { bool get canListAnything => macOSWorkflow.canListDevices; @override - Future> pollingGetDevices() async { + Future> pollingGetDevices({ Duration timeout }) async { if (!canListAnything) { return const []; } diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart index 099b92d4f0..5e8f0f135d 100644 --- a/packages/flutter_tools/lib/src/macos/xcode.dart +++ b/packages/flutter_tools/lib/src/macos/xcode.dart @@ -228,7 +228,10 @@ class XCDevice { return _xcdevicePath; } - Future> _getAllDevices({bool useCache = false}) async { + Future> _getAllDevices({ + bool useCache = false, + @required Duration timeout + }) async { if (!isInstalled) { _logger.printTrace("Xcode not found. Run 'flutter doctor' for more information."); return null; @@ -244,7 +247,7 @@ class XCDevice { 'xcdevice', 'list', '--timeout', - '1', + timeout.inSeconds.toString(), ], throwOnError: true, ); @@ -265,9 +268,9 @@ class XCDevice { List _cachedListResults; - /// List of devices available over USB. - Future> getAvailableTetheredIOSDevices() async { - final List allAvailableDevices = await _getAllDevices(); + /// [timeout] defaults to 1 second. + Future> getAvailableTetheredIOSDevices({ Duration timeout }) async { + final List allAvailableDevices = await _getAllDevices(timeout: timeout ?? const Duration(seconds: 1)); if (allAvailableDevices == null) { return const []; @@ -501,7 +504,10 @@ class XCDevice { /// List of all devices reporting errors. Future> getDiagnostics() async { - final List allAvailableDevices = await _getAllDevices(useCache: true); + final List allAvailableDevices = await _getAllDevices( + useCache: true, + timeout: const Duration(seconds: 1) + ); if (allAvailableDevices == null) { return const []; diff --git a/packages/flutter_tools/lib/src/tester/flutter_tester.dart b/packages/flutter_tools/lib/src/tester/flutter_tester.dart index fed7ec02b0..4cd7c9d7a0 100644 --- a/packages/flutter_tools/lib/src/tester/flutter_tester.dart +++ b/packages/flutter_tools/lib/src/tester/flutter_tester.dart @@ -246,7 +246,7 @@ class FlutterTesterDevices extends PollingDeviceDiscovery { bool get supportsPlatform => true; @override - Future> pollingGetDevices() async { + Future> pollingGetDevices({ Duration timeout }) async { return showFlutterTesterDevice ? [_testerDevice] : []; } } diff --git a/packages/flutter_tools/lib/src/web/web_device.dart b/packages/flutter_tools/lib/src/web/web_device.dart index d38e772160..f5a03ddc9e 100644 --- a/packages/flutter_tools/lib/src/web/web_device.dart +++ b/packages/flutter_tools/lib/src/web/web_device.dart @@ -185,7 +185,7 @@ class WebDevices extends PollingDeviceDiscovery { bool get canListAnything => featureFlags.isWebEnabled; @override - Future> pollingGetDevices() async { + Future> pollingGetDevices({ Duration timeout }) async { return [ if (_chromeIsAvailable) _webDevice, diff --git a/packages/flutter_tools/lib/src/windows/windows_device.dart b/packages/flutter_tools/lib/src/windows/windows_device.dart index f7f0b65d0e..5b6ac09b62 100644 --- a/packages/flutter_tools/lib/src/windows/windows_device.dart +++ b/packages/flutter_tools/lib/src/windows/windows_device.dart @@ -66,7 +66,7 @@ class WindowsDevices extends PollingDeviceDiscovery { bool get canListAnything => windowsWorkflow.canListDevices; @override - Future> pollingGetDevices() async { + Future> pollingGetDevices({ Duration timeout }) async { if (!canListAnything) { return const []; } diff --git a/packages/flutter_tools/test/general.shard/device_test.dart b/packages/flutter_tools/test/general.shard/device_test.dart index ebbf48848c..fbf8a06381 100644 --- a/packages/flutter_tools/test/general.shard/device_test.dart +++ b/packages/flutter_tools/test/general.shard/device_test.dart @@ -12,6 +12,7 @@ import 'package:mockito/mockito.dart'; import '../src/common.dart'; import '../src/context.dart'; +import '../src/mocks.dart'; void main() { group('DeviceManager', () { @@ -39,6 +40,26 @@ void main() { await expectDevice('0553790', [device1]); await expectDevice('Nexus', [device1, device2]); }); + + testUsingContext('getAllConnectedDevices caches', () async { + final _MockDevice device1 = _MockDevice('Nexus 5', '0553790d0a4e726f'); + final TestDeviceManager deviceManager = TestDeviceManager([device1]); + expect(await deviceManager.getAllConnectedDevices(), [device1]); + + final _MockDevice device2 = _MockDevice('Nexus 5X', '01abfc49119c410e'); + deviceManager.resetDevices([device2]); + expect(await deviceManager.getAllConnectedDevices(), [device1]); + }); + + testUsingContext('refreshAllConnectedDevices does not cache', () async { + final _MockDevice device1 = _MockDevice('Nexus 5', '0553790d0a4e726f'); + final TestDeviceManager deviceManager = TestDeviceManager([device1]); + expect(await deviceManager.refreshAllConnectedDevices(), [device1]); + + final _MockDevice device2 = _MockDevice('Nexus 5X', '01abfc49119c410e'); + deviceManager.resetDevices([device2]); + expect(await deviceManager.refreshAllConnectedDevices(), [device2]); + }); }); group('Filter devices', () { @@ -164,13 +185,19 @@ void main() { } class TestDeviceManager extends DeviceManager { - TestDeviceManager(this.allDevices); - - final List allDevices; - bool isAlwaysSupportedOverride; - + TestDeviceManager(List allDevices) { + _deviceDiscoverer = MockPollingDeviceDiscovery(); + resetDevices(allDevices); + } @override - Future> getAllConnectedDevices() async => allDevices; + List get deviceDiscoverers => [_deviceDiscoverer]; + MockPollingDeviceDiscovery _deviceDiscoverer; + + void resetDevices(List allDevices) { + _deviceDiscoverer.setDevices(allDevices); + } + + bool isAlwaysSupportedOverride; @override bool isDeviceSupportedForProject(Device device, FlutterProject flutterProject) { diff --git a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart index 1f48f0b85f..87ccbca874 100644 --- a/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart +++ b/packages/flutter_tools/test/general.shard/fuchsia/fuchsia_device_test.dart @@ -1344,7 +1344,7 @@ class FailingKernelCompiler implements FuchsiaKernelCompiler { class FakeFuchsiaDevFinder implements FuchsiaDevFinder { @override - Future> list() async { + Future> list({ Duration timeout }) async { return ['192.168.42.172 scare-cable-skip-joy']; } @@ -1356,7 +1356,7 @@ class FakeFuchsiaDevFinder implements FuchsiaDevFinder { class FailingDevFinder implements FuchsiaDevFinder { @override - Future> list() async { + Future> list({ Duration timeout }) async { return null; } diff --git a/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart b/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart index 63a1ead16b..7dc53c926f 100644 --- a/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart +++ b/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart @@ -5,7 +5,6 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.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/linux_device.dart'; import 'package:flutter_tools/src/device.dart'; @@ -17,14 +16,17 @@ import 'package:platform/platform.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { final LinuxDevice device = LinuxDevice(); final MockPlatform notLinux = MockPlatform(); - 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'); expect(await device.targetPlatform, TargetPlatform.linux_x64); expect(device.name, 'Linux'); @@ -36,13 +38,36 @@ void main() { 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( platform: notLinux, - featureFlags: featureFlags, + featureFlags: TestFeatureFlags(isLinuxEnabled: true), ).devices, []); }); + testWithoutContext('LinuxDevice: no devices listed if Linux feature flag disabled', () async { + expect(await LinuxDevices( + platform: mockLinuxPlatform, + featureFlags: TestFeatureFlags(isLinuxEnabled: false), + ).devices, []); + }); + + testWithoutContext('LinuxDevice: devices', () async { + expect(await LinuxDevices( + platform: mockLinuxPlatform, + featureFlags: TestFeatureFlags(isLinuxEnabled: true), + ).devices, hasLength(1)); + }); + + testWithoutContext('LinuxDevice: discoverDevices', () async { + // Timeout ignored. + final List 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 { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); diff --git a/packages/flutter_tools/test/general.shard/macos/macos_device_test.dart b/packages/flutter_tools/test/general.shard/macos/macos_device_test.dart index f88ebb1668..b1348de515 100644 --- a/packages/flutter_tools/test/general.shard/macos/macos_device_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/macos_device_test.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/build_info.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/macos_device.dart'; import 'package:flutter_tools/src/globals.dart' as globals; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { group(MacOSDevice, () { - final MockPlatform notMac = MockPlatform(); final MacOSDevice device = MacOSDevice(); final MockProcessManager mockProcessManager = MockProcessManager(); + + final MockPlatform notMac = MockPlatform(); when(notMac.isMacOS).thenReturn(false); when(notMac.environment).thenReturn(const {}); + + final MockPlatform mockMacPlatform = MockPlatform(); + when(mockMacPlatform.isMacOS).thenReturn(true); + when(mockProcessManager.run(any)).thenAnswer((Invocation invocation) async { return ProcessResult(0, 1, '', ''); }); @@ -48,6 +55,22 @@ void main() { Platform: () => notMac, }); + testUsingContext('devices', () async { + expect(await MacOSDevices().devices, hasLength(1)); + }, overrides: { + Platform: () => mockMacPlatform, + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), + }); + + testUsingContext('discoverDevices', () async { + // Timeout ignored. + final List devices = await MacOSDevices().discoverDevices(timeout: const Duration(seconds: 10)); + expect(devices, hasLength(1)); + }, overrides: { + Platform: () => mockMacPlatform, + FeatureFlags: () => TestFeatureFlags(isMacOSEnabled: true), + }); + testUsingContext('isSupportedForProject is true with editable host app', () async { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); diff --git a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart index 943212f668..4b4282ae03 100644 --- a/packages/flutter_tools/test/general.shard/macos/xcode_test.dart +++ b/packages/flutter_tools/test/general.shard/macos/xcode_test.dart @@ -359,6 +359,18 @@ void main() { Platform: () => macPlatform, }); + testWithoutContext('uses timeout', () async { + when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); + + when(processManager.runSync(['xcrun', '--find', 'xcdevice'])) + .thenReturn(ProcessResult(1, 0, '/path/to/xcdevice', '')); + + when(processManager.run(any)) + .thenAnswer((_) => Future.value(ProcessResult(1, 0, '[]', ''))); + await xcdevice.getAvailableTetheredIOSDevices(timeout: const Duration(seconds: 20)); + verify(processManager.run(['xcrun', 'xcdevice', 'list', '--timeout', '20'])).called(1); + }); + testUsingContext('ignores "Preparing debugger support for iPhone" error', () async { when(mockXcode.isInstalledAndMeetsVersionCheck).thenReturn(true); diff --git a/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart b/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart index 11ef8d69ee..ab3a09c646 100644 --- a/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart +++ b/packages/flutter_tools/test/general.shard/tester/flutter_tester_test.dart @@ -64,6 +64,15 @@ void main() { expect(device, isA()); expect(device.id, 'flutter-tester'); }); + + testUsingContext('discoverDevices', () async { + FlutterTesterDevices.showFlutterTesterDevice = true; + final FlutterTesterDevices discoverer = FlutterTesterDevices(); + + // Timeout ignored. + final List devices = await discoverer.discoverDevices(timeout: const Duration(seconds: 10)); + expect(devices, hasLength(1)); + }); }); group('FlutterTesterDevice', () { diff --git a/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart b/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart index 319cf55e8b..be2422833a 100644 --- a/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart +++ b/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart @@ -5,6 +5,7 @@ import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.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/windows/application_package.dart'; import 'package:flutter_tools/src/windows/windows_device.dart'; @@ -15,15 +16,19 @@ import 'package:platform/platform.dart'; import '../../src/common.dart'; import '../../src/context.dart'; +import '../../src/testbed.dart'; void main() { group(WindowsDevice, () { final WindowsDevice device = WindowsDevice(); - final MockPlatform notWindows = MockPlatform(); + final MockPlatform notWindows = MockPlatform(); when(notWindows.isWindows).thenReturn(false); when(notWindows.environment).thenReturn(const {}); + final MockPlatform mockWindowsPlatform = MockPlatform(); + when(mockWindowsPlatform.isWindows).thenReturn(true); + testUsingContext('defaults', () async { final PrebuiltWindowsApp windowsApp = PrebuiltWindowsApp(executable: 'foo'); expect(await device.targetPlatform, TargetPlatform.windows_x64); @@ -41,6 +46,22 @@ void main() { Platform: () => notWindows, }); + testUsingContext('WindowsDevices: devices', () async { + expect(await WindowsDevices().devices, hasLength(1)); + }, overrides: { + Platform: () => mockWindowsPlatform, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), + }); + + testUsingContext('WindowsDevices: discoverDevices', () async { + // Timeout ignored. + final List devices = await WindowsDevices().discoverDevices(timeout: const Duration(seconds: 10)); + expect(devices, hasLength(1)); + }, overrides: { + Platform: () => mockWindowsPlatform, + FeatureFlags: () => TestFeatureFlags(isWindowsEnabled: true), + }); + testUsingContext('isSupportedForProject is true with editable host app', () async { globals.fs.file('pubspec.yaml').createSync(); globals.fs.file('.packages').createSync(); diff --git a/packages/flutter_tools/test/src/context.dart b/packages/flutter_tools/test/src/context.dart index 9ba571d1fb..235ea72b38 100644 --- a/packages/flutter_tools/test/src/context.dart +++ b/packages/flutter_tools/test/src/context.dart @@ -218,6 +218,9 @@ class FakeDeviceManager implements DeviceManager { @override Future> getAllConnectedDevices() async => devices; + @override + Future> refreshAllConnectedDevices({ Duration timeout }) async => devices; + @override Future> getDevicesById(String deviceId) async { return devices.where((Device device) => device.id == deviceId).toList(); diff --git a/packages/flutter_tools/test/src/mocks.dart b/packages/flutter_tools/test/src/mocks.dart index 2304272ad1..47808de951 100644 --- a/packages/flutter_tools/test/src/mocks.dart +++ b/packages/flutter_tools/test/src/mocks.dart @@ -526,7 +526,12 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { final StreamController _onRemovedController = StreamController.broadcast(); @override - Future> pollingGetDevices() async => _devices; + Future> pollingGetDevices({ Duration timeout }) async { + lastPollingTimeout = timeout; + return _devices; + } + + Duration lastPollingTimeout; @override bool get supportsPlatform => true; @@ -534,14 +539,22 @@ class MockPollingDeviceDiscovery extends PollingDeviceDiscovery { @override bool get canListAnything => true; - void addDevice(MockAndroidDevice device) { + void addDevice(Device device) { _devices.add(device); - _onAddedController.add(device); } - @override - Future> get devices async => _devices; + void _removeDevice(Device device) { + _devices.remove(device); + _onRemovedController.add(device); + } + + void setDevices(List devices) { + while(_devices.isNotEmpty) { + _removeDevice(_devices.first); + } + devices.forEach(addDevice); + } @override Stream get onAdded => _onAddedController.stream;