5050 + (".bat" if os .name == "nt" else "" )
5151)
5252
53- logcat_started = False
53+ # Whether we've seen any output from Python yet.
54+ python_started = False
55+
56+ # Buffer for verbose output which will be displayed only if a test fails and
57+ # there has been no output from Python.
58+ hidden_output = []
59+
60+
61+ def log_verbose (context , line , stream = sys .stdout ):
62+ if context .verbose :
63+ stream .write (line )
64+ else :
65+ hidden_output .append ((stream , line ))
5466
5567
5668def delete_glob (pattern ):
@@ -118,7 +130,7 @@ def android_env(host):
118130 env_script = ANDROID_DIR / "android-env.sh"
119131 env_output = subprocess .run (
120132 f"set -eu; "
121- f"export HOST={ host } ; "
133+ f"HOST={ host } ; "
122134 f"PREFIX={ prefix } ; "
123135 f". { env_script } ; "
124136 f"export" ,
@@ -453,17 +465,19 @@ async def logcat_task(context, initial_devices):
453465
454466 # `--pid` requires API level 24 or higher.
455467 args = [adb , "-s" , serial , "logcat" , "--pid" , pid , "--format" , "tag" ]
456- hidden_output = []
468+ logcat_started = False
457469 async with async_process (
458470 * args , stdout = subprocess .PIPE , stderr = subprocess .STDOUT ,
459471 ) as process :
460472 while line := (await process .stdout .readline ()).decode (* DECODE_ARGS ):
461473 if match := re .fullmatch (r"([A-Z])/(.*)" , line , re .DOTALL ):
474+ logcat_started = True
462475 level , message = match .groups ()
463476 else :
464- # If the regex doesn't match, this is probably the second or
465- # subsequent line of a multi-line message. Python won't produce
466- # such messages, but other components might.
477+ # If the regex doesn't match, this is either a logcat startup
478+ # error, or the second or subsequent line of a multi-line
479+ # message. Python won't produce multi-line messages, but other
480+ # components might.
467481 level , message = None , line
468482
469483 # Exclude high-volume messages which are rarely useful.
@@ -483,25 +497,22 @@ async def logcat_task(context, initial_devices):
483497 # tag indicators from Python's stdout and stderr.
484498 for prefix in ["python.stdout: " , "python.stderr: " ]:
485499 if message .startswith (prefix ):
486- global logcat_started
487- logcat_started = True
500+ global python_started
501+ python_started = True
488502 stream .write (message .removeprefix (prefix ))
489503 break
490504 else :
491- if context .verbose :
492- # Non-Python messages add a lot of noise, but they may
493- # sometimes help explain a failure.
494- stream .write (line )
495- else :
496- hidden_output .append (line )
505+ # Non-Python messages add a lot of noise, but they may
506+ # sometimes help explain a failure.
507+ log_verbose (context , line , stream )
497508
498509 # If the device disconnects while logcat is running, which always
499510 # happens in --managed mode, some versions of adb return non-zero.
500511 # Distinguish this from a logcat startup error by checking whether we've
501- # received a message from Python yet.
512+ # received any logcat messages yet.
502513 status = await wait_for (process .wait (), timeout = 1 )
503514 if status != 0 and not logcat_started :
504- raise CalledProcessError (status , args , "" . join ( hidden_output ) )
515+ raise CalledProcessError (status , args )
505516
506517
507518def stop_app (serial ):
@@ -516,16 +527,6 @@ async def gradle_task(context):
516527 task_prefix = "connected"
517528 env ["ANDROID_SERIAL" ] = context .connected
518529
519- hidden_output = []
520-
521- def log (line ):
522- # Gradle may take several minutes to install SDK packages, so it's worth
523- # showing those messages even in non-verbose mode.
524- if context .verbose or line .startswith ('Preparing "Install' ):
525- sys .stdout .write (line )
526- else :
527- hidden_output .append (line )
528-
529530 if context .command :
530531 mode = "-c"
531532 module = context .command
@@ -550,27 +551,27 @@ def log(line):
550551 ]
551552 if context .verbose >= 2 :
552553 args .append ("--info" )
553- log ( "> " + join_command (args ))
554+ log_verbose ( context , f "> { join_command (args )} \n " )
554555
555556 try :
556557 async with async_process (
557558 * args , cwd = TESTBED_DIR , env = env ,
558559 stdout = subprocess .PIPE , stderr = subprocess .STDOUT ,
559560 ) as process :
560561 while line := (await process .stdout .readline ()).decode (* DECODE_ARGS ):
561- log (line )
562+ # Gradle may take several minutes to install SDK packages, so
563+ # it's worth showing those messages even in non-verbose mode.
564+ if line .startswith ('Preparing "Install' ):
565+ sys .stdout .write (line )
566+ else :
567+ log_verbose (context , line )
562568
563569 status = await wait_for (process .wait (), timeout = 1 )
564570 if status == 0 :
565571 exit (0 )
566572 else :
567573 raise CalledProcessError (status , args )
568574 finally :
569- # If logcat never started, then something has gone badly wrong, so the
570- # user probably wants to see the Gradle output even in non-verbose mode.
571- if hidden_output and not logcat_started :
572- sys .stdout .write ("" .join (hidden_output ))
573-
574575 # Gradle does not stop the tests when interrupted.
575576 if context .connected :
576577 stop_app (context .connected )
@@ -600,6 +601,12 @@ async def run_testbed(context):
600601 except* MySystemExit as e :
601602 raise SystemExit (* e .exceptions [0 ].args ) from None
602603 except* CalledProcessError as e :
604+ # If Python produced no output, then the user probably wants to see the
605+ # verbose output to explain why the test failed.
606+ if not python_started :
607+ for stream , line in hidden_output :
608+ stream .write (line )
609+
603610 # Extract it from the ExceptionGroup so it can be handled by `main`.
604611 raise e .exceptions [0 ]
605612
0 commit comments