mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

Fixes https://github.com/flutter/flutter/issues/150093 New tests added to cover that we at least pass the arguments we expect to adb. The test for #150093 is not ideal in that it does not verify the behavior of a failed process but instead ensures we set the parameter that contains the behavior we want. devicelab code and tests are not setup to enable fake process or fake output from stdin/stderr and hang if adb or no hardware are present.
386 lines
11 KiB
Dart
386 lines
11 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.
|
|
|
|
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:collection/collection.dart' show ListEquality, MapEquality;
|
|
|
|
import 'package:flutter_devicelab/framework/devices.dart';
|
|
import 'package:meta/meta.dart';
|
|
|
|
import 'common.dart';
|
|
|
|
void main() {
|
|
group('device', () {
|
|
late Device device;
|
|
|
|
setUp(() {
|
|
FakeDevice.resetLog();
|
|
device = FakeDevice(deviceId: 'fakeDeviceId');
|
|
});
|
|
|
|
tearDown(() {});
|
|
|
|
group('cpu check', () {
|
|
test('arm64', () async {
|
|
FakeDevice.pretendArm64();
|
|
final AndroidDevice androidDevice = device as AndroidDevice;
|
|
expect(await androidDevice.isArm64(), isTrue);
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'getprop', arguments: <String>['ro.product.cpu.abi']),
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('isAwake/isAsleep', () {
|
|
test('reads Awake', () async {
|
|
FakeDevice.pretendAwake();
|
|
expect(await device.isAwake(), isTrue);
|
|
expect(await device.isAsleep(), isFalse);
|
|
});
|
|
|
|
test('reads Awake - samsung devices', () async {
|
|
FakeDevice.pretendAwakeSamsung();
|
|
expect(await device.isAwake(), isTrue);
|
|
expect(await device.isAsleep(), isFalse);
|
|
});
|
|
|
|
test('reads Asleep', () async {
|
|
FakeDevice.pretendAsleep();
|
|
expect(await device.isAwake(), isFalse);
|
|
expect(await device.isAsleep(), isTrue);
|
|
});
|
|
});
|
|
|
|
group('togglePower', () {
|
|
test('sends power event', () async {
|
|
await device.togglePower();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'input', arguments: <String>['keyevent', '26']),
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('wakeUp', () {
|
|
test('when awake', () async {
|
|
FakeDevice.pretendAwake();
|
|
await device.wakeUp();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'dumpsys', arguments: <String>['power']),
|
|
]);
|
|
});
|
|
|
|
test('when asleep', () async {
|
|
FakeDevice.pretendAsleep();
|
|
await device.wakeUp();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'dumpsys', arguments: <String>['power']),
|
|
cmd(command: 'input', arguments: <String>['keyevent', '26']),
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('sendToSleep', () {
|
|
test('when asleep', () async {
|
|
FakeDevice.pretendAsleep();
|
|
await device.sendToSleep();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'dumpsys', arguments: <String>['power']),
|
|
]);
|
|
});
|
|
|
|
test('when awake', () async {
|
|
FakeDevice.pretendAwake();
|
|
await device.sendToSleep();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'dumpsys', arguments: <String>['power']),
|
|
cmd(command: 'input', arguments: <String>['keyevent', '26']),
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('unlock', () {
|
|
test('sends unlock event', () async {
|
|
FakeDevice.pretendAwake();
|
|
await device.unlock();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
|
|
cmd(command: 'dumpsys', arguments: <String>['power']),
|
|
cmd(command: 'input', arguments: <String>['keyevent', '82']),
|
|
]);
|
|
});
|
|
});
|
|
|
|
group('adb', () {
|
|
test('tap', () async {
|
|
FakeDevice.resetLog();
|
|
await device.tap(100, 200);
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'input', arguments: <String>['tap', '100', '200']),
|
|
]);
|
|
});
|
|
|
|
test('awaitDevice', () async {
|
|
FakeDevice.resetLog();
|
|
// The expected value from `adb shell getprop sys.boot_completed`
|
|
FakeDevice.output = '1';
|
|
await device.awaitDevice();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'adb', environment: <String, String>{
|
|
FakeDevice.canFailKey: 'false'
|
|
}, arguments: <String>[
|
|
'-s',
|
|
device.deviceId,
|
|
'wait-for-device',
|
|
]),
|
|
cmd(command: 'adb', environment: <String, String>{
|
|
FakeDevice.canFailKey: 'false',
|
|
}, arguments: <String>[
|
|
'-s',
|
|
device.deviceId,
|
|
'shell',
|
|
'getprop sys.boot_completed',
|
|
])
|
|
]);
|
|
});
|
|
|
|
test('reboot', () async {
|
|
FakeDevice.resetLog();
|
|
await device.reboot();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'adb', environment: <String, String>{
|
|
FakeDevice.canFailKey: 'false'
|
|
}, arguments: <String>[
|
|
'-s',
|
|
device.deviceId,
|
|
'reboot',
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('clearLog', () async {
|
|
FakeDevice.resetLog();
|
|
await device.clearLogs();
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'adb', environment: <String, String>{
|
|
FakeDevice.canFailKey: 'true'
|
|
}, arguments: <String>[
|
|
'-s',
|
|
device.deviceId,
|
|
'logcat',
|
|
'-c',
|
|
]),
|
|
]);
|
|
});
|
|
|
|
test('startLoggingToSink calls adb', () async {
|
|
FakeDevice.resetLog();
|
|
await device.startLoggingToSink(IOSink(_MemoryIOSink()));
|
|
expectLog(<CommandArgs>[
|
|
cmd(command: 'adb', environment: <String, String>{
|
|
FakeDevice.canFailKey: 'true'
|
|
}, arguments: <String>[
|
|
'-s',
|
|
device.deviceId,
|
|
'logcat',
|
|
'--clear',
|
|
]),
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
void expectLog(List<CommandArgs> log) {
|
|
expect(FakeDevice.commandLog, log);
|
|
}
|
|
|
|
CommandArgs cmd({
|
|
required String command,
|
|
List<String>? arguments,
|
|
Map<String, String>? environment,
|
|
}) {
|
|
return CommandArgs(
|
|
command: command,
|
|
arguments: arguments,
|
|
environment: environment,
|
|
);
|
|
}
|
|
|
|
@immutable
|
|
class CommandArgs {
|
|
const CommandArgs({ required this.command, this.arguments, this.environment });
|
|
|
|
final String command;
|
|
final List<String>? arguments;
|
|
final Map<String, String>? environment;
|
|
|
|
@override
|
|
String toString() => 'CommandArgs(command: $command, arguments: $arguments, environment: $environment)';
|
|
|
|
@override
|
|
bool operator==(Object other) {
|
|
if (other.runtimeType != CommandArgs) {
|
|
return false;
|
|
}
|
|
return other is CommandArgs
|
|
&& other.command == command
|
|
&& const ListEquality<String>().equals(other.arguments, arguments)
|
|
&& const MapEquality<String, String>().equals(other.environment, environment);
|
|
}
|
|
|
|
@override
|
|
int get hashCode {
|
|
return Object.hash(
|
|
command,
|
|
Object.hashAll(arguments ?? const <String>[]),
|
|
Object.hashAllUnordered(environment?.keys ?? const <String>[]),
|
|
Object.hashAllUnordered(environment?.values ?? const <String>[]),
|
|
);
|
|
}
|
|
}
|
|
|
|
class FakeDevice extends AndroidDevice {
|
|
FakeDevice({required super.deviceId});
|
|
|
|
static const String canFailKey = 'canFail';
|
|
|
|
static String output = '';
|
|
|
|
static List<CommandArgs> commandLog = <CommandArgs>[];
|
|
|
|
static void resetLog() {
|
|
commandLog.clear();
|
|
}
|
|
|
|
static void pretendAwake() {
|
|
output = '''
|
|
mWakefulness=Awake
|
|
''';
|
|
}
|
|
|
|
static void pretendAwakeSamsung() {
|
|
output = '''
|
|
getWakefulnessLocked()=Awake
|
|
''';
|
|
}
|
|
|
|
static void pretendAsleep() {
|
|
output = '''
|
|
mWakefulness=Asleep
|
|
''';
|
|
}
|
|
|
|
static void pretendArm64() {
|
|
output = '''
|
|
arm64
|
|
''';
|
|
}
|
|
|
|
@override
|
|
Future<String> adb(List<String> arguments,
|
|
{Map<String, String>? environment,
|
|
bool silent = false,
|
|
bool canFail = false}) async {
|
|
environment ??= <String, String>{};
|
|
commandLog.add(CommandArgs(
|
|
command: 'adb',
|
|
// ignore: prefer_spread_collections
|
|
arguments: <String>['-s', deviceId]..addAll(arguments),
|
|
environment: environment..putIfAbsent('canFail', () => '$canFail'),
|
|
));
|
|
return output;
|
|
}
|
|
|
|
@override
|
|
Future<String> shellEval(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) async {
|
|
commandLog.add(CommandArgs(
|
|
command: command,
|
|
arguments: arguments,
|
|
environment: environment,
|
|
));
|
|
return output;
|
|
}
|
|
|
|
@override
|
|
Future<void> shellExec(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) async {
|
|
commandLog.add(CommandArgs(
|
|
command: command,
|
|
arguments: arguments,
|
|
environment: environment,
|
|
));
|
|
}
|
|
}
|
|
|
|
/// An IOSink that collects whatever is written to it.
|
|
/// Inspired by packages/flutter_tools/lib/src/base/net.dart
|
|
class _MemoryIOSink implements IOSink {
|
|
@override
|
|
Encoding encoding = utf8;
|
|
|
|
final BytesBuilder writes = BytesBuilder(copy: false);
|
|
|
|
@override
|
|
void add(List<int> data) {
|
|
writes.add(data);
|
|
}
|
|
|
|
@override
|
|
Future<void> addStream(Stream<List<int>> stream) {
|
|
final Completer<void> completer = Completer<void>();
|
|
stream.listen(add).onDone(completer.complete);
|
|
return completer.future;
|
|
}
|
|
|
|
@override
|
|
void writeCharCode(int charCode) {
|
|
add(<int>[charCode]);
|
|
}
|
|
|
|
@override
|
|
void write(Object? obj) {
|
|
add(encoding.encode('$obj'));
|
|
}
|
|
|
|
@override
|
|
void writeln([Object? obj = '']) {
|
|
add(encoding.encode('$obj\n'));
|
|
}
|
|
|
|
@override
|
|
void writeAll(Iterable<dynamic> objects, [String separator = '']) {
|
|
bool addSeparator = false;
|
|
for (final dynamic object in objects) {
|
|
if (addSeparator) {
|
|
write(separator);
|
|
}
|
|
write(object);
|
|
addSeparator = true;
|
|
}
|
|
}
|
|
|
|
@override
|
|
void addError(dynamic error, [StackTrace? stackTrace]) {
|
|
throw UnimplementedError();
|
|
}
|
|
|
|
@override
|
|
Future<void> get done => close();
|
|
|
|
@override
|
|
Future<void> close() async {}
|
|
|
|
@override
|
|
Future<void> flush() async {}
|
|
}
|