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