Skip to content

Commit 1eb7cd2

Browse files
authored
allow adb to set canfail then use canFail=true for clearing logs (flutter#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.
1 parent 7292c94 commit 1eb7cd2

File tree

3 files changed

+162
-5
lines changed

3 files changed

+162
-5
lines changed

dev/devicelab/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ Running the devicelab will do things to your environment.
6060

6161
Notably, it will start and stop Gradle, for instance.
6262

63+
### Running tests in `test/...`
64+
65+
`dart test test/{NAME_OF_TEST}`
66+
6367
### Running specific tests
6468

6569
To run a test, use option `-t` (`--task`):

dev/devicelab/lib/framework/devices.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -706,13 +706,15 @@ class AndroidDevice extends Device {
706706
List<String> arguments, {
707707
Map<String, String>? environment,
708708
bool silent = false,
709+
bool canFail = false, // as in, whether failures are ok. False means that they are fatal.
709710
}) {
710711
return eval(
711712
adbPath,
712713
<String>['-s', deviceId, ...arguments],
713714
environment: environment,
714715
printStdout: !silent,
715716
printStderr: !silent,
717+
canFail: canFail,
716718
);
717719
}
718720

@@ -735,7 +737,7 @@ class AndroidDevice extends Device {
735737
@override
736738
Future<void> startLoggingToSink(IOSink sink, {bool clear = true}) async {
737739
if (clear) {
738-
await adb(<String>['logcat', '--clear'], silent: true);
740+
await adb(<String>['logcat', '--clear'], silent: true, canFail: true);
739741
}
740742
_loggingProcess = await startProcess(
741743
adbPath,
@@ -770,7 +772,7 @@ class AndroidDevice extends Device {
770772

771773
@override
772774
Future<void> clearLogs() {
773-
return adb(<String>['logcat', '-c']);
775+
return adb(<String>['logcat', '-c'], canFail: true);
774776
}
775777

776778
@override

dev/devicelab/test/adb_test.dart

Lines changed: 154 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'dart:async';
6+
import 'dart:convert';
7+
import 'dart:io';
8+
import 'dart:typed_data';
9+
510
import 'package:collection/collection.dart' show ListEquality, MapEquality;
611

712
import 'package:flutter_devicelab/framework/devices.dart';
@@ -18,8 +23,7 @@ void main() {
1823
device = FakeDevice(deviceId: 'fakeDeviceId');
1924
});
2025

21-
tearDown(() {
22-
});
26+
tearDown(() {});
2327

2428
group('cpu check', () {
2529
test('arm64', () async {
@@ -119,12 +123,80 @@ void main() {
119123

120124
group('adb', () {
121125
test('tap', () async {
126+
FakeDevice.resetLog();
122127
await device.tap(100, 200);
123128
expectLog(<CommandArgs>[
124-
cmd(command: 'getprop', arguments: <String>['ro.bootimage.build.fingerprint', ';', 'getprop', 'ro.build.version.release', ';', 'getprop', 'ro.build.version.sdk']),
125129
cmd(command: 'input', arguments: <String>['tap', '100', '200']),
126130
]);
127131
});
132+
133+
test('awaitDevice', () async {
134+
FakeDevice.resetLog();
135+
// The expected value from `adb shell getprop sys.boot_completed`
136+
FakeDevice.output = '1';
137+
await device.awaitDevice();
138+
expectLog(<CommandArgs>[
139+
cmd(command: 'adb', environment: <String, String>{
140+
FakeDevice.canFailKey: 'false'
141+
}, arguments: <String>[
142+
'-s',
143+
device.deviceId,
144+
'wait-for-device',
145+
]),
146+
cmd(command: 'adb', environment: <String, String>{
147+
FakeDevice.canFailKey: 'false',
148+
}, arguments: <String>[
149+
'-s',
150+
device.deviceId,
151+
'shell',
152+
'getprop sys.boot_completed',
153+
])
154+
]);
155+
});
156+
157+
test('reboot', () async {
158+
FakeDevice.resetLog();
159+
await device.reboot();
160+
expectLog(<CommandArgs>[
161+
cmd(command: 'adb', environment: <String, String>{
162+
FakeDevice.canFailKey: 'false'
163+
}, arguments: <String>[
164+
'-s',
165+
device.deviceId,
166+
'reboot',
167+
]),
168+
]);
169+
});
170+
171+
test('clearLog', () async {
172+
FakeDevice.resetLog();
173+
await device.clearLogs();
174+
expectLog(<CommandArgs>[
175+
cmd(command: 'adb', environment: <String, String>{
176+
FakeDevice.canFailKey: 'true'
177+
}, arguments: <String>[
178+
'-s',
179+
device.deviceId,
180+
'logcat',
181+
'-c',
182+
]),
183+
]);
184+
});
185+
186+
test('startLoggingToSink calls adb', () async {
187+
FakeDevice.resetLog();
188+
await device.startLoggingToSink(IOSink(_MemoryIOSink()));
189+
expectLog(<CommandArgs>[
190+
cmd(command: 'adb', environment: <String, String>{
191+
FakeDevice.canFailKey: 'true'
192+
}, arguments: <String>[
193+
'-s',
194+
device.deviceId,
195+
'logcat',
196+
'--clear',
197+
]),
198+
]);
199+
});
128200
});
129201
});
130202
}
@@ -181,6 +253,8 @@ class CommandArgs {
181253
class FakeDevice extends AndroidDevice {
182254
FakeDevice({required super.deviceId});
183255

256+
static const String canFailKey = 'canFail';
257+
184258
static String output = '';
185259

186260
static List<CommandArgs> commandLog = <CommandArgs>[];
@@ -213,6 +287,21 @@ class FakeDevice extends AndroidDevice {
213287
''';
214288
}
215289

290+
@override
291+
Future<String> adb(List<String> arguments,
292+
{Map<String, String>? environment,
293+
bool silent = false,
294+
bool canFail = false}) async {
295+
environment ??= <String, String>{};
296+
commandLog.add(CommandArgs(
297+
command: 'adb',
298+
// ignore: prefer_spread_collections
299+
arguments: <String>['-s', deviceId]..addAll(arguments),
300+
environment: environment..putIfAbsent('canFail', () => '$canFail'),
301+
));
302+
return output;
303+
}
304+
216305
@override
217306
Future<String> shellEval(String command, List<String> arguments, { Map<String, String>? environment, bool silent = false }) async {
218307
commandLog.add(CommandArgs(
@@ -232,3 +321,65 @@ class FakeDevice extends AndroidDevice {
232321
));
233322
}
234323
}
324+
325+
/// An IOSink that collects whatever is written to it.
326+
/// Inspired by packages/flutter_tools/lib/src/base/net.dart
327+
class _MemoryIOSink implements IOSink {
328+
@override
329+
Encoding encoding = utf8;
330+
331+
final BytesBuilder writes = BytesBuilder(copy: false);
332+
333+
@override
334+
void add(List<int> data) {
335+
writes.add(data);
336+
}
337+
338+
@override
339+
Future<void> addStream(Stream<List<int>> stream) {
340+
final Completer<void> completer = Completer<void>();
341+
stream.listen(add).onDone(completer.complete);
342+
return completer.future;
343+
}
344+
345+
@override
346+
void writeCharCode(int charCode) {
347+
add(<int>[charCode]);
348+
}
349+
350+
@override
351+
void write(Object? obj) {
352+
add(encoding.encode('$obj'));
353+
}
354+
355+
@override
356+
void writeln([Object? obj = '']) {
357+
add(encoding.encode('$obj\n'));
358+
}
359+
360+
@override
361+
void writeAll(Iterable<dynamic> objects, [String separator = '']) {
362+
bool addSeparator = false;
363+
for (final dynamic object in objects) {
364+
if (addSeparator) {
365+
write(separator);
366+
}
367+
write(object);
368+
addSeparator = true;
369+
}
370+
}
371+
372+
@override
373+
void addError(dynamic error, [StackTrace? stackTrace]) {
374+
throw UnimplementedError();
375+
}
376+
377+
@override
378+
Future<void> get done => close();
379+
380+
@override
381+
Future<void> close() async {}
382+
383+
@override
384+
Future<void> flush() async {}
385+
}

0 commit comments

Comments
 (0)