Skip to content

Commit 30c5751

Browse files
authored
[Android] Refactor the flutter run Android console output test (#115023)
* [Android] Refactor the flutter run Android console output test * CI bump
1 parent 577a88b commit 30c5751

File tree

2 files changed

+112
-154
lines changed

2 files changed

+112
-154
lines changed

dev/devicelab/bin/tasks/run_release_test.dart

Lines changed: 2 additions & 154 deletions
Original file line numberDiff line numberDiff line change
@@ -2,161 +2,9 @@
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-
9-
import 'package:flutter_devicelab/framework/devices.dart';
105
import 'package:flutter_devicelab/framework/framework.dart';
11-
import 'package:flutter_devicelab/framework/task_result.dart';
12-
import 'package:flutter_devicelab/framework/utils.dart';
13-
import 'package:path/path.dart' as path;
6+
import 'package:flutter_devicelab/tasks/run_tests.dart';
147

158
void main() {
16-
task(() async {
17-
final Device device = await devices.workingDevice;
18-
await device.unlock();
19-
final Directory appDir = dir(path.join(flutterDirectory.path, 'dev/integration_tests/ui'));
20-
await inDirectory(appDir, () async {
21-
final Completer<void> ready = Completer<void>();
22-
final List<String> stdout = <String>[];
23-
final List<String> stderr = <String>[];
24-
25-
// Uninstall if the app is already installed on the device to get to a clean state.
26-
print('uninstalling...');
27-
final Process uninstall = await startProcess(
28-
path.join(flutterDirectory.path, 'bin', 'flutter'),
29-
<String>['--suppress-analytics', 'install', '--uninstall-only', '-d', device.deviceId],
30-
)..stdout
31-
.transform<String>(utf8.decoder)
32-
.transform<String>(const LineSplitter())
33-
.listen((String line) {
34-
print('uninstall:stdout: $line');
35-
})..stderr
36-
.transform<String>(utf8.decoder)
37-
.transform<String>(const LineSplitter())
38-
.listen((String line) {
39-
print('uninstall:stderr: $line');
40-
stderr.add(line);
41-
});
42-
if (await uninstall.exitCode != 0) {
43-
throw 'flutter install --uninstall-only failed.';
44-
}
45-
46-
print('run: starting...');
47-
final Process run = await startProcess(
48-
path.join(flutterDirectory.path, 'bin', 'flutter'),
49-
<String>['--suppress-analytics', 'run', '--release', '-d', device.deviceId, 'lib/main.dart'],
50-
isBot: false, // we just want to test the output, not have any debugging info
51-
);
52-
int? runExitCode;
53-
run.stdout
54-
.transform<String>(utf8.decoder)
55-
.transform<String>(const LineSplitter())
56-
.listen((String line) {
57-
print('run:stdout: $line');
58-
if (
59-
!line.startsWith('Building flutter tool...') &&
60-
!line.startsWith('Running "flutter pub get" in ui...') &&
61-
!line.startsWith('Initializing gradle...') &&
62-
!line.contains('settings_aar.gradle') &&
63-
!line.startsWith('Resolving dependencies...') &&
64-
// Catch engine piped output from unrelated concurrent Flutter apps
65-
!line.contains(RegExp(r'[A-Z]\/flutter \([0-9]+\):')) &&
66-
// Empty lines could be due to the progress spinner breaking up.
67-
line.length > 1
68-
) {
69-
stdout.add(line);
70-
}
71-
if (line.contains('Quit (terminate the application on the device).')) {
72-
ready.complete();
73-
}
74-
});
75-
run.stderr
76-
.transform<String>(utf8.decoder)
77-
.transform<String>(const LineSplitter())
78-
// TODO(egarciad): Remove once https://github.com/flutter/flutter/issues/95131 is fixed.
79-
.skipWhile((String line) => line.contains('Mapping new ns'))
80-
.listen((String line) {
81-
print('run:stderr: $line');
82-
stderr.add(line);
83-
});
84-
unawaited(run.exitCode.then<void>((int exitCode) { runExitCode = exitCode; }));
85-
await Future.any<dynamic>(<Future<dynamic>>[ ready.future, run.exitCode ]);
86-
if (runExitCode != null) {
87-
throw 'Failed to run test app; runner unexpected exited, with exit code $runExitCode.';
88-
}
89-
run.stdin.write('q');
90-
91-
await run.exitCode;
92-
93-
if (stderr.isNotEmpty) {
94-
throw 'flutter run --release had output on standard error.';
95-
}
96-
97-
_findNextMatcherInList(
98-
stdout,
99-
(String line) => line.startsWith('Launching lib/main.dart on ') && line.endsWith(' in release mode...'),
100-
'Launching lib/main.dart on',
101-
);
102-
103-
_findNextMatcherInList(
104-
stdout,
105-
(String line) => line.startsWith("Running Gradle task 'assembleRelease'..."),
106-
"Running Gradle task 'assembleRelease'...",
107-
);
108-
109-
_findNextMatcherInList(
110-
stdout,
111-
(String line) => line.contains('Built build/app/outputs/flutter-apk/app-release.apk (') && line.contains('MB).'),
112-
'Built build/app/outputs/flutter-apk/app-release.apk',
113-
);
114-
115-
_findNextMatcherInList(
116-
stdout,
117-
(String line) => line.startsWith('Installing build/app/outputs/flutter-apk/app-release.apk...'),
118-
'Installing build/app/outputs/flutter-apk/app-release.apk...',
119-
);
120-
121-
_findNextMatcherInList(
122-
stdout,
123-
(String line) => line.contains('Quit (terminate the application on the device).'),
124-
'q Quit (terminate the application on the device)',
125-
);
126-
127-
_findNextMatcherInList(
128-
stdout,
129-
(String line) => line == 'Application finished.',
130-
'Application finished.',
131-
);
132-
});
133-
return TaskResult.success(null);
134-
});
135-
}
136-
137-
void _findNextMatcherInList(
138-
List<String> list,
139-
bool Function(String testLine) matcher,
140-
String errorMessageExpectedLine
141-
) {
142-
final List<String> copyOfListForErrorMessage = List<String>.from(list);
143-
144-
while (list.isNotEmpty) {
145-
final String nextLine = list.first;
146-
list.removeAt(0);
147-
148-
if (matcher(nextLine)) {
149-
return;
150-
}
151-
}
152-
153-
throw '''
154-
Did not find expected line
155-
156-
$errorMessageExpectedLine
157-
158-
in flutter run --release stdout
159-
160-
$copyOfListForErrorMessage
161-
''';
9+
task(createAndroidRunReleaseTest());
16210
}

dev/devicelab/lib/tasks/run_tests.dart

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,121 @@ import '../framework/framework.dart';
1111
import '../framework/task_result.dart';
1212
import '../framework/utils.dart';
1313

14+
TaskFunction createAndroidRunReleaseTest() {
15+
return AndroidRunOutputTest(release: true);
16+
}
17+
1418
TaskFunction createMacOSRunReleaseTest() {
1519
return DesktopRunOutputTest(
1620
// TODO(cbracken): https://github.com/flutter/flutter/issues/87508#issuecomment-1043753201
1721
// Switch to dev/integration_tests/ui once we have CocoaPods working on M1 Macs.
1822
'${flutterDirectory.path}/examples/hello_world',
1923
'lib/main.dart',
2024
release: true,
25+
allowStderr: true,
2126
);
2227
}
2328

29+
class AndroidRunOutputTest extends RunOutputTask {
30+
AndroidRunOutputTest({required super.release}) : super(
31+
'${flutterDirectory.path}/dev/integration_tests/ui',
32+
'lib/main.dart',
33+
);
34+
35+
@override
36+
Future<void> prepare(String deviceId) async {
37+
// Uninstall if the app is already installed on the device to get to a clean state.
38+
final List<String> stderr = <String>[];
39+
print('uninstalling...');
40+
final Process uninstall = await startFlutter(
41+
'install',
42+
options: <String>['--suppress-analytics', '--uninstall-only', '-d', deviceId],
43+
isBot: false,
44+
);
45+
uninstall.stdout
46+
.transform<String>(utf8.decoder)
47+
.transform<String>(const LineSplitter())
48+
.listen((String line) {
49+
print('uninstall:stdout: $line');
50+
});
51+
uninstall.stderr
52+
.transform<String>(utf8.decoder)
53+
.transform<String>(const LineSplitter())
54+
.listen((String line) {
55+
print('uninstall:stderr: $line');
56+
stderr.add(line);
57+
});
58+
if (await uninstall.exitCode != 0) {
59+
throw 'flutter install --uninstall-only failed.';
60+
}
61+
if (stderr.isNotEmpty) {
62+
throw 'flutter install --uninstall-only had output on standard error.';
63+
}
64+
}
65+
66+
@override
67+
bool isExpectedStderr(String line) {
68+
// TODO(egarciad): Remove once https://github.com/flutter/flutter/issues/95131 is fixed.
69+
return line.contains('Mapping new ns');
70+
}
71+
72+
@override
73+
TaskResult verify(List<String> stdout, List<String> stderr) {
74+
_findNextMatcherInList(
75+
stdout,
76+
(String line) => line.startsWith('Launching lib/main.dart on ') && line.endsWith(' in release mode...'),
77+
'Launching lib/main.dart on',
78+
);
79+
80+
_findNextMatcherInList(
81+
stdout,
82+
(String line) => line.startsWith("Running Gradle task 'assembleRelease'..."),
83+
"Running Gradle task 'assembleRelease'...",
84+
);
85+
86+
_findNextMatcherInList(
87+
stdout,
88+
(String line) => line.contains('Built build/app/outputs/flutter-apk/app-release.apk (') && line.contains('MB).'),
89+
'Built build/app/outputs/flutter-apk/app-release.apk',
90+
);
91+
92+
_findNextMatcherInList(
93+
stdout,
94+
(String line) => line.startsWith('Installing build/app/outputs/flutter-apk/app-release.apk...'),
95+
'Installing build/app/outputs/flutter-apk/app-release.apk...',
96+
);
97+
98+
_findNextMatcherInList(
99+
stdout,
100+
(String line) => line.contains('Quit (terminate the application on the device).'),
101+
'q Quit (terminate the application on the device)',
102+
);
103+
104+
_findNextMatcherInList(
105+
stdout,
106+
(String line) => line == 'Application finished.',
107+
'Application finished.',
108+
);
109+
110+
return TaskResult.success(null);
111+
}
112+
}
113+
24114
class DesktopRunOutputTest extends RunOutputTask {
25115
DesktopRunOutputTest(
26116
super.testDirectory,
27117
super.testTarget, {
28118
required super.release,
119+
this.allowStderr = false,
29120
}
30121
);
31122

123+
/// Whether `flutter run` is expected to produce output on stderr.
124+
final bool allowStderr;
125+
126+
@override
127+
bool isExpectedStderr(String line) => allowStderr;
128+
32129
@override
33130
TaskResult verify(List<String> stdout, List<String> stderr) {
34131
_findNextMatcherInList(
@@ -80,6 +177,8 @@ abstract class RunOutputTask {
80177
final List<String> stdout = <String>[];
81178
final List<String> stderr = <String>[];
82179

180+
await prepare(deviceId);
181+
83182
final List<String> options = <String>[
84183
testTarget,
85184
'-d',
@@ -107,6 +206,7 @@ abstract class RunOutputTask {
107206
run.stderr
108207
.transform<String>(utf8.decoder)
109208
.transform<String>(const LineSplitter())
209+
.skipWhile(isExpectedStderr)
110210
.listen((String line) {
111211
print('run:stderr: $line');
112212
stderr.add(line);
@@ -120,10 +220,20 @@ abstract class RunOutputTask {
120220

121221
await run.exitCode;
122222

223+
if (stderr.isNotEmpty) {
224+
throw 'flutter run ${release ? '--release' : ''} had unexpected output on standard error.';
225+
}
226+
123227
return verify(stdout, stderr);
124228
});
125229
}
126230

231+
/// Prepare the device for running the test app.
232+
Future<void> prepare(String deviceId) => Future<void>.value();
233+
234+
/// Returns true if this stderr output line is expected.
235+
bool isExpectedStderr(String line) => false;
236+
127237
/// Verify the output of `flutter run`.
128238
TaskResult verify(List<String> stdout, List<String> stderr) => throw UnimplementedError('verify is not implemented');
129239

0 commit comments

Comments
 (0)