From bbf7568ebd948103af12ff13567ff67f12c8043b Mon Sep 17 00:00:00 2001 From: Peter Ammon Date: Tue, 1 Jul 2025 18:27:14 -0700 Subject: [PATCH] test_driver.py: Properly report exceptions If a test fails by throwing an exception (in this case, "Too many open files") then that exception would propagate, be uncaught, and then the remaining tests would not be await'ed, leading to a hang. Fix this by properly catching and reporting exceptions. --- tests/test_driver.py | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/tests/test_driver.py b/tests/test_driver.py index 32bbc8b94..0a223fcc5 100755 --- a/tests/test_driver.py +++ b/tests/test_driver.py @@ -204,25 +204,31 @@ async def main(): if semaphore is not None: semaphore.release() - tasks = [run(f, arg) for f, arg in files] - for task in asyncio.as_completed(tasks): - result = await task - # TODO(python>3.8): use match statement - if isinstance(result, TestSkip): - arg = result.arg - skipcount += 1 - print_result(arg, "SKIPPED", BLUE) - elif isinstance(result, TestFail): - # fmt: off - arg, duration_ms, error_message = result.arg, result.duration_ms, result.error_message - # fmt: on - failcount += 1 - failed += [arg] - print_result(arg, "FAILED", RED, duration_ms, error_message) - elif isinstance(result, TestPass): - arg, duration_ms = result.arg, result.duration_ms - passcount += 1 - print_result(arg, "PASSED", GREEN, duration_ms) + tasks = [asyncio.create_task(run(f, arg), name=arg) for f, arg in files] + while tasks: + done, tasks = await asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED) + for task in done: + try: + result = await task + except Exception as e: + arg = task.get_name() + result = TestFail(arg, None, f"Test '{arg}' raised an exception: {e}") + # TODO(python>3.8): use match statement + if isinstance(result, TestSkip): + arg = result.arg + skipcount += 1 + print_result(arg, "SKIPPED", BLUE) + elif isinstance(result, TestFail): + # fmt: off + arg, duration_ms, error_message = result.arg, result.duration_ms, result.error_message + # fmt: on + failcount += 1 + failed += [arg] + print_result(arg, "FAILED", RED, duration_ms, error_message) + elif isinstance(result, TestPass): + arg, duration_ms = result.arg, result.duration_ms + passcount += 1 + print_result(arg, "PASSED", GREEN, duration_ms) if passcount + failcount + skipcount > 1: print(f"{passcount} / {passcount + failcount} passed ({skipcount} skipped)")