Commit 6cdab85d authored by Yegor's avatar Yegor Committed by GitHub

Sync test code with Cocoon (#6129)

* fix flutter watch benchmark

Syncs https://github.com/flutter/cocoon/commit/d2d7950ecd2c72421d75d9b5d54805cc554e92a8

* Split Android/iOS impl behind a unified interface

Syncs https://github.com/flutter/cocoon/commit/db87e10fa54317115479d0b85280c7e5eed08ff7

* Switch from pub get to flutter packages get

Syncs https://github.com/flutter/cocoon/commit/b378005cbbc5330058d5240a2970ca477c2c9722

* "silent" option in test runner; fix analysis errors;
parent 8a823328
...@@ -51,9 +51,11 @@ Future<Null> main(List<String> rawArgs) async { ...@@ -51,9 +51,11 @@ Future<Null> main(List<String> rawArgs) async {
return null; return null;
} }
bool silent = args['silent'];
for (String taskName in taskNames) { for (String taskName in taskNames) {
section('Running task "$taskName"'); section('Running task "$taskName"');
Map<String, dynamic> result = await runTask(taskName); Map<String, dynamic> result = await runTask(taskName, silent: silent);
if (!result['success']) if (!result['success'])
exitCode = 1; exitCode = 1;
...@@ -98,4 +100,9 @@ final ArgParser _argParser = new ArgParser() ...@@ -98,4 +100,9 @@ final ArgParser _argParser = new ArgParser()
); );
} }
}, },
)
..addFlag(
'silent',
negatable: true,
defaultsTo: false,
); );
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createComplexLayoutStartupTest(ios: false)); await task(createComplexLayoutStartupTest(os: DeviceOperatingSystem.android));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createComplexLayoutStartupTest(ios: true)); await task(createComplexLayoutStartupTest(os: DeviceOperatingSystem.ios));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createComplexLayoutScrollPerfTest(ios: false)); await task(createComplexLayoutScrollPerfTest(os: DeviceOperatingSystem.android));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createComplexLayoutScrollPerfTest(ios: true)); await task(createComplexLayoutScrollPerfTest(os: DeviceOperatingSystem.ios));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createFlutterGalleryStartupTest(ios: false)); await task(createFlutterGalleryStartupTest(os: DeviceOperatingSystem.android));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/gallery.dart'; import 'package:flutter_devicelab/tasks/gallery.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createGalleryTransitionTest(ios: false)); await task(createGalleryTransitionTest(os: DeviceOperatingSystem.android));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/perf_tests.dart'; import 'package:flutter_devicelab/tasks/perf_tests.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createFlutterGalleryStartupTest(ios: true)); await task(createFlutterGalleryStartupTest(os: DeviceOperatingSystem.ios));
} }
...@@ -5,8 +5,9 @@ ...@@ -5,8 +5,9 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter_devicelab/tasks/gallery.dart'; import 'package:flutter_devicelab/tasks/gallery.dart';
import 'package:flutter_devicelab/framework/adb.dart';
import 'package:flutter_devicelab/framework/framework.dart'; import 'package:flutter_devicelab/framework/framework.dart';
Future<Null> main() async { Future<Null> main() async {
await task(createGalleryTransitionTest(ios: true)); await task(createGalleryTransitionTest(os: DeviceOperatingSystem.ios));
} }
...@@ -12,7 +12,7 @@ import 'package:flutter_devicelab/framework/utils.dart'; ...@@ -12,7 +12,7 @@ import 'package:flutter_devicelab/framework/utils.dart';
void main() { void main() {
task(() async { task(() async {
Adb device = await adb(); Device device = await devices.workingDevice;
await device.unlock(); await device.unlock();
Directory appDir = Directory appDir =
dir(path.join(flutterDirectory.path, 'examples/flutter_gallery')); dir(path.join(flutterDirectory.path, 'examples/flutter_gallery'));
......
...@@ -6,109 +6,135 @@ import 'dart:async'; ...@@ -6,109 +6,135 @@ import 'dart:async';
import 'dart:io'; import 'dart:io';
import 'dart:math' as math; import 'dart:math' as math;
import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'utils.dart'; import 'utils.dart';
typedef Future<Adb> AdbGetter(); /// The root of the API for controlling devices.
DeviceDiscovery get devices => new DeviceDiscovery();
/// Device operating system the test is configured to test.
enum DeviceOperatingSystem { android, ios }
/// Device OS to test on.
DeviceOperatingSystem deviceOperatingSystem = DeviceOperatingSystem.android;
/// Discovers available devices and chooses one to work with.
abstract class DeviceDiscovery {
factory DeviceDiscovery() {
switch(deviceOperatingSystem) {
case DeviceOperatingSystem.android:
return new AndroidDeviceDiscovery();
case DeviceOperatingSystem.ios:
return new IosDeviceDiscovery();
default:
throw new StateError('Unsupported device operating system: {config.deviceOperatingSystem}');
}
}
/// Get an instance of [Adb]. /// Selects a device to work with, load-balancing between devices if more than
/// /// one are available.
/// See [realAdbGetter] for signature. This can be overwritten for testing. ///
AdbGetter adb = realAdbGetter; /// Calling this method does not guarantee that the same device will be
/// returned. For such behavior see [workingDevice].
Future<Null> chooseWorkingDevice();
Adb _currentDevice; /// A device to work with.
///
/// Returns the same device when called repeatedly (unlike
/// [chooseWorkingDevice]). This is useful when you need to perform multiple
/// perations on one.
Future<Device> get workingDevice;
/// Picks a random Android device out of connected devices and sets it as /// Lists all available devices' IDs.
/// [_currentDevice]. Future<List<String>> discoverDevices();
Future<Null> pickNextDevice() async {
List<Adb> allDevices =
(await Adb.deviceIds).map((String id) => new Adb(deviceId: id)).toList();
if (allDevices.length == 0) throw 'No Android devices detected'; /// Checks the health of the available devices.
Future<Map<String, HealthCheckResult>> checkDevices();
// TODO(yjbanov): filter out and warn about those with low battery level /// Prepares the system to run tasks.
_currentDevice = allDevices[new math.Random().nextInt(allDevices.length)]; Future<Null> performPreflightTasks();
} }
Future<Adb> realAdbGetter() async { /// A proxy for one specific device.
if (_currentDevice == null) await pickNextDevice(); abstract class Device {
return _currentDevice; /// A unique device identifier.
} String get deviceId;
/// Gets the ID of an unlocked device, unlocking it if necessary. /// Whether the device is awake.
// TODO(yjbanov): abstract away iOS from Android. Future<bool> isAwake();
Future<String> getUnlockedDeviceId({ bool ios: false }) async {
if (ios) {
// We currently do not have a way to lock/unlock iOS devices, or even to
// pick one out of many. So we pick the first random iPhone and assume it's
// already unlocked. For now we'll just keep them at minimum screen
// brightness so they don't drain battery too fast.
List<String> iosDeviceIds =
grep('UniqueDeviceID', from: await eval('ideviceinfo', <String>[]))
.map((String line) => line.split(' ').last)
.toList();
if (iosDeviceIds.isEmpty) throw 'No connected iOS devices found.'; /// Whether the device is asleep.
Future<bool> isAsleep();
return iosDeviceIds.first; /// Wake up the device if it is not awake.
} Future<Null> wakeUp();
Adb device = await adb(); /// Send the device to sleep mode.
await device.unlock(); Future<Null> sendToSleep();
return device.deviceId;
}
/// Android Debug Bridge (`adb`) client that exposes a subset of functions /// Emulates pressing the power button, toggling the device's on/off state.
/// relevant to on-device testing. Future<Null> togglePower();
class Adb {
Adb({ this.deviceId });
final String deviceId; /// Unlocks the device.
///
/// Assumes the device doesn't have a secure unlock pattern.
Future<Null> unlock();
}
class AndroidDeviceDiscovery implements DeviceDiscovery {
// Parses information about a device. Example: // Parses information about a device. Example:
// //
// 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper // 015d172c98400a03 device usb:340787200X product:nakasi model:Nexus_7 device:grouper
static final RegExp _kDeviceRegex = new RegExp(r'^(\S+)\s+(\S+)(.*)'); static final RegExp _kDeviceRegex = new RegExp(r'^(\S+)\s+(\S+)(.*)');
/// Reports connection health for every device. static AndroidDeviceDiscovery _instance;
static Future<Map<String, HealthCheckResult>> checkDevices() async {
Map<String, HealthCheckResult> results = <String, HealthCheckResult>{}; factory AndroidDeviceDiscovery() {
for (String deviceId in await deviceIds) { return _instance ??= new AndroidDeviceDiscovery._();
try { }
Adb device = new Adb(deviceId: deviceId);
// Just a smoke test that we can read wakefulness state AndroidDeviceDiscovery._();
// TODO(yjbanov): also check battery level
await device._getWakefulness(); AndroidDevice _workingDevice;
results['android-device-$deviceId'] = new HealthCheckResult.success();
} catch (e, s) { @override
results['android-device-$deviceId'] = new HealthCheckResult.error(e, s); Future<AndroidDevice> get workingDevice async {
} if (_workingDevice == null) {
await chooseWorkingDevice();
} }
return results;
return _workingDevice;
} }
/// Kills the `adb` server causing it to start a new instance upon next /// Picks a random Android device out of connected devices and sets it as
/// command. /// [workingDevice].
/// @override
/// Restarting `adb` helps with keeping device connections alive. When `adb` Future<Null> chooseWorkingDevice() async {
/// runs non-stop for too long it loses connections to devices. List<Device> allDevices = (await discoverDevices())
static Future<Null> restart() async { .map((String id) => new AndroidDevice(deviceId: id))
await exec(adbPath, <String>['kill-server'], canFail: false); .toList();
if (allDevices.isEmpty)
throw 'No Android devices detected';
// TODO(yjbanov): filter out and warn about those with low battery level
_workingDevice = allDevices[new math.Random().nextInt(allDevices.length)];
} }
/// List of device IDs visible to `adb`. @override
static Future<List<String>> get deviceIds async { Future<List<String>> discoverDevices() async {
List<String> output = List<String> output = (await eval(adbPath, <String>['devices', '-l'], canFail: false))
(await eval(adbPath, <String>['devices', '-l'], canFail: false)) .trim().split('\n');
.trim()
.split('\n');
List<String> results = <String>[]; List<String> results = <String>[];
for (String line in output) { for (String line in output) {
// Skip lines like: * daemon started successfully * // Skip lines like: * daemon started successfully *
if (line.startsWith('* daemon ')) continue; if (line.startsWith('* daemon '))
continue;
if (line.startsWith('List of devices')) continue; if (line.startsWith('List of devices'))
continue;
if (_kDeviceRegex.hasMatch(line)) { if (_kDeviceRegex.hasMatch(line)) {
Match match = _kDeviceRegex.firstMatch(line); Match match = _kDeviceRegex.firstMatch(line);
...@@ -127,28 +153,70 @@ class Adb { ...@@ -127,28 +153,70 @@ class Adb {
return results; return results;
} }
@override
Future<Map<String, HealthCheckResult>> checkDevices() async {
Map<String, HealthCheckResult> results = <String, HealthCheckResult>{};
for (String deviceId in await discoverDevices()) {
try {
AndroidDevice device = new AndroidDevice(deviceId: deviceId);
// Just a smoke test that we can read wakefulness state
// TODO(yjbanov): check battery level
await device._getWakefulness();
results['android-device-$deviceId'] = new HealthCheckResult.success();
} catch(e, s) {
results['android-device-$deviceId'] = new HealthCheckResult.error(e, s);
}
}
return results;
}
@override
Future<Null> performPreflightTasks() async {
// Kills the `adb` server causing it to start a new instance upon next
// command.
//
// Restarting `adb` helps with keeping device connections alive. When `adb`
// runs non-stop for too long it loses connections to devices. There may be
// a better method, but so far that's the best one I've found.
await exec(adbPath, <String>['kill-server'], canFail: false);
}
}
class AndroidDevice implements Device {
AndroidDevice({@required this.deviceId});
@override
final String deviceId;
/// Whether the device is awake. /// Whether the device is awake.
@override
Future<bool> isAwake() async { Future<bool> isAwake() async {
return await _getWakefulness() == 'Awake'; return await _getWakefulness() == 'Awake';
} }
/// Whether the device is asleep. /// Whether the device is asleep.
@override
Future<bool> isAsleep() async { Future<bool> isAsleep() async {
return await _getWakefulness() == 'Asleep'; return await _getWakefulness() == 'Asleep';
} }
/// Wake up the device if it is not awake using [togglePower]. /// Wake up the device if it is not awake using [togglePower].
@override
Future<Null> wakeUp() async { Future<Null> wakeUp() async {
if (!(await isAwake())) await togglePower(); if (!(await isAwake()))
await togglePower();
} }
/// Send the device to sleep mode if it is not asleep using [togglePower]. /// Send the device to sleep mode if it is not asleep using [togglePower].
@override
Future<Null> sendToSleep() async { Future<Null> sendToSleep() async {
if (!(await isAsleep())) await togglePower(); if (!(await isAsleep()))
await togglePower();
} }
/// Sends `KEYCODE_POWER` (26), which causes the device to toggle its mode /// Sends `KEYCODE_POWER` (26), which causes the device to toggle its mode
/// between awake and asleep. /// between awake and asleep.
@override
Future<Null> togglePower() async { Future<Null> togglePower() async {
await shellExec('input', const <String>['keyevent', '26']); await shellExec('input', const <String>['keyevent', '26']);
} }
...@@ -156,6 +224,7 @@ class Adb { ...@@ -156,6 +224,7 @@ class Adb {
/// Unlocks the device by sending `KEYCODE_MENU` (82). /// Unlocks the device by sending `KEYCODE_MENU` (82).
/// ///
/// This only works when the device doesn't have a secure unlock pattern. /// This only works when the device doesn't have a secure unlock pattern.
@override
Future<Null> unlock() async { Future<Null> unlock() async {
await wakeUp(); await wakeUp();
await shellExec('input', const <String>['keyevent', '82']); await shellExec('input', const <String>['keyevent', '82']);
...@@ -166,24 +235,115 @@ class Adb { ...@@ -166,24 +235,115 @@ class Adb {
/// See: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/PowerManagerInternal.java /// See: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/os/PowerManagerInternal.java
Future<String> _getWakefulness() async { Future<String> _getWakefulness() async {
String powerInfo = await shellEval('dumpsys', <String>['power']); String powerInfo = await shellEval('dumpsys', <String>['power']);
String wakefulness = String wakefulness = grep('mWakefulness=', from: powerInfo).single.split('=')[1].trim();
grep('mWakefulness=', from: powerInfo).single.split('=')[1].trim();
return wakefulness; return wakefulness;
} }
/// Executes [command] on `adb shell` and returns its exit code. /// Executes [command] on `adb shell` and returns its exit code.
Future<Null> shellExec(String command, List<String> arguments, Future<Null> shellExec(String command, List<String> arguments, {Map<String, String> env}) async {
{ Map<String, String> env }) async { await exec(adbPath, <String>['shell', command]..addAll(arguments), env: env, canFail: false);
await exec(adbPath, <String>['shell', command]..addAll(arguments),
env: env, canFail: false);
} }
/// Executes [command] on `adb shell` and returns its standard output as a [String]. /// Executes [command] on `adb shell` and returns its standard output as a [String].
Future<String> shellEval(String command, List<String> arguments, Future<String> shellEval(String command, List<String> arguments, {Map<String, String> env}) {
{ Map<String, String> env }) { return eval(adbPath, <String>['shell', command]..addAll(arguments), env: env, canFail: false);
return eval(adbPath, <String>['shell', command]..addAll(arguments), }
env: env, canFail: false); }
class IosDeviceDiscovery implements DeviceDiscovery {
static IosDeviceDiscovery _instance;
factory IosDeviceDiscovery() {
return _instance ??= new IosDeviceDiscovery._();
}
IosDeviceDiscovery._();
IosDevice _workingDevice;
@override
Future<IosDevice> get workingDevice async {
if (_workingDevice == null) {
await chooseWorkingDevice();
}
return _workingDevice;
}
/// Picks a random iOS device out of connected devices and sets it as
/// [workingDevice].
@override
Future<Null> chooseWorkingDevice() async {
List<IosDevice> allDevices = (await discoverDevices())
.map((String id) => new IosDevice(deviceId: id))
.toList();
if (allDevices.length == 0)
throw 'No iOS devices detected';
// TODO(yjbanov): filter out and warn about those with low battery level
_workingDevice = allDevices[new math.Random().nextInt(allDevices.length)];
}
@override
Future<List<String>> discoverDevices() async {
// TODO: use the -k UniqueDeviceID option, which requires much less parsing.
List<String> iosDeviceIds = grep('UniqueDeviceID', from: await eval('ideviceinfo', <String>[]))
.map((String line) => line.split(' ').last).toList();
if (iosDeviceIds.isEmpty)
throw 'No connected iOS devices found.';
return iosDeviceIds;
} }
@override
Future<Map<String, HealthCheckResult>> checkDevices() async {
Map<String, HealthCheckResult> results = <String, HealthCheckResult>{};
for (String deviceId in await discoverDevices()) {
// TODO: do a more meaningful connectivity check than just recording the ID
results['ios-device-$deviceId'] = new HealthCheckResult.success();
}
return results;
}
@override
Future<Null> performPreflightTasks() async {
// Currently we do not have preflight tasks for iOS.
return null;
}
}
/// iOS device.
class IosDevice implements Device {
const IosDevice({ @required this.deviceId });
@override
final String deviceId;
// The methods below are stubs for now. They will need to be expanded.
// We currently do not have a way to lock/unlock iOS devices. So we assume the
// devices are already unlocked. For now we'll just keep them at minimum
// screen brightness so they don't drain battery too fast.
@override
Future<bool> isAwake() async => true;
@override
Future<bool> isAsleep() async => false;
@override
Future<Null> wakeUp() async {}
@override
Future<Null> sendToSleep() async {}
@override
Future<Null> togglePower() async {}
@override
Future<Null> unlock() async {}
} }
/// Path to the `adb` executable. /// Path to the `adb` executable.
......
...@@ -19,7 +19,10 @@ const Duration taskTimeoutWithGracePeriod = const Duration(minutes: 11); ...@@ -19,7 +19,10 @@ const Duration taskTimeoutWithGracePeriod = const Duration(minutes: 11);
/// ///
/// [taskName] is the name of the task. The corresponding task executable is /// [taskName] is the name of the task. The corresponding task executable is
/// expected to be found under `bin/tasks`. /// expected to be found under `bin/tasks`.
Future<Map<String, dynamic>> runTask(String taskName) async { ///
/// Running the task in [silent] mode will suppress standard output from task
/// processes and only print standard errors.
Future<Map<String, dynamic>> runTask(String taskName, { bool silent: false }) async {
String taskExecutable = 'bin/tasks/$taskName.dart'; String taskExecutable = 'bin/tasks/$taskName.dart';
if (!file(taskExecutable).existsSync()) if (!file(taskExecutable).existsSync())
...@@ -42,7 +45,9 @@ Future<Map<String, dynamic>> runTask(String taskName) async { ...@@ -42,7 +45,9 @@ Future<Map<String, dynamic>> runTask(String taskName) async {
.transform(new Utf8Decoder()) .transform(new Utf8Decoder())
.transform(new LineSplitter()) .transform(new LineSplitter())
.listen((String line) { .listen((String line) {
stdout.writeln('[$taskName] [STDOUT] $line'); if (!silent) {
stdout.writeln('[$taskName] [STDOUT] $line');
}
}); });
StreamSubscription<String> stderrSub = runner.stderr StreamSubscription<String> stderrSub = runner.stderr
......
...@@ -242,12 +242,6 @@ String get dartBin => ...@@ -242,12 +242,6 @@ String get dartBin =>
Future<int> dart(List<String> args) => exec(dartBin, args); Future<int> dart(List<String> args) => exec(dartBin, args);
Future<int> pub(String command) {
return exec(
path.join(flutterDirectory.path, 'bin', 'cache', 'dart-sdk', 'bin', 'pub'),
<String>[command]);
}
Future<dynamic> inDirectory(dynamic directory, Future<dynamic> action()) async { Future<dynamic> inDirectory(dynamic directory, Future<dynamic> action()) async {
String previousCwd = cwd; String previousCwd = cwd;
try { try {
......
...@@ -105,8 +105,7 @@ class FlutterAnalyzeAppBenchmark extends Benchmark { ...@@ -105,8 +105,7 @@ class FlutterAnalyzeAppBenchmark extends Benchmark {
Future<num> run() async { Future<num> run() async {
rm(benchmarkFile); rm(benchmarkFile);
await inDirectory(megaDir, () async { await inDirectory(megaDir, () async {
await flutter('analyze', options: <String>[ await flutter('watch', options: <String>[
'--watch',
'--benchmark', '--benchmark',
]); ]);
}); });
......
...@@ -12,23 +12,25 @@ import '../framework/adb.dart'; ...@@ -12,23 +12,25 @@ import '../framework/adb.dart';
import '../framework/framework.dart'; import '../framework/framework.dart';
import '../framework/utils.dart'; import '../framework/utils.dart';
TaskFunction createGalleryTransitionTest({ @required bool ios: false }) { TaskFunction createGalleryTransitionTest({ @required DeviceOperatingSystem os }) {
return new GalleryTransitionTest(ios: ios); return new GalleryTransitionTest(os: os);
} }
class GalleryTransitionTest { class GalleryTransitionTest {
GalleryTransitionTest({ this.ios }); GalleryTransitionTest({ this.os }) {
deviceOperatingSystem = os;
}
final bool ios; final DeviceOperatingSystem os;
Future<TaskResult> call() async { Future<TaskResult> call() async {
String deviceId = await getUnlockedDeviceId(ios: ios); String deviceId = (await devices.workingDevice).deviceId;
Directory galleryDirectory = Directory galleryDirectory =
dir('${flutterDirectory.path}/examples/flutter_gallery'); dir('${flutterDirectory.path}/examples/flutter_gallery');
await inDirectory(galleryDirectory, () async { await inDirectory(galleryDirectory, () async {
await pub('get'); await flutter('packages', options: <String>['get']);
if (ios) { if (os == DeviceOperatingSystem.ios) {
// This causes an Xcode project to be created. // This causes an Xcode project to be created.
await flutter('build', options: <String>['ios', '--profile']); await flutter('build', options: <String>['ios', '--profile']);
} }
......
...@@ -11,26 +11,26 @@ import '../framework/adb.dart'; ...@@ -11,26 +11,26 @@ import '../framework/adb.dart';
import '../framework/framework.dart'; import '../framework/framework.dart';
import '../framework/utils.dart'; import '../framework/utils.dart';
TaskFunction createComplexLayoutScrollPerfTest({ @required bool ios: false }) { TaskFunction createComplexLayoutScrollPerfTest({ @required DeviceOperatingSystem os }) {
return new PerfTest( return new PerfTest(
'${flutterDirectory.path}/dev/benchmarks/complex_layout', '${flutterDirectory.path}/dev/benchmarks/complex_layout',
'test_driver/scroll_perf.dart', 'test_driver/scroll_perf.dart',
'complex_layout_scroll_perf', 'complex_layout_scroll_perf',
ios: ios os: os,
); );
} }
TaskFunction createFlutterGalleryStartupTest({ bool ios: false }) { TaskFunction createFlutterGalleryStartupTest({ @required DeviceOperatingSystem os }) {
return new StartupTest( return new StartupTest(
'${flutterDirectory.path}/examples/flutter_gallery', '${flutterDirectory.path}/examples/flutter_gallery',
ios: ios os: os,
); );
} }
TaskFunction createComplexLayoutStartupTest({ bool ios: false }) { TaskFunction createComplexLayoutStartupTest({ @required DeviceOperatingSystem os }) {
return new StartupTest( return new StartupTest(
'${flutterDirectory.path}/dev/benchmarks/complex_layout', '${flutterDirectory.path}/dev/benchmarks/complex_layout',
ios: ios os: os,
); );
} }
...@@ -46,17 +46,19 @@ TaskFunction createComplexLayoutBuildTest() { ...@@ -46,17 +46,19 @@ TaskFunction createComplexLayoutBuildTest() {
class StartupTest { class StartupTest {
static const Duration _startupTimeout = const Duration(minutes: 2); static const Duration _startupTimeout = const Duration(minutes: 2);
StartupTest(this.testDirectory, { this.ios }); StartupTest(this.testDirectory, { this.os }) {
deviceOperatingSystem = os;
}
final String testDirectory; final String testDirectory;
final bool ios; final DeviceOperatingSystem os;
Future<TaskResult> call() async { Future<TaskResult> call() async {
return await inDirectory(testDirectory, () async { return await inDirectory(testDirectory, () async {
String deviceId = await getUnlockedDeviceId(ios: ios); String deviceId = (await devices.workingDevice).deviceId;
await pub('get'); await flutter('packages', options: <String>['get']);
if (ios) { if (os == DeviceOperatingSystem.ios) {
// This causes an Xcode project to be created. // This causes an Xcode project to be created.
await flutter('build', options: <String>['ios', '--profile']); await flutter('build', options: <String>['ios', '--profile']);
} }
...@@ -80,19 +82,19 @@ class StartupTest { ...@@ -80,19 +82,19 @@ class StartupTest {
/// performance. /// performance.
class PerfTest { class PerfTest {
PerfTest(this.testDirectory, this.testTarget, this.timelineFileName, { this.ios }); PerfTest(this.testDirectory, this.testTarget, this.timelineFileName, { this.os });
final String testDirectory; final String testDirectory;
final String testTarget; final String testTarget;
final String timelineFileName; final String timelineFileName;
final bool ios; final DeviceOperatingSystem os;
Future<TaskResult> call() { Future<TaskResult> call() {
return inDirectory(testDirectory, () async { return inDirectory(testDirectory, () async {
String deviceId = await getUnlockedDeviceId(ios: ios); String deviceId = (await devices.workingDevice).deviceId;
await pub('get'); await flutter('packages', options: <String>['get']);
if (ios) { if (os == DeviceOperatingSystem.ios) {
// This causes an Xcode project to be created. // This causes an Xcode project to be created.
await flutter('build', options: <String>['ios', '--profile']); await flutter('build', options: <String>['ios', '--profile']);
} }
...@@ -124,9 +126,9 @@ class BuildTest { ...@@ -124,9 +126,9 @@ class BuildTest {
Future<TaskResult> call() async { Future<TaskResult> call() async {
return await inDirectory(testDirectory, () async { return await inDirectory(testDirectory, () async {
Adb device = await adb(); Device device = await devices.workingDevice;
await device.unlock(); await device.unlock();
await pub('get'); await flutter('packages', options: <String>['get']);
Stopwatch watch = new Stopwatch()..start(); Stopwatch watch = new Stopwatch()..start();
await flutter('build', options: <String>[ await flutter('build', options: <String>[
......
...@@ -25,7 +25,7 @@ class EditRefreshTask { ...@@ -25,7 +25,7 @@ class EditRefreshTask {
final DateTime timestamp; final DateTime timestamp;
Future<TaskResult> call() async { Future<TaskResult> call() async {
Adb device = await adb(); Device device = await devices.workingDevice;
await device.unlock(); await device.unlock();
Benchmark benchmark = new EditRefreshBenchmark(commit, timestamp); Benchmark benchmark = new EditRefreshBenchmark(commit, timestamp);
section(benchmark.name); section(benchmark.name);
...@@ -57,7 +57,7 @@ class EditRefreshBenchmark extends Benchmark { ...@@ -57,7 +57,7 @@ class EditRefreshBenchmark extends Benchmark {
@override @override
Future<num> run() async { Future<num> run() async {
Adb device = await adb(); Device device = await devices.workingDevice;
rm(benchmarkFile); rm(benchmarkFile);
int exitCode = await inDirectory(megaDir, () async { int exitCode = await inDirectory(megaDir, () async {
return await flutter('run', return await flutter('run',
......
...@@ -24,7 +24,7 @@ TaskFunction createBasicMaterialAppSizeTest() { ...@@ -24,7 +24,7 @@ TaskFunction createBasicMaterialAppSizeTest() {
throw 'Failed to create sample Flutter app in ${sampleDir.path}'; throw 'Failed to create sample Flutter app in ${sampleDir.path}';
await inDirectory(sampleDir, () async { await inDirectory(sampleDir, () async {
await pub('get'); await flutter('packages', options: <String>['get']);
await flutter('build', options: <String>['clean']); await flutter('build', options: <String>['clean']);
await flutter('build', options: <String>['apk', '--release']); await flutter('build', options: <String>['apk', '--release']);
apkSizeInBytes = await file('${sampleDir.path}/build/app.apk').length(); apkSizeInBytes = await file('${sampleDir.path}/build/app.apk').length();
......
...@@ -10,28 +10,27 @@ import 'package:collection/collection.dart'; ...@@ -10,28 +10,27 @@ import 'package:collection/collection.dart';
import 'package:flutter_devicelab/framework/adb.dart'; import 'package:flutter_devicelab/framework/adb.dart';
void main() { void main() {
group('adb', () { group('device', () {
Adb device; Device device;
setUp(() { setUp(() {
FakeAdb.resetLog(); FakeDevice.resetLog();
adb = null; device = null;
device = new FakeAdb(); device = new FakeDevice();
}); });
tearDown(() { tearDown(() {
adb = realAdbGetter;
}); });
group('isAwake/isAsleep', () { group('isAwake/isAsleep', () {
test('reads Awake', () async { test('reads Awake', () async {
FakeAdb.pretendAwake(); FakeDevice.pretendAwake();
expect(await device.isAwake(), isTrue); expect(await device.isAwake(), isTrue);
expect(await device.isAsleep(), isFalse); expect(await device.isAsleep(), isFalse);
}); });
test('reads Asleep', () async { test('reads Asleep', () async {
FakeAdb.pretendAsleep(); FakeDevice.pretendAsleep();
expect(await device.isAwake(), isFalse); expect(await device.isAwake(), isFalse);
expect(await device.isAsleep(), isTrue); expect(await device.isAsleep(), isTrue);
}); });
...@@ -48,7 +47,7 @@ void main() { ...@@ -48,7 +47,7 @@ void main() {
group('wakeUp', () { group('wakeUp', () {
test('when awake', () async { test('when awake', () async {
FakeAdb.pretendAwake(); FakeDevice.pretendAwake();
await device.wakeUp(); await device.wakeUp();
expectLog(<CommandArgs>[ expectLog(<CommandArgs>[
cmd(command: 'dumpsys', arguments: <String>['power']), cmd(command: 'dumpsys', arguments: <String>['power']),
...@@ -56,7 +55,7 @@ void main() { ...@@ -56,7 +55,7 @@ void main() {
}); });
test('when asleep', () async { test('when asleep', () async {
FakeAdb.pretendAsleep(); FakeDevice.pretendAsleep();
await device.wakeUp(); await device.wakeUp();
expectLog(<CommandArgs>[ expectLog(<CommandArgs>[
cmd(command: 'dumpsys', arguments: <String>['power']), cmd(command: 'dumpsys', arguments: <String>['power']),
...@@ -67,7 +66,7 @@ void main() { ...@@ -67,7 +66,7 @@ void main() {
group('sendToSleep', () { group('sendToSleep', () {
test('when asleep', () async { test('when asleep', () async {
FakeAdb.pretendAsleep(); FakeDevice.pretendAsleep();
await device.sendToSleep(); await device.sendToSleep();
expectLog(<CommandArgs>[ expectLog(<CommandArgs>[
cmd(command: 'dumpsys', arguments: <String>['power']), cmd(command: 'dumpsys', arguments: <String>['power']),
...@@ -75,7 +74,7 @@ void main() { ...@@ -75,7 +74,7 @@ void main() {
}); });
test('when awake', () async { test('when awake', () async {
FakeAdb.pretendAwake(); FakeDevice.pretendAwake();
await device.sendToSleep(); await device.sendToSleep();
expectLog(<CommandArgs>[ expectLog(<CommandArgs>[
cmd(command: 'dumpsys', arguments: <String>['power']), cmd(command: 'dumpsys', arguments: <String>['power']),
...@@ -86,7 +85,7 @@ void main() { ...@@ -86,7 +85,7 @@ void main() {
group('unlock', () { group('unlock', () {
test('sends unlock event', () async { test('sends unlock event', () async {
FakeAdb.pretendAwake(); FakeDevice.pretendAwake();
await device.unlock(); await device.unlock();
expectLog(<CommandArgs>[ expectLog(<CommandArgs>[
cmd(command: 'dumpsys', arguments: <String>['power']), cmd(command: 'dumpsys', arguments: <String>['power']),
...@@ -98,10 +97,10 @@ void main() { ...@@ -98,10 +97,10 @@ void main() {
} }
void expectLog(List<CommandArgs> log) { void expectLog(List<CommandArgs> log) {
expect(FakeAdb.commandLog, log); expect(FakeDevice.commandLog, log);
} }
CommandArgs cmd({ String command, List<String> arguments, Map<String, String> env }) => new CommandArgs( CommandArgs cmd({String command, List<String> arguments, Map<String, String> env}) => new CommandArgs(
command: command, command: command,
arguments: arguments, arguments: arguments,
env: env env: env
...@@ -110,7 +109,7 @@ CommandArgs cmd({ String command, List<String> arguments, Map<String, String> en ...@@ -110,7 +109,7 @@ CommandArgs cmd({ String command, List<String> arguments, Map<String, String> en
typedef dynamic ExitErrorFactory(); typedef dynamic ExitErrorFactory();
class CommandArgs { class CommandArgs {
CommandArgs({ this.command, this.arguments, this.env }); CommandArgs({this.command, this.arguments, this.env});
final String command; final String command;
final List<String> arguments; final List<String> arguments;
...@@ -142,8 +141,8 @@ class CommandArgs { ...@@ -142,8 +141,8 @@ class CommandArgs {
: null.hashCode; : null.hashCode;
} }
class FakeAdb extends Adb { class FakeDevice extends AndroidDevice {
FakeAdb({ String deviceId: null }) : super(deviceId: deviceId); FakeDevice({String deviceId: null}) : super(deviceId: deviceId);
static String output = ''; static String output = '';
static ExitErrorFactory exitErrorFactory = () => null; static ExitErrorFactory exitErrorFactory = () => null;
......
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