Kernel KVM virtualization development
 help / color / mirror / Atom feed
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


  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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox