2323// to the runFlutter function.
2424
2525// @dart = 2.8
26+ // This file is ready to transition, just uncomment /*?*/, /*!*/, and /*late*/.
2627
2728import 'dart:async' ;
2829import 'dart:convert' ;
@@ -113,39 +114,67 @@ class Multiple extends Transition {
113114
114115 @override
115116 String toString () {
117+ if (_originalPatterns.length == patterns.length) {
118+ return '${_originalPatterns .map (describe ).join (', ' )} (all matched)' ;
119+ }
116120 return '${_originalPatterns .map (describe ).join (', ' )} (matched ${_originalPatterns .length - patterns .length } so far)' ;
117121 }
118122}
119123
120- class ProcessTestResult {
121- const ProcessTestResult (this .exitCode, this .stdout, this .stderr);
122- final int exitCode;
123- final List <String > stdout;
124- final List <String > stderr;
124+ class LogLine {
125+ const LogLine (this .channel, this .stamp, this .message);
126+ final String channel;
127+ final String stamp;
128+ final String message;
129+
130+ bool get couldBeCrash => message.contains ('Oops; flutter has exited unexpectedly:' );
125131
126132 @override
127- String toString () => 'exit code $exitCode \n stdout:\n ${stdout .join ('\n ' )}\n stderr:\n ${stderr .join ('\n ' )}\n ' ;
128- }
133+ String toString () => '$stamp $channel : $message ' ;
129134
130- String clarify (String line) {
131- return line.runes.map <String >((int rune) {
132- if (rune >= 0x20 && rune <= 0x7F ) {
133- return String .fromCharCode (rune);
134- }
135- switch (rune) {
136- case 0x00 : return '<NUL>' ;
137- case 0x07 : return '<BEL>' ;
138- case 0x08 : return '<TAB>' ;
139- case 0x09 : return '<BS>' ;
140- case 0x0A : return '<LF>' ;
141- case 0x0D : return '<CR>' ;
142- }
143- return '<${rune .toRadixString (16 ).padLeft (rune <= 0xFF ? 2 : rune <= 0xFFFF ? 4 : 5 , '0' )}>' ;
144- }).join ('' );
135+ void printClearly () {
136+ print ('$stamp $channel : ${clarify (message )}' );
137+ }
138+
139+ static String clarify (String line) {
140+ return line.runes.map <String >((int rune) {
141+ if (rune >= 0x20 && rune <= 0x7F ) {
142+ return String .fromCharCode (rune);
143+ }
144+ switch (rune) {
145+ case 0x00 : return '<NUL>' ;
146+ case 0x07 : return '<BEL>' ;
147+ case 0x08 : return '<TAB>' ;
148+ case 0x09 : return '<BS>' ;
149+ case 0x0A : return '<LF>' ;
150+ case 0x0D : return '<CR>' ;
151+ }
152+ return '<${rune .toRadixString (16 ).padLeft (rune <= 0xFF ? 2 : rune <= 0xFFFF ? 4 : 5 , '0' )}>' ;
153+ }).join ('' );
154+ }
145155}
146156
147- void printClearly (String line) {
148- print (clarify (line));
157+ class ProcessTestResult {
158+ const ProcessTestResult (this .exitCode, this .logs);
159+ final int exitCode;
160+ final List <LogLine > logs;
161+
162+ List <String > get stdout {
163+ return logs
164+ .where ((LogLine log) => log.channel == 'stdout' )
165+ .map <String >((LogLine log) => log.message)
166+ .toList ();
167+ }
168+
169+ List <String > get stderr {
170+ return logs
171+ .where ((LogLine log) => log.channel == 'stderr' )
172+ .map <String >((LogLine log) => log.message)
173+ .toList ();
174+ }
175+
176+ @override
177+ String toString () => 'exit code $exitCode \n logs:\n ${logs .join ('\n ' )}\n ' ;
149178}
150179
151180Future <ProcessTestResult > runFlutter (
@@ -156,13 +185,12 @@ Future<ProcessTestResult> runFlutter(
156185 bool logging = true ,
157186 Duration expectedMaxDuration = const Duration (seconds: 25 ), // must be less than test timeout of 30 seconds!
158187}) async {
188+ final Stopwatch clock = Stopwatch ()..start ();
159189 final Process process = await processManager.start (
160190 < String > [flutterBin, ...arguments],
161191 workingDirectory: workingDirectory,
162192 );
163- final List <String > stdoutLog = < String > [];
164- final List <String > stderrLog = < String > [];
165- final List <String > stdinLog = < String > [];
193+ final List <LogLine > logs = < LogLine > [];
166194 int nextTransition = 0 ;
167195 void describeStatus () {
168196 if (transitions.isNotEmpty) {
@@ -175,13 +203,13 @@ Future<ProcessTestResult> runFlutter(
175203 ' ' } ${transitions [index ]}' );
176204 }
177205 }
178- if (stdoutLog.isEmpty && stderrLog.isEmpty && stdinLog .isEmpty) {
206+ if (logs .isEmpty) {
179207 print ('So far nothing has been logged${ debug ? "" : "; use debug:true to print all output" }.' );
180208 } else {
181209 print ('Log${ debug ? "" : " (only contains logged lines; use debug:true to print all output)" }:' );
182- stdoutLog. map < String >(( String line) => 'stdout: $ line ' ). forEach (printClearly);
183- stderrLog. map < String >(( String line) => 'stderr: $ line ' ). forEach ( printClearly);
184- stdinLog. map < String >(( String line) => 'stdin: $ line ' ). forEach (printClearly);
210+ for ( final LogLine log in logs) {
211+ log. printClearly ( );
212+ }
185213 }
186214 }
187215 bool streamingLogs = false ;
@@ -190,28 +218,30 @@ Future<ProcessTestResult> runFlutter(
190218 if (! streamingLogs) {
191219 streamingLogs = true ;
192220 if (! debug) {
193- print ('Test is taking a long time.' );
221+ print ('Test is taking a long time (${ clock . elapsed . inSeconds } seconds so far) .' );
194222 }
195223 describeStatus ();
196224 print ('(streaming all logs from this point on...)' );
197225 } else {
198226 print ('(taking a long time...)' );
199227 }
200228 }
229+ String stamp () => '[${(clock .elapsed .inMilliseconds / 1000.0 ).toStringAsFixed (1 ).padLeft (5 , " " )}s]' ;
201230 void processStdout (String line) {
231+ final LogLine log = LogLine ('stdout' , stamp (), line);
202232 if (logging) {
203- stdoutLog .add (line );
233+ logs .add (log );
204234 }
205235 if (streamingLogs) {
206- print ( 'stdout: $ line ' );
236+ log. printClearly ( );
207237 }
208238 if (nextTransition < transitions.length && transitions[nextTransition].matches (line)) {
209239 if (streamingLogs) {
210240 print ('(matched ${transitions [nextTransition ]})' );
211241 }
212242 if (transitions[nextTransition].logging != null ) {
213243 if (! logging && transitions[nextTransition].logging/*!*/ ) {
214- stdoutLog .add (line );
244+ logs .add (log );
215245 }
216246 logging = transitions[nextTransition].logging/*!*/ ;
217247 if (streamingLogs) {
@@ -225,9 +255,10 @@ Future<ProcessTestResult> runFlutter(
225255 if (transitions[nextTransition].handler != null ) {
226256 final String /*?*/ command = transitions[nextTransition].handler/*!*/ (line);
227257 if (command != null ) {
228- stdinLog.add (command);
258+ final LogLine inLog = LogLine ('stdin' , stamp (), command);
259+ logs.add (inLog);
229260 if (streamingLogs) {
230- print ( 'stdin: $ command ' );
261+ inLog. printClearly ( );
231262 }
232263 process.stdin.write (command);
233264 }
@@ -238,9 +269,10 @@ Future<ProcessTestResult> runFlutter(
238269 }
239270 }
240271 void processStderr (String line) {
241- stderrLog.add (line);
272+ final LogLine log = LogLine ('stdout' , stamp (), line);
273+ logs.add (log);
242274 if (streamingLogs) {
243- print ( 'stderr: $ line ' );
275+ log. printClearly ( );
244276 }
245277 }
246278 if (debug) {
@@ -251,19 +283,24 @@ Future<ProcessTestResult> runFlutter(
251283 process.stdout.transform <String >(utf8.decoder).transform <String >(const LineSplitter ()).listen (processStdout);
252284 process.stderr.transform <String >(utf8.decoder).transform <String >(const LineSplitter ()).listen (processStderr);
253285 unawaited (process.exitCode.timeout (expectedMaxDuration, onTimeout: () {
254- print ('(process is not quitting, trying to send a "q" just in case that helps)' );
286+ print ('${ stamp ()} (process is not quitting, trying to send a "q" just in case that helps)' );
255287 print ('(a functional test should never reach this point)' );
288+ final LogLine inLog = LogLine ('stdin' , stamp (), 'q' );
289+ logs.add (inLog);
290+ if (streamingLogs) {
291+ inLog.printClearly ();
292+ }
256293 process.stdin.write ('q' );
257- return null ;
294+ return - 1 ; // discarded
258295 }).catchError ((Object error) { /* ignore the error here, it'll be reported on the next line */ }));
259296 final int exitCode = await process.exitCode;
260297 if (streamingLogs) {
261- print ('(process terminated with exit code $exitCode )' );
298+ print ('${ stamp ()} (process terminated with exit code $exitCode )' );
262299 }
263300 timeout? .cancel ();
264301 if (nextTransition < transitions.length) {
265302 print ('The subprocess terminated before all the expected transitions had been matched.' );
266- if (stderrLog .any ((String line) => line.contains ( 'Oops; flutter has exited unexpectedly:' ) )) {
303+ if (logs .any ((LogLine line) => line.couldBeCrash )) {
267304 print ('The subprocess may in fact have crashed. Check the stderr logs below.' );
268305 }
269306 print ('The transition that we were hoping to see next but that we never saw was:' );
@@ -275,9 +312,9 @@ Future<ProcessTestResult> runFlutter(
275312 throw TestFailure ('Missed some expected transitions.' );
276313 }
277314 if (streamingLogs) {
278- print ('(completed execution successfully!)' );
315+ print ('${ stamp ()} (completed execution successfully!)' );
279316 }
280- return ProcessTestResult (exitCode, stdoutLog, stderrLog );
317+ return ProcessTestResult (exitCode, logs );
281318}
282319
283320const int progressMessageWidth = 64 ;
0 commit comments