diff options
author | Paolo Bonzini <pbonzini@redhat.com> | 2021-01-14 14:04:17 +0100 |
---|---|---|
committer | Jussi Pakkanen <jpakkane@gmail.com> | 2021-01-14 22:00:51 +0000 |
commit | ea2f34e2860680bab82aa7c2538971ab788209ce (patch) | |
tree | 68a849e101d5409e89be9423829ea485178fb585 | |
parent | f13b2b4b1d3e606e78b9dbd1a9b263e8384d8457 (diff) | |
download | meson-ea2f34e2860680bab82aa7c2538971ab788209ce.zip meson-ea2f34e2860680bab82aa7c2538971ab788209ce.tar.gz meson-ea2f34e2860680bab82aa7c2538971ab788209ce.tar.bz2 |
mtest: allow quickly interrupting the test run
The new behavior of interrupting the longest running test with Ctrl-C is useful
when tests hang, but not when the run is completely broken for some reason.
Psychology tells us that the user will compulsively spam Ctrl-C in this case,
so exit if three Ctrl-C's are detected within a second.
-rw-r--r-- | docs/markdown/snippets/meson_test_interrupt.md | 5 | ||||
-rw-r--r-- | mesonbuild/mtest.py | 22 |
2 files changed, 22 insertions, 5 deletions
diff --git a/docs/markdown/snippets/meson_test_interrupt.md b/docs/markdown/snippets/meson_test_interrupt.md new file mode 100644 index 0000000..2d1ed26 --- /dev/null +++ b/docs/markdown/snippets/meson_test_interrupt.md @@ -0,0 +1,5 @@ +## Ctrl-C behavior in `meson test` + +Starting from this version, sending a `SIGINT` signal (or pressing `Ctrl-C`) +to `meson test` will interrupt the longest running test. Pressing `Ctrl-C` +three times within a second will exit `meson test`. diff --git a/mesonbuild/mtest.py b/mesonbuild/mtest.py index 1485e53..7e2d221 100644 --- a/mesonbuild/mtest.py +++ b/mesonbuild/mtest.py @@ -55,6 +55,9 @@ GNU_SKIP_RETURNCODE = 77 # mean that the test failed even before testing what it is supposed to test. GNU_ERROR_RETURNCODE = 99 +# Exit if 3 Ctrl-C's are received within one second +MAX_CTRLC = 3 + def is_windows() -> bool: platname = platform.system().lower() return platname == 'windows' @@ -1549,6 +1552,7 @@ class TestHarness: futures = deque() # type: T.Deque[asyncio.Future] running_tests = dict() # type: T.Dict[asyncio.Future, str] interrupted = False + ctrlc_times = deque(maxlen=MAX_CTRLC) # type: T.Deque[float] async def run_test(test: SingleTestRunner) -> None: async with semaphore: @@ -1577,15 +1581,18 @@ class TestHarness: del running_tests[future] future.cancel() - def sigterm_handler() -> None: + def cancel_all_tests() -> None: nonlocal interrupted + interrupted = True + while running_tests: + cancel_one_test(False) + + def sigterm_handler() -> None: if interrupted: return - interrupted = True self.flush_logfiles() mlog.warning('Received SIGTERM, exiting') - while running_tests: - cancel_one_test(False) + cancel_all_tests() def sigint_handler() -> None: # We always pick the longest-running future that has not been cancelled @@ -1593,7 +1600,12 @@ class TestHarness: nonlocal interrupted if interrupted: return - if running_tests: + ctrlc_times.append(asyncio.get_event_loop().time()) + if len(ctrlc_times) == MAX_CTRLC and ctrlc_times[-1] - ctrlc_times[0] < 1: + self.flush_logfiles() + mlog.warning('CTRL-C detected, exiting') + cancel_all_tests() + elif running_tests: cancel_one_test(True) else: self.flush_logfiles() |