Skip to content

Commit

Permalink
allow adb to set canfail then use canFail=true for clearing logs (flu…
Browse files Browse the repository at this point in the history
…tter#150517)

Fixes flutter#150093

New tests added to cover that we at least pass the arguments we expect to adb. 

The test for flutter#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.
  • Loading branch information
reidbaker authored and sigurdm committed Jun 25, 2024
1 parent b056135 commit 50d4faf
Show file tree
Hide file tree
Showing 3 changed files with 162 additions and 5 deletions.
4 changes: 4 additions & 0 deletions dev/devicelab/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`):
Expand Down
6 changes: 4 additions & 2 deletions dev/devicelab/lib/framework/devices.dart
Original file line number Diff line number Diff line change
Expand Up @@ -706,13 +706,15 @@ 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,
<String>['-s', deviceId, ...arguments],
environment: environment,
printStdout: !silent,
printStderr: !silent,
canFail: canFail,
);
}

Expand All @@ -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,
Expand Down Expand Up @@ -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
Expand Down
157 changes: 154 additions & 3 deletions dev/devicelab/test/adb_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -18,8 +23,7 @@ void main() {
device = FakeDevice(deviceId: 'fakeDeviceId');
});

tearDown(() {
});
tearDown(() {});

group('cpu check', () {
test('arm64', () async {
Expand Down Expand Up @@ -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',
]),
]);
});
});
});
}
Expand Down Expand Up @@ -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>[];
Expand Down Expand Up @@ -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(
Expand All @@ -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 {}
}

0 comments on commit 50d4faf

Please sign in to comment.