diff --git a/packages/flutter_tools/lib/src/commands/custom_devices.dart b/packages/flutter_tools/lib/src/commands/custom_devices.dart index 4c144e1a35..fbc52fe615 100644 --- a/packages/flutter_tools/lib/src/commands/custom_devices.dart +++ b/packages/flutter_tools/lib/src/commands/custom_devices.dart @@ -736,11 +736,12 @@ class CustomDevicesAddCommand extends CustomDevicesCommandBase { '-o', 'ExitOnForwardFailure=yes', if (ipv6) '-6', '-L', '$formattedLoopbackIp:\${hostPort}:$formattedLoopbackIp:\${devicePort}', - sshTarget + sshTarget, + "echo 'Port forwarding success'; read" ] : null, forwardPortSuccessRegex: usePortForwarding - ? RegExp('Linux') + ? RegExp('Port forwarding success') : null, screenshotCommand: screenshotCommand.isNotEmpty diff --git a/packages/flutter_tools/lib/src/custom_devices/custom_device_config.dart b/packages/flutter_tools/lib/src/custom_devices/custom_device_config.dart index 1fda61d791..ea69950651 100644 --- a/packages/flutter_tools/lib/src/custom_devices/custom_device_config.dart +++ b/packages/flutter_tools/lib/src/custom_devices/custom_device_config.dart @@ -264,8 +264,9 @@ class CustomDeviceConfig { '-o', 'ExitOnForwardFailure=yes', '-L', r'127.0.0.1:${hostPort}:127.0.0.1:${devicePort}', 'pi@raspberrypi', + "echo 'Port forwarding success'; read", ], - forwardPortSuccessRegex: RegExp('Linux'), + forwardPortSuccessRegex: RegExp('Port forwarding success'), screenshotCommand: const [ 'ssh', '-o', 'BatchMode=yes', @@ -472,7 +473,7 @@ class CustomDeviceConfig { _kEnabled: enabled, _kPingCommand: pingCommand, _kPingSuccessRegex: pingSuccessRegex?.pattern, - _kPostBuildCommand: postBuildCommand, + _kPostBuildCommand: (postBuildCommand?.length ?? 0) > 0 ? postBuildCommand : null, _kInstallCommand: installCommand, _kUninstallCommand: uninstallCommand, _kRunDebugCommand: runDebugCommand, diff --git a/packages/flutter_tools/test/commands.shard/hermetic/custom_devices_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/custom_devices_test.dart index 38a4eea7ea..3799d9a944 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/custom_devices_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/custom_devices_test.dart @@ -82,9 +82,10 @@ const String defaultConfigLinux1 = r''' "ExitOnForwardFailure=yes", "-L", "127.0.0.1:${hostPort}:127.0.0.1:${devicePort}", - "pi@raspberrypi" + "pi@raspberrypi", + "echo 'Port forwarding success'; read" ], - "forwardPortSuccessRegex": "Linux", + "forwardPortSuccessRegex": "Port forwarding success", "screenshot": [ "ssh", "-o", @@ -145,9 +146,10 @@ const String defaultConfigLinux2 = r''' "ExitOnForwardFailure=yes", "-L", "127.0.0.1:${hostPort}:127.0.0.1:${devicePort}", - "pi@raspberrypi" + "pi@raspberrypi", + "echo 'Port forwarding success'; read" ], - "forwardPortSuccessRegex": "Linux", + "forwardPortSuccessRegex": "Port forwarding success", "screenshot": [ "ssh", "-o", @@ -503,6 +505,444 @@ void main() { ); // test add command + testUsingContext( + 'custom-devices add command correctly adds ssh device config on linux', + () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + + final CommandRunner runner = createCustomDevicesCommandRunner( + terminal: (Platform platform) => createFakeTerminalForAddingSshDevice( + platform: platform, + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: 'y', + hostname: 'testhostname', + username: 'testuser', + runDebug: 'testrundebug', + usePortForwarding: 'y', + screenshot: 'testscreenshot', + apply: 'y' + ), + fileSystem: fs, + processManager: FakeProcessManager.any(), + featureEnabled: true + ); + + await expectLater( + runner.run(const ['custom-devices', 'add', '--no-check']), + completes + ); + + final CustomDevicesConfig config = CustomDevicesConfig.test( + fileSystem: fs, + directory: fs.directory('/'), + logger: BufferLogger.test() + ); + + expect( + config.devices, + contains( + CustomDeviceConfig( + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: true, + pingCommand: const [ + 'ping', + '-c', '1', + '-w', '1', + 'testhostname' + ], + postBuildCommand: null, // ignore: avoid_redundant_argument_values + installCommand: const [ + 'scp', + '-r', + '-o', 'BatchMode=yes', + r'${localPath}', + r'testuser@testhostname:/tmp/${appName}' + ], + uninstallCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + r'rm -rf "/tmp/${appName}"' + ], + runDebugCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testrundebug' + ], + forwardPortCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-o', 'ExitOnForwardFailure=yes', + '-L', r'127.0.0.1:${hostPort}:127.0.0.1:${devicePort}', + 'testuser@testhostname', + "echo 'Port forwarding success'; read" + ], + forwardPortSuccessRegex: RegExp('Port forwarding success'), + screenshotCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testscreenshot' + ], + ) + ) + ); + } + ); + + testUsingContext( + 'custom-devices add command correctly adds ipv4 ssh device config', + () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + + final CommandRunner runner = createCustomDevicesCommandRunner( + terminal: (Platform platform) => createFakeTerminalForAddingSshDevice( + platform: platform, + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: 'y', + hostname: '192.168.178.1', + username: 'testuser', + runDebug: 'testrundebug', + usePortForwarding: 'y', + screenshot: 'testscreenshot', + apply: 'y', + ), + processManager: FakeProcessManager.any(), + fileSystem: fs, + featureEnabled: true + ); + + await expectLater( + runner.run(const ['custom-devices', 'add', '--no-check']), + completes + ); + + final CustomDevicesConfig config = CustomDevicesConfig.test( + fileSystem: fs, + directory: fs.directory('/'), + logger: BufferLogger.test() + ); + + expect( + config.devices, + contains( + CustomDeviceConfig( + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: true, + pingCommand: const [ + 'ping', + '-c', '1', + '-w', '1', + '192.168.178.1' + ], + postBuildCommand: null, // ignore: avoid_redundant_argument_values + installCommand: const [ + 'scp', + '-r', + '-o', 'BatchMode=yes', + r'${localPath}', + r'testuser@192.168.178.1:/tmp/${appName}' + ], + uninstallCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@192.168.178.1', + r'rm -rf "/tmp/${appName}"' + ], + runDebugCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@192.168.178.1', + 'testrundebug' + ], + forwardPortCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-o', 'ExitOnForwardFailure=yes', + '-L', r'127.0.0.1:${hostPort}:127.0.0.1:${devicePort}', + 'testuser@192.168.178.1', + "echo 'Port forwarding success'; read" + ], + forwardPortSuccessRegex: RegExp('Port forwarding success'), + screenshotCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@192.168.178.1', + 'testscreenshot' + ] + ) + ) + ); + } + ); + + testUsingContext( + 'custom-devices add command correctly adds ipv6 ssh device config', + () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + + final CommandRunner runner = createCustomDevicesCommandRunner( + terminal: (Platform platform) => createFakeTerminalForAddingSshDevice( + platform: platform, + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: 'y', + hostname: '::1', + username: 'testuser', + runDebug: 'testrundebug', + usePortForwarding: 'y', + screenshot: 'testscreenshot', + apply: 'y', + ), + fileSystem: fs, + featureEnabled: true + ); + + await expectLater( + runner.run(const ['custom-devices', 'add', '--no-check']), + completes + ); + + final CustomDevicesConfig config = CustomDevicesConfig.test( + fileSystem: fs, + directory: fs.directory('/'), + logger: BufferLogger.test() + ); + + expect( + config.devices, + contains( + CustomDeviceConfig( + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: true, + pingCommand: const [ + 'ping', + '-6', + '-c', '1', + '-w', '1', + '::1' + ], + postBuildCommand: null, // ignore: avoid_redundant_argument_values + installCommand: const [ + 'scp', + '-r', + '-o', 'BatchMode=yes', + '-6', + r'${localPath}', + r'testuser@[::1]:/tmp/${appName}' + ], + uninstallCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-6', + 'testuser@[::1]', + r'rm -rf "/tmp/${appName}"' + ], + runDebugCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-6', + 'testuser@[::1]', + 'testrundebug' + ], + forwardPortCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-o', 'ExitOnForwardFailure=yes', + '-6', + '-L', r'[::1]:${hostPort}:[::1]:${devicePort}', + 'testuser@[::1]', + "echo 'Port forwarding success'; read" + ], + forwardPortSuccessRegex: RegExp('Port forwarding success'), + screenshotCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-6', + 'testuser@[::1]', + 'testscreenshot' + ] + ) + ) + ); + } + ); + + testUsingContext( + 'custom-devices add command correctly adds non-forwarding ssh device config', + () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + + final CommandRunner runner = createCustomDevicesCommandRunner( + terminal: (Platform platform) => createFakeTerminalForAddingSshDevice( + platform: platform, + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: 'y', + hostname: 'testhostname', + username: 'testuser', + runDebug: 'testrundebug', + usePortForwarding: 'n', + screenshot: 'testscreenshot', + apply: 'y', + ), + fileSystem: fs, + featureEnabled: true + ); + + await expectLater( + runner.run(const ['custom-devices', 'add', '--no-check']), + completes + ); + + final CustomDevicesConfig config = CustomDevicesConfig.test( + fileSystem: fs, + directory: fs.directory('/'), + logger: BufferLogger.test() + ); + + expect( + config.devices, + contains( + const CustomDeviceConfig( + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: true, + pingCommand: [ + 'ping', + '-c', '1', + '-w', '1', + 'testhostname' + ], + postBuildCommand: null, // ignore: avoid_redundant_argument_values + installCommand: [ + 'scp', + '-r', + '-o', 'BatchMode=yes', + r'${localPath}', + r'testuser@testhostname:/tmp/${appName}' + ], + uninstallCommand: [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + r'rm -rf "/tmp/${appName}"' + ], + runDebugCommand: [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testrundebug' + ], + screenshotCommand: [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testscreenshot' + ] + ) + ) + ); + } + ); + + testUsingContext( + 'custom-devices add command correctly adds non-screenshotting ssh device config', + () async { + final MemoryFileSystem fs = MemoryFileSystem.test(); + + final CommandRunner runner = createCustomDevicesCommandRunner( + terminal: (Platform platform) => createFakeTerminalForAddingSshDevice( + platform: platform, + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: 'y', + hostname: 'testhostname', + username: 'testuser', + runDebug: 'testrundebug', + usePortForwarding: 'y', + screenshot: '', + apply: 'y', + ), + fileSystem: fs, + featureEnabled: true + ); + + await expectLater( + runner.run(const ['custom-devices', 'add', '--no-check']), + completes + ); + + final CustomDevicesConfig config = CustomDevicesConfig.test( + fileSystem: fs, + directory: fs.directory('/'), + logger: BufferLogger.test() + ); + + expect( + config.devices, + contains( + CustomDeviceConfig( + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: true, + pingCommand: const [ + 'ping', + '-c', '1', + '-w', '1', + 'testhostname' + ], + postBuildCommand: null, // ignore: avoid_redundant_argument_values + installCommand: const [ + 'scp', + '-r', + '-o', 'BatchMode=yes', + r'${localPath}', + r'testuser@testhostname:/tmp/${appName}' + ], + uninstallCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + r'rm -rf "/tmp/${appName}"' + ], + runDebugCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testrundebug' + ], + forwardPortCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-o', 'ExitOnForwardFailure=yes', + '-L', r'127.0.0.1:${hostPort}:127.0.0.1:${devicePort}', + 'testuser@testhostname', + "echo 'Port forwarding success'; read" + ], + forwardPortSuccessRegex: RegExp('Port forwarding success'), + ) + ) + ); + } + ); + testUsingContext( 'custom-devices delete command deletes device and creates backup', () async { @@ -737,4 +1177,101 @@ void main() { } ); }); + + group('windows', () { + setUp(() { + Cache.flutterRoot = windowsFlutterRoot; + }); + + testUsingContext( + 'custom-devices add command correctly adds ssh device config on windows', + () async { + final MemoryFileSystem fs = MemoryFileSystem.test(style: FileSystemStyle.windows); + + final CommandRunner runner = createCustomDevicesCommandRunner( + terminal: (Platform platform) => createFakeTerminalForAddingSshDevice( + platform: platform, + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: 'y', + hostname: 'testhostname', + username: 'testuser', + runDebug: 'testrundebug', + usePortForwarding: 'y', + screenshot: 'testscreenshot', + apply: 'y', + ), + fileSystem: fs, + platform: windowsPlatform, + featureEnabled: true + ); + + await expectLater( + runner.run(const ['custom-devices', 'add', '--no-check']), + completes + ); + + final CustomDevicesConfig config = CustomDevicesConfig.test( + fileSystem: fs, + directory: fs.directory('/'), + logger: BufferLogger.test() + ); + + expect( + config.devices, + contains( + CustomDeviceConfig( + id: 'testid', + label: 'testlabel', + sdkNameAndVersion: 'testsdknameandversion', + enabled: true, + pingCommand: const [ + 'ping', + '-n', '1', + '-w', '500', + 'testhostname' + ], + pingSuccessRegex: RegExp(r'[<=]\d+ms'), + postBuildCommand: null, // ignore: avoid_redundant_argument_values + installCommand: const [ + 'scp', + '-r', + '-o', 'BatchMode=yes', + r'${localPath}', + r'testuser@testhostname:/tmp/${appName}' + ], + uninstallCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + r'rm -rf "/tmp/${appName}"' + ], + runDebugCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testrundebug' + ], + forwardPortCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + '-o', 'ExitOnForwardFailure=yes', + '-L', r'127.0.0.1:${hostPort}:127.0.0.1:${devicePort}', + 'testuser@testhostname', + "echo 'Port forwarding success'; read" + ], + forwardPortSuccessRegex: RegExp('Port forwarding success'), + screenshotCommand: const [ + 'ssh', + '-o', 'BatchMode=yes', + 'testuser@testhostname', + 'testscreenshot' + ] + ) + ) + ); + }, + ); + }); }