Disallow running on unsupported devices (#97338)
diff --git a/packages/flutter_tools/lib/src/base/user_messages.dart b/packages/flutter_tools/lib/src/base/user_messages.dart
index f9c952c..362f4cc 100644
--- a/packages/flutter_tools/lib/src/base/user_messages.dart
+++ b/packages/flutter_tools/lib/src/base/user_messages.dart
@@ -247,8 +247,8 @@
String get flutterNoDevelopmentDevice =>
"Unable to locate a development device; please run 'flutter doctor' "
'for information about installing additional components.';
- String flutterNoMatchingDevice(String deviceId) => 'No devices found with name or id '
- "matching '$deviceId'";
+ String flutterNoMatchingDevice(String deviceId) => 'No supported devices found with name or id '
+ "matching '$deviceId'.";
String get flutterNoDevicesFound => 'No devices found';
String get flutterNoSupportedDevices => 'No supported devices connected.';
String flutterMissPlatformProjects(List<String> unsupportedDevicesType) =>
diff --git a/packages/flutter_tools/lib/src/device.dart b/packages/flutter_tools/lib/src/device.dart
index 9dd0eb5..3136079 100644
--- a/packages/flutter_tools/lib/src/device.dart
+++ b/packages/flutter_tools/lib/src/device.dart
@@ -254,7 +254,8 @@
await refreshAllConnectedDevices(timeout: timeout);
}
- List<Device> devices = await getDevices();
+ List<Device> devices = (await getDevices())
+ .where((Device device) => device.isSupported()).toList();
// Always remove web and fuchsia devices from `--all`. This setting
// currently requires devices to share a frontend_server and resident
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 267f7c3..87844dc 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -1364,6 +1364,12 @@
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
+ final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
+ if (allDevices.isNotEmpty) {
+ globals.printStatus('');
+ globals.printStatus('The following devices were found:');
+ await Device.printDevices(allDevices, globals.logger);
+ }
return null;
} else if (devices.isEmpty) {
if (deviceManager.hasSpecifiedAllDevices) {
diff --git a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
index e282859..40455d6 100644
--- a/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
+++ b/packages/flutter_tools/test/commands.shard/hermetic/run_test.dart
@@ -195,6 +195,33 @@
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
});
+ testUsingContext('exits and lists available devices when specified device not found', () async {
+ final RunCommand command = RunCommand();
+ final FakeDevice device = FakeDevice(isLocalEmulator: true);
+ mockDeviceManager
+ ..devices = <Device>[device]
+ ..hasSpecifiedDeviceId = true;
+
+ await expectLater(
+ () => createTestCommandRunner(command).run(<String>[
+ 'run',
+ '-d',
+ 'invalid-device-id',
+ '--no-pub',
+ '--no-hot',
+ ]),
+ throwsToolExit(),
+ );
+ expect(testLogger.statusText, contains("No supported devices found with name or id matching 'invalid-device-id'"));
+ expect(testLogger.statusText, contains('The following devices were found:'));
+ expect(testLogger.statusText, contains('FakeDevice (mobile) • fake_device • ios • (simulator)'));
+ }, overrides: <Type, Generator>{
+ DeviceManager: () => mockDeviceManager,
+ FileSystem: () => fs,
+ ProcessManager: () => FakeProcessManager.any(),
+ Cache: () => Cache.test(processManager: FakeProcessManager.any()),
+ });
+
testUsingContext('fails when targeted device is not Android with --device-user', () async {
fs.file('pubspec.yaml').createSync();
fs.file('.packages').writeAsStringSync('\n');
@@ -642,6 +669,9 @@
Future<List<Device>> findTargetDevices(FlutterProject flutterProject, {Duration timeout}) async {
return targetDevices;
}
+
+ @override
+ Future<List<Device>> getAllConnectedDevices() async => devices;
}
class FakeAndroidSdk extends Fake implements AndroidSdk {
diff --git a/packages/flutter_tools/test/general.shard/device_test.dart b/packages/flutter_tools/test/general.shard/device_test.dart
index 2bd6117..6349645 100644
--- a/packages/flutter_tools/test/general.shard/device_test.dart
+++ b/packages/flutter_tools/test/general.shard/device_test.dart
@@ -179,6 +179,7 @@
final FakeDevice nonEphemeralOne = FakeDevice('nonEphemeralOne', 'nonEphemeralOne', ephemeral: false);
final FakeDevice nonEphemeralTwo = FakeDevice('nonEphemeralTwo', 'nonEphemeralTwo', ephemeral: false);
final FakeDevice unsupported = FakeDevice('unsupported', 'unsupported', isSupported: false);
+ final FakeDevice unsupportedForProject = FakeDevice('unsupportedForProject', 'unsupportedForProject', isSupportedForProject: false);
final FakeDevice webDevice = FakeDevice('webby', 'webby')
..targetPlatform = Future<TargetPlatform>.value(TargetPlatform.web_javascript);
final FakeDevice fuchsiaDevice = FakeDevice('fuchsiay', 'fuchsiay')
@@ -190,6 +191,7 @@
nonEphemeralOne,
nonEphemeralTwo,
unsupported,
+ unsupportedForProject,
];
final DeviceManager deviceManager = TestDeviceManager(
@@ -327,9 +329,29 @@
);
});
- testWithoutContext('Removes a single unsupported device', () async {
+ testWithoutContext('Unsupported devices listed in all connected devices', () async {
final List<Device> devices = <Device>[
unsupported,
+ unsupportedForProject,
+ ];
+
+ final DeviceManager deviceManager = TestDeviceManager(
+ devices,
+ logger: BufferLogger.test(),
+ terminal: Terminal.test(),
+ );
+ final List<Device> filtered = await deviceManager.getAllConnectedDevices();
+
+ expect(filtered, <Device>[
+ unsupported,
+ unsupportedForProject,
+ ]);
+ });
+
+ testWithoutContext('Removes a unsupported devices', () async {
+ final List<Device> devices = <Device>[
+ unsupported,
+ unsupportedForProject,
];
final DeviceManager deviceManager = TestDeviceManager(
@@ -342,9 +364,10 @@
expect(filtered, <Device>[]);
});
- testWithoutContext('Does not remove an unsupported device if FlutterProject is null', () async {
+ testWithoutContext('Retains devices unsupported by the project if FlutterProject is null', () async {
final List<Device> devices = <Device>[
unsupported,
+ unsupportedForProject,
];
final DeviceManager deviceManager = TestDeviceManager(
@@ -354,7 +377,7 @@
);
final List<Device> filtered = await deviceManager.findTargetDevices(null);
- expect(filtered, <Device>[unsupported]);
+ expect(filtered, <Device>[unsupportedForProject]);
});
testWithoutContext('Removes web and fuchsia from --all', () async {
@@ -374,11 +397,12 @@
expect(filtered, <Device>[]);
});
- testWithoutContext('Removes unsupported devices from --all', () async {
+ testWithoutContext('Removes devices unsupported by the project from --all', () async {
final List<Device> devices = <Device>[
nonEphemeralOne,
nonEphemeralTwo,
unsupported,
+ unsupportedForProject,
];
final DeviceManager deviceManager = TestDeviceManager(
devices,
@@ -398,18 +422,19 @@
testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
final List<Device> devices = <Device>[
unsupported,
+ unsupportedForProject,
];
final TestDeviceManager deviceManager = TestDeviceManager(
devices,
logger: BufferLogger.test(),
terminal: Terminal.test(),
);
- deviceManager.isAlwaysSupportedOverride = true;
+ deviceManager.isAlwaysSupportedForProjectOverride = true;
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
expect(filtered, <Device>[
- unsupported,
+ unsupportedForProject,
]);
});
@@ -513,12 +538,12 @@
_fakeDeviceDiscoverer.setDevices(allDevices);
}
- bool? isAlwaysSupportedOverride;
+ bool? isAlwaysSupportedForProjectOverride;
@override
bool isDeviceSupportedForProject(Device device, FlutterProject? flutterProject) {
- if (isAlwaysSupportedOverride != null) {
- return isAlwaysSupportedOverride!;
+ if (isAlwaysSupportedForProjectOverride != null) {
+ return isAlwaysSupportedForProjectOverride!;
}
return super.isDeviceSupportedForProject(device, flutterProject);
}
diff --git a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
index 3beeef3..9d1792f 100644
--- a/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
+++ b/packages/flutter_tools/test/integration.shard/flutter_run_test.dart
@@ -45,7 +45,7 @@
expect(proc.stdout, isNot(contains('flutter has exited unexpectedly')));
expect(proc.stderr, isNot(contains('flutter has exited unexpectedly')));
if (!proc.stderr.toString().contains('Unable to locate a development')
- && !proc.stdout.toString().contains('No devices found with name or id matching')) {
+ && !proc.stdout.toString().contains('No supported devices found with name or id matching')) {
fail("'flutter run -d invalid-device-id' did not produce the expected error");
}
});
diff --git a/packages/flutter_tools/test/src/fake_devices.dart b/packages/flutter_tools/test/src/fake_devices.dart
index 7202808..ffa74fa 100644
--- a/packages/flutter_tools/test/src/fake_devices.dart
+++ b/packages/flutter_tools/test/src/fake_devices.dart
@@ -61,9 +61,11 @@
FakeDevice(this.name, String id, {
bool ephemeral = true,
bool isSupported = true,
+ bool isSupportedForProject = true,
PlatformType type = PlatformType.web,
LaunchResult? launchResult,
}) : _isSupported = isSupported,
+ _isSupportedForProject = isSupportedForProject,
_launchResult = launchResult ?? LaunchResult.succeeded(),
super(
id,
@@ -73,6 +75,7 @@
);
final bool _isSupported;
+ final bool _isSupportedForProject;
final LaunchResult _launchResult;
@override
@@ -110,7 +113,7 @@
void noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
@override
- bool isSupportedForProject(FlutterProject flutterProject) => _isSupported;
+ bool isSupportedForProject(FlutterProject flutterProject) => _isSupportedForProject;
@override
bool isSupported() => _isSupported;