[custom-devices] add screenshotting support (#80675)
This commit is contained in:
parent
7bff366b96
commit
82830fa1a0
@ -11,6 +11,7 @@ import 'package:process/process.dart';
|
||||
|
||||
import '../application_package.dart';
|
||||
import '../base/common.dart';
|
||||
import '../base/file_system.dart';
|
||||
import '../base/io.dart';
|
||||
import '../base/logger.dart';
|
||||
import '../base/process.dart';
|
||||
@ -594,6 +595,24 @@ class CustomDevice extends Device {
|
||||
@override
|
||||
Future<bool> get isLocalEmulator async => false;
|
||||
|
||||
@override
|
||||
bool get supportsScreenshot => _config.supportsScreenshotting;
|
||||
|
||||
@override
|
||||
Future<void> takeScreenshot(File outputFile) async {
|
||||
if (supportsScreenshot == false) {
|
||||
throw UnsupportedError('Screenshotting is not supported for this device.');
|
||||
}
|
||||
|
||||
final List<String> interpolated = interpolateCommand(
|
||||
_config.screenshotCommand,
|
||||
<String, String>{},
|
||||
);
|
||||
|
||||
final RunResult result = await _processUtils.run(interpolated, throwOnError: true);
|
||||
await outputFile.writeAsBytes(base64Decode(result.stdout));
|
||||
}
|
||||
|
||||
@override
|
||||
bool isSupported() {
|
||||
return true;
|
||||
|
@ -22,7 +22,8 @@ class CustomDeviceConfig {
|
||||
required this.uninstallCommand,
|
||||
required this.runDebugCommand,
|
||||
this.forwardPortCommand,
|
||||
this.forwardPortSuccessRegex
|
||||
this.forwardPortSuccessRegex,
|
||||
this.screenshotCommand
|
||||
}) : assert(forwardPortCommand == null || forwardPortSuccessRegex != null);
|
||||
|
||||
factory CustomDeviceConfig.fromJson(dynamic json) {
|
||||
@ -40,7 +41,8 @@ class CustomDeviceConfig {
|
||||
uninstallCommand: _castStringList(typedMap[_kUninstallCommand]!),
|
||||
runDebugCommand: _castStringList(typedMap[_kRunDebugCommand]!),
|
||||
forwardPortCommand: _castStringListOrNull(typedMap[_kForwardPortCommand]),
|
||||
forwardPortSuccessRegex: _convertToRegexOrNull(typedMap[_kForwardPortSuccessRegex])
|
||||
forwardPortSuccessRegex: _convertToRegexOrNull(typedMap[_kForwardPortSuccessRegex]),
|
||||
screenshotCommand: _castStringListOrNull(typedMap[_kScreenshotCommand])
|
||||
);
|
||||
}
|
||||
|
||||
@ -56,6 +58,7 @@ class CustomDeviceConfig {
|
||||
static const String _kRunDebugCommand = 'runDebug';
|
||||
static const String _kForwardPortCommand = 'forwardPort';
|
||||
static const String _kForwardPortSuccessRegex = 'forwardPortSuccessRegex';
|
||||
static const String _kScreenshotCommand = 'screenshot';
|
||||
|
||||
/// An example device config used for creating the default config file.
|
||||
static final CustomDeviceConfig example = CustomDeviceConfig(
|
||||
@ -70,7 +73,8 @@ class CustomDeviceConfig {
|
||||
uninstallCommand: const <String>['ssh', 'pi@raspberrypi', r'rm -rf "/tmp/${appName}"'],
|
||||
runDebugCommand: const <String>['ssh', 'pi@raspberrypi', r'flutter-pi "/tmp/${appName}"'],
|
||||
forwardPortCommand: const <String>['ssh', '-o', 'ExitOnForwardFailure=yes', '-L', r'127.0.0.1:${hostPort}:127.0.0.1:${devicePort}', 'pi@raspberrypi'],
|
||||
forwardPortSuccessRegex: RegExp('Linux')
|
||||
forwardPortSuccessRegex: RegExp('Linux'),
|
||||
screenshotCommand: const <String>['ssh', 'pi@raspberrypi', r"fbgrab /tmp/screenshot.png && cat /tmp/screenshot.png | base64 | tr -d ' \n\t'"]
|
||||
);
|
||||
|
||||
final String id;
|
||||
@ -85,9 +89,12 @@ class CustomDeviceConfig {
|
||||
final List<String> runDebugCommand;
|
||||
final List<String>? forwardPortCommand;
|
||||
final RegExp? forwardPortSuccessRegex;
|
||||
final List<String>? screenshotCommand;
|
||||
|
||||
bool get usesPortForwarding => forwardPortCommand != null;
|
||||
|
||||
bool get supportsScreenshotting => screenshotCommand != null;
|
||||
|
||||
static List<String> _castStringList(Object object) {
|
||||
return (object as List<dynamic>).cast<String>();
|
||||
}
|
||||
@ -113,7 +120,8 @@ class CustomDeviceConfig {
|
||||
_kUninstallCommand: uninstallCommand,
|
||||
_kRunDebugCommand: runDebugCommand,
|
||||
_kForwardPortCommand: forwardPortCommand,
|
||||
_kForwardPortSuccessRegex: forwardPortSuccessRegex?.pattern
|
||||
_kForwardPortSuccessRegex: forwardPortSuccessRegex?.pattern,
|
||||
_kScreenshotCommand: screenshotCommand,
|
||||
};
|
||||
}
|
||||
|
||||
@ -133,7 +141,9 @@ class CustomDeviceConfig {
|
||||
bool explicitForwardPortCommand = false,
|
||||
List<String>? forwardPortCommand,
|
||||
bool explicitForwardPortSuccessRegex = false,
|
||||
RegExp? forwardPortSuccessRegex
|
||||
RegExp? forwardPortSuccessRegex,
|
||||
bool explicitScreenshotCommand = false,
|
||||
List<String>? screenshotCommand
|
||||
}) {
|
||||
return CustomDeviceConfig(
|
||||
id: id ?? this.id,
|
||||
@ -147,7 +157,8 @@ class CustomDeviceConfig {
|
||||
uninstallCommand: uninstallCommand ?? this.uninstallCommand,
|
||||
runDebugCommand: runDebugCommand ?? this.runDebugCommand,
|
||||
forwardPortCommand: explicitForwardPortCommand ? forwardPortCommand : (forwardPortCommand ?? this.forwardPortCommand),
|
||||
forwardPortSuccessRegex: explicitForwardPortSuccessRegex ? forwardPortSuccessRegex : (forwardPortSuccessRegex ?? this.forwardPortSuccessRegex)
|
||||
forwardPortSuccessRegex: explicitForwardPortSuccessRegex ? forwardPortSuccessRegex : (forwardPortSuccessRegex ?? this.forwardPortSuccessRegex),
|
||||
screenshotCommand: explicitScreenshotCommand ? screenshotCommand : (screenshotCommand ?? this.screenshotCommand),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -107,6 +107,18 @@
|
||||
"format": "regex",
|
||||
"default": "Linux",
|
||||
"required": false
|
||||
},
|
||||
"screenshot": {
|
||||
"description": "Take a screenshot of the app as a png image. This command should take the screenshot, convert it to png, then base64 encode it and echo to stdout. Any stderr output will be ignored. If this command is not given, screenshotting will be disabled for this device.",
|
||||
"type": ["array", "null"],
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"minItems": 1,
|
||||
"default": [
|
||||
"ssh", "pi@raspberrypi", "fbgrab /tmp/screenshot.png && cat /tmp/screenshot.png | base64 | tr -d ' \\n\\t'"
|
||||
],
|
||||
"required": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import 'package:file/file.dart';
|
||||
import 'package:file/memory.dart';
|
||||
import 'package:file/src/interface/directory.dart';
|
||||
import 'package:file/src/interface/file.dart';
|
||||
import 'package:file_testing/file_testing.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_info.dart';
|
||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||
@ -108,7 +109,8 @@ void main() {
|
||||
uninstallCommand: const <String>['testuninstall'],
|
||||
runDebugCommand: const <String>['testrundebug'],
|
||||
forwardPortCommand: const <String>['testforwardport'],
|
||||
forwardPortSuccessRegex: RegExp('testforwardportsuccess')
|
||||
forwardPortSuccessRegex: RegExp('testforwardportsuccess'),
|
||||
screenshotCommand: const <String>['testscreenshot']
|
||||
);
|
||||
|
||||
const String testConfigPingSuccessOutput = 'testpingsuccess\n';
|
||||
@ -520,6 +522,63 @@ void main() {
|
||||
ProcessManager: () => FakeProcessManager.any()
|
||||
}
|
||||
);
|
||||
|
||||
testWithoutContext('CustomDevice screenshotting', () async {
|
||||
bool screenshotCommandWasExecuted = false;
|
||||
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
FakeCommand(
|
||||
command: testConfig.screenshotCommand,
|
||||
onRun: () => screenshotCommandWasExecuted = true,
|
||||
)
|
||||
]);
|
||||
|
||||
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||
final File screenshotFile = fs.file('screenshot.png');
|
||||
|
||||
final CustomDevice device = CustomDevice(
|
||||
config: testConfig,
|
||||
logger: BufferLogger.test(),
|
||||
processManager: processManager
|
||||
);
|
||||
|
||||
expect(device.supportsScreenshot, true);
|
||||
|
||||
await device.takeScreenshot(screenshotFile);
|
||||
expect(screenshotCommandWasExecuted, true);
|
||||
expect(screenshotFile, exists);
|
||||
});
|
||||
|
||||
testWithoutContext('CustomDevice without screenshotting support', () async {
|
||||
bool screenshotCommandWasExecuted = false;
|
||||
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
FakeCommand(
|
||||
command: testConfig.screenshotCommand,
|
||||
onRun: () => screenshotCommandWasExecuted = true,
|
||||
)
|
||||
]);
|
||||
|
||||
final MemoryFileSystem fs = MemoryFileSystem.test();
|
||||
final File screenshotFile = fs.file('screenshot.png');
|
||||
|
||||
final CustomDevice device = CustomDevice(
|
||||
config: testConfig.copyWith(
|
||||
explicitScreenshotCommand: true,
|
||||
screenshotCommand: null
|
||||
),
|
||||
logger: BufferLogger.test(),
|
||||
processManager: processManager
|
||||
);
|
||||
|
||||
expect(device.supportsScreenshot, false);
|
||||
expect(
|
||||
() => device.takeScreenshot(screenshotFile),
|
||||
throwsA(const TypeMatcher<UnsupportedError>()),
|
||||
);
|
||||
expect(screenshotCommandWasExecuted, false);
|
||||
expect(screenshotFile.existsSync(), false);
|
||||
});
|
||||
}
|
||||
|
||||
class FakeBundleBuilder extends Fake implements BundleBuilder {
|
||||
|
Loading…
x
Reference in New Issue
Block a user