From: Aaron Lewis <aaronlewis@google.com>
To: kvm@vger.kernel.org
Cc: alex@shazbot.org, dmatlack@google.com, jgg@nvidia.com,
Aaron Lewis <aaronlewis@google.com>, Kees Cook <kees@kernel.org>,
Andy Lutomirski <luto@amacapital.net>,
Will Drewry <wad@chromium.org>, Shuah Khan <shuah@kernel.org>,
linux-kselftest@vger.kernel.org
Subject: [PATCH v4 4/5] selftests: harness: Support custom command-line options
Date: Sat, 14 Nov 2026 13:21:59 +0000 [thread overview]
Message-ID: <20261114132200.113697-5-aaronlewis@google.com> (raw)
In-Reply-To: <20261114132200.113697-1-aaronlewis@google.com>
Because the selftest harness parses command-line parameters directly,
adding custom options to individual tests requires the test harness to
recognize them.
Add support to kselftest_harness.h for custom command-line options via
struct test_harness_cli_opts and test_harness_run_opts(), allowing tests
to register a custom getopt optstring, option handler, and optional help
callback while keeping test_harness_run() unchanged for existing callers.
Assert if the custom options string is problematic (e.g. conflicting
with built-in options or exceeding the buffer limit). This should only
happen when developing a test, and asserting makes the issue immediately
obvious to the developer.
Also update test_enabled() to match exact option characters rather than
tolower(opt) so custom options that differ only by case (such as 'R') do
not mistakenly match built-in single-case options like '-r'.
Signed-off-by: Aaron Lewis <aaronlewis@google.com>
---
tools/testing/selftests/kselftest_harness.h | 127 +++++++++++++++-----
1 file changed, 94 insertions(+), 33 deletions(-)
diff --git a/tools/testing/selftests/kselftest_harness.h b/tools/testing/selftests/kselftest_harness.h
index 29a19bc87084..562d4c0edac5 100644
--- a/tools/testing/selftests/kselftest_harness.h
+++ b/tools/testing/selftests/kselftest_harness.h
@@ -54,6 +54,7 @@
#define _GNU_SOURCE
#endif
#include <asm/types.h>
+#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <linux/unistd.h>
@@ -76,6 +77,18 @@ static inline void __kselftest_memset_safe(void *s, int c, size_t n)
memset(s, c, n);
}
+/**
+ * struct test_harness_cli_opts - Custom command-line options for test harness
+ * @optstring: getopt option string; must not conflict with harness options.
+ * @handler: Callback to handle custom options; returns KSFT_PASS or KSFT_FAIL.
+ * @help: Optional callback to print custom option help text.
+ */
+struct test_harness_cli_opts {
+ const char *optstring;
+ int (*handler)(int opt, char *optarg);
+ void (*help)(void);
+};
+
#define KSELFTEST_PRIO_TEST 20000
#define KSELFTEST_PRIO_XFAIL 20001
@@ -1097,11 +1110,60 @@ static void test_harness_list_tests(void)
}
}
-static int test_harness_argv_check(int argc, char **argv)
+#define OPTSTRING_LEN 128
+
+static void optstring_append_custom(char *optstring, size_t size,
+ const struct test_harness_cli_opts *opts)
+{
+ const char *c;
+
+ if (!opts || !opts->optstring)
+ return;
+
+ assert(strlen(optstring) + strlen(opts->optstring) < size);
+
+ for (c = opts->optstring; *c; c++) {
+ if (isalnum(*c))
+ assert(!strchr(optstring, *c));
+ }
+
+ strncat(optstring, opts->optstring, size - strlen(optstring) - 1);
+}
+
+static void test_harness_usage(const char *prog,
+ const struct test_harness_cli_opts *opts)
+{
+ fprintf(stderr,
+ "Usage: %s [-h|-l|-d] [-t|-T|-v|-V|-f|-F|-r name]\n"
+ "\t-h print help\n"
+ "\t-l list all tests\n"
+ "\t-d enable debug prints\n"
+ "\n"
+ "\t-t name include test\n"
+ "\t-T name exclude test\n"
+ "\t-v name include variant\n"
+ "\t-V name exclude variant\n"
+ "\t-f name include fixture\n"
+ "\t-F name exclude fixture\n"
+ "\t-r name run specified test\n"
+ "\n"
+ "Test filter options can be specified multiple times. The filtering stops\n"
+ "at the first match. For example to include all tests from variant 'bla'\n"
+ "but not test 'foo' specify '-T foo -v bla'.\n", prog);
+
+ if (opts && opts->help) {
+ fprintf(stderr, "\nCustom options:\n");
+ opts->help();
+ }
+}
+
+static int test_harness_argv_check(int argc, char **argv, const char *optstring,
+ const struct test_harness_cli_opts *opts)
{
int opt;
- while ((opt = getopt(argc, argv, "dhlF:f:V:v:t:T:r:")) != -1) {
+ optind = 1;
+ while ((opt = getopt(argc, argv, optstring)) != -1) {
switch (opt) {
case 'f':
case 'F':
@@ -1118,35 +1180,21 @@ static int test_harness_argv_check(int argc, char **argv)
ksft_debug_enabled = true;
break;
case 'h':
+ test_harness_usage(argv[0], opts);
+ return KSFT_SKIP;
default:
- fprintf(stderr,
- "Usage: %s [-h|-l|-d] [-t|-T|-v|-V|-f|-F|-r name]\n"
- "\t-h print help\n"
- "\t-l list all tests\n"
- "\t-d enable debug prints\n"
- "\n"
- "\t-t name include test\n"
- "\t-T name exclude test\n"
- "\t-v name include variant\n"
- "\t-V name exclude variant\n"
- "\t-f name include fixture\n"
- "\t-F name exclude fixture\n"
- "\t-r name run specified test\n"
- "\n"
- "Test filter options can be specified "
- "multiple times. The filtering stops\n"
- "at the first match. For example to "
- "include all tests from variant 'bla'\n"
- "but not test 'foo' specify '-T foo -v bla'.\n"
- "", argv[0]);
- return opt == 'h' ? KSFT_SKIP : KSFT_FAIL;
+ if (opts && opts->handler &&
+ opts->handler(opt, optarg) == KSFT_PASS)
+ break;
+ test_harness_usage(argv[0], opts);
+ return KSFT_FAIL;
}
}
return KSFT_PASS;
}
-static bool test_enabled(int argc, char **argv,
+static bool test_enabled(int argc, char **argv, const char *optstring,
struct __fixture_metadata *f,
struct __fixture_variant_metadata *v,
struct __test_metadata *t)
@@ -1156,24 +1204,28 @@ static bool test_enabled(int argc, char **argv,
int opt;
optind = 1;
- while ((opt = getopt(argc, argv, "dF:f:V:v:t:T:r:")) != -1) {
- if (opt != 'd')
- has_positive |= islower(opt);
-
- switch (tolower(opt)) {
+ while ((opt = getopt(argc, argv, optstring)) != -1) {
+ switch (opt) {
case 't':
+ case 'T':
+ has_positive |= islower(opt);
if (!strcmp(t->name, optarg))
return islower(opt);
break;
case 'f':
+ case 'F':
+ has_positive |= islower(opt);
if (!strcmp(f->name, optarg))
return islower(opt);
break;
case 'v':
+ case 'V':
+ has_positive |= islower(opt);
if (!strcmp(v->name, optarg))
return islower(opt);
break;
case 'r':
+ has_positive = true;
if (!tlen) {
flen = strlen(f->name);
vlen = strlen(v->name);
@@ -1262,8 +1314,10 @@ static void __run_test(struct __fixture_metadata *f,
diagnostic ? "%s" : NULL, diagnostic);
}
-static int test_harness_run(int argc, char **argv)
+static int test_harness_run_opts(int argc, char **argv,
+ const struct test_harness_cli_opts *opts)
{
+ char optstring[OPTSTRING_LEN] = "dhlF:f:V:v:t:T:r:";
struct __fixture_variant_metadata no_variant = { .name = "", };
struct __fixture_variant_metadata *v;
struct __fixture_metadata *f;
@@ -1274,7 +1328,9 @@ static int test_harness_run(int argc, char **argv)
unsigned int count = 0;
unsigned int pass_count = 0;
- ret = test_harness_argv_check(argc, argv);
+ optstring_append_custom(optstring, sizeof(optstring), opts);
+
+ ret = test_harness_argv_check(argc, argv, optstring, opts);
if (ret != KSFT_PASS)
return ret;
@@ -1283,7 +1339,7 @@ static int test_harness_run(int argc, char **argv)
unsigned int old_tests = test_count;
for (t = f->tests; t; t = t->next)
- if (test_enabled(argc, argv, f, v, t))
+ if (test_enabled(argc, argv, optstring, f, v, t))
test_count++;
if (old_tests != test_count)
@@ -1301,7 +1357,7 @@ static int test_harness_run(int argc, char **argv)
for (f = __fixture_list; f; f = f->next) {
for (v = f->variant ?: &no_variant; v; v = v->next) {
for (t = f->tests; t; t = t->next) {
- if (!test_enabled(argc, argv, f, v, t))
+ if (!test_enabled(argc, argv, optstring, f, v, t))
continue;
count++;
t->results = results;
@@ -1324,6 +1380,11 @@ static int test_harness_run(int argc, char **argv)
return KSFT_FAIL;
}
+static inline int test_harness_run(int argc, char **argv)
+{
+ return test_harness_run_opts(argc, argv, NULL);
+}
+
static void __attribute__((constructor(KSELFTEST_PRIO_TEST))) __constructor_order_first(void)
{
__constructor_order_forward = true;
--
2.56.0.611.g1030f0b940-goog
next prev parent reply other threads:[~2026-11-14 13:22 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-14 13:21 [PATCH v4 0/5] Introduce vfio_dma_mapping_perf_test Aaron Lewis
2026-11-14 13:21 ` [PATCH v4 1/5] vfio: selftests: Assert the region was unmapped in iommu_unmap() Aaron Lewis
2026-11-14 13:21 ` [PATCH v4 2/5] vfio: selftests: Introduce vfio_dma_mapping_perf_test Aaron Lewis
2026-11-14 13:21 ` [PATCH v4 3/5] vfio: selftests: Add memfd test to vfio_dma_mapping_perf_test Aaron Lewis
2026-11-14 13:21 ` Aaron Lewis [this message]
2026-11-14 13:22 ` [PATCH v4 5/5] vfio: selftests: Allow a size for vfio_dma_mapping_perf_test Aaron Lewis
2026-11-14 13:29 ` sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261114132200.113697-5-aaronlewis@google.com \
--to=aaronlewis@google.com \
--cc=alex@shazbot.org \
--cc=dmatlack@google.com \
--cc=jgg@nvidia.com \
--cc=kees@kernel.org \
--cc=kvm@vger.kernel.org \
--cc=linux-kselftest@vger.kernel.org \
--cc=luto@amacapital.net \
--cc=shuah@kernel.org \
--cc=wad@chromium.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.