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