Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 0f727a5

Browse files
authored
Improve, test, and fix a bug related to adb logcat filtering. (#51012)
1. Write tests for the `AdbLogLine` extension/parsing, and run it on CI. Fixes flutter/flutter#144164. 2. Improve the logging to include `androidemu`-related errors logs Work towards flutter/flutter#144164. 3. Clarified logic/fixed a bug related to handling `filterProcessId: ...`
1 parent ab4d6db commit 0f727a5

File tree

7 files changed

+314
-20
lines changed

7 files changed

+314
-20
lines changed

testing/run_tests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,7 @@ def build_dart_host_test_list(build_dir):
909909
),
910910
(os.path.join('flutter', 'testing', 'litetest'), []),
911911
(os.path.join('flutter', 'testing', 'skia_gold_client'), []),
912+
(os.path.join('flutter', 'testing', 'scenario_app'), []),
912913
(
913914
os.path.join('flutter', 'tools', 'api_check'),
914915
[os.path.join(BUILDROOT_DIR, 'flutter')],

testing/scenario_app/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ See also:
1818
- [`ios/`](ios/), the iOS-side native code and tests.
1919
- [`android/`](android/), the Android-side native code and tests.
2020

21-
[file_issue]: https://github.com/flutter/flutter/issues/new?labels=e:%20scenario-app,engine,platform-android,fyi-android,team-engine
21+
[file_issue]: https://github.com/flutter/flutter/issues/new?labels=e:%20scenario-app,engine,team-engine
2222

2323
## Running a smoke test on Firebase TestLab
2424

testing/scenario_app/bin/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,13 @@ This directory contains code specific to running Android integration tests.
55
The tests are uploaded and run on the device using `adb`, and screenshots are
66
captured and compared using Skia Gold (if available, for example on CI).
77

8+
See also:
9+
10+
- [File an issue][file_issue] with the `e: scenario-app, platform-android`
11+
labels.
12+
13+
[file_issue]: https://github.com/flutter/flutter/issues/new?labels=e:%20scenario-app,engine,platform-android,fyi-android,team-engine
14+
815
## Usage
916

1017
```sh

testing/scenario_app/bin/utils/adb_logcat_filtering.dart

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@
3030
/// See also: <https://developer.android.com/tools/logcat>.
3131
library;
3232

33+
import 'package:meta/meta.dart';
34+
3335
import 'logs.dart';
3436

3537
/// Represents a line of `adb logcat` output parsed into a structured form.
@@ -74,6 +76,34 @@ extension type const AdbLogLine._(Match _match) {
7476
return null;
7577
}
7678

79+
@visibleForTesting
80+
static const Set<String> knownNoiseTags = <String>{
81+
'CCodec',
82+
'CCodecBufferChannel',
83+
'CCodecConfig',
84+
'Codec2Client',
85+
'ColorUtils',
86+
'DMABUFHEAPS',
87+
'Gralloc4',
88+
'MediaCodec',
89+
'MonitoringInstr',
90+
'ResourceExtractor',
91+
'UsageTrackerFacilitator',
92+
'hw-BpHwBinder',
93+
'ziparchive',
94+
};
95+
96+
@visibleForTesting
97+
static const Set<String> knownUsefulTags = <String>{
98+
'ActivityManager',
99+
};
100+
101+
@visibleForTesting
102+
static const Set<String> knownUsefulErrorTags = <String>{
103+
'androidemu',
104+
'THREAD_STATE',
105+
};
106+
77107
/// Returns `true` if the log line is verbose.
78108
bool isVerbose({String? filterProcessId}) => !_isRelevant(filterProcessId: filterProcessId);
79109
bool _isRelevant({String? filterProcessId}) {
@@ -82,37 +112,31 @@ extension type const AdbLogLine._(Match _match) {
82112
return true;
83113
}
84114

85-
// Debug logs are rarely useful.
86-
if (severity == 'D') {
115+
// Verbose and debug logs are rarely useful.
116+
if (severity == 'V' || severity == 'D') {
87117
return false;
88118
}
89119

90-
// These are "known" noise tags.
91-
if (const <String>{
92-
'MonitoringInstr',
93-
'ResourceExtractor',
94-
'THREAD_STATE',
95-
'ziparchive',
96-
}.contains(name)) {
120+
if (knownNoiseTags.contains(name)) {
97121
return false;
98122
}
99123

100-
// These are "known" tags useful for debugging.
101-
if (const <String>{
102-
'utter.scenario',
103-
'utter.scenarios',
104-
'TestRunner',
105-
}.contains(name)) {
124+
if (knownUsefulTags.contains(name)) {
125+
return true;
126+
}
127+
128+
if (severity == 'E' && knownUsefulErrorTags.contains(name)) {
106129
return true;
107130
}
108131

109132
// If a process ID is specified, exclude logs _not_ from that process.
110-
if (filterProcessId != null && process != filterProcessId) {
111-
return false;
133+
if (filterProcessId == null) {
134+
// YOLO, let's keep it anyway.
135+
return name.toLowerCase().contains('flutter') ||
136+
message.toLowerCase().contains('flutter');
112137
}
113138

114-
// And... whatever, include anything with the word "flutter".
115-
return name.toLowerCase().contains('flutter') || message.toLowerCase().contains('flutter');
139+
return process == filterProcessId;
116140
}
117141

118142
/// Logs the line to the console.

testing/scenario_app/pubspec.yaml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ dependencies:
2525
vector_math: any
2626
skia_gold_client: any
2727

28+
dev_dependencies:
29+
litetest: any
30+
2831
dependency_overrides:
2932
args:
3033
path: ../../../third_party/dart/third_party/pkg/args
34+
async_helper:
35+
path: ../../../third_party/dart/pkg/async_helper
3136
collection:
3237
path: ../../../third_party/dart/third_party/pkg/collection
3338
crypto:
@@ -36,8 +41,12 @@ dependency_overrides:
3641
path: ../../tools/dir_contents_diff
3742
engine_repo_tools:
3843
path: ../../tools/pkg/engine_repo_tools
44+
expect:
45+
path: ../../../third_party/dart/pkg/expect
3946
file:
4047
path: ../../../third_party/dart/third_party/pkg/file/packages/file
48+
litetest:
49+
path: ../litetest
4150
meta:
4251
path: ../../../third_party/dart/pkg/meta
4352
path:
@@ -48,6 +57,8 @@ dependency_overrides:
4857
path: ../../third_party/pkg/process
4958
skia_gold_client:
5059
path: ../../testing/skia_gold_client
60+
smith:
61+
path: ../../../third_party/dart/pkg/smith
5162
sky_engine:
5263
path: ../../sky/packages/sky_engine
5364
typed_data:
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import 'package:litetest/litetest.dart';
2+
3+
import '../bin/utils/adb_logcat_filtering.dart';
4+
import 'src/fake_adb_logcat.dart';
5+
6+
void main() {
7+
/// Simulates the filtering of logcat output [lines].
8+
Iterable<String> filter(Iterable<String> lines, {int? filterProcessId}) {
9+
if (lines.isEmpty) {
10+
throw StateError('No log lines to filter. This is unexpected.');
11+
}
12+
return lines.where((String line) {
13+
final AdbLogLine? logLine = AdbLogLine.tryParse(line);
14+
if (logLine == null) {
15+
throw StateError('Invalid log line: $line');
16+
}
17+
final bool isVerbose = logLine.isVerbose(filterProcessId: filterProcessId?.toString());
18+
return !isVerbose;
19+
});
20+
}
21+
22+
test('should always retain fatal logs', () {
23+
final FakeAdbLogcat logcat = FakeAdbLogcat();
24+
final FakeAdbProcess process = logcat.withProcess();
25+
process.fatal('Something', 'A bad thing happened');
26+
27+
final Iterable<String> filtered = filter(logcat.drain());
28+
expect(filtered, hasLength(1));
29+
expect(filtered.first, contains('Something: A bad thing happened'));
30+
});
31+
32+
test('should never retain debug logs', () {
33+
final FakeAdbLogcat logcat = FakeAdbLogcat();
34+
final FakeAdbProcess process = logcat.withProcess();
35+
final String tag = AdbLogLine.knownNoiseTags.first;
36+
process.debug(tag, 'A debug message');
37+
38+
final Iterable<String> filtered = filter(logcat.drain());
39+
expect(filtered, isEmpty);
40+
});
41+
42+
test('should never retain logs from known "noise" tags', () {
43+
final FakeAdbLogcat logcat = FakeAdbLogcat();
44+
final FakeAdbProcess process = logcat.withProcess();
45+
final String tag = AdbLogLine.knownNoiseTags.first;
46+
process.info(tag, 'Flutter flutter flutter');
47+
48+
final Iterable<String> filtered = filter(logcat.drain());
49+
expect(filtered, isEmpty);
50+
});
51+
52+
test('should always retain logs from known "useful" tags', () {
53+
final FakeAdbLogcat logcat = FakeAdbLogcat();
54+
final FakeAdbProcess process = logcat.withProcess();
55+
final String tag = AdbLogLine.knownUsefulTags.first;
56+
process.info(tag, 'A useful message');
57+
58+
final Iterable<String> filtered = filter(logcat.drain());
59+
expect(filtered, hasLength(1));
60+
expect(filtered.first, contains('$tag: A useful message'));
61+
});
62+
63+
test('if a process ID is passed, retain the log', () {
64+
final FakeAdbLogcat logcat = FakeAdbLogcat();
65+
final FakeAdbProcess process = logcat.withProcess();
66+
process.info('SomeTag', 'A message');
67+
68+
final Iterable<String> filtered = filter(logcat.drain(), filterProcessId: process.processId);
69+
expect(filtered, hasLength(1));
70+
expect(filtered.first, contains('SomeTag: A message'));
71+
});
72+
73+
test('even if a process ID passed, retain logs containing "flutter"', () {
74+
final FakeAdbLogcat logcat = FakeAdbLogcat();
75+
final FakeAdbProcess process = logcat.withProcess();
76+
process.info('SomeTag', 'A message with flutter');
77+
78+
final Iterable<String> filtered = filter(logcat.drain(), filterProcessId: process.processId);
79+
expect(filtered, hasLength(1));
80+
expect(filtered.first, contains('SomeTag: A message with flutter'));
81+
});
82+
83+
test('should retain E-level flags from known "useful" error tags', () {
84+
final FakeAdbLogcat logcat = FakeAdbLogcat();
85+
final FakeAdbProcess process = logcat.withProcess();
86+
final String tag = AdbLogLine.knownUsefulErrorTags.first;
87+
process.error(tag, 'An error message');
88+
process.info(tag, 'An info message');
89+
90+
final Iterable<String> filtered = filter(logcat.drain());
91+
expect(filtered, hasLength(1));
92+
expect(filtered.first, contains('$tag: An error message'));
93+
});
94+
95+
test('should filter out error logs from unimportant processes', () {
96+
final FakeAdbLogcat logcat = FakeAdbLogcat();
97+
final FakeAdbProcess process = logcat.withProcess();
98+
99+
// I hate this one.
100+
const String tag = 'gs.intelligence';
101+
process.error(tag, 'No package ID ff found for resource ID 0xffffffff.');
102+
103+
final Iterable<String> filtered = filter(logcat.drain());
104+
expect(filtered, isEmpty);
105+
});
106+
}

0 commit comments

Comments
 (0)