1818import getpass
1919import logging
2020import os
21+ import re
2122import shutil
2223import subprocess
2324import sys
@@ -350,8 +351,6 @@ def run_pytest(phase: int, test_args: list[str], log_file: str) -> None:
350351 '--color=yes' ,
351352 '--no-header' ,
352353 '--maxfail=0' ,
353- '--log-file-level=debug' ,
354- f'--log-file={ log_path } ' ,
355354 f'--log-cli-level={ logging .getLevelName (state .log_level )} ' ,
356355 f'--vm={ state .vm_image_url } ' ,
357356 ] + test_args
@@ -363,16 +362,43 @@ def run_pytest(phase: int, test_args: list[str], log_file: str) -> None:
363362
364363 logging .debug (f"Running: { ' ' .join (cmd )} " )
365364 try :
366- subprocess .run (cmd , check = True , timeout = 3600 )
367- logging .info (f"Phase { phase } : Tests completed successfully" )
368- except subprocess .CalledProcessError as e :
369- logging .warning (f"Phase { phase } : Tests failed with exit code { e .returncode } " )
370- state .tests_failed = True
371- # Don't raise, allow other phases to run
365+ ansi_escape = re .compile (br'\x1b\[[0-9;]*[a-zA-Z]' )
366+
367+ # Launch pytest. We force colors so the terminal output stays pretty.
368+ # 'bufsize=1' and 'universal_newlines=False' allow us to process line by line.
369+ process = subprocess .Popen (
370+ cmd ,
371+ stdout = subprocess .PIPE ,
372+ stderr = subprocess .STDOUT
373+ )
374+
375+ assert process .stdout is not None
376+ with open (log_path , "wb" ) as f :
377+ # Read from the pipe until the process finishes
378+ for line in iter (process .stdout .readline , b'' ):
379+ # 1. Write the original colorful line to the actual terminal
380+ sys .stdout .buffer .write (line )
381+ sys .stdout .buffer .flush ()
382+
383+ # 2. Strip the codes and write the clean text to the file
384+ clean_line = ansi_escape .sub (b'' , line )
385+ f .write (clean_line )
386+ f .flush ()
387+
388+ process .wait ()
389+ if process .returncode != 0 :
390+ logging .warning (f"Phase { phase } : Tests failed with exit code { process .returncode } " )
391+ state .tests_failed = True
392+ else :
393+ logging .info (f"Phase { phase } : Tests completed successfully" )
372394 except subprocess .TimeoutExpired :
373395 logging .error (f"Phase { phase } : Tests timed out" )
374396 state .tests_failed = True
375397 raise
398+ except Exception as e :
399+ logging .error (f"Phase { phase } : Tests failed: { e } " )
400+ state .tests_failed = True
401+ raise
376402
377403
378404def setup_config_files () -> None :
0 commit comments