This is an automated email from the ASF dual-hosted git repository. bcall pushed a commit to branch parallel-autest in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit f432732fa2b09cf1257ebf314cfa20af182b9398 Author: Bryan Call <[email protected]> AuthorDate: Sat Feb 7 20:47:49 2026 -0800 Add live progress line with test counts and ETA Default output now shows a single in-place progress line (using \r) that updates as workers complete: [Parallel] 145/389 tests (37%) | 8/16 workers done | 52s elapsed | ETA: 1m 28s - Shows tests done/total with percentage - Workers completed out of total - Failed and skipped counts (when non-zero) - Elapsed time and estimated time remaining - Updates in-place for both parallel and serial phases - Verbose mode (-v) still prints per-worker detail lines above the progress --- tests/autest-parallel.py | 94 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 80 insertions(+), 14 deletions(-) diff --git a/tests/autest-parallel.py b/tests/autest-parallel.py index fad0d057c2..80bb094800 100755 --- a/tests/autest-parallel.py +++ b/tests/autest-parallel.py @@ -808,25 +808,65 @@ Examples: else: partitions = [] + # Compute totals for progress tracking + total_tests = len(parallel_tests) + (len(serial_tests_to_run) if serial_tests_to_run and not args.no_serial else 0) + serial_count = len(serial_tests_to_run) if serial_tests_to_run and not args.no_serial else 0 + if partitions: print(f"Running with {len(partitions)} parallel workers") + print(f"Total: {total_tests} tests ({len(parallel_tests)} parallel across {len(partitions)} workers" + f"{f', {serial_count} serial' if serial_count else ''})") print(f"Build root: {build_root}") print(f"Port offset step: {args.port_offset_step}") print(f"Sandbox: {args.sandbox}") if args.collect_timings: print("Collecting per-test timing data (tests run sequentially per worker)") - if serial_tests_to_run and not args.no_serial: - print(f"Serial tests will run after parallel tests complete ({len(serial_tests_to_run)} tests)") + print() # Create sandbox base directory sandbox_base = Path(args.sandbox) sandbox_base.mkdir(parents=True, exist_ok=True) + # Progress tracking state + tests_done = 0 + tests_passed = 0 + tests_failed = 0 + tests_skipped = 0 + workers_done = 0 + total_workers = len(partitions) + + def format_eta(elapsed: float, done: int, total: int) -> str: + """Estimate remaining time based on progress so far.""" + if done == 0 or done >= total: + return "--:--" + rate = elapsed / done + remaining = rate * (total - done) + mins, secs = divmod(int(remaining), 60) + if mins >= 60: + hours, mins = divmod(mins, 60) + return f"{hours}h {mins:02d}m" + return f"{mins}m {secs:02d}s" + + def print_progress(phase: str = "Parallel"): + """Print an in-place progress line using \\r.""" + elapsed = time.time() - start_time + elapsed_str = f"{int(elapsed)}s" + eta = format_eta(elapsed, tests_done, total_tests) + pct = (tests_done * 100 // total_tests) if total_tests > 0 else 0 + fail_str = f" | {tests_failed} FAILED" if tests_failed > 0 else "" + skip_str = f" | {tests_skipped} skipped" if tests_skipped > 0 else "" + line = (f"\r[{phase}] {tests_done}/{total_tests} tests ({pct}%) " + f"| {workers_done}/{total_workers} workers done" + f"{fail_str}{skip_str}" + f" | {elapsed_str} elapsed | ETA: {eta} ") + print(line, end='', flush=True) + # Run workers in parallel start_time = time.time() results: List[TestResult] = [] if partitions: + print_progress() with ProcessPoolExecutor(max_workers=len(partitions)) as executor: futures = {} for worker_id, worker_tests in enumerate(partitions): @@ -851,24 +891,43 @@ Examples: try: result = future.result() results.append(result) - status = "PASS" if result.failed == 0 else "FAIL" - ts = datetime.now().strftime("%H:%M:%S") - parts = [f"{result.passed} passed", f"{result.failed} failed"] - if result.skipped > 0: - parts.append(f"{result.skipped} skipped") - print(f"[{ts}] Worker:{worker_id:2d} Done: {', '.join(parts)} ({result.duration:.1f}s) [{status}]") + workers_done += 1 + tests_done += result.passed + result.failed + result.skipped + tests_passed += result.passed + tests_failed += result.failed + tests_skipped += result.skipped + + if args.verbose: + # In verbose mode, print detail line then progress + status = "PASS" if result.failed == 0 else "FAIL" + ts = datetime.now().strftime("%H:%M:%S") + parts = [f"{result.passed} passed", f"{result.failed} failed"] + if result.skipped > 0: + parts.append(f"{result.skipped} skipped") + # Clear the progress line, print detail, then re-print progress + print(f"\r[{ts}] Worker:{worker_id:2d} Done: {', '.join(parts)} " + f"({result.duration:.1f}s) [{status}]" + " " * 20) + + print_progress() except Exception as e: - print(f"[Worker {worker_id}] Error: {e}", file=sys.stderr) + print(f"\r[Worker {worker_id}] Error: {e}" + " " * 20, file=sys.stderr) + workers_done += 1 + tests_done += len(partitions[worker_id]) + tests_failed += len(partitions[worker_id]) results.append(TestResult( worker_id=worker_id, tests=partitions[worker_id], failed=len(partitions[worker_id]), output=str(e) )) + print_progress() + + # Clear the progress line after parallel phase + print() # Run serial tests after parallel tests complete if serial_tests_to_run and not args.no_serial: - print(f"\n{'=' * 70}") + print(f"{'=' * 70}") print("RUNNING SERIAL TESTS") print(f"{'=' * 70}") serial_start = time.time() @@ -891,20 +950,27 @@ Examples: if status == "PASS": serial_result.passed += 1 + tests_passed += 1 elif status == "SKIP": serial_result.skipped += 1 + tests_skipped += 1 else: serial_result.failed += 1 serial_result.failed_tests.append(test_name) + tests_failed += 1 + tests_done += 1 - timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") - print(f"{timestamp} {status:4s} {duration:6.1f}s Serial {idx:2d}/{len(serial_tests_to_run):2d} {test}", flush=True) + if args.verbose: + timestamp = datetime.now().strftime("%H:%M:%S") + print(f"\r[{timestamp}] {status:4s} {duration:6.1f}s Serial {idx:2d}/{len(serial_tests_to_run):2d} {test}" + " " * 20) + + print_progress(phase="Serial") serial_result.duration = time.time() - serial_start results.append(serial_result) - print(f"\n[Serial] Completed: {serial_result.passed} passed, " - f"{serial_result.failed} failed ({serial_result.duration:.1f}s)") + # Clear the progress line after serial phase + print() total_duration = time.time() - start_time
