@@ -319,6 +319,54 @@ def parse_autest_output(output: str) -> dict:
319319 return result
320320
321321
322+ def extract_failure_output (output : str , failed_tests : List [str ]) -> Dict [str , str ]:
323+ """
324+ Extract the detailed autest output section for each failed test.
325+
326+ Autest emits a block per test starting with a line like
327+ ``Test: <name>: Failed`` and continuing with indented detail lines until
328+ the next top-level ``Test:`` line or the end of meaningful output.
329+
330+ Args:
331+ output: Raw autest output from a worker
332+ failed_tests: List of test names that failed
333+
334+ Returns:
335+ Dictionary mapping each failed test name to its detail block
336+ """
337+ if not failed_tests :
338+ return {}
339+
340+ clean = strip_ansi (output )
341+ lines = clean .split ('\n ' )
342+
343+ # Identify line ranges for each top-level "Test: <name>:" block.
344+ test_block_re = re .compile (r'^Test:\s+(\S+):\s+(Passed|Failed|Skipped)' , re .IGNORECASE )
345+ block_starts : List [Tuple [int , str ]] = []
346+ for i , line in enumerate (lines ):
347+ m = test_block_re .match (line .strip ())
348+ if m :
349+ block_starts .append ((i , m .group (1 )))
350+
351+ failed_set = set (failed_tests )
352+ details : Dict [str , str ] = {}
353+
354+ for idx , (start_line , name ) in enumerate (block_starts ):
355+ if name not in failed_set :
356+ continue
357+ # The block extends until the next top-level Test: line or end of
358+ # output.
359+ if idx + 1 < len (block_starts ):
360+ end_line = block_starts [idx + 1 ][0 ]
361+ else :
362+ end_line = len (lines )
363+ block = '\n ' .join (lines [start_line :end_line ]).rstrip ()
364+ if block :
365+ details [name ] = block
366+
367+ return details
368+
369+
322370def run_single_test (test : str , script_dir : Path , sandbox : Path , ats_bin : str , build_root : str , extra_args : List [str ],
323371 env : dict ) -> Tuple [str , float , str , str ]:
324372 """
@@ -610,6 +658,20 @@ def print_summary(results: List[TestResult], total_duration: float, expected_tim
610658 for test in sorted (all_failed_tests ):
611659 print (f" - { test } " )
612660
661+ # Print detailed failure output extracted from each worker's autest
662+ # output so CI logs contain actionable diagnostics.
663+ print ("-" * 70 )
664+ print ("FAILED TEST OUTPUT:" )
665+ print ("-" * 70 )
666+ for r in results :
667+ if not r .failed_tests or not r .output :
668+ continue
669+ details = extract_failure_output (r .output , r .failed_tests )
670+ for test_name in sorted (details ):
671+ print (f"\n --- { test_name } ---" )
672+ print (details [test_name ])
673+ print ("-" * 70 )
674+
613675 # Check for timing discrepancies
614676 if expected_timings and actual_timings :
615677 timing_warnings = []
0 commit comments