Unverified Commit b9733522 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Implement takeScreenshot and add driver test for Fuchsia (#48611)

parent 52e0d980
// 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.
import 'dart:async';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/integration_tests.dart';
Future<void> main() async {
deviceOperatingSystem = DeviceOperatingSystem.fuchsia;
await task(createFlutterDriverScreenshotTest());
}
{
"program": {
"data": "data/flutter_driver_screenshot_test"
},
"sandbox": {
"services": [
"fuchsia.cobalt.LoggerFactory",
"fuchsia.fonts.Provider",
"fuchsia.logger.LogSink",
"fuchsia.modular.Clipboard",
"fuchsia.modular.ContextWriter",
"fuchsia.modular.DeviceMap",
"fuchsia.modular.ModuleContext",
"fuchsia.sys.Environment",
"fuchsia.sys.Launcher",
"fuchsia.testing.runner.TestRunner",
"fuchsia.ui.input.ImeService",
"fuchsia.ui.policy.Presenter",
"fuchsia.ui.scenic.Scenic"
]
}
}
......@@ -71,28 +71,20 @@ class _MyHomePageState extends State<_MyHomePage> {
Future<String> _handleDriverMessage(String message) async {
switch (message) {
case 'device_model':
String target;
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
switch (Theme.of(context).platform) {
case TargetPlatform.iOS:
target = 'ios';
break;
final IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
if (iosDeviceInfo.isPhysicalDevice) {
return iosDeviceInfo.utsname.machine;
}
return 'sim_' + iosDeviceInfo.name;
case TargetPlatform.android:
target = 'android';
break;
return (await deviceInfo.androidInfo).model;
case TargetPlatform.fuchsia:
return 'fuchsia';
default:
target = 'unsupported';
break;
}
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (target == 'ios') {
final IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
if (iosDeviceInfo.isPhysicalDevice) {
return iosDeviceInfo.utsname.machine;
} else {
return 'sim_' + iosDeviceInfo.name;
}
} else if (target == 'android') {
return (await deviceInfo.androidInfo).model;
return 'unsupported';
}
break;
}
......
......@@ -139,7 +139,7 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
FuchsiaDevices() : super('Fuchsia devices');
@override
bool get supportsPlatform => globals.platform.isLinux || globals.platform.isMacOS;
bool get supportsPlatform => isFuchsiaSupportedPlatform();
@override
bool get canListAnything => fuchsiaWorkflow.canListDevices;
......@@ -442,6 +442,39 @@ class FuchsiaDevice extends Device {
}
}
@override
bool get supportsScreenshot => isFuchsiaSupportedPlatform();
@override
Future<void> takeScreenshot(File outputFile) async {
if (outputFile.basename.split('.').last != 'ppm') {
throw '${outputFile.path} must be a .ppm file';
}
final RunResult screencapResult = await shell('screencap > /tmp/screenshot.ppm');
if (screencapResult.exitCode != 0) {
throw 'Could not take a screenshot on device $name:\n$screencapResult';
}
try {
final RunResult scpResult = await scp('/tmp/screenshot.ppm', outputFile.path);
if (scpResult.exitCode != 0) {
throw 'Failed to copy screenshot from device:\n$scpResult';
}
} finally {
try {
final RunResult deleteResult = await shell('rm /tmp/screenshot.ppm');
if (deleteResult.exitCode != 0) {
globals.printError(
'Failed to delete screenshot.ppm from the device:\n$deleteResult'
);
}
} catch (_) {
globals.printError(
'Failed to delete screenshot.ppm from the device'
);
}
}
}
@override
Future<TargetPlatform> get targetPlatform async => _targetPlatform ??= await _queryTargetPlatform();
......@@ -479,9 +512,6 @@ class FuchsiaDevice extends Device {
@override
void clearLogs() {}
@override
bool get supportsScreenshot => false;
bool get ipv6 {
try {
Uri.parseIPv6Address(id);
......@@ -545,6 +575,21 @@ class FuchsiaDevice extends Device {
]);
}
/// Transfer the file [origin] from the device to [destination].
Future<RunResult> scp(String origin, String destination) async {
if (fuchsiaArtifacts.sshConfig == null) {
throwToolExit('Cannot interact with device. No ssh config.\n'
'Try setting FUCHSIA_SSH_CONFIG or FUCHSIA_BUILD_DIR.');
}
return await processUtils.run(<String>[
'scp',
'-F',
fuchsiaArtifacts.sshConfig.absolute.path,
'$id:$origin',
destination,
]);
}
/// Finds the first port running a VM matching `isolateName` from the
/// provided set of `ports`.
///
......
......@@ -20,6 +20,11 @@ FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>();
/// The [FuchsiaArtifacts] instance.
FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>();
/// Returns [true] if the current platform supports Fuchsia targets.
bool isFuchsiaSupportedPlatform() {
return globals.platform.isLinux || globals.platform.isMacOS;
}
/// The Fuchsia SDK shell commands.
///
/// This workflow assumes development within the fuchsia source tree,
......@@ -110,7 +115,7 @@ class FuchsiaArtifacts {
/// FUCHSIA_SSH_CONFIG) to find the ssh configuration needed to talk to
/// a device.
factory FuchsiaArtifacts.find() {
if (!globals.platform.isLinux && !globals.platform.isMacOS) {
if (!isFuchsiaSupportedPlatform()) {
// Don't try to find the artifacts on platforms that are not supported.
return FuchsiaArtifacts();
}
......
......@@ -31,6 +31,7 @@ import 'package:flutter_tools/src/vmservice.dart';
import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart';
import 'package:process/process.dart';
import 'package:platform/platform.dart';
import '../../src/common.dart';
import '../../src/context.dart';
......@@ -325,6 +326,230 @@ void main() {
});
});
group('screenshot', () {
MockProcessManager mockProcessManager;
setUp(() {
mockProcessManager = MockProcessManager();
});
test('is supported on posix platforms', () {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
expect(device.supportsScreenshot, true);
}, testOn: 'posix');
testUsingContext('is not supported on Windows', () {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
expect(device.supportsScreenshot, false);
}, overrides: <Type, Generator>{
Platform: () => FakePlatform(
operatingSystem: 'windows',
),
});
test('takeScreenshot throws if file isn\'t .ppm', () async {
final FuchsiaDevice device = FuchsiaDevice('id', name: 'tester');
await expectLater(
() => device.takeScreenshot(globals.fs.file('file.invalid')),
throwsA(equals('file.invalid must be a .ppm file')),
);
}, testOn: 'posix');
testUsingContext('takeScreenshot throws if screencap failed', () async {
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'screencap > /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 1, '', '<error-message>'));
await expectLater(
() => device.takeScreenshot(globals.fs.file('file.ppm')),
throwsA(equals('Could not take a screenshot on device tester:\n<error-message>')),
);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
operatingSystem: 'linux',
),
}, testOn: 'posix');
testUsingContext('takeScreenshot throws if scp failed', () async {
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'screencap > /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
when(mockProcessManager.run(
const <String>[
'scp',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0:/tmp/screenshot.ppm',
'file.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 1, '', '<error-message>'));
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'rm /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
await expectLater(
() => device.takeScreenshot(globals.fs.file('file.ppm')),
throwsA(equals('Failed to copy screenshot from device:\n<error-message>')),
);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
operatingSystem: 'linux',
),
}, testOn: 'posix');
testUsingContext('takeScreenshot prints error if can\'t delete file from device', () async {
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'screencap > /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
when(mockProcessManager.run(
const <String>[
'scp',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0:/tmp/screenshot.ppm',
'file.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'rm /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 1, '', '<error-message>'));
try {
await device.takeScreenshot(globals.fs.file('file.ppm'));
} catch (_) {
assert(false);
}
expect(
testLogger.errorText,
contains('Failed to delete screenshot.ppm from the device:\n<error-message>'),
);
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
operatingSystem: 'linux',
),
}, testOn: 'posix');
testUsingContext('takeScreenshot returns', () async {
final FuchsiaDevice device = FuchsiaDevice('0.0.0.0', name: 'tester');
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'screencap > /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
when(mockProcessManager.run(
const <String>[
'scp',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0:/tmp/screenshot.ppm',
'file.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
when(mockProcessManager.run(
const <String>[
'ssh',
'-F',
'/fuchsia/out/default/.ssh',
'0.0.0.0',
'rm /tmp/screenshot.ppm',
],
workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'),
)).thenAnswer((_) async => ProcessResult(0, 0, '', ''));
try {
await device.takeScreenshot(globals.fs.file('file.ppm'));
} catch (_) {
assert(false);
}
}, overrides: <Type, Generator>{
ProcessManager: () => mockProcessManager,
Platform: () => FakePlatform(
environment: <String, String>{
'FUCHSIA_SSH_CONFIG': '/fuchsia/out/default/.ssh',
},
operatingSystem: 'linux',
),
}, testOn: 'posix');
});
group(FuchsiaIsolateDiscoveryProtocol, () {
MockPortForwarder portForwarder;
MockVMService vmService;
......
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