diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 245ca6d84cc..8443ba40f72 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -517,12 +517,8 @@ abstract class FlutterCommand extends Command { } devices = devices.where((Device device) => device.isSupported()).toList(); - // If the user has not specified all devices and has multiple connected - // then filter then list by those supported in the current project. If - // this ends up with a single device we can proceed as normal. if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices && !deviceManager.hasSpecifiedDeviceId) { - final FlutterProject flutterProject = FlutterProject.current(); - devices.removeWhere((Device device) => !device.isSupportedForProject(flutterProject)); + devices = filterDevices(devices); } if (devices.isEmpty) { @@ -698,3 +694,24 @@ abstract class FastFlutterCommand extends FlutterCommand { ); } } + +// If the user has not specified all devices and has multiple connected +// then filter the list by those supported in the current project and +// remove non-ephemeral device types. If this ends up with a single +// device we can proceed as normal. +@visibleForTesting +List filterDevices(List devices) { + final FlutterProject flutterProject = FlutterProject.current(); + devices = devices + .where((Device device) => device.isSupportedForProject(flutterProject)) + .toList(); + + // Note: ephemeral is nullable for device types where this is not well + // defined. + if (devices.any((Device device) => device.ephemeral == true)) { + devices = devices + .where((Device device) => device.ephemeral == true) + .toList(); + } + return devices; +} diff --git a/packages/flutter_tools/test/runner/flutter_command_test.dart b/packages/flutter_tools/test/runner/flutter_command_test.dart index a402b1df6fc..bc6582b012a 100644 --- a/packages/flutter_tools/test/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/runner/flutter_command_test.dart @@ -4,6 +4,8 @@ import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/base/time.dart'; +import 'package:flutter_tools/src/device.dart'; +import 'package:flutter_tools/src/project.dart'; import 'package:flutter_tools/src/usage.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/runner/flutter_command.dart'; @@ -289,6 +291,43 @@ void main() { FlutterVersion: () => betaVersion, }); }); + + group('Filter devices', () { + MockDevice ephemeral; + MockDevice nonEphemeralOne; + MockDevice nonEphemeralTwo; + MockDevice unsupported; + + setUp(() { + ephemeral = MockDevice(true); + nonEphemeralOne = MockDevice(false); + nonEphemeralTwo = MockDevice(false); + unsupported = MockDevice(true, false); + }); + + test('chooses ephemeral device', () { + final List filtered = filterDevices([ + ephemeral, + nonEphemeralOne, + nonEphemeralTwo, + unsupported, + ]); + + expect(filtered.single, ephemeral); + }); + + test('does not remove all non-ephemeral', () { + final List filtered = filterDevices([ + nonEphemeralOne, + nonEphemeralTwo, + ]); + + expect(filtered, [ + nonEphemeralOne, + nonEphemeralTwo, + ]); + }); + }); } @@ -309,3 +348,15 @@ class FakeCommand extends FlutterCommand { } class MockVersion extends Mock implements FlutterVersion {} + +class MockDevice extends Mock implements Device { + MockDevice(this.ephemeral, [this._isSupported = true]); + + @override + final bool ephemeral; + + bool _isSupported; + + @override + bool isSupportedForProject(FlutterProject flutterProject) => _isSupported; +}