Skip to content

Commit d81de6c

Browse files
bneradtbneradt
andauthored
autest: print worker diagnostics for exceptions (#13017)
Parallel autest runs can fail with worker exceptions that are not tied to a specific failed test. In CI that currently leaves only the summary counts, which makes the failure hard to diagnose. Print worker diagnostics whenever a worker reports exceptions or exits non-zero without attributing the problem to failed tests. Also accept plural summary keys while parsing autest output. Co-authored-by: bneradt <bneradt@yahooinc.com>
1 parent ac96eae commit d81de6c

1 file changed

Lines changed: 71 additions & 3 deletions

File tree

tests/autest-parallel.py.in

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -295,17 +295,17 @@ def parse_autest_output(output: str) -> dict:
295295
result['skipped'] = int(line.split(':')[-1].strip())
296296
except ValueError:
297297
pass
298-
elif 'Warning:' in line:
298+
elif re.match(r'warnings?:', line, re.IGNORECASE):
299299
try:
300300
result['warnings'] = int(line.split(':')[-1].strip())
301301
except ValueError:
302302
pass
303-
elif 'Exception:' in line:
303+
elif re.match(r'exceptions?:', line, re.IGNORECASE):
304304
try:
305305
result['exceptions'] = int(line.split(':')[-1].strip())
306306
except ValueError:
307307
pass
308-
elif 'Unknown:' in line:
308+
elif re.match(r'unknowns?:', line, re.IGNORECASE):
309309
try:
310310
result['unknown'] = int(line.split(':')[-1].strip())
311311
except ValueError:
@@ -367,6 +367,60 @@ def extract_failure_output(output: str, failed_tests: List[str]) -> Dict[str, st
367367
return details
368368

369369

370+
def extract_worker_diagnostics(output: str) -> str:
371+
"""
372+
Extract the most relevant worker diagnostics for exception/setup failures.
373+
374+
When autest reports an exception without attributing it to a specific
375+
failed test, the best fallback is the worker's captured output. This
376+
helper prefers traceback/error sections but falls back to the full worker
377+
output if no obvious diagnostic block is found.
378+
"""
379+
clean = strip_ansi(output).strip()
380+
if not clean:
381+
return ""
382+
383+
lines = clean.split('\n')
384+
ranges: List[Tuple[int, int]] = []
385+
386+
def is_marker(line: str) -> bool:
387+
lower = line.lower()
388+
return (
389+
'traceback (most recent call last):' in lower or 'error:' in lower or 'timeout' in lower or
390+
'interrupted' in lower
391+
)
392+
393+
for i, line in enumerate(lines):
394+
if not is_marker(line):
395+
continue
396+
start = max(0, i - 5)
397+
end = i + 1
398+
while end < len(lines):
399+
candidate = lines[end].strip()
400+
if not candidate and end > i + 1:
401+
break
402+
end += 1
403+
ranges.append((start, min(end, len(lines))))
404+
405+
if not ranges:
406+
return clean
407+
408+
merged_ranges: List[Tuple[int, int]] = []
409+
for start, end in ranges:
410+
if merged_ranges and start <= merged_ranges[-1][1]:
411+
merged_ranges[-1] = (merged_ranges[-1][0], max(merged_ranges[-1][1], end))
412+
else:
413+
merged_ranges.append((start, end))
414+
415+
blocks = []
416+
for start, end in merged_ranges:
417+
block = '\n'.join(lines[start:end]).rstrip()
418+
if block:
419+
blocks.append(block)
420+
421+
return '\n\n...\n\n'.join(blocks) if blocks else clean
422+
423+
370424
def run_single_test(test: str, script_dir: Path, sandbox: Path, ats_bin: str, build_root: str, extra_args: List[str],
371425
env: dict) -> Tuple[str, float, str, str]:
372426
"""
@@ -672,6 +726,20 @@ def print_summary(results: List[TestResult], total_duration: float, expected_tim
672726
print(details[test_name])
673727
print("-" * 70)
674728

729+
worker_diagnostics = [
730+
r for r in results
731+
if r.output and (r.exceptions > 0 or (r.return_code != 0 and not r.failed_tests))
732+
]
733+
if worker_diagnostics:
734+
print("-" * 70)
735+
print("WORKER DIAGNOSTICS:")
736+
print("-" * 70)
737+
for r in worker_diagnostics:
738+
label = "Serial" if r.is_serial else f"Worker {r.worker_id}"
739+
print(f"\n--- {label} (return code {r.return_code}) ---")
740+
print(extract_worker_diagnostics(r.output))
741+
print("-" * 70)
742+
675743
# Check for timing discrepancies
676744
if expected_timings and actual_timings:
677745
timing_warnings = []

0 commit comments

Comments
 (0)