allow adb to set canfail then use canFail=true for clearing logs (#150517)

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.
This commit is contained in:
Reid Baker 2024-06-24 15:13:24 -04:00 committed by GitHub
parent 7292c94bac
commit 1eb7cd2c73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 162 additions and 5 deletions

View file

@ -60,6 +60,10 @@ Running the devicelab will do things to your environment.
Notably, it will start and stop Gradle, for instance.
### Running tests in `test/...`
`dart test test/{NAME_OF_TEST}`
### Running specific tests
To run a test, use option `-t` (`--task`):

View file

@ -706,6 +706,7 @@ class AndroidDevice extends Device {
List<String> arguments, {
Map<String, String>? environment,
bool silent = false,
bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
}) {
return eval(
adbPath,
@ -713,6 +714,7 @@ class AndroidDevice extends Device {
environment: environment,
printStdout: !silent,
printStderr: !silent,
canFail: canFail,
);
}
@ -735,7 +737,7 @@ class AndroidDevice extends Device {
@override
Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async {
if (clear) {
await adb(<String>['logcat', '--clear'], silent: true);
await adb(<String>['logcat', '--clear'], silent: true, canFail: true);
}
_loggingProcess = await startProcess(
adbPath,
@ -770,7 +772,7 @@ class AndroidDevice extends Device {
@override
Future<void> clearLogs() {
return adb(<String>['logcat', '-c']);
return adb(<String>['logcat', '-c'], canFail: true);
}
@override

View file

@ -2,6 +2,11 @@
// 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';
@ -18,8 +23,7 @@ void main() {
device = FakeDevice(deviceId: 'fakeDeviceId');
});
tearDown(() {
});
tearDown(() {});
group('cpu check', () {
test('arm64', () async {
@ -119,12 +123,80 @@ void main() {
group('adb', () {
test('tap', () async {
FakeDevice.resetLog();
await device.tap(100, 200);
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>['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',
]),
]);
});
});
});
}
@ -181,6 +253,8 @@ class CommandArgs {
class FakeDevice extends AndroidDevice {
FakeDevice({required super.deviceId});
static const String canFailKey = 'canFail';
static String output = '';
static List<CommandArgs> commandLog = <CommandArgs>[];
@ -213,6 +287,21 @@ class FakeDevice extends AndroidDevice {
''';
}
@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(
@ -232,3 +321,65 @@ class FakeDevice extends AndroidDevice {
));
}
}
/// 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 {}
}