mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
1271 lines
38 KiB
Dart
1271 lines
38 KiB
Dart
// Copyright 2014 The Flutter Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// @dart = 2.8
|
|
|
|
import 'dart:async';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:args/args.dart';
|
|
import 'package:args/command_runner.dart';
|
|
import 'package:file/memory.dart';
|
|
import 'package:file_testing/file_testing.dart';
|
|
import 'package:flutter_tools/src/base/context.dart';
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/logger.dart';
|
|
import 'package:flutter_tools/src/base/os.dart';
|
|
import 'package:flutter_tools/src/base/platform.dart';
|
|
import 'package:flutter_tools/src/base/terminal.dart';
|
|
import 'package:flutter_tools/src/base/user_messages.dart';
|
|
import 'package:flutter_tools/src/cache.dart';
|
|
import 'package:flutter_tools/src/commands/custom_devices.dart';
|
|
import 'package:flutter_tools/src/custom_devices/custom_device_config.dart';
|
|
import 'package:flutter_tools/src/custom_devices/custom_devices_config.dart';
|
|
import 'package:flutter_tools/src/runner/flutter_command_runner.dart';
|
|
import 'package:meta/meta.dart';
|
|
|
|
import '../../src/common.dart';
|
|
import '../../src/context.dart';
|
|
import '../../src/fakes.dart';
|
|
|
|
const String linuxFlutterRoot = '/flutter';
|
|
const String windowsFlutterRoot = r'C:\flutter';
|
|
|
|
const String defaultConfigLinux1 = r'''
|
|
{
|
|
"$schema": "file:///flutter/packages/flutter_tools/static/custom-devices.schema.json",
|
|
"custom-devices": [
|
|
{
|
|
"id": "pi",
|
|
"label": "Raspberry Pi",
|
|
"sdkNameAndVersion": "Raspberry Pi 4 Model B+",
|
|
"platform": "linux-arm64",
|
|
"enabled": false,
|
|
"ping": [
|
|
"ping",
|
|
"-w",
|
|
"1",
|
|
"-c",
|
|
"1",
|
|
"raspberrypi"
|
|
],
|
|
"pingSuccessRegex": null,
|
|
"postBuild": null,
|
|
"install": [
|
|
"scp",
|
|
"-r",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"${localPath}",
|
|
"pi@raspberrypi:/tmp/${appName}"
|
|
],
|
|
"uninstall": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"pi@raspberrypi",
|
|
"rm -rf \"/tmp/${appName}\""
|
|
],
|
|
"runDebug": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"pi@raspberrypi",
|
|
"flutter-pi \"/tmp/${appName}\""
|
|
],
|
|
"forwardPort": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"-o",
|
|
"ExitOnForwardFailure=yes",
|
|
"-L",
|
|
"127.0.0.1:${hostPort}:127.0.0.1:${devicePort}",
|
|
"pi@raspberrypi",
|
|
"echo 'Port forwarding success'; read"
|
|
],
|
|
"forwardPortSuccessRegex": "Port forwarding success",
|
|
"screenshot": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"pi@raspberrypi",
|
|
"fbgrab /tmp/screenshot.png && cat /tmp/screenshot.png | base64 | tr -d ' \\n\\t'"
|
|
]
|
|
}
|
|
]
|
|
}
|
|
''';
|
|
const String defaultConfigLinux2 = r'''
|
|
{
|
|
"custom-devices": [
|
|
{
|
|
"id": "pi",
|
|
"label": "Raspberry Pi",
|
|
"sdkNameAndVersion": "Raspberry Pi 4 Model B+",
|
|
"platform": "linux-arm64",
|
|
"enabled": false,
|
|
"ping": [
|
|
"ping",
|
|
"-w",
|
|
"1",
|
|
"-c",
|
|
"1",
|
|
"raspberrypi"
|
|
],
|
|
"pingSuccessRegex": null,
|
|
"postBuild": null,
|
|
"install": [
|
|
"scp",
|
|
"-r",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"${localPath}",
|
|
"pi@raspberrypi:/tmp/${appName}"
|
|
],
|
|
"uninstall": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"pi@raspberrypi",
|
|
"rm -rf \"/tmp/${appName}\""
|
|
],
|
|
"runDebug": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"pi@raspberrypi",
|
|
"flutter-pi \"/tmp/${appName}\""
|
|
],
|
|
"forwardPort": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"-o",
|
|
"ExitOnForwardFailure=yes",
|
|
"-L",
|
|
"127.0.0.1:${hostPort}:127.0.0.1:${devicePort}",
|
|
"pi@raspberrypi",
|
|
"echo 'Port forwarding success'; read"
|
|
],
|
|
"forwardPortSuccessRegex": "Port forwarding success",
|
|
"screenshot": [
|
|
"ssh",
|
|
"-o",
|
|
"BatchMode=yes",
|
|
"pi@raspberrypi",
|
|
"fbgrab /tmp/screenshot.png && cat /tmp/screenshot.png | base64 | tr -d ' \\n\\t'"
|
|
]
|
|
}
|
|
],
|
|
"$schema": "file:///flutter/packages/flutter_tools/static/custom-devices.schema.json"
|
|
}
|
|
''';
|
|
|
|
final Platform windowsPlatform = FakePlatform(
|
|
operatingSystem: 'windows',
|
|
environment: <String, String>{
|
|
'FLUTTER_ROOT': windowsFlutterRoot,
|
|
}
|
|
);
|
|
|
|
class FakeTerminal implements Terminal {
|
|
factory FakeTerminal({Platform platform}) {
|
|
return FakeTerminal._private(
|
|
stdio: FakeStdio(),
|
|
platform: platform
|
|
);
|
|
}
|
|
|
|
FakeTerminal._private({
|
|
this.stdio,
|
|
Platform platform
|
|
}) :
|
|
terminal = AnsiTerminal(
|
|
stdio: stdio,
|
|
platform: platform
|
|
);
|
|
|
|
final FakeStdio stdio;
|
|
final AnsiTerminal terminal;
|
|
|
|
void simulateStdin(String line) {
|
|
stdio.simulateStdin(line);
|
|
}
|
|
|
|
@override
|
|
set usesTerminalUi(bool value) => terminal.usesTerminalUi = value;
|
|
|
|
@override
|
|
bool get usesTerminalUi => terminal.usesTerminalUi;
|
|
|
|
@override
|
|
String bolden(String message) => terminal.bolden(message);
|
|
|
|
@override
|
|
String clearScreen() => terminal.clearScreen();
|
|
|
|
@override
|
|
String color(String message, TerminalColor color) => terminal.color(message, color);
|
|
|
|
@override
|
|
Stream<String> get keystrokes => terminal.keystrokes;
|
|
|
|
@override
|
|
Future<String> promptForCharInput(
|
|
List<String> acceptedCharacters, {
|
|
Logger logger,
|
|
String prompt,
|
|
int defaultChoiceIndex,
|
|
bool displayAcceptedCharacters = true
|
|
}) => terminal.promptForCharInput(
|
|
acceptedCharacters,
|
|
logger: logger,
|
|
prompt: prompt,
|
|
defaultChoiceIndex: defaultChoiceIndex,
|
|
displayAcceptedCharacters: displayAcceptedCharacters
|
|
);
|
|
|
|
@override
|
|
bool get singleCharMode => terminal.singleCharMode;
|
|
@override
|
|
set singleCharMode(bool value) => terminal.singleCharMode = value;
|
|
|
|
@override
|
|
bool get stdinHasTerminal => terminal.stdinHasTerminal;
|
|
|
|
@override
|
|
String get successMark => terminal.successMark;
|
|
|
|
@override
|
|
bool get supportsColor => terminal.supportsColor;
|
|
|
|
@override
|
|
bool get supportsEmoji => terminal.supportsEmoji;
|
|
|
|
@override
|
|
String get warningMark => terminal.warningMark;
|
|
|
|
@override
|
|
int get preferredStyle => terminal.preferredStyle;
|
|
}
|
|
|
|
class FakeCommandRunner extends FlutterCommandRunner {
|
|
FakeCommandRunner({
|
|
@required Platform platform,
|
|
@required FileSystem fileSystem,
|
|
@required Logger logger,
|
|
UserMessages userMessages
|
|
}) : _platform = platform,
|
|
_fileSystem = fileSystem,
|
|
_logger = logger,
|
|
_userMessages = userMessages ?? UserMessages(),
|
|
assert(platform != null),
|
|
assert(fileSystem != null),
|
|
assert(logger != null);
|
|
|
|
final Platform _platform;
|
|
final FileSystem _fileSystem;
|
|
final Logger _logger;
|
|
final UserMessages _userMessages;
|
|
|
|
@override
|
|
Future<void> runCommand(ArgResults topLevelResults) async {
|
|
final Logger logger = (topLevelResults['verbose'] as bool) ? VerboseLogger(_logger) : _logger;
|
|
|
|
return context.run<void>(
|
|
overrides: <Type, Generator>{
|
|
Logger: () => logger,
|
|
},
|
|
body: () {
|
|
Cache.flutterRoot ??= Cache.defaultFlutterRoot(
|
|
platform: _platform,
|
|
fileSystem: _fileSystem,
|
|
userMessages: _userMessages,
|
|
);
|
|
// For compatibility with tests that set this to a relative path.
|
|
Cache.flutterRoot = _fileSystem.path.normalize(_fileSystem.path.absolute(Cache.flutterRoot));
|
|
return super.runCommand(topLevelResults);
|
|
}
|
|
);
|
|
}
|
|
}
|
|
|
|
/// May take platform, logger, processManager and fileSystem from context if
|
|
/// not explicitly specified.
|
|
CustomDevicesCommand createCustomDevicesCommand({
|
|
CustomDevicesConfig Function(FileSystem, Logger) config,
|
|
Terminal Function(Platform) terminal,
|
|
Platform platform,
|
|
FileSystem fileSystem,
|
|
ProcessManager processManager,
|
|
Logger logger,
|
|
PrintFn usagePrintFn,
|
|
bool featureEnabled = false
|
|
}) {
|
|
platform ??= FakePlatform();
|
|
processManager ??= FakeProcessManager.any();
|
|
fileSystem ??= MemoryFileSystem.test();
|
|
usagePrintFn ??= print;
|
|
logger ??= BufferLogger.test();
|
|
|
|
return CustomDevicesCommand.test(
|
|
customDevicesConfig: config != null
|
|
? config(fileSystem, logger)
|
|
: CustomDevicesConfig.test(
|
|
platform: platform,
|
|
fileSystem: fileSystem,
|
|
directory: fileSystem.directory('/'),
|
|
logger: logger
|
|
),
|
|
operatingSystemUtils: FakeOperatingSystemUtils(
|
|
hostPlatform: platform.isLinux ? HostPlatform.linux_x64
|
|
: platform.isWindows ? HostPlatform.windows_x64
|
|
: platform.isMacOS ? HostPlatform.darwin_x64
|
|
: throw UnsupportedError('Unsupported operating system')
|
|
),
|
|
terminal: terminal != null
|
|
? terminal(platform)
|
|
: FakeTerminal(platform: platform),
|
|
platform: platform,
|
|
featureFlags: TestFeatureFlags(areCustomDevicesEnabled: featureEnabled),
|
|
processManager: processManager,
|
|
fileSystem: fileSystem,
|
|
logger: logger,
|
|
usagePrintFn: usagePrintFn,
|
|
);
|
|
}
|
|
|
|
/// May take platform, logger, processManager and fileSystem from context if
|
|
/// not explicitly specified.
|
|
CommandRunner<void> createCustomDevicesCommandRunner({
|
|
CustomDevicesConfig Function(FileSystem, Logger) config,
|
|
Terminal Function(Platform) terminal,
|
|
Platform platform,
|
|
FileSystem fileSystem,
|
|
ProcessManager processManager,
|
|
Logger logger,
|
|
PrintFn usagePrintFn,
|
|
bool featureEnabled = false,
|
|
}) {
|
|
platform ??= FakePlatform();
|
|
fileSystem ??= MemoryFileSystem.test();
|
|
logger ??= BufferLogger.test();
|
|
|
|
return FakeCommandRunner(
|
|
platform: platform,
|
|
fileSystem: fileSystem,
|
|
logger: logger
|
|
)..addCommand(
|
|
createCustomDevicesCommand(
|
|
config: config,
|
|
terminal: terminal,
|
|
platform: platform,
|
|
fileSystem: fileSystem,
|
|
processManager: processManager,
|
|
logger: logger,
|
|
usagePrintFn: usagePrintFn,
|
|
featureEnabled: featureEnabled
|
|
)
|
|
);
|
|
}
|
|
|
|
FakeTerminal createFakeTerminalForAddingSshDevice({
|
|
@required Platform platform,
|
|
@required String id,
|
|
@required String label,
|
|
@required String sdkNameAndVersion,
|
|
@required String enabled,
|
|
@required String hostname,
|
|
@required String username,
|
|
@required String runDebug,
|
|
@required String usePortForwarding,
|
|
@required String screenshot,
|
|
@required String apply
|
|
}) {
|
|
return FakeTerminal(platform: platform)
|
|
..simulateStdin(id)
|
|
..simulateStdin(label)
|
|
..simulateStdin(sdkNameAndVersion)
|
|
..simulateStdin(enabled)
|
|
..simulateStdin(hostname)
|
|
..simulateStdin(username)
|
|
..simulateStdin(runDebug)
|
|
..simulateStdin(usePortForwarding)
|
|
..simulateStdin(screenshot)
|
|
..simulateStdin(apply);
|
|
}
|
|
|
|
void main() {
|
|
const String featureNotEnabledMessage = 'Custom devices feature must be enabled. Enable using `flutter config --enable-custom-devices`.';
|
|
|
|
setUpAll(() {
|
|
Cache.disableLocking();
|
|
});
|
|
|
|
group('linux', () {
|
|
setUp(() {
|
|
Cache.flutterRoot = linuxFlutterRoot;
|
|
});
|
|
|
|
testUsingContext(
|
|
'custom-devices command shows config file in help when feature is enabled',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
logger: logger,
|
|
usagePrintFn: (Object o) => logger.printStatus(o.toString()),
|
|
featureEnabled: true
|
|
);
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', '--help']),
|
|
completes
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains('Makes changes to the config file at "/.flutter_custom_devices.json".')
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'running custom-devices command without arguments prints usage',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
logger: logger,
|
|
usagePrintFn: (Object o) => logger.printStatus(o.toString()),
|
|
featureEnabled: true
|
|
);
|
|
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices']),
|
|
completes
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains('Makes changes to the config file at "/.flutter_custom_devices.json".')
|
|
);
|
|
}
|
|
);
|
|
|
|
// test behaviour with disabled feature
|
|
testUsingContext(
|
|
'custom-devices add command fails when feature is not enabled',
|
|
() async {
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner();
|
|
expect(
|
|
runner.run(const <String>['custom-devices', 'add']),
|
|
throwsToolExit(message: featureNotEnabledMessage),
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices delete command fails when feature is not enabled',
|
|
() async {
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner();
|
|
expect(
|
|
runner.run(const <String>['custom-devices', 'delete', '-d', 'testid']),
|
|
throwsToolExit(message: featureNotEnabledMessage),
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices list command fails when feature is not enabled',
|
|
() async {
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner();
|
|
expect(
|
|
runner.run(const <String>['custom-devices', 'list']),
|
|
throwsToolExit(message: featureNotEnabledMessage),
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices reset command fails when feature is not enabled',
|
|
() async {
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner();
|
|
expect(
|
|
runner.run(const <String>['custom-devices', 'reset']),
|
|
throwsToolExit(message: featureNotEnabledMessage),
|
|
);
|
|
}
|
|
);
|
|
|
|
// test add command
|
|
testUsingContext(
|
|
'custom-devices add command correctly adds ssh device config on linux',
|
|
() async {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
|
|
final CommandRunner<void> 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 <String>['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 <String>[
|
|
'ping',
|
|
'-c', '1',
|
|
'-w', '1',
|
|
'testhostname',
|
|
],
|
|
postBuildCommand: null, // ignore: avoid_redundant_argument_values
|
|
installCommand: const <String>[
|
|
'scp',
|
|
'-r',
|
|
'-o', 'BatchMode=yes',
|
|
r'${localPath}',
|
|
r'testuser@testhostname:/tmp/${appName}',
|
|
],
|
|
uninstallCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
r'rm -rf "/tmp/${appName}"',
|
|
],
|
|
runDebugCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
'testrundebug',
|
|
],
|
|
forwardPortCommand: const <String>[
|
|
'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 <String>[
|
|
'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<void> 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 <String>['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 <String>[
|
|
'ping',
|
|
'-c', '1',
|
|
'-w', '1',
|
|
'192.168.178.1',
|
|
],
|
|
postBuildCommand: null, // ignore: avoid_redundant_argument_values
|
|
installCommand: const <String>[
|
|
'scp',
|
|
'-r',
|
|
'-o', 'BatchMode=yes',
|
|
r'${localPath}',
|
|
r'testuser@192.168.178.1:/tmp/${appName}',
|
|
],
|
|
uninstallCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@192.168.178.1',
|
|
r'rm -rf "/tmp/${appName}"',
|
|
],
|
|
runDebugCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@192.168.178.1',
|
|
'testrundebug',
|
|
],
|
|
forwardPortCommand: const <String>[
|
|
'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 <String>[
|
|
'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<void> 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 <String>['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 <String>[
|
|
'ping',
|
|
'-6',
|
|
'-c', '1',
|
|
'-w', '1',
|
|
'::1',
|
|
],
|
|
postBuildCommand: null, // ignore: avoid_redundant_argument_values
|
|
installCommand: const <String>[
|
|
'scp',
|
|
'-r',
|
|
'-o', 'BatchMode=yes',
|
|
'-6',
|
|
r'${localPath}',
|
|
r'testuser@[::1]:/tmp/${appName}',
|
|
],
|
|
uninstallCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'-6',
|
|
'testuser@[::1]',
|
|
r'rm -rf "/tmp/${appName}"',
|
|
],
|
|
runDebugCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'-6',
|
|
'testuser@[::1]',
|
|
'testrundebug',
|
|
],
|
|
forwardPortCommand: const <String>[
|
|
'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 <String>[
|
|
'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<void> 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 <String>['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: <String>[
|
|
'ping',
|
|
'-c', '1',
|
|
'-w', '1',
|
|
'testhostname',
|
|
],
|
|
postBuildCommand: null, // ignore: avoid_redundant_argument_values
|
|
installCommand: <String>[
|
|
'scp',
|
|
'-r',
|
|
'-o', 'BatchMode=yes',
|
|
r'${localPath}',
|
|
r'testuser@testhostname:/tmp/${appName}',
|
|
],
|
|
uninstallCommand: <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
r'rm -rf "/tmp/${appName}"',
|
|
],
|
|
runDebugCommand: <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
'testrundebug',
|
|
],
|
|
screenshotCommand: <String>[
|
|
'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<void> 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 <String>['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 <String>[
|
|
'ping',
|
|
'-c', '1',
|
|
'-w', '1',
|
|
'testhostname',
|
|
],
|
|
postBuildCommand: null, // ignore: avoid_redundant_argument_values
|
|
installCommand: const <String>[
|
|
'scp',
|
|
'-r',
|
|
'-o', 'BatchMode=yes',
|
|
r'${localPath}',
|
|
r'testuser@testhostname:/tmp/${appName}',
|
|
],
|
|
uninstallCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
r'rm -rf "/tmp/${appName}"',
|
|
],
|
|
runDebugCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
'testrundebug',
|
|
],
|
|
forwardPortCommand: const <String>[
|
|
'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 {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
|
|
final CustomDevicesConfig config = CustomDevicesConfig.test(
|
|
fileSystem: fs,
|
|
directory: fs.directory('/'),
|
|
logger: BufferLogger.test(),
|
|
);
|
|
|
|
config.add(CustomDeviceConfig.exampleUnix.copyWith(id: 'testid'));
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
config: (_, __) => config,
|
|
fileSystem: fs,
|
|
featureEnabled: true
|
|
);
|
|
|
|
final Uint8List contentsBefore = fs.file('.flutter_custom_devices.json').readAsBytesSync();
|
|
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'delete', '-d', 'testid']),
|
|
completes
|
|
);
|
|
expect(fs.file('/.flutter_custom_devices.json.bak'), exists);
|
|
expect(config.devices, hasLength(0));
|
|
|
|
final Uint8List backupContents = fs.file('.flutter_custom_devices.json.bak').readAsBytesSync();
|
|
expect(contentsBefore, equals(backupContents));
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices delete command without device argument throws tool exit',
|
|
() async {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
|
|
final CustomDevicesConfig config = CustomDevicesConfig.test(
|
|
fileSystem: fs,
|
|
directory: fs.directory('/'),
|
|
logger: BufferLogger.test(),
|
|
);
|
|
config.add(CustomDeviceConfig.exampleUnix.copyWith(id: 'testid2'));
|
|
final Uint8List contentsBefore = fs.file('.flutter_custom_devices.json').readAsBytesSync();
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
featureEnabled: true
|
|
);
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'delete']),
|
|
throwsToolExit()
|
|
);
|
|
|
|
final Uint8List contentsAfter = fs.file('.flutter_custom_devices.json').readAsBytesSync();
|
|
expect(contentsBefore, equals(contentsAfter));
|
|
expect(fs.file('.flutter_custom_devices.json.bak').existsSync(), isFalse);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices delete command throws tool exit with invalid device id',
|
|
() async {
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
featureEnabled: true
|
|
);
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'delete', '-d', 'testid']),
|
|
throwsToolExit(message: 'Couldn\'t find device with id "testid" in config at "/.flutter_custom_devices.json"')
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices list command throws tool exit when config contains errors',
|
|
() async {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
fs.file('.flutter_custom_devices.json').writeAsStringSync('{"custom-devices": {}}');
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
fileSystem: fs,
|
|
logger: logger,
|
|
featureEnabled: true
|
|
);
|
|
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'list']),
|
|
throwsToolExit(message: 'Could not list custom devices.')
|
|
);
|
|
expect(
|
|
logger.errorText,
|
|
contains("Could not load custom devices config. config['custom-devices'] is not a JSON array.")
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices list command prints message when no devices found',
|
|
() async {
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
logger: logger,
|
|
featureEnabled: true
|
|
);
|
|
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'list']),
|
|
completes
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains('No custom devices found in "/.flutter_custom_devices.json"')
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices list command lists all devices',
|
|
() async {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
CustomDevicesConfig.test(
|
|
fileSystem: fs,
|
|
directory: fs.directory('/'),
|
|
logger: logger,
|
|
)..add(
|
|
CustomDeviceConfig.exampleUnix.copyWith(id: 'testid', label: 'testlabel', enabled: true)
|
|
)..add(
|
|
CustomDeviceConfig.exampleUnix.copyWith(id: 'testid2', label: 'testlabel2', enabled: false)
|
|
);
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
logger: logger,
|
|
fileSystem: fs,
|
|
featureEnabled: true
|
|
);
|
|
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'list']),
|
|
completes
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains('List of custom devices in "/.flutter_custom_devices.json":')
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains('id: testid, label: testlabel, enabled: true')
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains('id: testid2, label: testlabel2, enabled: false')
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
'custom-devices reset correctly backs up the config file',
|
|
() async {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
CustomDevicesConfig.test(
|
|
fileSystem: fs,
|
|
directory: fs.directory('/'),
|
|
logger: logger,
|
|
)..add(
|
|
CustomDeviceConfig.exampleUnix.copyWith(id: 'testid', label: 'testlabel', enabled: true)
|
|
)..add(
|
|
CustomDeviceConfig.exampleUnix.copyWith(id: 'testid2', label: 'testlabel2', enabled: false)
|
|
);
|
|
|
|
final Uint8List contentsBefore = fs.file('.flutter_custom_devices.json').readAsBytesSync();
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
logger: logger,
|
|
fileSystem: fs,
|
|
featureEnabled: true
|
|
);
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'reset']),
|
|
completes
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains(
|
|
'Successfully resetted the custom devices config file and created a '
|
|
'backup at "/.flutter_custom_devices.json.bak".'
|
|
)
|
|
);
|
|
|
|
final Uint8List backupContents = fs.file('.flutter_custom_devices.json.bak').readAsBytesSync();
|
|
expect(contentsBefore, equals(backupContents));
|
|
expect(
|
|
fs.file('.flutter_custom_devices.json').readAsStringSync(),
|
|
anyOf(equals(defaultConfigLinux1), equals(defaultConfigLinux2))
|
|
);
|
|
}
|
|
);
|
|
|
|
testUsingContext(
|
|
"custom-devices reset outputs correct msg when config file didn't exist",
|
|
() async {
|
|
final MemoryFileSystem fs = MemoryFileSystem.test();
|
|
final BufferLogger logger = BufferLogger.test();
|
|
|
|
final CommandRunner<void> runner = createCustomDevicesCommandRunner(
|
|
logger: logger,
|
|
fileSystem: fs,
|
|
featureEnabled: true
|
|
);
|
|
await expectLater(
|
|
runner.run(const <String>['custom-devices', 'reset']),
|
|
completes
|
|
);
|
|
expect(
|
|
logger.statusText,
|
|
contains(
|
|
'Successfully resetted the custom devices config file.'
|
|
)
|
|
);
|
|
|
|
expect(fs.file('.flutter_custom_devices.json.bak'), isNot(exists));
|
|
expect(
|
|
fs.file('.flutter_custom_devices.json').readAsStringSync(),
|
|
anyOf(equals(defaultConfigLinux1), equals(defaultConfigLinux2))
|
|
);
|
|
}
|
|
);
|
|
});
|
|
|
|
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<void> 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 <String>['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 <String>[
|
|
'ping',
|
|
'-n', '1',
|
|
'-w', '500',
|
|
'testhostname',
|
|
],
|
|
pingSuccessRegex: RegExp(r'[<=]\d+ms'),
|
|
postBuildCommand: null, // ignore: avoid_redundant_argument_values
|
|
installCommand: const <String>[
|
|
'scp',
|
|
'-r',
|
|
'-o', 'BatchMode=yes',
|
|
r'${localPath}',
|
|
r'testuser@testhostname:/tmp/${appName}',
|
|
],
|
|
uninstallCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
r'rm -rf "/tmp/${appName}"',
|
|
],
|
|
runDebugCommand: const <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
'testrundebug',
|
|
],
|
|
forwardPortCommand: const <String>[
|
|
'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 <String>[
|
|
'ssh',
|
|
'-o', 'BatchMode=yes',
|
|
'testuser@testhostname',
|
|
'testscreenshot',
|
|
],
|
|
),
|
|
),
|
|
);
|
|
},
|
|
);
|
|
});
|
|
}
|