Skip to content

Commit a599c23

Browse files
authored
Disallow running on unsupported devices (#97338)
1 parent 4cca04d commit a599c23

File tree

7 files changed

+79
-14
lines changed

7 files changed

+79
-14
lines changed

packages/flutter_tools/lib/src/base/user_messages.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -247,8 +247,8 @@ class UserMessages {
247247
String get flutterNoDevelopmentDevice =>
248248
"Unable to locate a development device; please run 'flutter doctor' "
249249
'for information about installing additional components.';
250-
String flutterNoMatchingDevice(String deviceId) => 'No devices found with name or id '
251-
"matching '$deviceId'";
250+
String flutterNoMatchingDevice(String deviceId) => 'No supported devices found with name or id '
251+
"matching '$deviceId'.";
252252
String get flutterNoDevicesFound => 'No devices found';
253253
String get flutterNoSupportedDevices => 'No supported devices connected.';
254254
String flutterMissPlatformProjects(List<String> unsupportedDevicesType) =>

packages/flutter_tools/lib/src/device.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ abstract class DeviceManager {
254254
await refreshAllConnectedDevices(timeout: timeout);
255255
}
256256

257-
List<Device> devices = await getDevices();
257+
List<Device> devices = (await getDevices())
258+
.where((Device device) => device.isSupported()).toList();
258259

259260
// Always remove web and fuchsia devices from `--all`. This setting
260261
// currently requires devices to share a frontend_server and resident

packages/flutter_tools/lib/src/runner/flutter_command.dart

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1364,6 +1364,12 @@ abstract class FlutterCommand extends Command<void> {
13641364

13651365
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
13661366
globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
1367+
final List<Device> allDevices = await deviceManager.getAllConnectedDevices();
1368+
if (allDevices.isNotEmpty) {
1369+
globals.printStatus('');
1370+
globals.printStatus('The following devices were found:');
1371+
await Device.printDevices(allDevices, globals.logger);
1372+
}
13671373
return null;
13681374
} else if (devices.isEmpty) {
13691375
if (deviceManager.hasSpecifiedAllDevices) {

packages/flutter_tools/test/commands.shard/hermetic/run_test.dart

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,33 @@ void main() {
195195
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
196196
});
197197

198+
testUsingContext('exits and lists available devices when specified device not found', () async {
199+
final RunCommand command = RunCommand();
200+
final FakeDevice device = FakeDevice(isLocalEmulator: true);
201+
mockDeviceManager
202+
..devices = <Device>[device]
203+
..hasSpecifiedDeviceId = true;
204+
205+
await expectLater(
206+
() => createTestCommandRunner(command).run(<String>[
207+
'run',
208+
'-d',
209+
'invalid-device-id',
210+
'--no-pub',
211+
'--no-hot',
212+
]),
213+
throwsToolExit(),
214+
);
215+
expect(testLogger.statusText, contains("No supported devices found with name or id matching 'invalid-device-id'"));
216+
expect(testLogger.statusText, contains('The following devices were found:'));
217+
expect(testLogger.statusText, contains('FakeDevice (mobile) • fake_device • ios • (simulator)'));
218+
}, overrides: <Type, Generator>{
219+
DeviceManager: () => mockDeviceManager,
220+
FileSystem: () => fs,
221+
ProcessManager: () => FakeProcessManager.any(),
222+
Cache: () => Cache.test(processManager: FakeProcessManager.any()),
223+
});
224+
198225
testUsingContext('fails when targeted device is not Android with --device-user', () async {
199226
fs.file('pubspec.yaml').createSync();
200227
fs.file('.packages').writeAsStringSync('\n');
@@ -642,6 +669,9 @@ class FakeDeviceManager extends Fake implements DeviceManager {
642669
Future<List<Device>> findTargetDevices(FlutterProject flutterProject, {Duration timeout}) async {
643670
return targetDevices;
644671
}
672+
673+
@override
674+
Future<List<Device>> getAllConnectedDevices() async => devices;
645675
}
646676

647677
class FakeAndroidSdk extends Fake implements AndroidSdk {

packages/flutter_tools/test/general.shard/device_test.dart

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ void main() {
179179
final FakeDevice nonEphemeralOne = FakeDevice('nonEphemeralOne', 'nonEphemeralOne', ephemeral: false);
180180
final FakeDevice nonEphemeralTwo = FakeDevice('nonEphemeralTwo', 'nonEphemeralTwo', ephemeral: false);
181181
final FakeDevice unsupported = FakeDevice('unsupported', 'unsupported', isSupported: false);
182+
final FakeDevice unsupportedForProject = FakeDevice('unsupportedForProject', 'unsupportedForProject', isSupportedForProject: false);
182183
final FakeDevice webDevice = FakeDevice('webby', 'webby')
183184
..targetPlatform = Future<TargetPlatform>.value(TargetPlatform.web_javascript);
184185
final FakeDevice fuchsiaDevice = FakeDevice('fuchsiay', 'fuchsiay')
@@ -190,6 +191,7 @@ void main() {
190191
nonEphemeralOne,
191192
nonEphemeralTwo,
192193
unsupported,
194+
unsupportedForProject,
193195
];
194196

195197
final DeviceManager deviceManager = TestDeviceManager(
@@ -327,9 +329,29 @@ void main() {
327329
);
328330
});
329331

330-
testWithoutContext('Removes a single unsupported device', () async {
332+
testWithoutContext('Unsupported devices listed in all connected devices', () async {
331333
final List<Device> devices = <Device>[
332334
unsupported,
335+
unsupportedForProject,
336+
];
337+
338+
final DeviceManager deviceManager = TestDeviceManager(
339+
devices,
340+
logger: BufferLogger.test(),
341+
terminal: Terminal.test(),
342+
);
343+
final List<Device> filtered = await deviceManager.getAllConnectedDevices();
344+
345+
expect(filtered, <Device>[
346+
unsupported,
347+
unsupportedForProject,
348+
]);
349+
});
350+
351+
testWithoutContext('Removes a unsupported devices', () async {
352+
final List<Device> devices = <Device>[
353+
unsupported,
354+
unsupportedForProject,
333355
];
334356

335357
final DeviceManager deviceManager = TestDeviceManager(
@@ -342,9 +364,10 @@ void main() {
342364
expect(filtered, <Device>[]);
343365
});
344366

345-
testWithoutContext('Does not remove an unsupported device if FlutterProject is null', () async {
367+
testWithoutContext('Retains devices unsupported by the project if FlutterProject is null', () async {
346368
final List<Device> devices = <Device>[
347369
unsupported,
370+
unsupportedForProject,
348371
];
349372

350373
final DeviceManager deviceManager = TestDeviceManager(
@@ -354,7 +377,7 @@ void main() {
354377
);
355378
final List<Device> filtered = await deviceManager.findTargetDevices(null);
356379

357-
expect(filtered, <Device>[unsupported]);
380+
expect(filtered, <Device>[unsupportedForProject]);
358381
});
359382

360383
testWithoutContext('Removes web and fuchsia from --all', () async {
@@ -374,11 +397,12 @@ void main() {
374397
expect(filtered, <Device>[]);
375398
});
376399

377-
testWithoutContext('Removes unsupported devices from --all', () async {
400+
testWithoutContext('Removes devices unsupported by the project from --all', () async {
378401
final List<Device> devices = <Device>[
379402
nonEphemeralOne,
380403
nonEphemeralTwo,
381404
unsupported,
405+
unsupportedForProject,
382406
];
383407
final DeviceManager deviceManager = TestDeviceManager(
384408
devices,
@@ -398,18 +422,19 @@ void main() {
398422
testWithoutContext('uses DeviceManager.isDeviceSupportedForProject instead of device.isSupportedForProject', () async {
399423
final List<Device> devices = <Device>[
400424
unsupported,
425+
unsupportedForProject,
401426
];
402427
final TestDeviceManager deviceManager = TestDeviceManager(
403428
devices,
404429
logger: BufferLogger.test(),
405430
terminal: Terminal.test(),
406431
);
407-
deviceManager.isAlwaysSupportedOverride = true;
432+
deviceManager.isAlwaysSupportedForProjectOverride = true;
408433

409434
final List<Device> filtered = await deviceManager.findTargetDevices(FakeFlutterProject());
410435

411436
expect(filtered, <Device>[
412-
unsupported,
437+
unsupportedForProject,
413438
]);
414439
});
415440

@@ -513,12 +538,12 @@ class TestDeviceManager extends DeviceManager {
513538
_fakeDeviceDiscoverer.setDevices(allDevices);
514539
}
515540

516-
bool? isAlwaysSupportedOverride;
541+
bool? isAlwaysSupportedForProjectOverride;
517542

518543
@override
519544
bool isDeviceSupportedForProject(Device device, FlutterProject? flutterProject) {
520-
if (isAlwaysSupportedOverride != null) {
521-
return isAlwaysSupportedOverride!;
545+
if (isAlwaysSupportedForProjectOverride != null) {
546+
return isAlwaysSupportedForProjectOverride!;
522547
}
523548
return super.isDeviceSupportedForProject(device, flutterProject);
524549
}

packages/flutter_tools/test/integration.shard/flutter_run_test.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ void main() {
4545
expect(proc.stdout, isNot(contains('flutter has exited unexpectedly')));
4646
expect(proc.stderr, isNot(contains('flutter has exited unexpectedly')));
4747
if (!proc.stderr.toString().contains('Unable to locate a development')
48-
&& !proc.stdout.toString().contains('No devices found with name or id matching')) {
48+
&& !proc.stdout.toString().contains('No supported devices found with name or id matching')) {
4949
fail("'flutter run -d invalid-device-id' did not produce the expected error");
5050
}
5151
});

packages/flutter_tools/test/src/fake_devices.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,11 @@ class FakeDevice extends Device {
6161
FakeDevice(this.name, String id, {
6262
bool ephemeral = true,
6363
bool isSupported = true,
64+
bool isSupportedForProject = true,
6465
PlatformType type = PlatformType.web,
6566
LaunchResult? launchResult,
6667
}) : _isSupported = isSupported,
68+
_isSupportedForProject = isSupportedForProject,
6769
_launchResult = launchResult ?? LaunchResult.succeeded(),
6870
super(
6971
id,
@@ -73,6 +75,7 @@ class FakeDevice extends Device {
7375
);
7476

7577
final bool _isSupported;
78+
final bool _isSupportedForProject;
7679
final LaunchResult _launchResult;
7780

7881
@override
@@ -110,7 +113,7 @@ class FakeDevice extends Device {
110113
void noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
111114

112115
@override
113-
bool isSupportedForProject(FlutterProject flutterProject) => _isSupported;
116+
bool isSupportedForProject(FlutterProject flutterProject) => _isSupportedForProject;
114117

115118
@override
116119
bool isSupported() => _isSupported;

0 commit comments

Comments
 (0)