aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2021-01-14 14:04:17 +0100
committerJussi Pakkanen <jpakkane@gmail.com>2021-01-14 22:00:51 +0000
commitea2f34e2860680bab82aa7c2538971ab788209ce (patch)
tree68a849e101d5409e89be9423829ea485178fb585
parentf13b2b4b1d3e606e78b9dbd1a9b263e8384d8457 (diff)
downloadmeson-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.md5
-rw-r--r--mesonbuild/mtest.py22
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()