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> { ...@@ -71,28 +71,20 @@ class _MyHomePageState extends State<_MyHomePage> {
Future<String> _handleDriverMessage(String message) async { Future<String> _handleDriverMessage(String message) async {
switch (message) { switch (message) {
case 'device_model': case 'device_model':
String target; final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
switch (Theme.of(context).platform) { switch (Theme.of(context).platform) {
case TargetPlatform.iOS: case TargetPlatform.iOS:
target = 'ios';
break;
case TargetPlatform.android:
target = 'android';
break;
default:
target = 'unsupported';
break;
}
final DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
if (target == 'ios') {
final IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo; final IosDeviceInfo iosDeviceInfo = await deviceInfo.iosInfo;
if (iosDeviceInfo.isPhysicalDevice) { if (iosDeviceInfo.isPhysicalDevice) {
return iosDeviceInfo.utsname.machine; return iosDeviceInfo.utsname.machine;
} else {
return 'sim_' + iosDeviceInfo.name;
} }
} else if (target == 'android') { return 'sim_' + iosDeviceInfo.name;
case TargetPlatform.android:
return (await deviceInfo.androidInfo).model; return (await deviceInfo.androidInfo).model;
case TargetPlatform.fuchsia:
return 'fuchsia';
default:
return 'unsupported';
} }
break; break;
} }
......
...@@ -139,7 +139,7 @@ class FuchsiaDevices extends PollingDeviceDiscovery { ...@@ -139,7 +139,7 @@ class FuchsiaDevices extends PollingDeviceDiscovery {
FuchsiaDevices() : super('Fuchsia devices'); FuchsiaDevices() : super('Fuchsia devices');
@override @override
bool get supportsPlatform => globals.platform.isLinux || globals.platform.isMacOS; bool get supportsPlatform => isFuchsiaSupportedPlatform();
@override @override
bool get canListAnything => fuchsiaWorkflow.canListDevices; bool get canListAnything => fuchsiaWorkflow.canListDevices;
...@@ -442,6 +442,39 @@ class FuchsiaDevice extends Device { ...@@ -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 @override
Future<TargetPlatform> get targetPlatform async => _targetPlatform ??= await _queryTargetPlatform(); Future<TargetPlatform> get targetPlatform async => _targetPlatform ??= await _queryTargetPlatform();
...@@ -479,9 +512,6 @@ class FuchsiaDevice extends Device { ...@@ -479,9 +512,6 @@ class FuchsiaDevice extends Device {
@override @override
void clearLogs() {} void clearLogs() {}
@override
bool get supportsScreenshot => false;
bool get ipv6 { bool get ipv6 {
try { try {
Uri.parseIPv6Address(id); Uri.parseIPv6Address(id);
...@@ -545,6 +575,21 @@ class FuchsiaDevice extends Device { ...@@ -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 /// Finds the first port running a VM matching `isolateName` from the
/// provided set of `ports`. /// provided set of `ports`.
/// ///
......
...@@ -20,6 +20,11 @@ FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>(); ...@@ -20,6 +20,11 @@ FuchsiaSdk get fuchsiaSdk => context.get<FuchsiaSdk>();
/// The [FuchsiaArtifacts] instance. /// The [FuchsiaArtifacts] instance.
FuchsiaArtifacts get fuchsiaArtifacts => context.get<FuchsiaArtifacts>(); 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. /// The Fuchsia SDK shell commands.
/// ///
/// This workflow assumes development within the fuchsia source tree, /// This workflow assumes development within the fuchsia source tree,
...@@ -110,7 +115,7 @@ class FuchsiaArtifacts { ...@@ -110,7 +115,7 @@ class FuchsiaArtifacts {
/// FUCHSIA_SSH_CONFIG) to find the ssh configuration needed to talk to /// FUCHSIA_SSH_CONFIG) to find the ssh configuration needed to talk to
/// a device. /// a device.
factory FuchsiaArtifacts.find() { 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. // Don't try to find the artifacts on platforms that are not supported.
return FuchsiaArtifacts(); return FuchsiaArtifacts();
} }
......
...@@ -31,6 +31,7 @@ import 'package:flutter_tools/src/vmservice.dart'; ...@@ -31,6 +31,7 @@ import 'package:flutter_tools/src/vmservice.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:process/process.dart'; import 'package:process/process.dart';
import 'package:platform/platform.dart';
import '../../src/common.dart'; import '../../src/common.dart';
import '../../src/context.dart'; import '../../src/context.dart';
...@@ -325,6 +326,230 @@ void main() { ...@@ -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, () { group(FuchsiaIsolateDiscoveryProtocol, () {
MockPortForwarder portForwarder; MockPortForwarder portForwarder;
MockVMService vmService; 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