Unverified Commit 61751835 authored by Jenn Magder's avatar Jenn Magder Committed by GitHub

Add --device-timeout flag to device-related commands (#64834)

parent 608f0606
......@@ -102,6 +102,7 @@ class AttachCommand extends FlutterCommand {
);
usesTrackWidgetCreation(verboseHelp: verboseHelp);
addDdsOptions(verboseHelp: verboseHelp);
usesDeviceTimeoutOption();
hotRunnerFactory ??= HotRunnerFactory();
}
......
......@@ -22,8 +22,9 @@ class DevicesCommand extends FlutterCommand {
'timeout',
abbr: 't',
defaultsTo: null,
help: 'Time in seconds to wait for devices to attach. Longer timeouts may be necessary for networked devices.'
help: '(deprecated) Use --device-timeout instead',
);
usesDeviceTimeoutOption();
}
@override
......@@ -32,20 +33,25 @@ class DevicesCommand extends FlutterCommand {
@override
final String description = 'List all connected devices.';
Duration get timeout {
if (argResults['timeout'] == null) {
return null;
}
if (_timeout == null) {
@override
Duration get deviceDiscoveryTimeout {
if (argResults['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 Duration(seconds: timeoutSeconds);
}
return super.deviceDiscoveryTimeout;
}
@override
Future<void> validateCommand() {
if (argResults['timeout'] != null) {
globals.printError('--timeout has been deprecated, use --${FlutterOptions.kDeviceTimeout} instead');
}
return _timeout;
return super.validateCommand();
}
Duration _timeout;
@override
Future<FlutterCommandResult> runCommand() async {
......@@ -56,7 +62,7 @@ class DevicesCommand extends FlutterCommand {
exitCode: 1);
}
final List<Device> devices = await globals.deviceManager.refreshAllConnectedDevices(timeout: timeout);
final List<Device> devices = await globals.deviceManager.refreshAllConnectedDevices(timeout: deviceDiscoveryTimeout);
if (boolArg('machine')) {
await printDevicesAsJson(devices);
......@@ -68,8 +74,8 @@ class DevicesCommand extends FlutterCommand {
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. ');
if (deviceDiscoveryTimeout == null) {
status.write('You may also try increasing the time to wait for connected devices with the --${FlutterOptions.kDeviceTimeout} flag. ');
}
status.write('Visit https://flutter.dev/setup/ for troubleshooting tips.');
......
......@@ -146,7 +146,7 @@ class DriveCommand extends RunCommandBase {
@override
Future<void> validateCommand() async {
if (userIdentifier != null) {
final Device device = await findTargetDevice();
final Device device = await findTargetDevice(timeout: deviceDiscoveryTimeout);
if (device is! AndroidDevice) {
throwToolExit('--${FlutterOptions.kDeviceUser} is only supported for Android');
}
......@@ -161,7 +161,7 @@ class DriveCommand extends RunCommandBase {
throwToolExit(null);
}
_device = await findTargetDevice();
_device = await findTargetDevice(timeout: deviceDiscoveryTimeout);
if (device == null) {
throwToolExit(null);
}
......@@ -388,9 +388,9 @@ $ex
}
}
Future<Device> findTargetDevice() async {
Future<Device> findTargetDevice({ @required Duration timeout }) async {
final DeviceManager deviceManager = globals.deviceManager;
final List<Device> devices = await deviceManager.findTargetDevices(FlutterProject.current());
final List<Device> devices = await deviceManager.findTargetDevices(FlutterProject.current(), timeout: timeout);
if (deviceManager.hasSpecifiedDeviceId) {
if (devices.isEmpty) {
......
......@@ -16,6 +16,7 @@ class InstallCommand extends FlutterCommand with DeviceBasedDevelopmentArtifacts
InstallCommand() {
requiresPubspecYaml();
usesDeviceUserOption();
usesDeviceTimeoutOption();
argParser.addFlag('uninstall-only',
negatable: true,
defaultsTo: false,
......
......@@ -18,6 +18,7 @@ class LogsCommand extends FlutterCommand {
abbr: 'c',
help: 'Clear log history before reading from logs.',
);
usesDeviceTimeoutOption();
}
@override
......
......@@ -75,6 +75,7 @@ abstract class RunCommandBase extends FlutterCommand with DeviceBasedDevelopment
usesTrackWidgetCreation(verboseHelp: verboseHelp);
addNullSafetyModeOptions(hide: !verboseHelp);
usesDeviceUserOption();
usesDeviceTimeoutOption();
}
bool get traceStartup => boolArg('trace-startup');
......
......@@ -51,6 +51,7 @@ class ScreenshotCommand extends FlutterCommand {
},
defaultsTo: _kDeviceType,
);
usesDeviceTimeoutOption();
}
@override
......
......@@ -209,7 +209,12 @@ abstract class DeviceManager {
/// * If the user did not specify a device id and there is more than one
/// device connected, then filter out unsupported devices and prioritize
/// ephemeral devices.
Future<List<Device>> findTargetDevices(FlutterProject flutterProject) async {
Future<List<Device>> findTargetDevices(FlutterProject flutterProject, { Duration timeout }) async {
if (timeout != null) {
// Reset the cache with the specified timeout.
await refreshAllConnectedDevices(timeout: timeout);
}
List<Device> devices = await getDevices();
// Always remove web and fuchsia devices from `--all`. This setting
......
......@@ -113,6 +113,7 @@ class FlutterOptions {
static const String kPerformanceMeasurementFile = 'performance-measurement-file';
static const String kNullSafety = 'sound-null-safety';
static const String kDeviceUser = 'device-user';
static const String kDeviceTimeout = 'device-timeout';
static const String kAnalyzeSize = 'analyze-size';
static const String kNullAssertions = 'null-assertions';
}
......@@ -404,6 +405,28 @@ abstract class FlutterCommand extends Command<void> {
valueHelp: '10');
}
void usesDeviceTimeoutOption() {
argParser.addOption(
FlutterOptions.kDeviceTimeout,
help: 'Time in seconds to wait for devices to attach. Longer timeouts may be necessary for networked devices.',
valueHelp: '10'
);
}
Duration get deviceDiscoveryTimeout {
if (_deviceDiscoveryTimeout == null
&& argResults.options.contains(FlutterOptions.kDeviceTimeout)
&& argResults.wasParsed(FlutterOptions.kDeviceTimeout)) {
final int timeoutSeconds = int.tryParse(stringArg(FlutterOptions.kDeviceTimeout));
if (timeoutSeconds == null) {
throwToolExit( 'Could not parse --${FlutterOptions.kDeviceTimeout} argument. It must be an integer.');
}
_deviceDiscoveryTimeout = Duration(seconds: timeoutSeconds);
}
return _deviceDiscoveryTimeout;
}
Duration _deviceDiscoveryTimeout;
void addBuildModeFlags({ bool defaultToRelease = true, bool verboseHelp = false, bool excludeDebug = false }) {
// A release build must be the default if a debug build is not possible.
assert(defaultToRelease || !excludeDebug);
......@@ -970,7 +993,7 @@ abstract class FlutterCommand extends Command<void> {
return null;
}
final DeviceManager deviceManager = globals.deviceManager;
List<Device> devices = await deviceManager.findTargetDevices(FlutterProject.current());
List<Device> devices = await deviceManager.findTargetDevices(FlutterProject.current(), timeout: deviceDiscoveryTimeout);
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId));
......
......@@ -342,6 +342,8 @@ void main() {
'-v',
'--device-user',
'10',
'--device-timeout',
'15',
]);
final VerificationResult verificationResult = verify(
mockHotRunnerFactory.build(
......
......@@ -281,7 +281,7 @@ void main() {
when(mockDevice.name).thenReturn('specified-device');
when(mockDevice.id).thenReturn('123');
final Device device = await findTargetDevice();
final Device device = await findTargetDevice(timeout: null);
expect(device.name, 'specified-device');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
......@@ -293,7 +293,7 @@ void main() {
Platform platform() => FakePlatform(operatingSystem: operatingSystem);
testUsingContext('returns null if no devices found', () async {
expect(await findTargetDevice(), isNull);
expect(await findTargetDevice(timeout: null), isNull);
}, overrides: <Type, Generator>{
FileSystem: () => fs,
ProcessManager: () => FakeProcessManager.any(),
......@@ -305,7 +305,7 @@ void main() {
when(mockDevice.name).thenReturn('mock-android-device');
testDeviceManager.addDevice(mockDevice);
final Device device = await findTargetDevice();
final Device device = await findTargetDevice(timeout: null);
expect(device.name, 'mock-android-device');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
......@@ -337,7 +337,7 @@ void main() {
testDeviceManager.addDevice(mockDevice);
testDeviceManager.addDevice(mockUnsupportedDevice);
final Device device = await findTargetDevice();
final Device device = await findTargetDevice(timeout: null);
expect(device.name, 'mock-android-device');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
......@@ -366,7 +366,7 @@ void main() {
when(mockDevice.isLocalEmulator)
.thenAnswer((Invocation invocation) => Future<bool>.value(true));
final Device device = await findTargetDevice();
final Device device = await findTargetDevice(timeout: null);
expect(device.name, 'mock-simulator');
}, overrides: <Type, Generator>{
FileSystem: () => fs,
......
......@@ -178,7 +178,7 @@ void main() {
when(mockDeviceManager.getDevices()).thenAnswer(
(Invocation invocation) => Future<List<Device>>.value(noDevices)
);
when(mockDeviceManager.findTargetDevices(any)).thenAnswer(
when(mockDeviceManager.findTargetDevices(any, timeout: anyNamed('timeout'))).thenAnswer(
(Invocation invocation) => Future<List<Device>>.value(noDevices)
);
......@@ -214,7 +214,7 @@ void main() {
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) async {
return <Device>[device];
});
when(mockDeviceManager.findTargetDevices(any)).thenAnswer((Invocation invocation) async {
when(mockDeviceManager.findTargetDevices(any, timeout: anyNamed('timeout'))).thenAnswer((Invocation invocation) async {
return <Device>[device];
});
when(mockDeviceManager.hasSpecifiedAllDevices).thenReturn(false);
......@@ -254,7 +254,7 @@ void main() {
]);
});
when(mockDeviceManager.findTargetDevices(any)).thenAnswer(
when(mockDeviceManager.findTargetDevices(any, timeout: anyNamed('timeout'))).thenAnswer(
(Invocation invocation) => Future<List<Device>>.value(<Device>[]),
);
......@@ -301,7 +301,8 @@ void main() {
);
// No devices are attached, we just want to verify update the cache
// BEFORE checking for devices
when(mockDeviceManager.findTargetDevices(any)).thenAnswer(
const Duration timeout = Duration(seconds: 10);
when(mockDeviceManager.findTargetDevices(any, timeout: timeout)).thenAnswer(
(Invocation invocation) => Future<List<Device>>.value(<Device>[])
);
......@@ -309,6 +310,8 @@ void main() {
await createTestCommandRunner(command).run(<String>[
'run',
'--no-pub',
'--device-timeout',
'10',
]);
fail('Exception expected');
} on ToolExit catch (e) {
......@@ -324,7 +327,7 @@ void main() {
// as part of gathering `requiredArtifacts`
mockDeviceManager.getDevices(),
// in validateCommand()
mockDeviceManager.findTargetDevices(any),
mockDeviceManager.findTargetDevices(any, timeout: anyNamed('timeout')),
]);
}, overrides: <Type, Generator>{
ApplicationPackageFactory: () => mockApplicationPackageFactory,
......@@ -365,7 +368,7 @@ void main() {
(Invocation invocation) => Future<List<Device>>.value(<Device>[mockDevice])
);
when(mockDeviceManager.findTargetDevices(any)).thenAnswer(
when(mockDeviceManager.findTargetDevices(any, timeout: anyNamed('timeout'))).thenAnswer(
(Invocation invocation) => Future<List<Device>>.value(<Device>[mockDevice])
);
......@@ -434,7 +437,7 @@ void main() {
when(mockDeviceManager.getDevices()).thenAnswer((Invocation invocation) {
return Future<List<Device>>.value(<Device>[fakeDevice]);
});
when(mockDeviceManager.findTargetDevices(any)).thenAnswer(
when(mockDeviceManager.findTargetDevices(any, timeout: anyNamed('timeout'))).thenAnswer(
(Invocation invocation) => Future<List<Device>>.value(<Device>[fakeDevice])
);
});
......
......@@ -58,8 +58,10 @@ void main() {
// 3. A device discoverer that succeeds.
final DeviceManager deviceManager = TestDeviceManager(
devices,
testLongPollingDeviceDiscovery: true,
testThrowingDeviceDiscovery: true,
deviceDiscoveryOverrides: <DeviceDiscovery>[
ThrowingPollingDeviceDiscovery(),
LongPollingDeviceDiscovery(),
],
);
Future<void> expectDevice(String id, List<Device> expected) async {
......@@ -89,7 +91,9 @@ void main() {
// 2. A device discoverer that succeeds.
final DeviceManager deviceManager = TestDeviceManager(
devices,
testThrowingDeviceDiscovery: true
deviceDiscoveryOverrides: <DeviceDiscovery>[
ThrowingPollingDeviceDiscovery(),
],
);
Future<void> expectDevice(String id, List<Device> expected) async {
......@@ -387,7 +391,62 @@ void main() {
Artifacts: () => Artifacts.test(),
Cache: () => cache,
});
testUsingContext('does not refresh device cache without a timeout', () async {
final List<Device> devices = <Device>[
ephemeralOne,
];
final MockDeviceDiscovery mockDeviceDiscovery = MockDeviceDiscovery();
when(mockDeviceDiscovery.supportsPlatform).thenReturn(true);
// when(mockDeviceDiscovery.discoverDevices(timeout: timeout)).thenAnswer((_) async => devices);
when(mockDeviceDiscovery.devices).thenAnswer((_) async => devices);
// when(mockDeviceDiscovery.discoverDevices(timeout: timeout)).thenAnswer((_) async => devices);
final DeviceManager deviceManager = TestDeviceManager(<Device>[], deviceDiscoveryOverrides: <DeviceDiscovery>[
mockDeviceDiscovery
]);
deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(
FlutterProject.current(),
);
expect(filtered.single, ephemeralOne);
verify(mockDeviceDiscovery.devices).called(1);
verifyNever(mockDeviceDiscovery.discoverDevices(timeout: anyNamed('timeout')));
}, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(),
Cache: () => cache,
});
testUsingContext('refreshes device cache with a timeout', () async {
final List<Device> devices = <Device>[
ephemeralOne,
];
const Duration timeout = Duration(seconds: 2);
final MockDeviceDiscovery mockDeviceDiscovery = MockDeviceDiscovery();
when(mockDeviceDiscovery.supportsPlatform).thenReturn(true);
when(mockDeviceDiscovery.discoverDevices(timeout: timeout)).thenAnswer((_) async => devices);
when(mockDeviceDiscovery.devices).thenAnswer((_) async => devices);
// when(mockDeviceDiscovery.discoverDevices(timeout: timeout)).thenAnswer((_) async => devices);
final DeviceManager deviceManager = TestDeviceManager(<Device>[], deviceDiscoveryOverrides: <DeviceDiscovery>[
mockDeviceDiscovery
]);
deviceManager.specifiedDeviceId = ephemeralOne.id;
final List<Device> filtered = await deviceManager.findTargetDevices(
FlutterProject.current(),
timeout: timeout,
);
expect(filtered.single, ephemeralOne);
verify(mockDeviceDiscovery.devices).called(1);
verify(mockDeviceDiscovery.discoverDevices(timeout: anyNamed('timeout'))).called(1);
}, overrides: <Type, Generator>{
Artifacts: () => Artifacts.test(),
Cache: () => cache,
});
});
group('ForwardedPort', () {
group('dispose()', () {
testUsingContext('does not throw exception if no process is present', () {
......@@ -427,16 +486,13 @@ void main() {
class TestDeviceManager extends DeviceManager {
TestDeviceManager(List<Device> allDevices, {
bool testLongPollingDeviceDiscovery = false,
bool testThrowingDeviceDiscovery = false,
List<DeviceDiscovery> deviceDiscoveryOverrides,
}) {
_fakeDeviceDiscoverer = FakePollingDeviceDiscovery();
_deviceDiscoverers = <DeviceDiscovery>[
if (testLongPollingDeviceDiscovery)
LongPollingDeviceDiscovery(),
if (testThrowingDeviceDiscovery)
ThrowingPollingDeviceDiscovery(),
_fakeDeviceDiscoverer,
if (deviceDiscoveryOverrides != null)
...deviceDiscoveryOverrides
];
resetDevices(allDevices);
}
......@@ -464,3 +520,4 @@ class MockProcess extends Mock implements Process {}
class MockTerminal extends Mock implements AnsiTerminal {}
class MockStdio extends Mock implements Stdio {}
class MockCache extends Mock implements Cache {}
class MockDeviceDiscovery extends Mock implements DeviceDiscovery {}
......@@ -248,7 +248,7 @@ class FakeDeviceManager implements DeviceManager {
}
@override
Future<List<Device>> findTargetDevices(FlutterProject flutterProject) async {
Future<List<Device>> findTargetDevices(FlutterProject flutterProject, { Duration timeout }) async {
return devices;
}
}
......
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