From 4b215a5b24969d0ee5184fe75115ee31912403ff Mon Sep 17 00:00:00 2001 From: Mateo LE FLEM Date: Sun, 24 May 2026 07:38:09 +0200 Subject: [PATCH] cli: add --default-timeout option for tests with no explicit timeout --- doc/env.rst | 5 ++++- include/criterion/options.h | 9 +++++++++ src/core/runner_coroutine.c | 2 ++ src/entry/params.c | 10 ++++++++-- test/cram/help.t | 6 ++++-- test/cram/timeout.t | 10 ++++++++++ test/full/default-timeout.c | 18 ++++++++++++++++++ test/full/meson.build | 4 ++++ 8 files changed, 59 insertions(+), 5 deletions(-) create mode 100644 test/cram/timeout.t create mode 100644 test/full/default-timeout.c diff --git a/doc/env.rst b/doc/env.rst index 4f2b977a7..202343f08 100644 --- a/doc/env.rst +++ b/doc/env.rst @@ -20,7 +20,10 @@ Command line arguments a number of jobs ideal for your hardware configuration. * ``--filter [PATTERN]``: Run tests whose string identifier matches the given shell wildcard pattern (see dedicated section below). (\*nix only) -* ``--timeout [TIMEOUT]``: Set a timeout (in seconds) for all tests +* ``--timeout [TIMEOUT]``: Cap the timeout (in seconds) for all tests, + overriding any per-test value that exceeds it. +* ``--default-timeout [TIMEOUT]``: Set a fallback timeout (in seconds) + applied only to tests with no timeout set. * ``--debug[=debugger]``: Run tests with a debugging server attached. ``debugger`` can be 'gdb', 'lldb', or 'windbg' (windows only). * ``--debug-transport [TRANSPORT]``: Make the debugging server use the diff --git a/include/criterion/options.h b/include/criterion/options.h index 821717a0a..2f4113f40 100644 --- a/include/criterion/options.h +++ b/include/criterion/options.h @@ -183,6 +183,15 @@ struct criterion_options { */ double timeout; + /** + * A default timeout applied to each test that has no timeout set, in seconds. + * + * If the value is non-positive, no default is applied. + * + * default: 0 + */ + double default_timeout; + /** * Fully report statistics from test workers, including those that are * not reported by default (like passing assertions). diff --git a/src/core/runner_coroutine.c b/src/core/runner_coroutine.c index 533e233a0..631f6995b 100644 --- a/src/core/runner_coroutine.c +++ b/src/core/runner_coroutine.c @@ -344,6 +344,8 @@ static bxf_instance *run_test(struct run_next_context *ctx, timeout = ctx->test->data->timeout; if (criterion_options.timeout > 0 && timeout > criterion_options.timeout) timeout = criterion_options.timeout; + if (timeout <= 0 && criterion_options.default_timeout > 0) + timeout = criterion_options.default_timeout; sp.iquotas.runtime = timeout; diff --git a/src/entry/params.c b/src/entry/params.c index 67f3de04b..b1da2be04 100644 --- a/src/entry/params.c +++ b/src/entry/params.c @@ -70,8 +70,12 @@ "name of the source file on a failure\n" \ " --filter [PATTERN]: run tests matching the " \ "given pattern\n" \ - " --timeout [TIMEOUT]: set a timeout (in seconds) " \ - "for all tests\n" \ + " --timeout [TIMEOUT]: cap the timeout (in " \ + "seconds) for all tests, overriding any per-test " \ + "value that exceeds it\n" \ + " --default-timeout [TIMEOUT]: set a fallback " \ + "timeout (in seconds) applied only to tests with " \ + "no timeout set\n" \ " --tap[=FILE]: writes TAP report in FILE " \ "(no file or \"-\" means stderr)\n" \ " --xml[=FILE]: writes XML report in FILE " \ @@ -264,6 +268,7 @@ CR_API int criterion_handle_args(int argc, char *argv[], { "ascii", no_argument, 0, 'k' }, { "jobs", required_argument, 0, 'j' }, { "timeout", required_argument, 0, 't' }, + { "default-timeout", required_argument, 0, 'g' }, { "fail-fast", no_argument, 0, 'f' }, { "short-filename", no_argument, 0, 'S' }, { "single", required_argument, 0, 's' }, @@ -394,6 +399,7 @@ CR_API int criterion_handle_args(int argc, char *argv[], case 'q': quiet = true; break; case 't': criterion_options.timeout = atof(optarg); break; + case 'g': criterion_options.default_timeout = atof(optarg); break; case 'd': if (!parse_dbg(optarg)) diff --git a/test/cram/help.t b/test/cram/help.t index 6955a566d..da9cede6a 100644 --- a/test/cram/help.t +++ b/test/cram/help.t @@ -16,7 +16,8 @@ Display the help message --ascii: don't use fancy unicode symbols or colors in the output -S or --short-filename: only display the base name of the source file on a failure --filter [PATTERN]: run tests matching the given pattern - --timeout [TIMEOUT]: set a timeout (in seconds) for all tests + --timeout [TIMEOUT]: cap the timeout (in seconds) for all tests, overriding any per-test value that exceeds it + --default-timeout [TIMEOUT]: set a fallback timeout (in seconds) applied only to tests with no timeout set --tap[=FILE]: writes TAP report in FILE (no file or "-" means stderr) --xml[=FILE]: writes XML report in FILE (no file or "-" means stderr) --json[=FILE]: writes JSON report in FILE (no file or "-" means stderr) @@ -49,7 +50,8 @@ C++ equivalents --ascii: don't use fancy unicode symbols or colors in the output -S or --short-filename: only display the base name of the source file on a failure --filter [PATTERN]: run tests matching the given pattern - --timeout [TIMEOUT]: set a timeout (in seconds) for all tests + --timeout [TIMEOUT]: cap the timeout (in seconds) for all tests, overriding any per-test value that exceeds it + --default-timeout [TIMEOUT]: set a fallback timeout (in seconds) applied only to tests with no timeout set --tap[=FILE]: writes TAP report in FILE (no file or "-" means stderr) --xml[=FILE]: writes XML report in FILE (no file or "-" means stderr) --json[=FILE]: writes JSON report in FILE (no file or "-" means stderr) diff --git a/test/cram/timeout.t b/test/cram/timeout.t new file mode 100644 index 000000000..d4d3f9785 --- /dev/null +++ b/test/cram/timeout.t @@ -0,0 +1,10 @@ +--default-timeout applies to tests with no explicit timeout + + $ default-timeout.c.bin --default-timeout 1 --filter='timeout/no_timeout_set' + \[FAIL\] timeout::no_timeout_set: Timed out. \([0-9.]*s\) (re) + [====] Synthesis: Tested: 1 | Passing: 0 | Failing: 1 | Crashing: 0 + +--default-timeout does not override an explicit per-test timeout + + $ default-timeout.c.bin --default-timeout 1 --filter='timeout/explicit_not_overridden' + [====] Synthesis: Tested: 1 | Passing: 1 | Failing: 0 | Crashing: 0 diff --git a/test/full/default-timeout.c b/test/full/default-timeout.c new file mode 100644 index 000000000..6229387ab --- /dev/null +++ b/test/full/default-timeout.c @@ -0,0 +1,18 @@ +#include + +#ifdef _WIN32 +# include +# define sleep(x) Sleep(x * 1000) +#else +# include +#endif + +/* No .timeout set, should be killed when --default-timeout is passed. */ +Test(timeout, no_timeout_set) { + sleep(10); +} + +/* Explicit .timeout = 5, --default-timeout 1 must not override it. */ +Test(timeout, explicit_not_overridden, .timeout = 5.) { + sleep(2); +} diff --git a/test/full/meson.build b/test/full/meson.build index d64015f38..9d30b0aa7 100644 --- a/test/full/meson.build +++ b/test/full/meson.build @@ -28,6 +28,10 @@ if has_cxx endif endif +executable('default-timeout.c.bin', 'default-timeout.c', + include_directories: [criterion_api], + link_with: libcriterion.get_shared_lib()) + foreach tst : full_tests e = executable(tst + '.bin', tst, include_directories: [criterion_api],