linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/9] kunit: Refactor and extend KUnit's
@ 2025-07-29 19:36 Marie Zhussupova
  2025-07-29 19:36 ` [PATCH 1/9] kunit: Add parent kunit for parameterized test context Marie Zhussupova
                   ` (8 more replies)
  0 siblings, 9 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

Hello!

KUnit offers a parameterized testing framework, where tests can be
run multiple times with different inputs.

Currently, the same `struct kunit` is used for each parameter
execution. After each run, the test instance gets cleaned up.
This creates the following limitations:

a. There is no way to store resources that are accessible across
   the individual parameter test executions.
b. It's not possible to pass additional context besides the
   previous parameter to `generate_params()` to get the next
   parameter.
c. Test users are restricted to using pre-defined static arrays
   of parameter objects or `generate_params()` to define their
   parameters. There is no flexibility to pass a custom dynamic
   array without using `generate_params()`, which can be complex
   if generating the next parameter depends on more than just
   the single previous parameter (e.g., two or more previous
   parameters).

This patch series resolves these limitations by:

1. [P 1] Giving each parameterized test execution its own
   `struct kunit`. This aligns more with the definition of a
   `struct kunit` as a running instance of a test. It will also
   remove the need to manage state, such as resetting the
   `test->priv` field or the `test->status_comment` after every
   parameter run.

2. [P 1] Introducing a parent pointer of type `struct kunit`.
   Behind the scenes, a parent instance for the parameterized
   tests will be created. It won't be used to execute any test
   logic, but will instead be used as a context for shared
   resources. Each individual running instance of a test will
   now have a reference to that parent instance and thus, have
   access to those resources.

3. [P 2] Introducing `param_init()` and `param_exit()` functions
   that can set up and clean up the parent instance of the
   parameterized tests. They will run once before and after the
   parameterized series and provide a way for the user to
   access the parent instance to add the parameter array or any
   other resources to it, including custom ones to the
   `test->parent->priv` field or to `test->parent->resources`
   via the Resource API (link below).

https://elixir.bootlin.com/linux/v6.16-rc7/source/include/kunit/resource.h

4. [P 3, 4 & 5] Passing the parent `struct kunit` as an additional
   parameter to `generate_params()`. This provides
   `generate_params()` with more available context, making
   parameter generation much more flexible. The
   `generate_params()` implementations in the KCSAN and drm/xe
   tests have been adapted to match the new function pointer
   signature.

5. [P 6] Introducing a `params_data` field in `struct kunit`.
   This will allow the parent instance of a test to have direct
   storage of the parameter array, enabling features like using
   dynamic parameter arrays or using context beyond just the
   previous parameter.

Thank you!
-Marie

Marie Zhussupova (9):
  kunit: Add parent kunit for parameterized test context
  kunit: Introduce param_init/exit for parameterized test shared context
    management
  kunit: Pass additional context to generate_params for parameterized
    testing
  kcsan: test: Update parameter generator to new signature
  drm/xe: Update parameter generator to new signature
  kunit: Enable direct registration of parameter arrays to a KUnit test
  kunit: Add example parameterized test with shared resources and direct
    static parameter array setup
  kunit: Add example parameterized test with direct dynamic parameter
    array setup
  Documentation: kunit: Document new parameterized test features

 Documentation/dev-tools/kunit/usage.rst | 455 +++++++++++++++++++++++-
 drivers/gpu/drm/xe/tests/xe_pci.c       |   2 +-
 include/kunit/test.h                    |  98 ++++-
 kernel/kcsan/kcsan_test.c               |   2 +-
 lib/kunit/kunit-example-test.c          | 207 +++++++++++
 lib/kunit/test.c                        |  82 ++++-
 6 files changed, 818 insertions(+), 28 deletions(-)

-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 1/9] kunit: Add parent kunit for parameterized test context
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-08-02  9:44   ` David Gow
  2025-08-05 15:17   ` Rae Moar
  2025-07-29 19:36 ` [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management Marie Zhussupova
                   ` (7 subsequent siblings)
  8 siblings, 2 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

Currently, KUnit parameterized tests lack a mechanism
to share resources across individual test invocations
because the same `struct kunit` instance is reused for
each test.

This patch refactors kunit_run_tests() to provide each
parameterized test with its own `struct kunit` instance.
A new parent pointer is added to `struct kunit`, allowing
individual parameterized tests to reference a shared
parent kunit instance. Resources added to this parent
will then be accessible to all individual parameter
test executions.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 include/kunit/test.h | 12 ++++++++++--
 lib/kunit/test.c     | 32 +++++++++++++++++++-------------
 2 files changed, 29 insertions(+), 15 deletions(-)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index 39c768f87dc9..a42d0c8cb985 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -268,14 +268,22 @@ struct kunit_suite_set {
  *
  * @priv: for user to store arbitrary data. Commonly used to pass data
  *	  created in the init function (see &struct kunit_suite).
+ * @parent: for user to store data that they want to shared across
+ *	    parameterized tests.
  *
  * Used to store information about the current context under which the test
  * is running. Most of this data is private and should only be accessed
- * indirectly via public functions; the one exception is @priv which can be
- * used by the test writer to store arbitrary data.
+ * indirectly via public functions; the two exceptions are @priv and @parent
+ * which can be used by the test writer to store arbitrary data or data that is
+ * available to all parameter test executions, respectively.
  */
 struct kunit {
 	void *priv;
+	/*
+	 * Reference to the parent struct kunit for storing shared resources
+	 * during parameterized testing.
+	 */
+	struct kunit *parent;
 
 	/* private: internal use only. */
 	const char *name; /* Read only after initialization! */
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index f3c6b11f12b8..4d6a39eb2c80 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -647,6 +647,7 @@ int kunit_run_tests(struct kunit_suite *suite)
 	struct kunit_case *test_case;
 	struct kunit_result_stats suite_stats = { 0 };
 	struct kunit_result_stats total_stats = { 0 };
+	const void *curr_param;
 
 	/* Taint the kernel so we know we've run tests. */
 	add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
@@ -679,36 +680,39 @@ int kunit_run_tests(struct kunit_suite *suite)
 		} else {
 			/* Get initial param. */
 			param_desc[0] = '\0';
-			test.param_value = test_case->generate_params(NULL, param_desc);
+			/* TODO: Make generate_params try-catch */
+			curr_param = test_case->generate_params(NULL, param_desc);
 			test_case->status = KUNIT_SKIPPED;
 			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
 				  "KTAP version 1\n");
 			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
 				  "# Subtest: %s", test_case->name);
 
-			while (test.param_value) {
-				kunit_run_case_catch_errors(suite, test_case, &test);
+			while (curr_param) {
+				struct kunit param_test = {
+					.param_value = curr_param,
+					.param_index = ++test.param_index,
+					.parent = &test,
+				};
+				kunit_init_test(&param_test, test_case->name, test_case->log);
+				kunit_run_case_catch_errors(suite, test_case, &param_test);
 
 				if (param_desc[0] == '\0') {
 					snprintf(param_desc, sizeof(param_desc),
 						 "param-%d", test.param_index);
 				}
 
-				kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
-						      test.status,
-						      test.param_index + 1,
+				kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
+						      param_test.status,
+						      param_test.param_index,
 						      param_desc,
-						      test.status_comment);
+						      param_test.status_comment);
 
-				kunit_update_stats(&param_stats, test.status);
+				kunit_update_stats(&param_stats, param_test.status);
 
 				/* Get next param. */
 				param_desc[0] = '\0';
-				test.param_value = test_case->generate_params(test.param_value, param_desc);
-				test.param_index++;
-				test.status = KUNIT_SUCCESS;
-				test.status_comment[0] = '\0';
-				test.priv = NULL;
+				curr_param = test_case->generate_params(curr_param, param_desc);
 			}
 		}
 
@@ -723,6 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
 
 		kunit_update_stats(&suite_stats, test_case->status);
 		kunit_accumulate_stats(&total_stats, param_stats);
+		/* TODO: Put this kunit_cleanup into a try-catch. */
+		kunit_cleanup(&test);
 	}
 
 	if (suite->suite_exit)
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
  2025-07-29 19:36 ` [PATCH 1/9] kunit: Add parent kunit for parameterized test context Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-07-30 13:50   ` kernel test robot
                     ` (2 more replies)
  2025-07-29 19:36 ` [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing Marie Zhussupova
                   ` (6 subsequent siblings)
  8 siblings, 3 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

Add `param_init` and `param_exit` function pointers to
`struct kunit_case`. Users will be able to set them
via the new `KUNIT_CASE_PARAM_WITH_INIT` macro.

These functions are invoked by kunit_run_tests() once before
and once after the entire parameterized test series, respectively.
They will receive the parent kunit test instance, allowing users
to register and manage shared resources. Resources added to this
parent kunit test will be accessible to all individual parameterized
tests, facilitating init and exit for shared state.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 include/kunit/test.h | 33 ++++++++++++++++++++++++++++++++-
 lib/kunit/test.c     | 23 ++++++++++++++++++++++-
 2 files changed, 54 insertions(+), 2 deletions(-)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index a42d0c8cb985..d8dac7efd745 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -92,6 +92,8 @@ struct kunit_attributes {
  * @name:     the name of the test case.
  * @generate_params: the generator function for parameterized tests.
  * @attr:     the attributes associated with the test
+ * @param_init: The init function to run before parameterized tests.
+ * @param_exit: The exit function to run after parameterized tests.
  *
  * A test case is a function with the signature,
  * ``void (*)(struct kunit *)``
@@ -129,6 +131,13 @@ struct kunit_case {
 	const void* (*generate_params)(const void *prev, char *desc);
 	struct kunit_attributes attr;
 
+	/*
+	 * Optional user-defined functions: one to register shared resources once
+	 * before the parameterized test series, and another to release them after.
+	 */
+	int (*param_init)(struct kunit *test);
+	void (*param_exit)(struct kunit *test);
+
 	/* private: internal use only. */
 	enum kunit_status status;
 	char *module_name;
@@ -218,6 +227,27 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
 		  .generate_params = gen_params,				\
 		  .attr = attributes, .module_name = KBUILD_MODNAME}
 
+/**
+ * KUNIT_CASE_PARAM_WITH_INIT() - Define a parameterized KUnit test case with custom
+ * init and exit functions.
+ * @test_name: The function implementing the test case.
+ * @gen_params: The function to generate parameters for the test case.
+ * @init: The init function to run before parameterized tests.
+ * @exit: The exit function to run after parameterized tests.
+ *
+ * Provides the option to register init and exit functions that take in the
+ * parent of the parameterized tests and run once before and once after the
+ * parameterized test series. The init function can be used to add any resources
+ * to share between the parameterized tests or to pass parameter arrays. The
+ * exit function can be used to clean up any resources that are not managed by
+ * the test.
+ */
+#define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)		\
+		{ .run_case = test_name, .name = #test_name,			\
+		  .generate_params = gen_params,				\
+		  .param_init = init, .param_exit = exit,			\
+		  .module_name = KBUILD_MODNAME}
+
 /**
  * struct kunit_suite - describes a related collection of &struct kunit_case
  *
@@ -269,7 +299,8 @@ struct kunit_suite_set {
  * @priv: for user to store arbitrary data. Commonly used to pass data
  *	  created in the init function (see &struct kunit_suite).
  * @parent: for user to store data that they want to shared across
- *	    parameterized tests.
+ *	    parameterized tests. Typically, the data is provided in
+ *	    the param_init function (see &struct kunit_case).
  *
  * Used to store information about the current context under which the test
  * is running. Most of this data is private and should only be accessed
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index 4d6a39eb2c80..d80b5990d85d 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -641,6 +641,19 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
 	total->total += add.total;
 }
 
+static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
+{
+	if (test_case->param_init) {
+		int err = test_case->param_init(test);
+
+		if (err) {
+			kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
+				"# failed to initialize parent parameter test.");
+			test_case->status = KUNIT_FAILURE;
+		}
+	}
+}
+
 int kunit_run_tests(struct kunit_suite *suite)
 {
 	char param_desc[KUNIT_PARAM_DESC_SIZE];
@@ -668,6 +681,8 @@ int kunit_run_tests(struct kunit_suite *suite)
 		struct kunit_result_stats param_stats = { 0 };
 
 		kunit_init_test(&test, test_case->name, test_case->log);
+		__kunit_init_parent_test(test_case, &test);
+
 		if (test_case->status == KUNIT_SKIPPED) {
 			/* Test marked as skip */
 			test.status = KUNIT_SKIPPED;
@@ -677,7 +692,7 @@ int kunit_run_tests(struct kunit_suite *suite)
 			test_case->status = KUNIT_SKIPPED;
 			kunit_run_case_catch_errors(suite, test_case, &test);
 			kunit_update_stats(&param_stats, test.status);
-		} else {
+		} else if (test_case->status != KUNIT_FAILURE) {
 			/* Get initial param. */
 			param_desc[0] = '\0';
 			/* TODO: Make generate_params try-catch */
@@ -727,6 +742,12 @@ int kunit_run_tests(struct kunit_suite *suite)
 
 		kunit_update_stats(&suite_stats, test_case->status);
 		kunit_accumulate_stats(&total_stats, param_stats);
+		/*
+		 * TODO: Put into a try catch. Since we don't need suite->exit
+		 * for it we can't reuse kunit_try_run_cleanup for this yet.
+		 */
+		if (test_case->param_exit)
+			test_case->param_exit(&test);
 		/* TODO: Put this kunit_cleanup into a try-catch. */
 		kunit_cleanup(&test);
 	}
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
  2025-07-29 19:36 ` [PATCH 1/9] kunit: Add parent kunit for parameterized test context Marie Zhussupova
  2025-07-29 19:36 ` [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-07-30 14:32   ` kernel test robot
                     ` (2 more replies)
  2025-07-29 19:36 ` [PATCH 4/9] kcsan: test: Update parameter generator to new signature Marie Zhussupova
                   ` (5 subsequent siblings)
  8 siblings, 3 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

To enable more complex parameterized test scenarios,
the `generate_params` function sometimes needs additional
context beyond just the previously generated parameter.
This patch modifies the `generate_params` function signature
to include an extra `struct kunit *test` argument, giving
users access to the parent kunit test's context when
generating subsequent parameters.

The `struct kunit *test` argument was added as the first parameter
to the function signature as it aligns with the convention
of other KUnit functions that accept `struct kunit *test` first.
This also mirrors the "this" or "self" reference found
in object-oriented programming languages.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 include/kunit/test.h | 9 ++++++---
 lib/kunit/test.c     | 5 +++--
 2 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index d8dac7efd745..4ba65dc35710 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -128,7 +128,8 @@ struct kunit_attributes {
 struct kunit_case {
 	void (*run_case)(struct kunit *test);
 	const char *name;
-	const void* (*generate_params)(const void *prev, char *desc);
+	const void* (*generate_params)(struct kunit *test,
+				       const void *prev, char *desc);
 	struct kunit_attributes attr;
 
 	/*
@@ -1701,7 +1702,8 @@ do {									       \
  * Define function @name_gen_params which uses @array to generate parameters.
  */
 #define KUNIT_ARRAY_PARAM(name, array, get_desc)						\
-	static const void *name##_gen_params(const void *prev, char *desc)			\
+	static const void *name##_gen_params(struct kunit *test,				\
+					     const void *prev, char *desc)			\
 	{											\
 		typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);	\
 		if (__next - (array) < ARRAY_SIZE((array))) {					\
@@ -1722,7 +1724,8 @@ do {									       \
  * Define function @name_gen_params which uses @array to generate parameters.
  */
 #define KUNIT_ARRAY_PARAM_DESC(name, array, desc_member)					\
-	static const void *name##_gen_params(const void *prev, char *desc)			\
+	static const void *name##_gen_params(struct kunit *test,				\
+					     const void *prev, char *desc)			\
 	{											\
 		typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);	\
 		if (__next - (array) < ARRAY_SIZE((array))) {					\
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index d80b5990d85d..f50ef82179c4 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -696,7 +696,7 @@ int kunit_run_tests(struct kunit_suite *suite)
 			/* Get initial param. */
 			param_desc[0] = '\0';
 			/* TODO: Make generate_params try-catch */
-			curr_param = test_case->generate_params(NULL, param_desc);
+			curr_param = test_case->generate_params(&test, NULL, param_desc);
 			test_case->status = KUNIT_SKIPPED;
 			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
 				  "KTAP version 1\n");
@@ -727,7 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
 
 				/* Get next param. */
 				param_desc[0] = '\0';
-				curr_param = test_case->generate_params(curr_param, param_desc);
+				curr_param = test_case->generate_params(&test, curr_param,
+									param_desc);
 			}
 		}
 
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 4/9] kcsan: test: Update parameter generator to new signature
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
                   ` (2 preceding siblings ...)
  2025-07-29 19:36 ` [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-08-02  9:44   ` David Gow
  2025-07-29 19:36 ` [PATCH 5/9] drm/xe: " Marie Zhussupova
                   ` (4 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

This patch modifies `nthreads_gen_params` in kcsan_test.c
to accept an additional `struct kunit *test` argument.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 kernel/kcsan/kcsan_test.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/kcsan/kcsan_test.c b/kernel/kcsan/kcsan_test.c
index c2871180edcc..fc76648525ac 100644
--- a/kernel/kcsan/kcsan_test.c
+++ b/kernel/kcsan/kcsan_test.c
@@ -1383,7 +1383,7 @@ static void test_atomic_builtins_missing_barrier(struct kunit *test)
  * The thread counts are chosen to cover potentially interesting boundaries and
  * corner cases (2 to 5), and then stress the system with larger counts.
  */
-static const void *nthreads_gen_params(const void *prev, char *desc)
+static const void *nthreads_gen_params(struct kunit *test, const void *prev, char *desc)
 {
 	long nthreads = (long)prev;
 
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 5/9] drm/xe: Update parameter generator to new signature
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
                   ` (3 preceding siblings ...)
  2025-07-29 19:36 ` [PATCH 4/9] kcsan: test: Update parameter generator to new signature Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-08-02  9:44   ` David Gow
  2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
                   ` (3 subsequent siblings)
  8 siblings, 1 reply; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

This patch modifies `xe_pci_live_device_gen_param`
in xe_pci.c to accept an additional `struct kunit *test`
argument.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 drivers/gpu/drm/xe/tests/xe_pci.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c
index 1d3e2e50c355..62c016e84227 100644
--- a/drivers/gpu/drm/xe/tests/xe_pci.c
+++ b/drivers/gpu/drm/xe/tests/xe_pci.c
@@ -129,7 +129,7 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_fake_device_init);
  * Return: pointer to the next &struct xe_device ready to be used as a parameter
  *         or NULL if there are no more Xe devices on the system.
  */
-const void *xe_pci_live_device_gen_param(const void *prev, char *desc)
+const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc)
 {
 	const struct xe_device *xe = prev;
 	struct device *dev = xe ? xe->drm.dev : NULL;
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
                   ` (4 preceding siblings ...)
  2025-07-29 19:36 ` [PATCH 5/9] drm/xe: " Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-07-29 20:14   ` Rae Moar
                     ` (3 more replies)
  2025-07-29 19:36 ` [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup Marie Zhussupova
                   ` (2 subsequent siblings)
  8 siblings, 4 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

KUnit parameterized tests currently support two
primary methods for getting parameters:
1.  Defining custom logic within a `generate_params`
    function.
2.  Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
    macros with pre-defined static arrays.

These methods present limitations when dealing with
dynamically generated parameter arrays, or in scenarios
where populating parameters sequentially via
`generate_params` is inefficient or overly complex.

This patch addresses these limitations by adding a new
`params_data` field to `struct kunit`, of the type
`kunit_params`. The struct `kunit_params` is designed to
store the parameter array itself, along with essential metadata
including the parameter count, parameter size, and a
`get_description` function for providing custom descriptions
for individual parameters.

The `params_data` field can be populated by calling the new
`kunit_register_params_array` macro from within a
`param_init` function. By attaching the parameter array
directly to the parent kunit test instance, these parameters
can be iterated over in kunit_run_tests() behind the scenes.

This modification provides greater flexibility to the
KUnit framework, allowing testers to easily register and
utilize both dynamic and static parameter arrays.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
 lib/kunit/test.c     | 26 ++++++++++++++++++++-
 2 files changed, 75 insertions(+), 5 deletions(-)

diff --git a/include/kunit/test.h b/include/kunit/test.h
index 4ba65dc35710..9143f0e22323 100644
--- a/include/kunit/test.h
+++ b/include/kunit/test.h
@@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  */
 #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)		\
 		{ .run_case = test_name, .name = #test_name,			\
-		  .generate_params = gen_params,				\
+		  .generate_params = (gen_params)				\
+		   ?: kunit_get_next_param_and_desc,				\
 		  .param_init = init, .param_exit = exit,			\
 		  .module_name = KBUILD_MODNAME}
 
@@ -294,6 +295,21 @@ struct kunit_suite_set {
 	struct kunit_suite * const *end;
 };
 
+/* Stores the pointer to the parameter array and its metadata. */
+struct kunit_params {
+	/*
+	 * Reference to the parameter array for the parameterized tests. This
+	 * is NULL if a parameter array wasn't directly passed to the
+	 * parent kunit struct via the kunit_register_params_array macro.
+	 */
+	const void *params;
+	/* Reference to a function that gets the description of a parameter. */
+	void (*get_description)(const void *param, char *desc);
+
+	int num_params;
+	size_t elem_size;
+};
+
 /**
  * struct kunit - represents a running instance of a test.
  *
@@ -302,12 +318,14 @@ struct kunit_suite_set {
  * @parent: for user to store data that they want to shared across
  *	    parameterized tests. Typically, the data is provided in
  *	    the param_init function (see &struct kunit_case).
+ * @params_data: for users to directly store the parameter array.
  *
  * Used to store information about the current context under which the test
  * is running. Most of this data is private and should only be accessed
- * indirectly via public functions; the two exceptions are @priv and @parent
- * which can be used by the test writer to store arbitrary data or data that is
- * available to all parameter test executions, respectively.
+ * indirectly via public functions. There are three exceptions to this: @priv,
+ * @parent, and @params_data. These members can be used by the test writer to
+ * store arbitrary data, data available to all parameter test executions, and
+ * the parameter array, respectively.
  */
 struct kunit {
 	void *priv;
@@ -316,6 +334,8 @@ struct kunit {
 	 * during parameterized testing.
 	 */
 	struct kunit *parent;
+	/* Stores the params array and all data related to it. */
+	struct kunit_params params_data;
 
 	/* private: internal use only. */
 	const char *name; /* Read only after initialization! */
@@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
 struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
 		struct kunit_suite_set suite_set);
 
+const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);
+
 #if IS_BUILTIN(CONFIG_KUNIT)
 int kunit_run_all_tests(void);
 #else
@@ -1735,6 +1757,30 @@ do {									       \
 		return NULL;									\
 	}
 
+/**
+ * kunit_register_params_array() - Register parameters for a KUnit test.
+ * @test: The KUnit test structure to which parameters will be added.
+ * @params_arr: An array of test parameters.
+ * @param_cnt: Number of parameters.
+ * @get_desc: A pointer to a function that generates a string description for
+ * a given parameter element.
+ *
+ * This macro initializes the @test's parameter array data, storing information
+ * including the parameter array, its count, the element size, and the parameter
+ * description function within `test->params_data`. KUnit's built-in
+ * `kunit_get_next_param_and_desc` function will automatically read this
+ * data when a custom `generate_params` function isn't provided.
+ */
+#define kunit_register_params_array(test, params_arr, param_cnt, get_desc)			\
+	do {											\
+		struct kunit *_test = (test);						\
+		const typeof((params_arr)[0]) * _params_ptr = &(params_arr)[0];			\
+		_test->params_data.params = _params_ptr;					\
+		_test->params_data.num_params = (param_cnt);					\
+		_test->params_data.elem_size = sizeof(*_params_ptr);				\
+		_test->params_data.get_description = (get_desc);				\
+	} while (0)
+
 // TODO(dlatypov@google.com): consider eventually migrating users to explicitly
 // include resource.h themselves if they need it.
 #include <kunit/resource.h>
diff --git a/lib/kunit/test.c b/lib/kunit/test.c
index f50ef82179c4..2f4b7087db3f 100644
--- a/lib/kunit/test.c
+++ b/lib/kunit/test.c
@@ -337,6 +337,13 @@ void __kunit_do_failed_assertion(struct kunit *test,
 }
 EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
 
+static void __kunit_init_params(struct kunit *test)
+{
+	test->params_data.params = NULL;
+	test->params_data.num_params = 0;
+	test->params_data.elem_size = 0;
+}
+
 void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
 {
 	spin_lock_init(&test->lock);
@@ -347,6 +354,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
 		string_stream_clear(log);
 	test->status = KUNIT_SUCCESS;
 	test->status_comment[0] = '\0';
+	__kunit_init_params(test);
 }
 EXPORT_SYMBOL_GPL(kunit_init_test);
 
@@ -641,6 +649,22 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
 	total->total += add.total;
 }
 
+const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc)
+{
+	struct kunit_params *params_arr = &test->params_data;
+	const void *param;
+
+	if (test->param_index < params_arr->num_params) {
+		param = (char *)params_arr->params
+			+ test->param_index * params_arr->elem_size;
+
+		if (params_arr->get_description)
+			params_arr->get_description(param, desc);
+		return param;
+	}
+	return NULL;
+}
+
 static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
 {
 	if (test_case->param_init) {
@@ -687,7 +711,7 @@ int kunit_run_tests(struct kunit_suite *suite)
 			/* Test marked as skip */
 			test.status = KUNIT_SKIPPED;
 			kunit_update_stats(&param_stats, test.status);
-		} else if (!test_case->generate_params) {
+		} else if (!test_case->generate_params && !test.params_data.params) {
 			/* Non-parameterised test. */
 			test_case->status = KUNIT_SKIPPED;
 			kunit_run_case_catch_errors(suite, test_case, &test);
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
                   ` (5 preceding siblings ...)
  2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-07-30 12:25   ` kernel test robot
                     ` (2 more replies)
  2025-07-29 19:36 ` [PATCH 8/9] kunit: Add example parameterized test with direct dynamic " Marie Zhussupova
  2025-07-29 19:36 ` [PATCH 9/9] Documentation: kunit: Document new parameterized test features Marie Zhussupova
  8 siblings, 3 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

Add `example_params_test_with_init` to illustrate how to manage
shared resources across parameterized KUnit tests. This example
showcases the use of the new `param_init` function and its registration
to a test using the `KUNIT_CASE_PARAM_WITH_INIT` macro.

Additionally, the test demonstrates:
- How to directly assign a static parameter array to a test via
  `kunit_register_params_array`.
- Leveraging the Resource API for test resource management.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 lib/kunit/kunit-example-test.c | 112 +++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
index 3056d6bc705d..5bf559e243f6 100644
--- a/lib/kunit/kunit-example-test.c
+++ b/lib/kunit/kunit-example-test.c
@@ -277,6 +277,116 @@ static void example_slow_test(struct kunit *test)
 	KUNIT_EXPECT_EQ(test, 1 + 1, 2);
 }
 
+/*
+ * This custom function allocates memory for the kunit_resource data field.
+ * The function is passed to kunit_alloc_resource() and executed once
+ * by the internal helper __kunit_add_resource().
+ */
+static int example_resource_init(struct kunit_resource *res, void *context)
+{
+	int *info = kmalloc(sizeof(*info), GFP_KERNEL);
+
+	if (!info)
+		return -ENOMEM;
+	*info = *(int *)context;
+	res->data = info;
+	return 0;
+}
+
+/*
+ * This function deallocates memory for the 'kunit_resource' data field.
+ * The function is passed to kunit_alloc_resource() and automatically
+ * executes within kunit_release_resource() when the resource's reference
+ * count, via kunit_put_resource(), drops to zero. KUnit uses reference
+ * counting to ensure that resources are not freed prematurely.
+ */
+static void example_resource_free(struct kunit_resource *res)
+{
+	kfree(res->data);
+}
+
+/*
+ * This match function is invoked by kunit_find_resource() to locate
+ * a test resource based on defined criteria. The current example
+ * uniquely identifies the resource by its free function; however,
+ * alternative custom criteria can be implemented. Refer to
+ * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
+ */
+static bool example_resource_alloc_match(struct kunit *test,
+					 struct kunit_resource *res,
+					 void *match_data)
+{
+	return res->data && res->free == example_resource_free;
+}
+
+/*
+ * This is an example of a function that provides a description for each of the
+ * parameters.
+ */
+static void example_param_array_get_desc(const void *p, char *desc)
+{
+	const struct example_param *param = p;
+
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+		 "example check if %d is less than or equal to 3", param->value);
+}
+
+/*
+ * Initializes the parent kunit struct for parameterized KUnit tests.
+ * This function enables sharing resources across all parameterized
+ * tests by adding them to the `parent` kunit test struct. It also supports
+ * registering either static or dynamic arrays of test parameters.
+ */
+static int example_param_init(struct kunit *test)
+{
+	int ctx = 3; /* Data to be stored. */
+	int arr_size = ARRAY_SIZE(example_params_array);
+
+	/*
+	 * This allocates a struct kunit_resource, sets its data field to
+	 * ctx, and adds it to the kunit struct's resources list. Note that
+	 * this is test managed so we don't need to have a custom exit function
+	 * to free it.
+	 */
+	void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
+					  GFP_KERNEL, &ctx);
+
+	if (!data)
+		return -ENOMEM;
+	/* Pass the static param array information to the parent struct kunit. */
+	kunit_register_params_array(test, example_params_array, arr_size,
+				    example_param_array_get_desc);
+	return 0;
+}
+
+/*
+ * This is an example of a parameterized test that uses shared resources
+ * available from the struct kunit parent field of the kunit struct.
+ */
+static void example_params_test_with_init(struct kunit *test)
+{
+	int threshold;
+	struct kunit_resource *res;
+	const struct example_param *param = test->param_value;
+
+	/* By design, param pointer will not be NULL. */
+	KUNIT_ASSERT_NOT_NULL(test, param);
+
+	/* Here we access the parent pointer of the test to find the shared resource. */
+	res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
+
+	KUNIT_ASSERT_NOT_NULL(test, res);
+
+	/* Since the data field in kunit_resource is a void pointer we need to typecast it. */
+	threshold = *((int *)res->data);
+
+	/* Assert that the parameter is less than or equal to a certain threshold. */
+	KUNIT_ASSERT_LE(test, param->value, threshold);
+
+	/* This decreases the reference count after calling kunit_find_resource(). */
+	kunit_put_resource(res);
+}
+
 /*
  * Here we make a list of all the test cases we want to add to the test suite
  * below.
@@ -296,6 +406,8 @@ static struct kunit_case example_test_cases[] = {
 	KUNIT_CASE(example_static_stub_using_fn_ptr_test),
 	KUNIT_CASE(example_priv_test),
 	KUNIT_CASE_PARAM(example_params_test, example_gen_params),
+	KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
+				   example_param_init, NULL),
 	KUNIT_CASE_SLOW(example_slow_test),
 	{}
 };
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 8/9] kunit: Add example parameterized test with direct dynamic parameter array setup
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
                   ` (6 preceding siblings ...)
  2025-07-29 19:36 ` [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-08-02  9:44   ` David Gow
  2025-08-05 15:19   ` Rae Moar
  2025-07-29 19:36 ` [PATCH 9/9] Documentation: kunit: Document new parameterized test features Marie Zhussupova
  8 siblings, 2 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

Introduce `example_params_test_with_init_dynamic_arr`. This new
KUnit test demonstrates directly assigning a dynamic parameter
array using the `kunit_register_params_array` macro. It highlights the
use of `param_init` and `param_exit` for proper initialization and
cleanup, and their registration to the test with
`KUNIT_CASE_PARAM_WITH_INIT`.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 lib/kunit/kunit-example-test.c | 95 ++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
index 5bf559e243f6..3ab121d81bf6 100644
--- a/lib/kunit/kunit-example-test.c
+++ b/lib/kunit/kunit-example-test.c
@@ -387,6 +387,98 @@ static void example_params_test_with_init(struct kunit *test)
 	kunit_put_resource(res);
 }
 
+/*
+ * Helper function to create a parameter array of Fibonacci numbers. This example
+ * highlights a parameter generation scenario that is:
+ * 1. Not feasible to fully pre-generate at compile time.
+ * 2. Challenging to implement with a standard 'generate_params' function,
+ * as it typically only provides the immediately 'prev' parameter, while
+ * Fibonacci requires access to two preceding values for calculation.
+ */
+static void *make_fibonacci_params(int seq_size)
+{
+	int *seq;
+
+	if (seq_size <= 0)
+		return NULL;
+
+	seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
+
+	if (!seq)
+		return NULL;
+
+	if (seq_size >= 1)
+		seq[0] = 0;
+	if (seq_size >= 2)
+		seq[1] = 1;
+	for (int i = 2; i < seq_size; i++)
+		seq[i] = seq[i - 1] + seq[i - 2];
+	return seq;
+}
+
+/*
+ * This is an example of a function that provides a description for each of the
+ * parameters.
+ */
+static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
+{
+	const int *fib_num = p;
+
+	snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
+}
+
+/*
+ * Example of a parameterized test init function that registers a dynamic array.
+ */
+static int example_param_init_dynamic_arr(struct kunit *test)
+{
+	int seq_size = 6;
+	int *fibonacci_params = make_fibonacci_params(seq_size);
+
+	if (!fibonacci_params)
+		return -ENOMEM;
+
+	/*
+	 * Passes the dynamic parameter array information to the parent struct kunit.
+	 * The array and its metadata will be stored in test->parent->params_data.
+	 * The array itself will be located in params_data.params.
+	 */
+	kunit_register_params_array(test, fibonacci_params, seq_size,
+				    example_param_dynamic_arr_get_desc);
+	return 0;
+}
+
+/**
+ * Function to clean up the parameterized test's parent kunit struct if
+ * there were custom allocations.
+ */
+static void example_param_exit_dynamic_arr(struct kunit *test)
+{
+	/*
+	 * We allocated this array, so we need to free it.
+	 * Since the parent parameter instance is passed here,
+	 * we can directly access the array via `test->params_data.params`
+	 * instead of `test->parent->params_data.params`.
+	 */
+	kfree(test->params_data.params);
+}
+
+/*
+ * Example of test that uses the registered dynamic array to perform assertions
+ * and expectations.
+ */
+static void example_params_test_with_init_dynamic_arr(struct kunit *test)
+{
+	const int *param = test->param_value;
+	int param_val;
+
+	/* By design, param pointer will not be NULL. */
+	KUNIT_ASSERT_NOT_NULL(test, param);
+
+	param_val = *param;
+	KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
+}
+
 /*
  * Here we make a list of all the test cases we want to add to the test suite
  * below.
@@ -408,6 +500,9 @@ static struct kunit_case example_test_cases[] = {
 	KUNIT_CASE_PARAM(example_params_test, example_gen_params),
 	KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
 				   example_param_init, NULL),
+	KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
+				   example_param_init_dynamic_arr,
+				   example_param_exit_dynamic_arr),
 	KUNIT_CASE_SLOW(example_slow_test),
 	{}
 };
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* [PATCH 9/9] Documentation: kunit: Document new parameterized test features
  2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
                   ` (7 preceding siblings ...)
  2025-07-29 19:36 ` [PATCH 8/9] kunit: Add example parameterized test with direct dynamic " Marie Zhussupova
@ 2025-07-29 19:36 ` Marie Zhussupova
  2025-08-02  9:45   ` David Gow
  2025-08-05 15:19   ` Rae Moar
  8 siblings, 2 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-29 19:36 UTC (permalink / raw)
  To: rmoar, davidgow, shuah, brendan.higgins
  Cc: elver, dvyukov, lucas.demarchi, thomas.hellstrom, rodrigo.vivi,
	linux-kselftest, kunit-dev, kasan-dev, intel-xe, dri-devel,
	linux-kernel, Marie Zhussupova

-Update the KUnit documentation to explain the concept
of a parent parameterized test.
-Add examples demonstrating different ways of passing
parameters to parameterized tests and how to manage
shared resources between them.

Signed-off-by: Marie Zhussupova <marievic@google.com>
---
 Documentation/dev-tools/kunit/usage.rst | 455 +++++++++++++++++++++++-
 1 file changed, 449 insertions(+), 6 deletions(-)

diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
index 066ecda1dd98..be1d656053cf 100644
--- a/Documentation/dev-tools/kunit/usage.rst
+++ b/Documentation/dev-tools/kunit/usage.rst
@@ -542,11 +542,21 @@ There is more boilerplate code involved, but it can:
 Parameterized Testing
 ~~~~~~~~~~~~~~~~~~~~~
 
-The table-driven testing pattern is common enough that KUnit has special
-support for it.
-
-By reusing the same ``cases`` array from above, we can write the test as a
-"parameterized test" with the following.
+To efficiently and elegantly validate a test case against a variety of inputs,
+KUnit also provides a parameterized testing framework. This feature formalizes
+and extends the concept of table-driven tests discussed previously, offering
+a more integrated and flexible way to handle multiple test scenarios with
+minimal code duplication.
+
+Passing Parameters to the Test Cases
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+There are three main ways to provide the parameters to a test case:
+
+Array Parameter Macros (``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC``):
+   KUnit provides special support for the common table-driven testing pattern.
+   By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
+   ``cases`` array from the previous section, we can create a parameterized test
+   as shown below:
 
 .. code-block:: c
 
@@ -555,7 +565,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
 		const char *str;
 		const char *sha1;
 	};
-	const struct sha1_test_case cases[] = {
+	static const struct sha1_test_case cases[] = {
 		{
 			.str = "hello world",
 			.sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
@@ -590,6 +600,439 @@ By reusing the same ``cases`` array from above, we can write the test as a
 		{}
 	};
 
+Custom Parameter Generator (``generate_params``):
+   You can pass your own ``generate_params`` function to the ``KUNIT_CASE_PARAM``
+   or ``KUNIT_CASE_PARAM_WITH_INIT`` macros. This function is responsible for
+   generating parameters one by one. It receives the previously generated parameter
+   as the ``prev`` argument (which is ``NULL`` on the first call) and can also
+   access any context available from the parent ``struct kunit`` passed as the
+   ``test`` argument. KUnit calls this function repeatedly until it returns
+   ``NULL``. Below is an example of how it works:
+
+.. code-block:: c
+
+	#define MAX_TEST_BUFFER_SIZE 8
+
+	// Example generator function. It produces a sequence of buffer sizes that
+	// are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
+	static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
+	{
+		long prev_buffer_size = (long)prev;
+		long next_buffer_size = 1; // Start with an initial size of 1.
+
+		// Stop generating parameters if the limit is reached or exceeded.
+		if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
+			return NULL;
+
+		// For subsequent calls, calculate the next size by doubling the previous one.
+		if (prev)
+			next_buffer_size = prev_buffer_size << 1;
+
+		return (void *)next_buffer_size;
+	}
+
+	// Simple test to validate that kunit_kzalloc provides zeroed memory.
+	static void buffer_zero_test(struct kunit *test)
+	{
+		long buffer_size = (long)test->param_value;
+		// Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
+		// memory "parameter managed," meaning it's automatically cleaned up at
+		// the end of each parameter execution.
+		int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
+
+		// Ensure the allocation was successful.
+		KUNIT_ASSERT_NOT_NULL(test, buf);
+
+		// Loop through the buffer and confirm every element is zero.
+		for (int i = 0; i < buffer_size; i++)
+			KUNIT_EXPECT_EQ(test, buf[i], 0);
+	}
+
+	static struct kunit_case buffer_test_cases[] = {
+		KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
+		{}
+	};
+
+Direct Registration in Parameter Init Function (using ``kunit_register_params_array``):
+   For more complex scenarios, you can directly register a parameter array with
+   a test case instead of using a ``generate_params`` function. This is done by
+   passing the array to the ``kunit_register_params_array`` macro within an
+   initialization function for the parameterized test series
+   (i.e., a function named ``param_init``). To better understand this mechanism
+   please refer to the "Adding Shared Resources" section below.
+
+   This method supports both dynamically built and static arrays.
+
+   As the following code shows, the ``example_param_init_dynamic_arr`` function
+   utilizes ``make_fibonacci_params`` to create a dynamic array, which is then
+   registered using ``kunit_register_params_array``. The corresponding exit
+   function, ``example_param_exit``, is responsible for freeing this dynamically
+   allocated params array after the parameterized test series ends.
+
+.. code-block:: c
+
+	/*
+	 * Helper function to create a parameter array of Fibonacci numbers. This example
+	 * highlights a parameter generation scenario that is:
+	 * 1. Not feasible to fully pre-generate at compile time.
+	 * 2. Challenging to implement with a standard 'generate_params' function,
+	 * as it typically only provides the immediately 'prev' parameter, while
+	 * Fibonacci requires access to two preceding values for calculation.
+	 */
+	static void *make_fibonacci_params(int seq_size)
+	{
+		int *seq;
+
+		if (seq_size <= 0)
+			return NULL;
+
+		seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
+
+		if (!seq)
+			return NULL;
+
+		if (seq_size >= 1)
+			seq[0] = 0;
+		if (seq_size >= 2)
+			seq[1] = 1;
+		for (int i = 2; i < seq_size; i++)
+			seq[i] = seq[i - 1] + seq[i - 2];
+		return seq;
+	}
+
+	// This is an example of a function that provides a description for each of the
+	// parameters.
+	static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
+	{
+		const int *fib_num = p;
+
+		snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
+	}
+
+	// Example of a parameterized test init function that registers a dynamic array.
+	static int example_param_init_dynamic_arr(struct kunit *test)
+	{
+		int seq_size = 6;
+		int *fibonacci_params = make_fibonacci_params(seq_size);
+
+		if (!fibonacci_params)
+			return -ENOMEM;
+
+		/*
+		 * Passes the dynamic parameter array information to the parent struct kunit.
+		 * The array and its metadata will be stored in test->parent->params_data.
+		 * The array itself will be located in params_data.params.
+		 */
+		kunit_register_params_array(test, fibonacci_params, seq_size,
+					    example_param_dynamic_arr_get_desc);
+		return 0;
+	}
+
+	// Function to clean up the parameterized test's parent kunit struct if
+	// there were custom allocations.
+	static void example_param_exit_dynamic_arr(struct kunit *test)
+	{
+		/*
+		 * We allocated this array, so we need to free it.
+		 * Since the parent parameter instance is passed here,
+		 * we can directly access the array via `test->params_data.params`
+		 * instead of `test->parent->params_data.params`.
+		 */
+		kfree(test->params_data.params);
+	}
+
+	/*
+	 * Example of test that uses the registered dynamic array to perform assertions
+	 * and expectations.
+	 */
+	static void example_params_test_with_init_dynamic_arr(struct kunit *test)
+	{
+		const int *param = test->param_value;
+		int param_val;
+
+		/* By design, param pointer will not be NULL. */
+		KUNIT_ASSERT_NOT_NULL(test, param);
+
+		param_val = *param;
+		KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
+	}
+
+	static struct kunit_case example_tests[] = {
+		// The NULL here stands in for the generate_params function
+		KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
+					   example_param_init_dynamic_arr,
+					   example_param_exit_dynamic_arr),
+		{}
+	};
+
+
+Adding Shared Resources
+^^^^^^^^^^^^^^^^^^^^^^^
+All parameterized test executions in this framework have a parent test of type
+``struct kunit``. This parent is not used to execute any test logic itself;
+instead, it serves as a container for shared context that can be accessed by
+all its individual test executions (or parameters). Therefore, each individual
+test execution holds a pointer to this parent, accessible via a field named
+``parent``.
+
+It's possible to add resources to share between the individual test executions
+within a parameterized test series by using the ``KUNIT_CASE_PARAM_WITH_INIT``
+macro, to which you pass custom ``param_init`` and ``param_exit`` functions.
+These functions run once before and once after the entire parameterized test
+series, respectively. The ``param_init`` function can be used for adding any
+resources to the resources field of a parent test and also provide an additional
+way of setting the parameter array. The ``param_exit`` function can be used
+release any resources that were not test managed i.e. not automatically cleaned
+up after the test ends.
+
+.. note::
+   If both a ``generate_params`` function is passed to ``KUNIT_CASE_PARAM_WITH_INIT``
+   and an array is registered via ``kunit_register_params_array`` in
+   ``param_init``, the ``generate_params`` function will be used to get
+   the parameters.
+
+Both ``param_init`` and ``param_exit`` are passed the parent instance of a test
+(parent ``struct kunit``) behind the scenes. However, the test case function
+receives the individual instance of a test for each parameter. Therefore, to
+manage and access shared resources from within a test case function, you must use
+``test->parent``.
+
+.. note::
+   The ``suite->init()`` function, which runs before each parameter execution,
+   receives the individual instance of a test for each parameter. Therefore,
+   resources set up in ``suite->init()`` are reset for each individual
+   parameterized test execution and are only visible within that specific test.
+
+For instance, finding a shared resource allocated by the Resource API requires
+passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
+all other APIs that might be used in the test case function, including
+``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
+Documentation/dev-tools/kunit/api/test.rst and the
+Documentation/dev-tools/kunit/api/resource.rst).
+
+The code below shows how you can add the shared resources. Note that this code
+utilizes the Resource API, which you can read more about here:
+Documentation/dev-tools/kunit/api/resource.rst.
+
+.. code-block:: c
+
+	/* An example parameter array. */
+	static const struct example_param {
+		int value;
+	} example_params_array[] = {
+		{ .value = 3, },
+		{ .value = 2, },
+		{ .value = 1, },
+		{ .value = 0, },
+	};
+
+	/*
+	 * This custom function allocates memory for the kunit_resource data field.
+	 * The function is passed to kunit_alloc_resource() and executed once
+	 * by the internal helper __kunit_add_resource().
+	 */
+	static int example_resource_init(struct kunit_resource *res, void *context)
+	{
+		int *info = kmalloc(sizeof(*info), GFP_KERNEL);
+
+		if (!info)
+			return -ENOMEM;
+		*info = *(int *)context;
+		res->data = info;
+		return 0;
+	}
+
+	/*
+	 * This function deallocates memory for the 'kunit_resource' data field.
+	 * The function is passed to kunit_alloc_resource() and automatically
+	 * executes within kunit_release_resource() when the resource's reference
+	 * count, via kunit_put_resource(), drops to zero. KUnit uses reference
+	 * counting to ensure that resources are not freed prematurely.
+	 */
+	static void example_resource_free(struct kunit_resource *res)
+	{
+		kfree(res->data);
+	}
+
+	/*
+	 * This match function is invoked by kunit_find_resource() to locate
+	 * a test resource based on defined criteria. The current example
+	 * uniquely identifies the resource by its free function; however,
+	 * alternative custom criteria can be implemented. Refer to
+	 * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
+	 */
+	static bool example_resource_alloc_match(struct kunit *test,
+						 struct kunit_resource *res,
+						 void *match_data)
+	{
+		return res->data && res->free == example_resource_free;
+	}
+
+	/*
+	 * This is an example of a function that provides a description for each of the
+	 * parameters.
+	*/
+	static void example_param_array_get_desc(const void *p, char *desc)
+	{
+		const struct example_param *param = p;
+
+		snprintf(desc, KUNIT_PARAM_DESC_SIZE,
+			"example check if %d is less than or equal to 3", param->value);
+	}
+
+	/*
+	 * Initializes the parent kunit struct for parameterized KUnit tests.
+	 * This function enables sharing resources across all parameterized
+	 * tests by adding them to the `parent` kunit test struct. It also supports
+	 * registering either static or dynamic arrays of test parameters.
+	 */
+	static int example_param_init(struct kunit *test)
+	{
+		int ctx = 3; /* Data to be stored. */
+		int arr_size = ARRAY_SIZE(example_params_array);
+
+		/*
+		 * This allocates a struct kunit_resource, sets its data field to
+		 * ctx, and adds it to the kunit struct's resources list. Note that
+		 * this is test managed so we don't need to have a custom exit function
+		 * to free it.
+		 */
+		void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
+						  GFP_KERNEL, &ctx);
+
+		if (!data)
+			return -ENOMEM;
+		/* Pass the static param array information to the parent struct kunit. */
+		kunit_register_params_array(test, example_params_array, arr_size,
+					    example_param_array_get_desc);
+		return 0;
+	}
+
+	/*
+	* This is an example of a parameterized test that uses shared resources
+	* available from the struct kunit parent field of the kunit struct.
+	*/
+	static void example_params_test_with_init(struct kunit *test)
+	{
+		int threshold;
+		struct kunit_resource *res;
+		const struct example_param *param = test->param_value;
+
+		/* By design, param pointer will not be NULL. */
+		KUNIT_ASSERT_NOT_NULL(test, param);
+
+		/* Here we need to access the parent pointer of the test to find the shared resource. */
+		res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
+
+		KUNIT_ASSERT_NOT_NULL(test, res);
+
+		/* Since the data field in kunit_resource is a void pointer we need to typecast it. */
+		threshold = *((int *)res->data);
+
+		/* Assert that the parameter is less than or equal to a certain threshold. */
+		KUNIT_ASSERT_LE(test, param->value, threshold);
+
+		/* This decreases the reference count after calling kunit_find_resource(). */
+		kunit_put_resource(res);
+	}
+
+
+	static struct kunit_case example_tests[] = {
+		KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
+					   example_param_init, NULL),
+		{}
+	};
+
+As an alternative to using the KUnit Resource API for shared resources, you can
+place them in ``test->parent->priv``. It can store data that needs to persist
+and be accessible across all executions within a parameterized test series.
+
+As stated previously ``param_init`` and ``param_exit`` receive the parent
+``struct kunit`` instance. So, you can directly use ``test->priv`` within them
+to manage shared resources. However, from within the test case function, you must
+navigate up to the parent i.e. use ``test->parent->priv`` to access those same
+resources.
+
+The resources placed in ``test->parent-priv`` will also need to be allocated in
+memory to persist across the parameterized tests executions. If memory is
+allocated using the memory allocation APIs provided by KUnit (described more in
+the section below), you will not need to worry about deallocating them as they
+will be managed by the parent parameterized test that gets automatically cleaned
+up upon the end of the parameterized test series.
+
+The code below demonstrates example usage of the ``priv`` field for shared
+resources:
+
+.. code-block:: c
+
+	/* An example parameter array. */
+	static const struct example_param {
+		int value;
+	} example_params_array[] = {
+		{ .value = 3, },
+		{ .value = 2, },
+		{ .value = 1, },
+		{ .value = 0, },
+	};
+
+	/*
+	 * Initializes the parent kunit struct for parameterized KUnit tests.
+	 * This function enables sharing resources across all parameterized
+	 * tests.
+	 */
+	static int example_param_init_priv(struct kunit *test)
+	{
+		int ctx = 3; /* Data to be stored. */
+		int arr_size = ARRAY_SIZE(example_params_array);
+
+		/*
+		 * Allocate memory using kunit_kzalloc(). Since the `param_init`
+		 * function receives the parent instance of test, this memory
+		 * allocation will be scoped to the lifetime of the whole
+		 * parameterized test series.
+		 */
+		test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
+
+		/* Assign the context value to test->priv.*/
+		*((int *)test->priv) = ctx;
+
+		/* Pass the static param array information to the parent struct kunit. */
+		kunit_register_params_array(test, example_params_array, arr_size, NULL);
+		return 0;
+	}
+
+	/*
+	* This is an example of a parameterized test that uses shared resources
+	* available from the struct kunit parent field of the kunit struct.
+	*/
+	static void example_params_test_with_init_priv(struct kunit *test)
+	{
+		int threshold;
+		const struct example_param *param = test->param_value;
+
+		/* By design, param pointer will not be NULL. */
+		KUNIT_ASSERT_NOT_NULL(test, param);
+
+		/* By design, test->parent will also not be NULL. */
+		KUNIT_ASSERT_NOT_NULL(test, test->parent);
+
+		/* Assert that test->parent->priv has data. */
+		KUNIT_ASSERT_NOT_NULL(test, test->parent->priv);
+
+		/* Here we need to use test->parent->priv to access the shared resource. */
+		threshold = *(int *)test->parent->priv;
+
+		/* Assert that the parameter is less than or equal to a certain threshold. */
+		KUNIT_ASSERT_LE(test, param->value, threshold);
+	}
+
+
+	static struct kunit_case example_tests[] = {
+		KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv, NULL,
+					   example_param_init_priv, NULL),
+		{}
+	};
+
 Allocating Memory
 -----------------
 
-- 
2.50.1.552.g942d659e1b-goog


^ permalink raw reply related	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
@ 2025-07-29 20:14   ` Rae Moar
  2025-07-29 20:26     ` Rae Moar
  2025-07-31 15:58   ` Dan Carpenter
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 41+ messages in thread
From: Rae Moar @ 2025-07-29 20:14 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> KUnit parameterized tests currently support two
> primary methods for getting parameters:
> 1.  Defining custom logic within a `generate_params`
>     function.
> 2.  Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
>     macros with pre-defined static arrays.
>
> These methods present limitations when dealing with
> dynamically generated parameter arrays, or in scenarios
> where populating parameters sequentially via
> `generate_params` is inefficient or overly complex.
>
> This patch addresses these limitations by adding a new
> `params_data` field to `struct kunit`, of the type
> `kunit_params`. The struct `kunit_params` is designed to
> store the parameter array itself, along with essential metadata
> including the parameter count, parameter size, and a
> `get_description` function for providing custom descriptions
> for individual parameters.
>
> The `params_data` field can be populated by calling the new
> `kunit_register_params_array` macro from within a
> `param_init` function. By attaching the parameter array
> directly to the parent kunit test instance, these parameters
> can be iterated over in kunit_run_tests() behind the scenes.
>
> This modification provides greater flexibility to the
> KUnit framework, allowing testers to easily register and
> utilize both dynamic and static parameter arrays.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---
>  include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
>  lib/kunit/test.c     | 26 ++++++++++++++++++++-
>  2 files changed, 75 insertions(+), 5 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 4ba65dc35710..9143f0e22323 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
>   */
>  #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
>                 { .run_case = test_name, .name = #test_name,                    \
> -                 .generate_params = gen_params,                                \
> +                 .generate_params = (gen_params)                               \
> +                  ?: kunit_get_next_param_and_desc,                            \
>                   .param_init = init, .param_exit = exit,                       \
>                   .module_name = KBUILD_MODNAME}
>
> @@ -294,6 +295,21 @@ struct kunit_suite_set {
>         struct kunit_suite * const *end;
>  };
>
> +/* Stores the pointer to the parameter array and its metadata. */
> +struct kunit_params {
> +       /*
> +        * Reference to the parameter array for the parameterized tests. This
> +        * is NULL if a parameter array wasn't directly passed to the
> +        * parent kunit struct via the kunit_register_params_array macro.
> +        */
> +       const void *params;
> +       /* Reference to a function that gets the description of a parameter. */
> +       void (*get_description)(const void *param, char *desc);
> +
> +       int num_params;
> +       size_t elem_size;
> +};
> +
>  /**
>   * struct kunit - represents a running instance of a test.
>   *
> @@ -302,12 +318,14 @@ struct kunit_suite_set {
>   * @parent: for user to store data that they want to shared across
>   *         parameterized tests. Typically, the data is provided in
>   *         the param_init function (see &struct kunit_case).
> + * @params_data: for users to directly store the parameter array.
>   *
>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> - * indirectly via public functions; the two exceptions are @priv and @parent
> - * which can be used by the test writer to store arbitrary data or data that is
> - * available to all parameter test executions, respectively.
> + * indirectly via public functions. There are three exceptions to this: @priv,
> + * @parent, and @params_data. These members can be used by the test writer to
> + * store arbitrary data, data available to all parameter test executions, and
> + * the parameter array, respectively.
>   */
>  struct kunit {
>         void *priv;
> @@ -316,6 +334,8 @@ struct kunit {
>          * during parameterized testing.
>          */
>         struct kunit *parent;
> +       /* Stores the params array and all data related to it. */
> +       struct kunit_params params_data;
>
>         /* private: internal use only. */
>         const char *name; /* Read only after initialization! */
> @@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
>  struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
>                 struct kunit_suite_set suite_set);
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);

Hello!

Thanks for sending out this series! I will do a full review of it. For
now, I noticed that I get an error when I try to run KUnit tests as
modules. I get the following error: "ERROR: modpost:
"kunit_get_next_param_and_desc" [lib/kunit/kunit-example-test.ko]
undefined!". As a possible fix, I suggest moving the function
definition into the header file and making it a static inline
function.

Thanks!
-Rae

> +
>  #if IS_BUILTIN(CONFIG_KUNIT)
>  int kunit_run_all_tests(void);
>  #else
> @@ -1735,6 +1757,30 @@ do {                                                                            \
>                 return NULL;                                                                    \
>         }
>
> +/**
> + * kunit_register_params_array() - Register parameters for a KUnit test.
> + * @test: The KUnit test structure to which parameters will be added.
> + * @params_arr: An array of test parameters.
> + * @param_cnt: Number of parameters.
> + * @get_desc: A pointer to a function that generates a string description for
> + * a given parameter element.
> + *
> + * This macro initializes the @test's parameter array data, storing information
> + * including the parameter array, its count, the element size, and the parameter
> + * description function within `test->params_data`. KUnit's built-in
> + * `kunit_get_next_param_and_desc` function will automatically read this
> + * data when a custom `generate_params` function isn't provided.
> + */
> +#define kunit_register_params_array(test, params_arr, param_cnt, get_desc)                     \
> +       do {                                                                                    \
> +               struct kunit *_test = (test);                                           \
> +               const typeof((params_arr)[0]) * _params_ptr = &(params_arr)[0];                 \
> +               _test->params_data.params = _params_ptr;                                        \
> +               _test->params_data.num_params = (param_cnt);                                    \
> +               _test->params_data.elem_size = sizeof(*_params_ptr);                            \
> +               _test->params_data.get_description = (get_desc);                                \
> +       } while (0)
> +
>  // TODO(dlatypov@google.com): consider eventually migrating users to explicitly
>  // include resource.h themselves if they need it.
>  #include <kunit/resource.h>
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index f50ef82179c4..2f4b7087db3f 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -337,6 +337,13 @@ void __kunit_do_failed_assertion(struct kunit *test,
>  }
>  EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
>
> +static void __kunit_init_params(struct kunit *test)
> +{
> +       test->params_data.params = NULL;
> +       test->params_data.num_params = 0;
> +       test->params_data.elem_size = 0;
> +}
> +
>  void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
>  {
>         spin_lock_init(&test->lock);
> @@ -347,6 +354,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
>                 string_stream_clear(log);
>         test->status = KUNIT_SUCCESS;
>         test->status_comment[0] = '\0';
> +       __kunit_init_params(test);
>  }
>  EXPORT_SYMBOL_GPL(kunit_init_test);
>
> @@ -641,6 +649,22 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
>         total->total += add.total;
>  }
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc)
> +{
> +       struct kunit_params *params_arr = &test->params_data;
> +       const void *param;
> +
> +       if (test->param_index < params_arr->num_params) {
> +               param = (char *)params_arr->params
> +                       + test->param_index * params_arr->elem_size;
> +
> +               if (params_arr->get_description)
> +                       params_arr->get_description(param, desc);
> +               return param;
> +       }
> +       return NULL;
> +}
> +
>  static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
>  {
>         if (test_case->param_init) {
> @@ -687,7 +711,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         /* Test marked as skip */
>                         test.status = KUNIT_SKIPPED;
>                         kunit_update_stats(&param_stats, test.status);
> -               } else if (!test_case->generate_params) {
> +               } else if (!test_case->generate_params && !test.params_data.params) {
>                         /* Non-parameterised test. */
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_run_case_catch_errors(suite, test_case, &test);
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-29 20:14   ` Rae Moar
@ 2025-07-29 20:26     ` Rae Moar
  0 siblings, 0 replies; 41+ messages in thread
From: Rae Moar @ 2025-07-29 20:26 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 4:14 PM Rae Moar <rmoar@google.com> wrote:
>
> On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
> >
> > KUnit parameterized tests currently support two
> > primary methods for getting parameters:
> > 1.  Defining custom logic within a `generate_params`
> >     function.
> > 2.  Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
> >     macros with pre-defined static arrays.
> >
> > These methods present limitations when dealing with
> > dynamically generated parameter arrays, or in scenarios
> > where populating parameters sequentially via
> > `generate_params` is inefficient or overly complex.
> >
> > This patch addresses these limitations by adding a new
> > `params_data` field to `struct kunit`, of the type
> > `kunit_params`. The struct `kunit_params` is designed to
> > store the parameter array itself, along with essential metadata
> > including the parameter count, parameter size, and a
> > `get_description` function for providing custom descriptions
> > for individual parameters.
> >
> > The `params_data` field can be populated by calling the new
> > `kunit_register_params_array` macro from within a
> > `param_init` function. By attaching the parameter array
> > directly to the parent kunit test instance, these parameters
> > can be iterated over in kunit_run_tests() behind the scenes.
> >
> > This modification provides greater flexibility to the
> > KUnit framework, allowing testers to easily register and
> > utilize both dynamic and static parameter arrays.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
> > ---
> >  include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
> >  lib/kunit/test.c     | 26 ++++++++++++++++++++-
> >  2 files changed, 75 insertions(+), 5 deletions(-)
> >
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index 4ba65dc35710..9143f0e22323 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
> >   */
> >  #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
> >                 { .run_case = test_name, .name = #test_name,                    \
> > -                 .generate_params = gen_params,                                \
> > +                 .generate_params = (gen_params)                               \
> > +                  ?: kunit_get_next_param_and_desc,                            \
> >                   .param_init = init, .param_exit = exit,                       \
> >                   .module_name = KBUILD_MODNAME}
> >
> > @@ -294,6 +295,21 @@ struct kunit_suite_set {
> >         struct kunit_suite * const *end;
> >  };
> >
> > +/* Stores the pointer to the parameter array and its metadata. */
> > +struct kunit_params {
> > +       /*
> > +        * Reference to the parameter array for the parameterized tests. This
> > +        * is NULL if a parameter array wasn't directly passed to the
> > +        * parent kunit struct via the kunit_register_params_array macro.
> > +        */
> > +       const void *params;
> > +       /* Reference to a function that gets the description of a parameter. */
> > +       void (*get_description)(const void *param, char *desc);
> > +
> > +       int num_params;
> > +       size_t elem_size;
> > +};
> > +
> >  /**
> >   * struct kunit - represents a running instance of a test.
> >   *
> > @@ -302,12 +318,14 @@ struct kunit_suite_set {
> >   * @parent: for user to store data that they want to shared across
> >   *         parameterized tests. Typically, the data is provided in
> >   *         the param_init function (see &struct kunit_case).
> > + * @params_data: for users to directly store the parameter array.
> >   *
> >   * Used to store information about the current context under which the test
> >   * is running. Most of this data is private and should only be accessed
> > - * indirectly via public functions; the two exceptions are @priv and @parent
> > - * which can be used by the test writer to store arbitrary data or data that is
> > - * available to all parameter test executions, respectively.
> > + * indirectly via public functions. There are three exceptions to this: @priv,
> > + * @parent, and @params_data. These members can be used by the test writer to
> > + * store arbitrary data, data available to all parameter test executions, and
> > + * the parameter array, respectively.
> >   */
> >  struct kunit {
> >         void *priv;
> > @@ -316,6 +334,8 @@ struct kunit {
> >          * during parameterized testing.
> >          */
> >         struct kunit *parent;
> > +       /* Stores the params array and all data related to it. */
> > +       struct kunit_params params_data;
> >
> >         /* private: internal use only. */
> >         const char *name; /* Read only after initialization! */
> > @@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
> >  struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
> >                 struct kunit_suite_set suite_set);
> >
> > +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);
>
> Hello!
>
> Thanks for sending out this series! I will do a full review of it. For
> now, I noticed that I get an error when I try to run KUnit tests as
> modules. I get the following error: "ERROR: modpost:
> "kunit_get_next_param_and_desc" [lib/kunit/kunit-example-test.ko]
> undefined!". As a possible fix, I suggest moving the function
> definition into the header file and making it a static inline
> function.
>
> Thanks!
> -Rae
>

Hello! Feel free to also use EXPORT_SYMBOL_GPL(). Either solution
should work here. Thanks!

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup
  2025-07-29 19:36 ` [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup Marie Zhussupova
@ 2025-07-30 12:25   ` kernel test robot
  2025-08-02  9:44   ` David Gow
  2025-08-05 15:19   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: kernel test robot @ 2025-07-30 12:25 UTC (permalink / raw)
  To: Marie Zhussupova, rmoar, davidgow, shuah, brendan.higgins
  Cc: oe-kbuild-all, elver, dvyukov, lucas.demarchi, thomas.hellstrom,
	rodrigo.vivi, linux-kselftest, kunit-dev, kasan-dev, intel-xe,
	dri-devel, linux-kernel, Marie Zhussupova

Hi Marie,

kernel test robot noticed the following build errors:

[auto build test ERROR on shuah-kselftest/kunit]
[also build test ERROR on shuah-kselftest/kunit-fixes drm-xe/drm-xe-next linus/master v6.16 next-20250730]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Marie-Zhussupova/kunit-Add-parent-kunit-for-parameterized-test-context/20250730-033818
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit
patch link:    https://lore.kernel.org/r/20250729193647.3410634-8-marievic%40google.com
patch subject: [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup
config: arc-randconfig-001-20250730 (https://download.01.org/0day-ci/archive/20250730/202507302042.9Aw3rrmW-lkp@intel.com/config)
compiler: arc-linux-gcc (GCC) 8.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250730/202507302042.9Aw3rrmW-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507302042.9Aw3rrmW-lkp@intel.com/

All errors (new ones prefixed by >>, old ones prefixed by <<):

>> ERROR: modpost: "kunit_get_next_param_and_desc" [lib/kunit/kunit-example-test.ko] undefined!

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management
  2025-07-29 19:36 ` [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management Marie Zhussupova
@ 2025-07-30 13:50   ` kernel test robot
  2025-08-02  9:44   ` David Gow
  2025-08-05 15:17   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: kernel test robot @ 2025-07-30 13:50 UTC (permalink / raw)
  To: Marie Zhussupova, rmoar, davidgow, shuah, brendan.higgins
  Cc: llvm, oe-kbuild-all, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel, Marie Zhussupova

Hi Marie,

kernel test robot noticed the following build errors:

[auto build test ERROR on shuah-kselftest/kunit]
[also build test ERROR on shuah-kselftest/kunit-fixes drm-xe/drm-xe-next linus/master v6.16 next-20250730]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Marie-Zhussupova/kunit-Add-parent-kunit-for-parameterized-test-context/20250730-033818
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit
patch link:    https://lore.kernel.org/r/20250729193647.3410634-3-marievic%40google.com
patch subject: [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management
config: x86_64-rhel-9.4-rust (https://download.01.org/0day-ci/archive/20250730/202507302114.xQU4zmX5-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
rustc: rustc 1.88.0 (6b00bc388 2025-06-23)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250730/202507302114.xQU4zmX5-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507302114.xQU4zmX5-lkp@intel.com/

All errors (new ones prefixed by >>):

>> error[E0063]: missing fields `param_exit` and `param_init` in initializer of `kunit_case`
   --> rust/kernel/kunit.rs:200:5
   |
   200 |     kernel::bindings::kunit_case {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `param_exit` and `param_init`
--
>> error[E0063]: missing fields `param_exit` and `param_init` in initializer of `kunit_case`
   --> rust/kernel/kunit.rs:219:5
   |
   219 |     kernel::bindings::kunit_case {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `param_exit` and `param_init`

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing
  2025-07-29 19:36 ` [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing Marie Zhussupova
@ 2025-07-30 14:32   ` kernel test robot
  2025-08-02  9:44   ` David Gow
  2025-08-05 15:18   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: kernel test robot @ 2025-07-30 14:32 UTC (permalink / raw)
  To: Marie Zhussupova, rmoar, davidgow, shuah, brendan.higgins
  Cc: llvm, oe-kbuild-all, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel, Marie Zhussupova

Hi Marie,

kernel test robot noticed the following build errors:

[auto build test ERROR on shuah-kselftest/kunit]
[also build test ERROR on shuah-kselftest/kunit-fixes drm-xe/drm-xe-next linus/master v6.16 next-20250730]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Marie-Zhussupova/kunit-Add-parent-kunit-for-parameterized-test-context/20250730-033818
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit
patch link:    https://lore.kernel.org/r/20250729193647.3410634-4-marievic%40google.com
patch subject: [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing
config: arm64-randconfig-002-20250730 (https://download.01.org/0day-ci/archive/20250730/202507302223.BTl33Nvo-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250730/202507302223.BTl33Nvo-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202507302223.BTl33Nvo-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/gpu/drm/xe/xe_migrate.c:1917:
>> drivers/gpu/drm/xe/tests/xe_migrate.c:772:44: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     772 |         KUNIT_CASE_PARAM(xe_migrate_sanity_kunit, xe_pci_live_device_gen_param),
         |                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   In file included from drivers/gpu/drm/xe/xe_migrate.c:1917:
   drivers/gpu/drm/xe/tests/xe_migrate.c:773:42: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     773 |         KUNIT_CASE_PARAM(xe_validate_ccs_kunit, xe_pci_live_device_gen_param),
         |                                                 ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   2 errors generated.
--
   In file included from drivers/gpu/drm/xe/xe_dma_buf.c:319:
>> drivers/gpu/drm/xe/tests/xe_dma_buf.c:285:37: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     285 |         KUNIT_CASE_PARAM(xe_dma_buf_kunit, xe_pci_live_device_gen_param),
         |                                            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   1 error generated.
--
   In file included from drivers/gpu/drm/xe/xe_bo.c:3128:
>> drivers/gpu/drm/xe/tests/xe_bo.c:610:41: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     610 |         KUNIT_CASE_PARAM(xe_ccs_migrate_kunit, xe_pci_live_device_gen_param),
         |                                                ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   In file included from drivers/gpu/drm/xe/xe_bo.c:3128:
   drivers/gpu/drm/xe/tests/xe_bo.c:611:38: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     611 |         KUNIT_CASE_PARAM(xe_bo_evict_kunit, xe_pci_live_device_gen_param),
         |                                             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   In file included from drivers/gpu/drm/xe/xe_bo.c:3128:
   drivers/gpu/drm/xe/tests/xe_bo.c:624:44: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     624 |         KUNIT_CASE_PARAM_ATTR(xe_bo_shrink_kunit, xe_pci_live_device_gen_param,
         |                                                   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:228:24: note: expanded from macro 'KUNIT_CASE_PARAM_ATTR'
     228 |                   .generate_params = gen_params,                                \
         |                                      ^~~~~~~~~~
   3 errors generated.
--
   In file included from drivers/gpu/drm/xe/xe_mocs.c:799:
>> drivers/gpu/drm/xe/tests/xe_mocs.c:193:46: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     193 |         KUNIT_CASE_PARAM(xe_live_mocs_kernel_kunit, xe_pci_live_device_gen_param),
         |                                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   In file included from drivers/gpu/drm/xe/xe_mocs.c:799:
   drivers/gpu/drm/xe/tests/xe_mocs.c:194:45: error: incompatible function pointer types initializing 'const void *(*)(struct kunit *, const void *, char *)' with an expression of type 'const void *(const void *, char *)' [-Wincompatible-function-pointer-types]
     194 |         KUNIT_CASE_PARAM(xe_live_mocs_reset_kunit, xe_pci_live_device_gen_param),
         |                                                    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/kunit/test.h:215:24: note: expanded from macro 'KUNIT_CASE_PARAM'
     215 |                   .generate_params = gen_params, .module_name = KBUILD_MODNAME}
         |                                      ^~~~~~~~~~
   2 errors generated.


vim +772 drivers/gpu/drm/xe/tests/xe_migrate.c

54f07cfc016226 Akshata Jahagirdar 2024-07-17  770  
0237368193e897 Michal Wajdeczko   2024-07-08  771  static struct kunit_case xe_migrate_tests[] = {
37db1e77628551 Michal Wajdeczko   2024-07-20 @772  	KUNIT_CASE_PARAM(xe_migrate_sanity_kunit, xe_pci_live_device_gen_param),
37db1e77628551 Michal Wajdeczko   2024-07-20  773  	KUNIT_CASE_PARAM(xe_validate_ccs_kunit, xe_pci_live_device_gen_param),
0237368193e897 Michal Wajdeczko   2024-07-08  774  	{}
0237368193e897 Michal Wajdeczko   2024-07-08  775  };
0237368193e897 Michal Wajdeczko   2024-07-08  776  

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
  2025-07-29 20:14   ` Rae Moar
@ 2025-07-31 15:58   ` Dan Carpenter
  2025-07-31 20:01     ` Marie Zhussupova
  2025-08-02  9:44   ` David Gow
  2025-08-05 15:18   ` Rae Moar
  3 siblings, 1 reply; 41+ messages in thread
From: Dan Carpenter @ 2025-07-31 15:58 UTC (permalink / raw)
  To: oe-kbuild, Marie Zhussupova, rmoar, davidgow, shuah,
	brendan.higgins
  Cc: lkp, oe-kbuild-all, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel, Marie Zhussupova

Hi Marie,

kernel test robot noticed the following build warnings:

https://git-scm.com/docs/git-format-patch#_base_tree_information]

url:    https://github.com/intel-lab-lkp/linux/commits/Marie-Zhussupova/kunit-Add-parent-kunit-for-parameterized-test-context/20250730-033818
base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit
patch link:    https://lore.kernel.org/r/20250729193647.3410634-7-marievic%40google.com
patch subject: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
config: nios2-randconfig-r072-20250731 (https://download.01.org/0day-ci/archive/20250731/202507310854.pZvIcswn-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 8.5.0

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
| Closes: https://lore.kernel.org/r/202507310854.pZvIcswn-lkp@intel.com/

New smatch warnings:
lib/kunit/test.c:723 kunit_run_tests() error: we previously assumed 'test_case->generate_params' could be null (see line 714)

vim +723 lib/kunit/test.c

914cc63eea6fbe Brendan Higgins     2019-09-23  681  int kunit_run_tests(struct kunit_suite *suite)
914cc63eea6fbe Brendan Higgins     2019-09-23  682  {
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  683  	char param_desc[KUNIT_PARAM_DESC_SIZE];
914cc63eea6fbe Brendan Higgins     2019-09-23  684  	struct kunit_case *test_case;
acd8e8407b8fcc David Gow           2021-08-03  685  	struct kunit_result_stats suite_stats = { 0 };
acd8e8407b8fcc David Gow           2021-08-03  686  	struct kunit_result_stats total_stats = { 0 };
8631cd2cf5fbf2 Marie Zhussupova    2025-07-29  687  	const void *curr_param;
914cc63eea6fbe Brendan Higgins     2019-09-23  688  
c272612cb4a2f7 David Gow           2022-07-01  689  	/* Taint the kernel so we know we've run tests. */
c272612cb4a2f7 David Gow           2022-07-01  690  	add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
c272612cb4a2f7 David Gow           2022-07-01  691  
1cdba21db2ca31 Daniel Latypov      2022-04-29  692  	if (suite->suite_init) {
1cdba21db2ca31 Daniel Latypov      2022-04-29  693  		suite->suite_init_err = suite->suite_init(suite);
1cdba21db2ca31 Daniel Latypov      2022-04-29  694  		if (suite->suite_init_err) {
1cdba21db2ca31 Daniel Latypov      2022-04-29  695  			kunit_err(suite, KUNIT_SUBTEST_INDENT
1cdba21db2ca31 Daniel Latypov      2022-04-29  696  				  "# failed to initialize (%d)", suite->suite_init_err);
1cdba21db2ca31 Daniel Latypov      2022-04-29  697  			goto suite_end;
1cdba21db2ca31 Daniel Latypov      2022-04-29  698  		}
1cdba21db2ca31 Daniel Latypov      2022-04-29  699  	}
1cdba21db2ca31 Daniel Latypov      2022-04-29  700  
cae56e1740f559 Daniel Latypov      2022-04-29  701  	kunit_print_suite_start(suite);
914cc63eea6fbe Brendan Higgins     2019-09-23  702  
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  703  	kunit_suite_for_each_test_case(suite, test_case) {
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  704  		struct kunit test = { .param_value = NULL, .param_index = 0 };
acd8e8407b8fcc David Gow           2021-08-03  705  		struct kunit_result_stats param_stats = { 0 };
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  706  
887d85a0736ff3 Rae Moar            2023-03-08  707  		kunit_init_test(&test, test_case->name, test_case->log);
03806177fa4cbb Marie Zhussupova    2025-07-29  708  		__kunit_init_parent_test(test_case, &test);
03806177fa4cbb Marie Zhussupova    2025-07-29  709  
529534e8cba3e6 Rae Moar            2023-07-25  710  		if (test_case->status == KUNIT_SKIPPED) {
529534e8cba3e6 Rae Moar            2023-07-25  711  			/* Test marked as skip */
529534e8cba3e6 Rae Moar            2023-07-25  712  			test.status = KUNIT_SKIPPED;
529534e8cba3e6 Rae Moar            2023-07-25  713  			kunit_update_stats(&param_stats, test.status);
44c50ed8e59936 Marie Zhussupova    2025-07-29 @714  		} else if (!test_case->generate_params && !test.params_data.params) {
                                                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^
Imagine ->generate_parms is NULL but test.params_data.params is
non-NULL.

37dbb4c7c7442d David Gow           2021-11-02  715  			/* Non-parameterised test. */
529534e8cba3e6 Rae Moar            2023-07-25  716  			test_case->status = KUNIT_SKIPPED;
37dbb4c7c7442d David Gow           2021-11-02  717  			kunit_run_case_catch_errors(suite, test_case, &test);
37dbb4c7c7442d David Gow           2021-11-02  718  			kunit_update_stats(&param_stats, test.status);
03806177fa4cbb Marie Zhussupova    2025-07-29  719  		} else if (test_case->status != KUNIT_FAILURE) {
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  720  			/* Get initial param. */
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  721  			param_desc[0] = '\0';
8631cd2cf5fbf2 Marie Zhussupova    2025-07-29  722  			/* TODO: Make generate_params try-catch */
13ee0c64bd88a3 Marie Zhussupova    2025-07-29 @723  			curr_param = test_case->generate_params(&test, NULL, param_desc);
                                                                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^
Then this could crash.

I suspect that this is fine, but I bet that in the previous
condition, just testing one would probably have been sufficient
or maybe we could have change && to ||.

529534e8cba3e6 Rae Moar            2023-07-25  724  			test_case->status = KUNIT_SKIPPED;
6c738b52316c58 Rae Moar            2022-11-23  725  			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
6c738b52316c58 Rae Moar            2022-11-23  726  				  "KTAP version 1\n");
44b7da5fcd4c99 David Gow           2021-11-02  727  			kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
44b7da5fcd4c99 David Gow           2021-11-02  728  				  "# Subtest: %s", test_case->name);
fadb08e7c7501e Arpitha Raghunandan 2020-11-16  729  
8631cd2cf5fbf2 Marie Zhussupova    2025-07-29  730  			while (curr_param) {

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-31 15:58   ` Dan Carpenter
@ 2025-07-31 20:01     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-07-31 20:01 UTC (permalink / raw)
  To: Dan Carpenter
  Cc: oe-kbuild, rmoar, davidgow, shuah, brendan.higgins, lkp,
	oe-kbuild-all, elver, dvyukov, lucas.demarchi, thomas.hellstrom,
	rodrigo.vivi, linux-kselftest, kunit-dev, kasan-dev, intel-xe,
	dri-devel, linux-kernel

On Thu, Jul 31, 2025 at 11:58 AM Dan Carpenter <dan.carpenter@linaro.org> wrote:
>
> Hi Marie,
>
> kernel test robot noticed the following build warnings:
>
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url:    https://github.com/intel-lab-lkp/linux/commits/Marie-Zhussupova/kunit-Add-parent-kunit-for-parameterized-test-context/20250730-033818
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git kunit
> patch link:    https://lore.kernel.org/r/20250729193647.3410634-7-marievic%40google.com
> patch subject: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
> config: nios2-randconfig-r072-20250731 (https://download.01.org/0day-ci/archive/20250731/202507310854.pZvIcswn-lkp@intel.com/config)
> compiler: nios2-linux-gcc (GCC) 8.5.0
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
> | Closes: https://lore.kernel.org/r/202507310854.pZvIcswn-lkp@intel.com/
>
> New smatch warnings:
> lib/kunit/test.c:723 kunit_run_tests() error: we previously assumed 'test_case->generate_params' could be null (see line 714)
>
> vim +723 lib/kunit/test.c
>
> 914cc63eea6fbe Brendan Higgins     2019-09-23  681  int kunit_run_tests(struct kunit_suite *suite)
> 914cc63eea6fbe Brendan Higgins     2019-09-23  682  {
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  683      char param_desc[KUNIT_PARAM_DESC_SIZE];
> 914cc63eea6fbe Brendan Higgins     2019-09-23  684      struct kunit_case *test_case;
> acd8e8407b8fcc David Gow           2021-08-03  685      struct kunit_result_stats suite_stats = { 0 };
> acd8e8407b8fcc David Gow           2021-08-03  686      struct kunit_result_stats total_stats = { 0 };
> 8631cd2cf5fbf2 Marie Zhussupova    2025-07-29  687      const void *curr_param;
> 914cc63eea6fbe Brendan Higgins     2019-09-23  688
> c272612cb4a2f7 David Gow           2022-07-01  689      /* Taint the kernel so we know we've run tests. */
> c272612cb4a2f7 David Gow           2022-07-01  690      add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
> c272612cb4a2f7 David Gow           2022-07-01  691
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  692      if (suite->suite_init) {
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  693              suite->suite_init_err = suite->suite_init(suite);
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  694              if (suite->suite_init_err) {
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  695                      kunit_err(suite, KUNIT_SUBTEST_INDENT
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  696                                "# failed to initialize (%d)", suite->suite_init_err);
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  697                      goto suite_end;
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  698              }
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  699      }
> 1cdba21db2ca31 Daniel Latypov      2022-04-29  700
> cae56e1740f559 Daniel Latypov      2022-04-29  701      kunit_print_suite_start(suite);
> 914cc63eea6fbe Brendan Higgins     2019-09-23  702
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  703      kunit_suite_for_each_test_case(suite, test_case) {
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  704              struct kunit test = { .param_value = NULL, .param_index = 0 };
> acd8e8407b8fcc David Gow           2021-08-03  705              struct kunit_result_stats param_stats = { 0 };
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  706
> 887d85a0736ff3 Rae Moar            2023-03-08  707              kunit_init_test(&test, test_case->name, test_case->log);
> 03806177fa4cbb Marie Zhussupova    2025-07-29  708              __kunit_init_parent_test(test_case, &test);
> 03806177fa4cbb Marie Zhussupova    2025-07-29  709
> 529534e8cba3e6 Rae Moar            2023-07-25  710              if (test_case->status == KUNIT_SKIPPED) {
> 529534e8cba3e6 Rae Moar            2023-07-25  711                      /* Test marked as skip */
> 529534e8cba3e6 Rae Moar            2023-07-25  712                      test.status = KUNIT_SKIPPED;
> 529534e8cba3e6 Rae Moar            2023-07-25  713                      kunit_update_stats(&param_stats, test.status);
> 44c50ed8e59936 Marie Zhussupova    2025-07-29 @714              } else if (!test_case->generate_params && !test.params_data.params) {
>                                                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^
> Imagine ->generate_parms is NULL but test.params_data.params is
> non-NULL.
>
> 37dbb4c7c7442d David Gow           2021-11-02  715                      /* Non-parameterised test. */
> 529534e8cba3e6 Rae Moar            2023-07-25  716                      test_case->status = KUNIT_SKIPPED;
> 37dbb4c7c7442d David Gow           2021-11-02  717                      kunit_run_case_catch_errors(suite, test_case, &test);
> 37dbb4c7c7442d David Gow           2021-11-02  718                      kunit_update_stats(&param_stats, test.status);
> 03806177fa4cbb Marie Zhussupova    2025-07-29  719              } else if (test_case->status != KUNIT_FAILURE) {
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  720                      /* Get initial param. */
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  721                      param_desc[0] = '\0';
> 8631cd2cf5fbf2 Marie Zhussupova    2025-07-29  722                      /* TODO: Make generate_params try-catch */
> 13ee0c64bd88a3 Marie Zhussupova    2025-07-29 @723                      curr_param = test_case->generate_params(&test, NULL, param_desc);
>                                                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^
> Then this could crash.
>
> I suspect that this is fine, but I bet that in the previous
> condition, just testing one would probably have been sufficient
> or maybe we could have change && to ||.

Hello Dan,

My apologies for the HTML version of this email earlier. Here is the
plain text version.

As of now, test.params_data.params can only be populated in a param_init
function, which can only be used if we register the test case with a
KUNIT_CASE_PARAM_WITH_INIT macro. That macro auto populates
test_case->generate_params with a function called
kunit_get_next_param_and_desc()
(which iterates over the parameter array) if the test user didn't provide their
own generator function. So, there shouldn't be a case where
test_case->generate_params is NULL but test.params_data.params is NON-NULL.

However, to be robust, we could add a NULL check  before calling
test_case->generate_params on line 723.

Thank you!
-Marie

>
> 529534e8cba3e6 Rae Moar            2023-07-25  724                      test_case->status = KUNIT_SKIPPED;
> 6c738b52316c58 Rae Moar            2022-11-23  725                      kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> 6c738b52316c58 Rae Moar            2022-11-23  726                                "KTAP version 1\n");
> 44b7da5fcd4c99 David Gow           2021-11-02  727                      kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> 44b7da5fcd4c99 David Gow           2021-11-02  728                                "# Subtest: %s", test_case->name);
> fadb08e7c7501e Arpitha Raghunandan 2020-11-16  729
> 8631cd2cf5fbf2 Marie Zhussupova    2025-07-29  730                      while (curr_param) {
>
> --
> 0-DAY CI Kernel Test Service
> https://github.com/intel/lkp-tests/wiki
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/9] kunit: Add parent kunit for parameterized test context
  2025-07-29 19:36 ` [PATCH 1/9] kunit: Add parent kunit for parameterized test context Marie Zhussupova
@ 2025-08-02  9:44   ` David Gow
  2025-08-05 15:17   ` Rae Moar
  1 sibling, 0 replies; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 7687 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> Currently, KUnit parameterized tests lack a mechanism
> to share resources across individual test invocations
> because the same `struct kunit` instance is reused for
> each test.
>
> This patch refactors kunit_run_tests() to provide each
> parameterized test with its own `struct kunit` instance.
> A new parent pointer is added to `struct kunit`, allowing
> individual parameterized tests to reference a shared
> parent kunit instance. Resources added to this parent
> will then be accessible to all individual parameter
> test executions.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

I'm definitely a fan of this, though it's not without its downsides.

For most tests, I don't think this will be a noticeable difference,
since the "shared" struct kunit is reset after each parameter anyway,
so this is a pretty safe change.

Having a 'parent' struct kunit, and hence potentially a hierarchy,
does give us a good way of implementing more complicated parameterised
tests which might need some more persistent state. (Ultimately, we
could have another level for suites, and then allow
suite_init/suite_exit to setup something persistent, too.)

Anyway, this looks good to me. I've left some small notes below, but
nothing I think is actionable.

Reviewed-by: David Gow <davidgow@google.com>

Cheers,
-- David

>  include/kunit/test.h | 12 ++++++++++--
>  lib/kunit/test.c     | 32 +++++++++++++++++++-------------
>  2 files changed, 29 insertions(+), 15 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 39c768f87dc9..a42d0c8cb985 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -268,14 +268,22 @@ struct kunit_suite_set {
>   *
>   * @priv: for user to store arbitrary data. Commonly used to pass data
>   *       created in the init function (see &struct kunit_suite).
> + * @parent: for user to store data that they want to shared across
> + *         parameterized tests.

A part of me would prefer this not to explicitly call out
parameterized tests here, as the obvious extensions to this (having
suites have a struct kunit, or other forms of test hierarchy) would
still make use of this in non-parameterised use-cases.

But since parameterised tests are the only current use-case for it, I
can live with it if you'd prefer.

>   *
>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> - * indirectly via public functions; the one exception is @priv which can be
> - * used by the test writer to store arbitrary data.
> + * indirectly via public functions; the two exceptions are @priv and @parent
> + * which can be used by the test writer to store arbitrary data or data that is
> + * available to all parameter test executions, respectively.
>   */
>  struct kunit {
>         void *priv;
> +       /*
> +        * Reference to the parent struct kunit for storing shared resources
> +        * during parameterized testing.
> +        */
> +       struct kunit *parent;
>
>         /* private: internal use only. */
>         const char *name; /* Read only after initialization! */
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index f3c6b11f12b8..4d6a39eb2c80 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -647,6 +647,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>         struct kunit_case *test_case;
>         struct kunit_result_stats suite_stats = { 0 };
>         struct kunit_result_stats total_stats = { 0 };
> +       const void *curr_param;
>
>         /* Taint the kernel so we know we've run tests. */
>         add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
> @@ -679,36 +680,39 @@ int kunit_run_tests(struct kunit_suite *suite)
>                 } else {
>                         /* Get initial param. */
>                         param_desc[0] = '\0';
> -                       test.param_value = test_case->generate_params(NULL, param_desc);
> +                       /* TODO: Make generate_params try-catch */

Thanks for adding the TODO here: this isn't a regression, but it's
good to note that we should get around to fixing it.

> +                       curr_param = test_case->generate_params(NULL, param_desc);
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
>                                   "KTAP version 1\n");
>                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
>                                   "# Subtest: %s", test_case->name);
>
> -                       while (test.param_value) {
> -                               kunit_run_case_catch_errors(suite, test_case, &test);
> +                       while (curr_param) {
> +                               struct kunit param_test = {
> +                                       .param_value = curr_param,
> +                                       .param_index = ++test.param_index,
> +                                       .parent = &test,
> +                               };
> +                               kunit_init_test(&param_test, test_case->name, test_case->log);
> +                               kunit_run_case_catch_errors(suite, test_case, &param_test);
>
>                                 if (param_desc[0] == '\0') {
>                                         snprintf(param_desc, sizeof(param_desc),
>                                                  "param-%d", test.param_index);
>                                 }
>
> -                               kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
> -                                                     test.status,
> -                                                     test.param_index + 1,
> +                               kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
> +                                                     param_test.status,
> +                                                     param_test.param_index,
>                                                       param_desc,
> -                                                     test.status_comment);
> +                                                     param_test.status_comment);
>
> -                               kunit_update_stats(&param_stats, test.status);
> +                               kunit_update_stats(&param_stats, param_test.status);
>
>                                 /* Get next param. */
>                                 param_desc[0] = '\0';
> -                               test.param_value = test_case->generate_params(test.param_value, param_desc);
> -                               test.param_index++;
> -                               test.status = KUNIT_SUCCESS;
> -                               test.status_comment[0] = '\0';
> -                               test.priv = NULL;
> +                               curr_param = test_case->generate_params(curr_param, param_desc);
>                         }
>                 }
>
> @@ -723,6 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
>
>                 kunit_update_stats(&suite_stats, test_case->status);
>                 kunit_accumulate_stats(&total_stats, param_stats);
> +               /* TODO: Put this kunit_cleanup into a try-catch. */
> +               kunit_cleanup(&test);

Hmm... it is a shame we can't easily include this in the existing
try-catch mechanism. Definitely worth fixing in a follow-up.


>         }
>
>         if (suite->suite_exit)
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management
  2025-07-29 19:36 ` [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management Marie Zhussupova
  2025-07-30 13:50   ` kernel test robot
@ 2025-08-02  9:44   ` David Gow
  2025-08-05 15:17   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 7916 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> Add `param_init` and `param_exit` function pointers to
> `struct kunit_case`. Users will be able to set them
> via the new `KUNIT_CASE_PARAM_WITH_INIT` macro.
>
> These functions are invoked by kunit_run_tests() once before
> and once after the entire parameterized test series, respectively.
> They will receive the parent kunit test instance, allowing users
> to register and manage shared resources. Resources added to this
> parent kunit test will be accessible to all individual parameterized
> tests, facilitating init and exit for shared state.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

Thanks: this looks good to me, modulo the issues below (particularly
the Rust breakage).

I was initially unsure of the names 'param_init' and 'param_exit',
preferring just 'init'/'exit', but have since come to like it, as
otherwise it'd be easy to confuse it with the kunit_suite init/exit
functions, which behave differently. So happy to keep that.

With the Rust issue fixes, this is:
Reviewed-by: David Gow <davidgow@google.com>

Cheers,
-- David

>  include/kunit/test.h | 33 ++++++++++++++++++++++++++++++++-
>  lib/kunit/test.c     | 23 ++++++++++++++++++++++-
>  2 files changed, 54 insertions(+), 2 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index a42d0c8cb985..d8dac7efd745 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -92,6 +92,8 @@ struct kunit_attributes {
>   * @name:     the name of the test case.
>   * @generate_params: the generator function for parameterized tests.
>   * @attr:     the attributes associated with the test
> + * @param_init: The init function to run before parameterized tests.
> + * @param_exit: The exit function to run after parameterized tests.
>   *
>   * A test case is a function with the signature,
>   * ``void (*)(struct kunit *)``
> @@ -129,6 +131,13 @@ struct kunit_case {
>         const void* (*generate_params)(const void *prev, char *desc);
>         struct kunit_attributes attr;
>
> +       /*
> +        * Optional user-defined functions: one to register shared resources once
> +        * before the parameterized test series, and another to release them after.
> +        */
> +       int (*param_init)(struct kunit *test);
> +       void (*param_exit)(struct kunit *test);
> +

As noted by the test robot, these need to be initialised in
rust/kernel/kunit.rs when a kunit_case is being set up. Since
parameterised tests aren't used in Rust, they can just be set to None.

>         /* private: internal use only. */
>         enum kunit_status status;
>         char *module_name;
> @@ -218,6 +227,27 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
>                   .generate_params = gen_params,                                \
>                   .attr = attributes, .module_name = KBUILD_MODNAME}
>
> +/**
> + * KUNIT_CASE_PARAM_WITH_INIT() - Define a parameterized KUnit test case with custom
> + * init and exit functions.
> + * @test_name: The function implementing the test case.
> + * @gen_params: The function to generate parameters for the test case.
> + * @init: The init function to run before parameterized tests.
> + * @exit: The exit function to run after parameterized tests.
> + *
> + * Provides the option to register init and exit functions that take in the
> + * parent of the parameterized tests and run once before and once after the
> + * parameterized test series. The init function can be used to add any resources
> + * to share between the parameterized tests or to pass parameter arrays. The
> + * exit function can be used to clean up any resources that are not managed by
> + * the test.
> + */
> +#define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
> +               { .run_case = test_name, .name = #test_name,                    \
> +                 .generate_params = gen_params,                                \
> +                 .param_init = init, .param_exit = exit,                       \
> +                 .module_name = KBUILD_MODNAME}
> +
>  /**
>   * struct kunit_suite - describes a related collection of &struct kunit_case
>   *
> @@ -269,7 +299,8 @@ struct kunit_suite_set {
>   * @priv: for user to store arbitrary data. Commonly used to pass data
>   *       created in the init function (see &struct kunit_suite).
>   * @parent: for user to store data that they want to shared across
> - *         parameterized tests.
> + *         parameterized tests. Typically, the data is provided in
> + *         the param_init function (see &struct kunit_case).
>   *
>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 4d6a39eb2c80..d80b5990d85d 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -641,6 +641,19 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
>         total->total += add.total;
>  }
>
> +static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)

There's no fundamental reason this needs to start '__': other internal
functions here don't.

(But please keep it static and internal.)

> +{
> +       if (test_case->param_init) {
> +               int err = test_case->param_init(test);
> +
> +               if (err) {
> +                       kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> +                               "# failed to initialize parent parameter test.");
> +                       test_case->status = KUNIT_FAILURE;
> +               }
> +       }
> +}
> +
>  int kunit_run_tests(struct kunit_suite *suite)
>  {
>         char param_desc[KUNIT_PARAM_DESC_SIZE];
> @@ -668,6 +681,8 @@ int kunit_run_tests(struct kunit_suite *suite)
>                 struct kunit_result_stats param_stats = { 0 };
>
>                 kunit_init_test(&test, test_case->name, test_case->log);
> +               __kunit_init_parent_test(test_case, &test);
> +
>                 if (test_case->status == KUNIT_SKIPPED) {
>                         /* Test marked as skip */
>                         test.status = KUNIT_SKIPPED;
> @@ -677,7 +692,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_run_case_catch_errors(suite, test_case, &test);
>                         kunit_update_stats(&param_stats, test.status);
> -               } else {
> +               } else if (test_case->status != KUNIT_FAILURE) {
>                         /* Get initial param. */
>                         param_desc[0] = '\0';
>                         /* TODO: Make generate_params try-catch */
> @@ -727,6 +742,12 @@ int kunit_run_tests(struct kunit_suite *suite)
>
>                 kunit_update_stats(&suite_stats, test_case->status);
>                 kunit_accumulate_stats(&total_stats, param_stats);
> +               /*
> +                * TODO: Put into a try catch. Since we don't need suite->exit
> +                * for it we can't reuse kunit_try_run_cleanup for this yet.
> +                */
> +               if (test_case->param_exit)
> +                       test_case->param_exit(&test);

Not thrilled that this is introducing another TODO here, but since we
already have a number of places around parameterised tests where we're
not running things from the separate try-catch thread, I don't think
we should hold up useful features on it. The actual test code is still
properly handled.

But I'm looking forward to going through and cleaning all of these up
at some point.


>                 /* TODO: Put this kunit_cleanup into a try-catch. */
>                 kunit_cleanup(&test);
>         }
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing
  2025-07-29 19:36 ` [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing Marie Zhussupova
  2025-07-30 14:32   ` kernel test robot
@ 2025-08-02  9:44   ` David Gow
  2025-08-05 15:18   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5286 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> To enable more complex parameterized test scenarios,
> the `generate_params` function sometimes needs additional
> context beyond just the previously generated parameter.
> This patch modifies the `generate_params` function signature
> to include an extra `struct kunit *test` argument, giving
> users access to the parent kunit test's context when
> generating subsequent parameters.
>
> The `struct kunit *test` argument was added as the first parameter
> to the function signature as it aligns with the convention
> of other KUnit functions that accept `struct kunit *test` first.
> This also mirrors the "this" or "self" reference found
> in object-oriented programming languages.

This matches my prejudices well. :-)

> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

At last! This will be very useful as a way of having more complicated
generator functions (or, indeed, any generator function which wants
state without hacking it into an enormously complicated parameter
object).

The only thing worth noting is that this breaks the existing tests
until the next two patches are accepted, which could be a pain for
bisecting. I can live with it, since the breakage is obvious and
confined to the span of a couple of patches in the same series and to
test-only code, but if others prefer, we can squash these together.

Reviewed-by: David Gow <davidgow@google.com>

Cheers,
-- David


>  include/kunit/test.h | 9 ++++++---
>  lib/kunit/test.c     | 5 +++--
>  2 files changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index d8dac7efd745..4ba65dc35710 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -128,7 +128,8 @@ struct kunit_attributes {
>  struct kunit_case {
>         void (*run_case)(struct kunit *test);
>         const char *name;
> -       const void* (*generate_params)(const void *prev, char *desc);
> +       const void* (*generate_params)(struct kunit *test,
> +                                      const void *prev, char *desc);
>         struct kunit_attributes attr;
>
>         /*
> @@ -1701,7 +1702,8 @@ do {                                                                             \
>   * Define function @name_gen_params which uses @array to generate parameters.
>   */
>  #define KUNIT_ARRAY_PARAM(name, array, get_desc)                                               \
> -       static const void *name##_gen_params(const void *prev, char *desc)                      \
> +       static const void *name##_gen_params(struct kunit *test,                                \
> +                                            const void *prev, char *desc)                      \
>         {                                                                                       \
>                 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
>                 if (__next - (array) < ARRAY_SIZE((array))) {                                   \
> @@ -1722,7 +1724,8 @@ do {                                                                             \
>   * Define function @name_gen_params which uses @array to generate parameters.
>   */
>  #define KUNIT_ARRAY_PARAM_DESC(name, array, desc_member)                                       \
> -       static const void *name##_gen_params(const void *prev, char *desc)                      \
> +       static const void *name##_gen_params(struct kunit *test,                                \
> +                                            const void *prev, char *desc)                      \
>         {                                                                                       \
>                 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
>                 if (__next - (array) < ARRAY_SIZE((array))) {                                   \
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index d80b5990d85d..f50ef82179c4 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -696,7 +696,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         /* Get initial param. */
>                         param_desc[0] = '\0';
>                         /* TODO: Make generate_params try-catch */
> -                       curr_param = test_case->generate_params(NULL, param_desc);
> +                       curr_param = test_case->generate_params(&test, NULL, param_desc);
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
>                                   "KTAP version 1\n");
> @@ -727,7 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
>
>                                 /* Get next param. */
>                                 param_desc[0] = '\0';
> -                               curr_param = test_case->generate_params(curr_param, param_desc);
> +                               curr_param = test_case->generate_params(&test, curr_param,
> +                                                                       param_desc);
>                         }
>                 }
>
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 4/9] kcsan: test: Update parameter generator to new signature
  2025-07-29 19:36 ` [PATCH 4/9] kcsan: test: Update parameter generator to new signature Marie Zhussupova
@ 2025-08-02  9:44   ` David Gow
  2025-08-07 11:11     ` Marco Elver
  0 siblings, 1 reply; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1293 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> This patch modifies `nthreads_gen_params` in kcsan_test.c
> to accept an additional `struct kunit *test` argument.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

This is a pretty straightforward fix after patch 3. KCSAN folks, would
you prefer this kept as a separate patch, or squashed into the
previous one (so there's no commit where this is broken)?

Either way,
Reviewed-by: David Gow <davidgow@google.com>


-- David

>  kernel/kcsan/kcsan_test.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/kernel/kcsan/kcsan_test.c b/kernel/kcsan/kcsan_test.c
> index c2871180edcc..fc76648525ac 100644
> --- a/kernel/kcsan/kcsan_test.c
> +++ b/kernel/kcsan/kcsan_test.c
> @@ -1383,7 +1383,7 @@ static void test_atomic_builtins_missing_barrier(struct kunit *test)
>   * The thread counts are chosen to cover potentially interesting boundaries and
>   * corner cases (2 to 5), and then stress the system with larger counts.
>   */
> -static const void *nthreads_gen_params(const void *prev, char *desc)
> +static const void *nthreads_gen_params(struct kunit *test, const void *prev, char *desc)
>  {
>         long nthreads = (long)prev;
>
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 5/9] drm/xe: Update parameter generator to new signature
  2025-07-29 19:36 ` [PATCH 5/9] drm/xe: " Marie Zhussupova
@ 2025-08-02  9:44   ` David Gow
  2025-08-07 10:59     ` Lucas De Marchi
  0 siblings, 1 reply; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 1362 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> This patch modifies `xe_pci_live_device_gen_param`
> in xe_pci.c to accept an additional `struct kunit *test`
> argument.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---


This is a pretty straightforward fix after patch 3. xe folks, would
you prefer this kept as a separate patch, or squashed into patch 3
(which changed the function signature)?

Either way,
Reviewed-by: David Gow <davidgow@google.com>


-- David


>  drivers/gpu/drm/xe/tests/xe_pci.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/gpu/drm/xe/tests/xe_pci.c b/drivers/gpu/drm/xe/tests/xe_pci.c
> index 1d3e2e50c355..62c016e84227 100644
> --- a/drivers/gpu/drm/xe/tests/xe_pci.c
> +++ b/drivers/gpu/drm/xe/tests/xe_pci.c
> @@ -129,7 +129,7 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_fake_device_init);
>   * Return: pointer to the next &struct xe_device ready to be used as a parameter
>   *         or NULL if there are no more Xe devices on the system.
>   */
> -const void *xe_pci_live_device_gen_param(const void *prev, char *desc)
> +const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc)
>  {
>         const struct xe_device *xe = prev;
>         struct device *dev = xe ? xe->drm.dev : NULL;
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup
  2025-07-29 19:36 ` [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup Marie Zhussupova
  2025-07-30 12:25   ` kernel test robot
@ 2025-08-02  9:44   ` David Gow
  2025-08-06 17:50     ` Marie Zhussupova
  2025-08-05 15:19   ` Rae Moar
  2 siblings, 1 reply; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 7090 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> Add `example_params_test_with_init` to illustrate how to manage
> shared resources across parameterized KUnit tests. This example
> showcases the use of the new `param_init` function and its registration
> to a test using the `KUNIT_CASE_PARAM_WITH_INIT` macro.
>
> Additionally, the test demonstrates:
> - How to directly assign a static parameter array to a test via
>   `kunit_register_params_array`.
> - Leveraging the Resource API for test resource management.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

Thanks for writing some examples! This is great, and makes the rest of
the series much easier to understand.

(It also reminds me how much I hate the verbose parts of the resource
API, but it's definitely out of scope to refactor that here. :-))

It does seem like this is a lot of effort to go through for one shared
integer, though. In the real world, I'd suggest using
kunit->parent->priv here. As an example, though, it's fine (though
maybe using a named resource or even kunit_kzalloc() or similar would
give a better example of how convenient this could be.

It's also not entirely clear why we're using
kunit_register_params_array() for a static array, when
KUNIT_ARRAY_PARAM() exists. (This is clearly because the latter
doesn't support init functions; and I see why we don't necessarily
want to make the number of macros explode through adding
KUNIT_ARRAY_PARAM_WITH_INIT() et al, but maybe we should note that in
the commit description, either here or before.)

Actual test looks fine, though:

Reviewed-by: David Gow <davidgow@google.com>

Cheers,
-- David


>  lib/kunit/kunit-example-test.c | 112 +++++++++++++++++++++++++++++++++
>  1 file changed, 112 insertions(+)
>
> diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> index 3056d6bc705d..5bf559e243f6 100644
> --- a/lib/kunit/kunit-example-test.c
> +++ b/lib/kunit/kunit-example-test.c
> @@ -277,6 +277,116 @@ static void example_slow_test(struct kunit *test)
>         KUNIT_EXPECT_EQ(test, 1 + 1, 2);
>  }
>
> +/*
> + * This custom function allocates memory for the kunit_resource data field.
> + * The function is passed to kunit_alloc_resource() and executed once
> + * by the internal helper __kunit_add_resource().
> + */
> +static int example_resource_init(struct kunit_resource *res, void *context)
> +{
> +       int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> +
> +       if (!info)
> +               return -ENOMEM;
> +       *info = *(int *)context;
> +       res->data = info;
> +       return 0;
> +}
> +
> +/*
> + * This function deallocates memory for the 'kunit_resource' data field.
> + * The function is passed to kunit_alloc_resource() and automatically
> + * executes within kunit_release_resource() when the resource's reference
> + * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> + * counting to ensure that resources are not freed prematurely.
> + */
> +static void example_resource_free(struct kunit_resource *res)
> +{
> +       kfree(res->data);
> +}
> +
> +/*
> + * This match function is invoked by kunit_find_resource() to locate
> + * a test resource based on defined criteria. The current example
> + * uniquely identifies the resource by its free function; however,
> + * alternative custom criteria can be implemented. Refer to
> + * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> + */
> +static bool example_resource_alloc_match(struct kunit *test,
> +                                        struct kunit_resource *res,
> +                                        void *match_data)
> +{
> +       return res->data && res->free == example_resource_free;
> +}
> +
> +/*
> + * This is an example of a function that provides a description for each of the
> + * parameters.
> + */
> +static void example_param_array_get_desc(const void *p, char *desc)
> +{
> +       const struct example_param *param = p;
> +
> +       snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> +                "example check if %d is less than or equal to 3", param->value);
> +}
> +
> +/*
> + * Initializes the parent kunit struct for parameterized KUnit tests.
> + * This function enables sharing resources across all parameterized
> + * tests by adding them to the `parent` kunit test struct. It also supports
> + * registering either static or dynamic arrays of test parameters.
> + */
> +static int example_param_init(struct kunit *test)
> +{
> +       int ctx = 3; /* Data to be stored. */
> +       int arr_size = ARRAY_SIZE(example_params_array);
> +
> +       /*
> +        * This allocates a struct kunit_resource, sets its data field to
> +        * ctx, and adds it to the kunit struct's resources list. Note that
> +        * this is test managed so we don't need to have a custom exit function
> +        * to free it.
> +        */
> +       void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> +                                         GFP_KERNEL, &ctx);
> +
> +       if (!data)
> +               return -ENOMEM;
> +       /* Pass the static param array information to the parent struct kunit. */
> +       kunit_register_params_array(test, example_params_array, arr_size,
> +                                   example_param_array_get_desc);
> +       return 0;
> +}
> +
> +/*
> + * This is an example of a parameterized test that uses shared resources
> + * available from the struct kunit parent field of the kunit struct.
> + */
> +static void example_params_test_with_init(struct kunit *test)
> +{
> +       int threshold;
> +       struct kunit_resource *res;
> +       const struct example_param *param = test->param_value;
> +
> +       /* By design, param pointer will not be NULL. */
> +       KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +       /* Here we access the parent pointer of the test to find the shared resource. */
> +       res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> +
> +       KUNIT_ASSERT_NOT_NULL(test, res);
> +
> +       /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> +       threshold = *((int *)res->data);
> +
> +       /* Assert that the parameter is less than or equal to a certain threshold. */
> +       KUNIT_ASSERT_LE(test, param->value, threshold);
> +
> +       /* This decreases the reference count after calling kunit_find_resource(). */
> +       kunit_put_resource(res);
> +}
> +
>  /*
>   * Here we make a list of all the test cases we want to add to the test suite
>   * below.
> @@ -296,6 +406,8 @@ static struct kunit_case example_test_cases[] = {
>         KUNIT_CASE(example_static_stub_using_fn_ptr_test),
>         KUNIT_CASE(example_priv_test),
>         KUNIT_CASE_PARAM(example_params_test, example_gen_params),
> +       KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> +                                  example_param_init, NULL),
>         KUNIT_CASE_SLOW(example_slow_test),
>         {}
>  };
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 8/9] kunit: Add example parameterized test with direct dynamic parameter array setup
  2025-07-29 19:36 ` [PATCH 8/9] kunit: Add example parameterized test with direct dynamic " Marie Zhussupova
@ 2025-08-02  9:44   ` David Gow
  2025-08-05 15:19   ` Rae Moar
  1 sibling, 0 replies; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 5457 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> Introduce `example_params_test_with_init_dynamic_arr`. This new
> KUnit test demonstrates directly assigning a dynamic parameter
> array using the `kunit_register_params_array` macro. It highlights the
> use of `param_init` and `param_exit` for proper initialization and
> cleanup, and their registration to the test with
> `KUNIT_CASE_PARAM_WITH_INIT`.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

This is an excellent example, thanks. (I much prefer it to the
previous one. In fact, if we could use some shared resource in this,
we could probably get rid of the previous one entirely.)

Reviewed-by: David Gow <davidgow@google.com>

Cheers,
-- David

>  lib/kunit/kunit-example-test.c | 95 ++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>
> diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> index 5bf559e243f6..3ab121d81bf6 100644
> --- a/lib/kunit/kunit-example-test.c
> +++ b/lib/kunit/kunit-example-test.c
> @@ -387,6 +387,98 @@ static void example_params_test_with_init(struct kunit *test)
>         kunit_put_resource(res);
>  }
>
> +/*
> + * Helper function to create a parameter array of Fibonacci numbers. This example
> + * highlights a parameter generation scenario that is:
> + * 1. Not feasible to fully pre-generate at compile time.
> + * 2. Challenging to implement with a standard 'generate_params' function,
> + * as it typically only provides the immediately 'prev' parameter, while
> + * Fibonacci requires access to two preceding values for calculation.
> + */
> +static void *make_fibonacci_params(int seq_size)
> +{
> +       int *seq;
> +
> +       if (seq_size <= 0)
> +               return NULL;
> +
> +       seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);

If we used kunit_kmalloc_array here (we'd need to pass test through
somehow, though), we could have a good example of a shared resource
here.

> +
> +       if (!seq)
> +               return NULL;
> +
> +       if (seq_size >= 1)
> +               seq[0] = 0;
> +       if (seq_size >= 2)
> +               seq[1] = 1;
> +       for (int i = 2; i < seq_size; i++)
> +               seq[i] = seq[i - 1] + seq[i - 2];
> +       return seq;
> +}
> +
> +/*
> + * This is an example of a function that provides a description for each of the
> + * parameters.
> + */
> +static void example_param_dynamic_arr_get_desc(const void *p, char *desc)

Seeing this makes me wonder whether we should pass struct *kunit to
the get_desc function, too.

Thoughts?

> +{
> +       const int *fib_num = p;
> +
> +       snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
> +}
> +
> +/*
> + * Example of a parameterized test init function that registers a dynamic array.
> + */
> +static int example_param_init_dynamic_arr(struct kunit *test)
> +{
> +       int seq_size = 6;
> +       int *fibonacci_params = make_fibonacci_params(seq_size);
> +
> +       if (!fibonacci_params)
> +               return -ENOMEM;
> +
> +       /*
> +        * Passes the dynamic parameter array information to the parent struct kunit.
> +        * The array and its metadata will be stored in test->parent->params_data.
> +        * The array itself will be located in params_data.params.
> +        */
> +       kunit_register_params_array(test, fibonacci_params, seq_size,
> +                                   example_param_dynamic_arr_get_desc);
> +       return 0;
> +}
> +
> +/**
> + * Function to clean up the parameterized test's parent kunit struct if
> + * there were custom allocations.
> + */
> +static void example_param_exit_dynamic_arr(struct kunit *test)
> +{
> +       /*
> +        * We allocated this array, so we need to free it.
> +        * Since the parent parameter instance is passed here,
> +        * we can directly access the array via `test->params_data.params`
> +        * instead of `test->parent->params_data.params`.
> +        */
> +       kfree(test->params_data.params);

If we used kunit_kmalloc_array above, though, we'd miss this good
example. So I'm torn...

(I suppose we could use kunit_kfree() anyway, though, and just rely on
the shared resource management for early aborts.)


> +}
> +
> +/*
> + * Example of test that uses the registered dynamic array to perform assertions
> + * and expectations.
> + */
> +static void example_params_test_with_init_dynamic_arr(struct kunit *test)
> +{
> +       const int *param = test->param_value;
> +       int param_val;
> +
> +       /* By design, param pointer will not be NULL. */
> +       KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +       param_val = *param;
> +       KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
> +}
> +
>  /*
>   * Here we make a list of all the test cases we want to add to the test suite
>   * below.
> @@ -408,6 +500,9 @@ static struct kunit_case example_test_cases[] = {
>         KUNIT_CASE_PARAM(example_params_test, example_gen_params),
>         KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
>                                    example_param_init, NULL),
> +       KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
> +                                  example_param_init_dynamic_arr,
> +                                  example_param_exit_dynamic_arr),
>         KUNIT_CASE_SLOW(example_slow_test),
>         {}
>  };
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
  2025-07-29 20:14   ` Rae Moar
  2025-07-31 15:58   ` Dan Carpenter
@ 2025-08-02  9:44   ` David Gow
  2025-08-05 15:18   ` Rae Moar
  3 siblings, 0 replies; 41+ messages in thread
From: David Gow @ 2025-08-02  9:44 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 11940 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> KUnit parameterized tests currently support two
> primary methods for getting parameters:
> 1.  Defining custom logic within a `generate_params`
>     function.
> 2.  Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
>     macros with pre-defined static arrays.
>
> These methods present limitations when dealing with
> dynamically generated parameter arrays, or in scenarios
> where populating parameters sequentially via
> `generate_params` is inefficient or overly complex.
>
> This patch addresses these limitations by adding a new
> `params_data` field to `struct kunit`, of the type
> `kunit_params`. The struct `kunit_params` is designed to
> store the parameter array itself, along with essential metadata
> including the parameter count, parameter size, and a
> `get_description` function for providing custom descriptions
> for individual parameters.
>
> The `params_data` field can be populated by calling the new
> `kunit_register_params_array` macro from within a
> `param_init` function. By attaching the parameter array
> directly to the parent kunit test instance, these parameters
> can be iterated over in kunit_run_tests() behind the scenes.
>
> This modification provides greater flexibility to the
> KUnit framework, allowing testers to easily register and
> utilize both dynamic and static parameter arrays.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

A few thoughts:
- Refactoring out the parameter data into struct kunit_params makes
sense, particularly given the new features.
- We now have 3 APIs to handle parameters (all of which use a
generator function behind the scenes): a static array (which uses a
macro to invent a custom generator function), a dynamic array (which
uses kunit_get_next_param_and_desc as a -- possibly implied --
generator function), and a user-provided generator function. I don't
think that's a problem, but maybe these can be brought slightly closer
together in implementation?
- Should users pass NULL or kunit_get_next_param_and_desc as the
gen_params function if they're using a dynamic array? We seem to
support both here, but it's unsure which is preferred. My gut feeling
is that we should either totally pick one or the other: if we say NULL
implies kunit_get_next_param_and_desc, we should just set
kunit_params.generate_params to NULL, and only check when we call it;
if we suggest people use kunit_get_next_param_and_desc, we should not
support NULL at all (it goes back to implying a non-parameterised
test). My gut preference is for the latter, though it's not without
downsides.

More comments below.

Cheers,
-- David

>  include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
>  lib/kunit/test.c     | 26 ++++++++++++++++++++-
>  2 files changed, 75 insertions(+), 5 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 4ba65dc35710..9143f0e22323 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
>   */
>  #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
>                 { .run_case = test_name, .name = #test_name,                    \
> -                 .generate_params = gen_params,                                \
> +                 .generate_params = (gen_params)                               \
> +                  ?: kunit_get_next_param_and_desc,                            \

A part of me wonders whether we need this, or should just tell people
to pass kunit_get_next_param_and_desc manually, rather than NULL.

(If so, _maybe_ it'd make sense to rename it to something more
obvious, like kunit_get_array_param?)

>                   .param_init = init, .param_exit = exit,                       \
>                   .module_name = KBUILD_MODNAME}
>
> @@ -294,6 +295,21 @@ struct kunit_suite_set {
>         struct kunit_suite * const *end;
>  };
>
> +/* Stores the pointer to the parameter array and its metadata. */
> +struct kunit_params {
> +       /*
> +        * Reference to the parameter array for the parameterized tests. This
> +        * is NULL if a parameter array wasn't directly passed to the
> +        * parent kunit struct via the kunit_register_params_array macro.
> +        */

Would it make sense to update KUNIT_ARRAY_PARAM(,_DESC) to use this member?

I'd imagine we could get rid of the custom generated function for
KUNIT_ARRAY_PARAM, though we'd
still need it for the _DESC variant.

(Unless you want to go overboard, add a new offset_of_description
member, and make it possible to pass a description field even with
dynamic arrays. Though I don't know of anyone who actually needs
that.)

The existing "generated function" method is still correct (and maybe
slightly faster, though if this is a serious bottleneck, I'll eat my
hat), so I don't know that this change is necessary. Maybe worth
trying, or looking at for a follow-up.

> +       const void *params;
> +       /* Reference to a function that gets the description of a parameter. */
> +       void (*get_description)(const void *param, char *desc);
> +
> +       int num_params;
> +       size_t elem_size;
> +};
> +
>  /**
>   * struct kunit - represents a running instance of a test.
>   *
> @@ -302,12 +318,14 @@ struct kunit_suite_set {
>   * @parent: for user to store data that they want to shared across
>   *         parameterized tests. Typically, the data is provided in
>   *         the param_init function (see &struct kunit_case).
> + * @params_data: for users to directly store the parameter array.
>   *
>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> - * indirectly via public functions; the two exceptions are @priv and @parent
> - * which can be used by the test writer to store arbitrary data or data that is
> - * available to all parameter test executions, respectively.
> + * indirectly via public functions. There are three exceptions to this: @priv,
> + * @parent, and @params_data. These members can be used by the test writer to
> + * store arbitrary data, data available to all parameter test executions, and
> + * the parameter array, respectively.
>   */
>  struct kunit {
>         void *priv;
> @@ -316,6 +334,8 @@ struct kunit {
>          * during parameterized testing.
>          */
>         struct kunit *parent;
> +       /* Stores the params array and all data related to it. */
> +       struct kunit_params params_data;
>
>         /* private: internal use only. */
>         const char *name; /* Read only after initialization! */
> @@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
>  struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
>                 struct kunit_suite_set suite_set);
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);
> +
>  #if IS_BUILTIN(CONFIG_KUNIT)
>  int kunit_run_all_tests(void);
>  #else
> @@ -1735,6 +1757,30 @@ do {                                                                            \
>                 return NULL;                                                                    \
>         }
>
> +/**
> + * kunit_register_params_array() - Register parameters for a KUnit test.
> + * @test: The KUnit test structure to which parameters will be added.
> + * @params_arr: An array of test parameters.
> + * @param_cnt: Number of parameters.
> + * @get_desc: A pointer to a function that generates a string description for
> + * a given parameter element.
> + *
> + * This macro initializes the @test's parameter array data, storing information
> + * including the parameter array, its count, the element size, and the parameter
> + * description function within `test->params_data`. KUnit's built-in
> + * `kunit_get_next_param_and_desc` function will automatically read this
> + * data when a custom `generate_params` function isn't provided.
> + */
> +#define kunit_register_params_array(test, params_arr, param_cnt, get_desc)                     \
> +       do {                                                                                    \
> +               struct kunit *_test = (test);                                           \
> +               const typeof((params_arr)[0]) * _params_ptr = &(params_arr)[0];                 \
> +               _test->params_data.params = _params_ptr;                                        \
> +               _test->params_data.num_params = (param_cnt);                                    \
> +               _test->params_data.elem_size = sizeof(*_params_ptr);                            \
> +               _test->params_data.get_description = (get_desc);                                \
> +       } while (0)
> +
>  // TODO(dlatypov@google.com): consider eventually migrating users to explicitly
>  // include resource.h themselves if they need it.
>  #include <kunit/resource.h>
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index f50ef82179c4..2f4b7087db3f 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -337,6 +337,13 @@ void __kunit_do_failed_assertion(struct kunit *test,
>  }
>  EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
>
> +static void __kunit_init_params(struct kunit *test)
> +{
> +       test->params_data.params = NULL;
> +       test->params_data.num_params = 0;
> +       test->params_data.elem_size = 0;
> +}
> +
>  void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
>  {
>         spin_lock_init(&test->lock);
> @@ -347,6 +354,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
>                 string_stream_clear(log);
>         test->status = KUNIT_SUCCESS;
>         test->status_comment[0] = '\0';
> +       __kunit_init_params(test);
>  }
>  EXPORT_SYMBOL_GPL(kunit_init_test);
>
> @@ -641,6 +649,22 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
>         total->total += add.total;
>  }
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc)

As Rae points out, this needs to be exported if we want this to work in modules.

(Unless you want to go all of the way down the "generate_params ==
NULL implies a parameter array" route, in which case we could get away
with it.)

> +{
> +       struct kunit_params *params_arr = &test->params_data;
> +       const void *param;
> +
> +       if (test->param_index < params_arr->num_params) {
> +               param = (char *)params_arr->params
> +                       + test->param_index * params_arr->elem_size;
> +
> +               if (params_arr->get_description)
> +                       params_arr->get_description(param, desc);
> +               return param;
> +       }
> +       return NULL;
> +}
> +
>  static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
>  {
>         if (test_case->param_init) {
> @@ -687,7 +711,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         /* Test marked as skip */
>                         test.status = KUNIT_SKIPPED;
>                         kunit_update_stats(&param_stats, test.status);
> -               } else if (!test_case->generate_params) {
> +               } else if (!test_case->generate_params && !test.params_data.params) {

As above, this really depends on if we want generate_params == NULL to
imply that we're using a parameter array, or if it should imply that
we're a non-parameterised test.


>                         /* Non-parameterised test. */
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_run_case_catch_errors(suite, test_case, &test);
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 9/9] Documentation: kunit: Document new parameterized test features
  2025-07-29 19:36 ` [PATCH 9/9] Documentation: kunit: Document new parameterized test features Marie Zhussupova
@ 2025-08-02  9:45   ` David Gow
  2025-08-08 13:02     ` Marie Zhussupova
  2025-08-05 15:19   ` Rae Moar
  1 sibling, 1 reply; 41+ messages in thread
From: David Gow @ 2025-08-02  9:45 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

[-- Attachment #1: Type: text/plain, Size: 24351 bytes --]

On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>
> -Update the KUnit documentation to explain the concept
> of a parent parameterized test.
> -Add examples demonstrating different ways of passing
> parameters to parameterized tests and how to manage
> shared resources between them.
>

Nit: We don't need the dot points ('-') here. Just make them paragraphs.

> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---

Thanks very, very much for including such detailed documentation.

I do think some of the examples could be trimmed / left in the
kunit-example-test.c file and referenced, as they're long enough that
it's difficult to focus on the essentials. But otherwise, this looks
great.

A few small notes below, but otherwise:

Reviewed-by: David Gow <davidgow@google.com>

Cheers,
-- David

>  Documentation/dev-tools/kunit/usage.rst | 455 +++++++++++++++++++++++-
>  1 file changed, 449 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
> index 066ecda1dd98..be1d656053cf 100644
> --- a/Documentation/dev-tools/kunit/usage.rst
> +++ b/Documentation/dev-tools/kunit/usage.rst
> @@ -542,11 +542,21 @@ There is more boilerplate code involved, but it can:
>  Parameterized Testing
>  ~~~~~~~~~~~~~~~~~~~~~
>
> -The table-driven testing pattern is common enough that KUnit has special
> -support for it.
> -
> -By reusing the same ``cases`` array from above, we can write the test as a
> -"parameterized test" with the following.
> +To efficiently and elegantly validate a test case against a variety of inputs,
> +KUnit also provides a parameterized testing framework. This feature formalizes
> +and extends the concept of table-driven tests discussed previously, offering
> +a more integrated and flexible way to handle multiple test scenarios with
> +minimal code duplication.

Nit: maybe we can tone down the adjectives slightly here. I do like
parameterised testing a lot, but it probably doesn't need to be
"efficient", "elegant", "integrated", and "flexible".

> +
> +Passing Parameters to the Test Cases
> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +There are three main ways to provide the parameters to a test case:
> +
> +Array Parameter Macros (``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC``):
> +   KUnit provides special support for the common table-driven testing pattern.
> +   By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
> +   ``cases`` array from the previous section, we can create a parameterized test
> +   as shown below:
>
>  .. code-block:: c
>
> @@ -555,7 +565,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
>                 const char *str;
>                 const char *sha1;
>         };
> -       const struct sha1_test_case cases[] = {
> +       static const struct sha1_test_case cases[] = {
>                 {
>                         .str = "hello world",
>                         .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
> @@ -590,6 +600,439 @@ By reusing the same ``cases`` array from above, we can write the test as a
>                 {}
>         };
>
> +Custom Parameter Generator (``generate_params``):
> +   You can pass your own ``generate_params`` function to the ``KUNIT_CASE_PARAM``
> +   or ``KUNIT_CASE_PARAM_WITH_INIT`` macros. This function is responsible for
> +   generating parameters one by one. It receives the previously generated parameter
> +   as the ``prev`` argument (which is ``NULL`` on the first call) and can also
> +   access any context available from the parent ``struct kunit`` passed as the
> +   ``test`` argument. KUnit calls this function repeatedly until it returns
> +   ``NULL``. Below is an example of how it works:
> +
> +.. code-block:: c
> +
> +       #define MAX_TEST_BUFFER_SIZE 8
> +
> +       // Example generator function. It produces a sequence of buffer sizes that
> +       // are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
> +       static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
> +       {
> +               long prev_buffer_size = (long)prev;
> +               long next_buffer_size = 1; // Start with an initial size of 1.
> +
> +               // Stop generating parameters if the limit is reached or exceeded.
> +               if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
> +                       return NULL;
> +
> +               // For subsequent calls, calculate the next size by doubling the previous one.
> +               if (prev)
> +                       next_buffer_size = prev_buffer_size << 1;
> +
> +               return (void *)next_buffer_size;
> +       }
> +
> +       // Simple test to validate that kunit_kzalloc provides zeroed memory.
> +       static void buffer_zero_test(struct kunit *test)
> +       {
> +               long buffer_size = (long)test->param_value;
> +               // Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
> +               // memory "parameter managed," meaning it's automatically cleaned up at
> +               // the end of each parameter execution.
> +               int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
> +
> +               // Ensure the allocation was successful.
> +               KUNIT_ASSERT_NOT_NULL(test, buf);
> +
> +               // Loop through the buffer and confirm every element is zero.
> +               for (int i = 0; i < buffer_size; i++)
> +                       KUNIT_EXPECT_EQ(test, buf[i], 0);
> +       }
> +
> +       static struct kunit_case buffer_test_cases[] = {
> +               KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
> +               {}
> +       };
> +
> +Direct Registration in Parameter Init Function (using ``kunit_register_params_array``):

Maybe we should highlight this as being array-based more explicitly.
"Runtime Array Registration in the Init function" or similar?

> +   For more complex scenarios, you can directly register a parameter array with
> +   a test case instead of using a ``generate_params`` function. This is done by
> +   passing the array to the ``kunit_register_params_array`` macro within an
> +   initialization function for the parameterized test series
> +   (i.e., a function named ``param_init``). To better understand this mechanism
> +   please refer to the "Adding Shared Resources" section below.
> +
> +   This method supports both dynamically built and static arrays.
> +
> +   As the following code shows, the ``example_param_init_dynamic_arr`` function
> +   utilizes ``make_fibonacci_params`` to create a dynamic array, which is then
> +   registered using ``kunit_register_params_array``. The corresponding exit
> +   function, ``example_param_exit``, is responsible for freeing this dynamically
> +   allocated params array after the parameterized test series ends.
> +
> +.. code-block:: c
> +
> +       /*
> +        * Helper function to create a parameter array of Fibonacci numbers. This example
> +        * highlights a parameter generation scenario that is:
> +        * 1. Not feasible to fully pre-generate at compile time.
> +        * 2. Challenging to implement with a standard 'generate_params' function,
> +        * as it typically only provides the immediately 'prev' parameter, while
> +        * Fibonacci requires access to two preceding values for calculation.
> +        */
> +       static void *make_fibonacci_params(int seq_size)
> +       {
> +               int *seq;
> +
> +               if (seq_size <= 0)
> +                       return NULL;
> +
> +               seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
> +
> +               if (!seq)
> +                       return NULL;
> +
> +               if (seq_size >= 1)
> +                       seq[0] = 0;
> +               if (seq_size >= 2)
> +                       seq[1] = 1;
> +               for (int i = 2; i < seq_size; i++)
> +                       seq[i] = seq[i - 1] + seq[i - 2];
> +               return seq;
> +       }
> +
> +       // This is an example of a function that provides a description for each of the
> +       // parameters.
> +       static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
> +       {
> +               const int *fib_num = p;
> +
> +               snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
> +       }
> +
> +       // Example of a parameterized test init function that registers a dynamic array.
> +       static int example_param_init_dynamic_arr(struct kunit *test)
> +       {
> +               int seq_size = 6;
> +               int *fibonacci_params = make_fibonacci_params(seq_size);
> +
> +               if (!fibonacci_params)
> +                       return -ENOMEM;
> +
> +               /*
> +                * Passes the dynamic parameter array information to the parent struct kunit.
> +                * The array and its metadata will be stored in test->parent->params_data.
> +                * The array itself will be located in params_data.params.
> +                */
> +               kunit_register_params_array(test, fibonacci_params, seq_size,
> +                                           example_param_dynamic_arr_get_desc);
> +               return 0;
> +       }
> +
> +       // Function to clean up the parameterized test's parent kunit struct if
> +       // there were custom allocations.
> +       static void example_param_exit_dynamic_arr(struct kunit *test)
> +       {
> +               /*
> +                * We allocated this array, so we need to free it.
> +                * Since the parent parameter instance is passed here,
> +                * we can directly access the array via `test->params_data.params`
> +                * instead of `test->parent->params_data.params`.
> +                */
> +               kfree(test->params_data.params);
> +       }
> +
> +       /*
> +        * Example of test that uses the registered dynamic array to perform assertions
> +        * and expectations.
> +        */
> +       static void example_params_test_with_init_dynamic_arr(struct kunit *test)
> +       {
> +               const int *param = test->param_value;
> +               int param_val;
> +
> +               /* By design, param pointer will not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +               param_val = *param;
> +               KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
> +       }
> +
> +       static struct kunit_case example_tests[] = {
> +               // The NULL here stands in for the generate_params function
> +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
> +                                          example_param_init_dynamic_arr,
> +                                          example_param_exit_dynamic_arr),
> +               {}
> +       };
> +

This is a long example, which already exists in the source code
(kunit-example-test.c). Could we just include some highlights (e.g.,
the init function and the KUNIT_CASE_PARAM_WITH_INIT call), and link
to the source code for the rest?

> +Adding Shared Resources
> +^^^^^^^^^^^^^^^^^^^^^^^
> +All parameterized test executions in this framework have a parent test of type
> +``struct kunit``. This parent is not used to execute any test logic itself;
> +instead, it serves as a container for shared context that can be accessed by
> +all its individual test executions (or parameters). Therefore, each individual
> +test execution holds a pointer to this parent, accessible via a field named
> +``parent``.
> +
> +It's possible to add resources to share between the individual test executions
> +within a parameterized test series by using the ``KUNIT_CASE_PARAM_WITH_INIT``
> +macro, to which you pass custom ``param_init`` and ``param_exit`` functions.
> +These functions run once before and once after the entire parameterized test
> +series, respectively. The ``param_init`` function can be used for adding any
> +resources to the resources field of a parent test and also provide an additional
> +way of setting the parameter array. The ``param_exit`` function can be used
> +release any resources that were not test managed i.e. not automatically cleaned
> +up after the test ends.
> +
> +.. note::
> +   If both a ``generate_params`` function is passed to ``KUNIT_CASE_PARAM_WITH_INIT``
> +   and an array is registered via ``kunit_register_params_array`` in
> +   ``param_init``, the ``generate_params`` function will be used to get
> +   the parameters.

Maybe note that the ``generate_params`` function can use the array
passed, though?

> +
> +Both ``param_init`` and ``param_exit`` are passed the parent instance of a test
> +(parent ``struct kunit``) behind the scenes. However, the test case function
> +receives the individual instance of a test for each parameter. Therefore, to
> +manage and access shared resources from within a test case function, you must use
> +``test->parent``.
> +
> +.. note::
> +   The ``suite->init()`` function, which runs before each parameter execution,
> +   receives the individual instance of a test for each parameter. Therefore,
> +   resources set up in ``suite->init()`` are reset for each individual
> +   parameterized test execution and are only visible within that specific test.
> +
> +For instance, finding a shared resource allocated by the Resource API requires
> +passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
> +all other APIs that might be used in the test case function, including
> +``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
> +Documentation/dev-tools/kunit/api/test.rst and the
> +Documentation/dev-tools/kunit/api/resource.rst).
> +
> +The code below shows how you can add the shared resources. Note that this code
> +utilizes the Resource API, which you can read more about here:
> +Documentation/dev-tools/kunit/api/resource.rst.
> +
> +.. code-block:: c
> +
> +       /* An example parameter array. */
> +       static const struct example_param {
> +               int value;
> +       } example_params_array[] = {
> +               { .value = 3, },
> +               { .value = 2, },
> +               { .value = 1, },
> +               { .value = 0, },
> +       };
> +
> +       /*
> +        * This custom function allocates memory for the kunit_resource data field.
> +        * The function is passed to kunit_alloc_resource() and executed once
> +        * by the internal helper __kunit_add_resource().
> +        */
> +       static int example_resource_init(struct kunit_resource *res, void *context)
> +       {
> +               int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> +
> +               if (!info)
> +                       return -ENOMEM;
> +               *info = *(int *)context;
> +               res->data = info;
> +               return 0;
> +       }
> +
> +       /*
> +        * This function deallocates memory for the 'kunit_resource' data field.
> +        * The function is passed to kunit_alloc_resource() and automatically
> +        * executes within kunit_release_resource() when the resource's reference
> +        * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> +        * counting to ensure that resources are not freed prematurely.
> +        */
> +       static void example_resource_free(struct kunit_resource *res)
> +       {
> +               kfree(res->data);
> +       }
> +
> +       /*
> +        * This match function is invoked by kunit_find_resource() to locate
> +        * a test resource based on defined criteria. The current example
> +        * uniquely identifies the resource by its free function; however,
> +        * alternative custom criteria can be implemented. Refer to
> +        * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> +        */
> +       static bool example_resource_alloc_match(struct kunit *test,
> +                                                struct kunit_resource *res,
> +                                                void *match_data)
> +       {
> +               return res->data && res->free == example_resource_free;
> +       }
> +
> +       /*
> +        * This is an example of a function that provides a description for each of the
> +        * parameters.
> +       */
> +       static void example_param_array_get_desc(const void *p, char *desc)
> +       {
> +               const struct example_param *param = p;
> +
> +               snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> +                       "example check if %d is less than or equal to 3", param->value);
> +       }
> +
> +       /*
> +        * Initializes the parent kunit struct for parameterized KUnit tests.
> +        * This function enables sharing resources across all parameterized
> +        * tests by adding them to the `parent` kunit test struct. It also supports
> +        * registering either static or dynamic arrays of test parameters.
> +        */
> +       static int example_param_init(struct kunit *test)
> +       {
> +               int ctx = 3; /* Data to be stored. */
> +               int arr_size = ARRAY_SIZE(example_params_array);
> +
> +               /*
> +                * This allocates a struct kunit_resource, sets its data field to
> +                * ctx, and adds it to the kunit struct's resources list. Note that
> +                * this is test managed so we don't need to have a custom exit function
> +                * to free it.
> +                */
> +               void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> +                                                 GFP_KERNEL, &ctx);
> +
> +               if (!data)
> +                       return -ENOMEM;
> +               /* Pass the static param array information to the parent struct kunit. */
> +               kunit_register_params_array(test, example_params_array, arr_size,
> +                                           example_param_array_get_desc);
> +               return 0;
> +       }
> +
> +       /*
> +       * This is an example of a parameterized test that uses shared resources
> +       * available from the struct kunit parent field of the kunit struct.
> +       */
> +       static void example_params_test_with_init(struct kunit *test)
> +       {
> +               int threshold;
> +               struct kunit_resource *res;
> +               const struct example_param *param = test->param_value;
> +
> +               /* By design, param pointer will not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +               /* Here we need to access the parent pointer of the test to find the shared resource. */
> +               res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> +
> +               KUNIT_ASSERT_NOT_NULL(test, res);
> +
> +               /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> +               threshold = *((int *)res->data);
> +
> +               /* Assert that the parameter is less than or equal to a certain threshold. */
> +               KUNIT_ASSERT_LE(test, param->value, threshold);
> +
> +               /* This decreases the reference count after calling kunit_find_resource(). */
> +               kunit_put_resource(res);
> +       }
> +
> +
> +       static struct kunit_case example_tests[] = {
> +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> +                                          example_param_init, NULL),
> +               {}
> +       };
> +

This is a really long example, which already exists in
kunit-example-test.c. Can we either link to it there (and just include
the most critical lines here), or have a smaller, less-complete
example inline here?


> +As an alternative to using the KUnit Resource API for shared resources, you can
> +place them in ``test->parent->priv``. It can store data that needs to persist
> +and be accessible across all executions within a parameterized test series.
> +
> +As stated previously ``param_init`` and ``param_exit`` receive the parent
> +``struct kunit`` instance. So, you can directly use ``test->priv`` within them
> +to manage shared resources. However, from within the test case function, you must
> +navigate up to the parent i.e. use ``test->parent->priv`` to access those same
> +resources.
> +
> +The resources placed in ``test->parent-priv`` will also need to be allocated in
> +memory to persist across the parameterized tests executions. If memory is

Nit: 'parameterized test executions' singular?

> +allocated using the memory allocation APIs provided by KUnit (described more in
> +the section below), you will not need to worry about deallocating them as they
> +will be managed by the parent parameterized test that gets automatically cleaned
> +up upon the end of the parameterized test series.
> +
> +The code below demonstrates example usage of the ``priv`` field for shared
> +resources:
> +
> +.. code-block:: c
> +
> +       /* An example parameter array. */
> +       static const struct example_param {
> +               int value;
> +       } example_params_array[] = {
> +               { .value = 3, },
> +               { .value = 2, },
> +               { .value = 1, },
> +               { .value = 0, },
> +       };
> +
> +       /*
> +        * Initializes the parent kunit struct for parameterized KUnit tests.
> +        * This function enables sharing resources across all parameterized
> +        * tests.
> +        */
> +       static int example_param_init_priv(struct kunit *test)
> +       {
> +               int ctx = 3; /* Data to be stored. */
> +               int arr_size = ARRAY_SIZE(example_params_array);
> +
> +               /*
> +                * Allocate memory using kunit_kzalloc(). Since the `param_init`
> +                * function receives the parent instance of test, this memory
> +                * allocation will be scoped to the lifetime of the whole
> +                * parameterized test series.
> +                */
> +               test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
> +
> +               /* Assign the context value to test->priv.*/
> +               *((int *)test->priv) = ctx;
> +
> +               /* Pass the static param array information to the parent struct kunit. */
> +               kunit_register_params_array(test, example_params_array, arr_size, NULL);
> +               return 0;
> +       }
> +
> +       /*
> +       * This is an example of a parameterized test that uses shared resources
> +       * available from the struct kunit parent field of the kunit struct.
> +       */
> +       static void example_params_test_with_init_priv(struct kunit *test)
> +       {
> +               int threshold;
> +               const struct example_param *param = test->param_value;
> +
> +               /* By design, param pointer will not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +               /* By design, test->parent will also not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, test->parent);
> +
> +               /* Assert that test->parent->priv has data. */
> +               KUNIT_ASSERT_NOT_NULL(test, test->parent->priv);
> +
> +               /* Here we need to use test->parent->priv to access the shared resource. */
> +               threshold = *(int *)test->parent->priv;
> +
> +               /* Assert that the parameter is less than or equal to a certain threshold. */
> +               KUNIT_ASSERT_LE(test, param->value, threshold);
> +       }
> +
> +
> +       static struct kunit_case example_tests[] = {
> +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv, NULL,
> +                                          example_param_init_priv, NULL),
> +               {}
> +       };
> +

Again, this is a little long, but it's not as bad as the others, and
isn't in the example tests, so I'm okay with leaving it. Though maybe
we could get rid of some of the asserts for the purpose of keeping the
documentation focused and readable.


>  Allocating Memory
>  -----------------
>
> --
> 2.50.1.552.g942d659e1b-goog
>

[-- Attachment #2: S/MIME Cryptographic Signature --]
[-- Type: application/pkcs7-signature, Size: 5281 bytes --]

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/9] kunit: Add parent kunit for parameterized test context
  2025-07-29 19:36 ` [PATCH 1/9] kunit: Add parent kunit for parameterized test context Marie Zhussupova
  2025-08-02  9:44   ` David Gow
@ 2025-08-05 15:17   ` Rae Moar
  2025-08-08 12:20     ` Marie Zhussupova
  1 sibling, 1 reply; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:17 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> Currently, KUnit parameterized tests lack a mechanism
> to share resources across individual test invocations
> because the same `struct kunit` instance is reused for
> each test.
>
> This patch refactors kunit_run_tests() to provide each
> parameterized test with its own `struct kunit` instance.
> A new parent pointer is added to `struct kunit`, allowing
> individual parameterized tests to reference a shared
> parent kunit instance. Resources added to this parent
> will then be accessible to all individual parameter
> test executions.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>

Hello!

Thank you so much for sending out this series. I have wanted to see an
update of our parameterized test framework for a while. I have a few
comments below for this patch. But otherwise it is looking good.

Reviewed-by: Rae Moar <rmoar@google.com>

Thanks!
-Rae

> ---
>  include/kunit/test.h | 12 ++++++++++--
>  lib/kunit/test.c     | 32 +++++++++++++++++++-------------
>  2 files changed, 29 insertions(+), 15 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 39c768f87dc9..a42d0c8cb985 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -268,14 +268,22 @@ struct kunit_suite_set {
>   *
>   * @priv: for user to store arbitrary data. Commonly used to pass data
>   *       created in the init function (see &struct kunit_suite).
> + * @parent: for user to store data that they want to shared across
> + *         parameterized tests.
>   *

As David mentioned, I would also prefer that this provides a more
general description of the @parent field here. Although this is
currently only used for parameterized tests, it could have other use
cases in the future.

>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> - * indirectly via public functions; the one exception is @priv which can be
> - * used by the test writer to store arbitrary data.
> + * indirectly via public functions; the two exceptions are @priv and @parent
> + * which can be used by the test writer to store arbitrary data or data that is
> + * available to all parameter test executions, respectively.

In addition, I would prefer that the call out to @parent here is also
changed to a more general description of the @parent field. However,
feel free to also include the description of the use case for the
parameterized tests.

>   */
>  struct kunit {
>         void *priv;
> +       /*
> +        * Reference to the parent struct kunit for storing shared resources
> +        * during parameterized testing.
> +        */

I am more 50/50 on changing this description. Could change it just to:
"Reference to the parent struct kunit for storing shared resources."

> +       struct kunit *parent;
>
>         /* private: internal use only. */
>         const char *name; /* Read only after initialization! */
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index f3c6b11f12b8..4d6a39eb2c80 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -647,6 +647,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>         struct kunit_case *test_case;
>         struct kunit_result_stats suite_stats = { 0 };
>         struct kunit_result_stats total_stats = { 0 };
> +       const void *curr_param;
>
>         /* Taint the kernel so we know we've run tests. */
>         add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
> @@ -679,36 +680,39 @@ int kunit_run_tests(struct kunit_suite *suite)
>                 } else {
>                         /* Get initial param. */
>                         param_desc[0] = '\0';
> -                       test.param_value = test_case->generate_params(NULL, param_desc);
> +                       /* TODO: Make generate_params try-catch */
> +                       curr_param = test_case->generate_params(NULL, param_desc);
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
>                                   "KTAP version 1\n");
>                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
>                                   "# Subtest: %s", test_case->name);
>
> -                       while (test.param_value) {
> -                               kunit_run_case_catch_errors(suite, test_case, &test);
> +                       while (curr_param) {
> +                               struct kunit param_test = {
> +                                       .param_value = curr_param,
> +                                       .param_index = ++test.param_index,
> +                                       .parent = &test,
> +                               };
> +                               kunit_init_test(&param_test, test_case->name, test_case->log);
> +                               kunit_run_case_catch_errors(suite, test_case, &param_test);
>
>                                 if (param_desc[0] == '\0') {
>                                         snprintf(param_desc, sizeof(param_desc),
>                                                  "param-%d", test.param_index);

This probably doesn't matter too much either way but should this be
param_test.param_index instead? This would cover the case where the
param_index is changed during the test run even though it shouldn't.

>                                 }
>
> -                               kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
> -                                                     test.status,
> -                                                     test.param_index + 1,
> +                               kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
> +                                                     param_test.status,
> +                                                     param_test.param_index,
>                                                       param_desc,
> -                                                     test.status_comment);
> +                                                     param_test.status_comment);
>
> -                               kunit_update_stats(&param_stats, test.status);
> +                               kunit_update_stats(&param_stats, param_test.status);
>
>                                 /* Get next param. */
>                                 param_desc[0] = '\0';
> -                               test.param_value = test_case->generate_params(test.param_value, param_desc);
> -                               test.param_index++;
> -                               test.status = KUNIT_SUCCESS;
> -                               test.status_comment[0] = '\0';
> -                               test.priv = NULL;
> +                               curr_param = test_case->generate_params(curr_param, param_desc);
>                         }
>                 }
>
> @@ -723,6 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
>
>                 kunit_update_stats(&suite_stats, test_case->status);
>                 kunit_accumulate_stats(&total_stats, param_stats);
> +               /* TODO: Put this kunit_cleanup into a try-catch. */
> +               kunit_cleanup(&test);

I might be missing something here but why not do this cleanup before
the printing stage and only if the test was a parent param test?



>         }
>
>         if (suite->suite_exit)
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management
  2025-07-29 19:36 ` [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management Marie Zhussupova
  2025-07-30 13:50   ` kernel test robot
  2025-08-02  9:44   ` David Gow
@ 2025-08-05 15:17   ` Rae Moar
  2025-08-08 13:01     ` Marie Zhussupova
  2 siblings, 1 reply; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:17 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> Add `param_init` and `param_exit` function pointers to
> `struct kunit_case`. Users will be able to set them
> via the new `KUNIT_CASE_PARAM_WITH_INIT` macro.

Hello!

Very intrigued by this idea to add an init and exit function for
parameterized tests. In a way, this allows parameterized test series
to act more like suites. Either way I am happy to see more flexibility
being brought to the parameterized test framework.

I have a few comments below that I would like to discuss before a
final review. But this patch is looking good.

Thanks!
-Rae

>
> These functions are invoked by kunit_run_tests() once before
> and once after the entire parameterized test series, respectively.

This is a philosophical question but should we refer to a group of
parameterized tests as a parameterized test series or a parameterized
test suite? In the KTAP, the appearance is identical to a suite but in
the running of the tests it acts distinct to a test case or suite.
Curious on David's opinion here.

> They will receive the parent kunit test instance, allowing users
> to register and manage shared resources. Resources added to this
> parent kunit test will be accessible to all individual parameterized
> tests, facilitating init and exit for shared state.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>
> ---
>  include/kunit/test.h | 33 ++++++++++++++++++++++++++++++++-
>  lib/kunit/test.c     | 23 ++++++++++++++++++++++-
>  2 files changed, 54 insertions(+), 2 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index a42d0c8cb985..d8dac7efd745 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -92,6 +92,8 @@ struct kunit_attributes {
>   * @name:     the name of the test case.
>   * @generate_params: the generator function for parameterized tests.
>   * @attr:     the attributes associated with the test
> + * @param_init: The init function to run before parameterized tests.
> + * @param_exit: The exit function to run after parameterized tests.

If we decide on a terminology for the parameterized test group, it
might be clearer to label these "The init function to run before
parameterized test [suite/series]." and same for the exit function.

>   *
>   * A test case is a function with the signature,
>   * ``void (*)(struct kunit *)``
> @@ -129,6 +131,13 @@ struct kunit_case {
>         const void* (*generate_params)(const void *prev, char *desc);
>         struct kunit_attributes attr;
>
> +       /*
> +        * Optional user-defined functions: one to register shared resources once
> +        * before the parameterized test series, and another to release them after.
> +        */
> +       int (*param_init)(struct kunit *test);
> +       void (*param_exit)(struct kunit *test);
> +
>         /* private: internal use only. */
>         enum kunit_status status;
>         char *module_name;
> @@ -218,6 +227,27 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
>                   .generate_params = gen_params,                                \
>                   .attr = attributes, .module_name = KBUILD_MODNAME}
>
> +/**
> + * KUNIT_CASE_PARAM_WITH_INIT() - Define a parameterized KUnit test case with custom
> + * init and exit functions.
> + * @test_name: The function implementing the test case.
> + * @gen_params: The function to generate parameters for the test case.
> + * @init: The init function to run before parameterized tests.
> + * @exit: The exit function to run after parameterized tests.

If we do change the description above of param_init/param_exit, it
might be nice to change it here too.

> + *
> + * Provides the option to register init and exit functions that take in the
> + * parent of the parameterized tests and run once before and once after the
> + * parameterized test series. The init function can be used to add any resources
> + * to share between the parameterized tests or to pass parameter arrays. The
> + * exit function can be used to clean up any resources that are not managed by
> + * the test.
> + */
> +#define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
> +               { .run_case = test_name, .name = #test_name,                    \
> +                 .generate_params = gen_params,                                \
> +                 .param_init = init, .param_exit = exit,                       \
> +                 .module_name = KBUILD_MODNAME}
> +
>  /**
>   * struct kunit_suite - describes a related collection of &struct kunit_case
>   *
> @@ -269,7 +299,8 @@ struct kunit_suite_set {
>   * @priv: for user to store arbitrary data. Commonly used to pass data
>   *       created in the init function (see &struct kunit_suite).
>   * @parent: for user to store data that they want to shared across
> - *         parameterized tests.
> + *         parameterized tests. Typically, the data is provided in
> + *         the param_init function (see &struct kunit_case).
>   *
>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index 4d6a39eb2c80..d80b5990d85d 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -641,6 +641,19 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
>         total->total += add.total;
>  }
>
> +static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)

It would be nice to include "param" in this function name. Currently
it sounds more like you are initializing the @parent field of struct
kunit *test.

> +{
> +       if (test_case->param_init) {
> +               int err = test_case->param_init(test);
> +
> +               if (err) {
> +                       kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> +                               "# failed to initialize parent parameter test.");
> +                       test_case->status = KUNIT_FAILURE;
> +               }
> +       }
> +}
> +
>  int kunit_run_tests(struct kunit_suite *suite)
>  {
>         char param_desc[KUNIT_PARAM_DESC_SIZE];
> @@ -668,6 +681,8 @@ int kunit_run_tests(struct kunit_suite *suite)
>                 struct kunit_result_stats param_stats = { 0 };
>
>                 kunit_init_test(&test, test_case->name, test_case->log);
> +               __kunit_init_parent_test(test_case, &test);
> +

Is it possible to move this so this function is only called when it is
a parameterized test? I see the check for KUNIT_FAILURE is useful but
I think I would still prefer this within the section for parameterized
tests.

>                 if (test_case->status == KUNIT_SKIPPED) {
>                         /* Test marked as skip */
>                         test.status = KUNIT_SKIPPED;
> @@ -677,7 +692,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_run_case_catch_errors(suite, test_case, &test);
>                         kunit_update_stats(&param_stats, test.status);
> -               } else {
> +               } else if (test_case->status != KUNIT_FAILURE) {
>                         /* Get initial param. */
>                         param_desc[0] = '\0';
>                         /* TODO: Make generate_params try-catch */
> @@ -727,6 +742,12 @@ int kunit_run_tests(struct kunit_suite *suite)
>
>                 kunit_update_stats(&suite_stats, test_case->status);
>                 kunit_accumulate_stats(&total_stats, param_stats);
> +               /*
> +                * TODO: Put into a try catch. Since we don't need suite->exit
> +                * for it we can't reuse kunit_try_run_cleanup for this yet.
> +                */
> +               if (test_case->param_exit)
> +                       test_case->param_exit(&test);

Also here I am not sure why this is done outside of the check for if
the test is parameterized? Either way this should definitely be done
before the test stats and ok/not ok line are printed because if there
is any log output during the param_exit function it is necessary to
print that before the status line to identify that that log
corresponds with that test.

Also just curious why you chose to implement a function to perform the
param_init but not the param_exit?



>                 /* TODO: Put this kunit_cleanup into a try-catch. */
>                 kunit_cleanup(&test);
>         }
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing
  2025-07-29 19:36 ` [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing Marie Zhussupova
  2025-07-30 14:32   ` kernel test robot
  2025-08-02  9:44   ` David Gow
@ 2025-08-05 15:18   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:18 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> To enable more complex parameterized test scenarios,
> the `generate_params` function sometimes needs additional
> context beyond just the previously generated parameter.
> This patch modifies the `generate_params` function signature
> to include an extra `struct kunit *test` argument, giving
> users access to the parent kunit test's context when
> generating subsequent parameters.
>
> The `struct kunit *test` argument was added as the first parameter
> to the function signature as it aligns with the convention
> of other KUnit functions that accept `struct kunit *test` first.
> This also mirrors the "this" or "self" reference found
> in object-oriented programming languages.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>

Hello!

Extremely happy about this change. This will provide us much more
flexibility when defining test parameters. Thank you for this
implementation!

Reviewed-by: Rae Moar <rmoar@google.com>


-Rae

> ---
>  include/kunit/test.h | 9 ++++++---
>  lib/kunit/test.c     | 5 +++--
>  2 files changed, 9 insertions(+), 5 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index d8dac7efd745..4ba65dc35710 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -128,7 +128,8 @@ struct kunit_attributes {
>  struct kunit_case {
>         void (*run_case)(struct kunit *test);
>         const char *name;
> -       const void* (*generate_params)(const void *prev, char *desc);
> +       const void* (*generate_params)(struct kunit *test,
> +                                      const void *prev, char *desc);
>         struct kunit_attributes attr;
>
>         /*
> @@ -1701,7 +1702,8 @@ do {                                                                             \
>   * Define function @name_gen_params which uses @array to generate parameters.
>   */
>  #define KUNIT_ARRAY_PARAM(name, array, get_desc)                                               \
> -       static const void *name##_gen_params(const void *prev, char *desc)                      \
> +       static const void *name##_gen_params(struct kunit *test,                                \
> +                                            const void *prev, char *desc)                      \
>         {                                                                                       \
>                 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
>                 if (__next - (array) < ARRAY_SIZE((array))) {                                   \
> @@ -1722,7 +1724,8 @@ do {                                                                             \
>   * Define function @name_gen_params which uses @array to generate parameters.
>   */
>  #define KUNIT_ARRAY_PARAM_DESC(name, array, desc_member)                                       \
> -       static const void *name##_gen_params(const void *prev, char *desc)                      \
> +       static const void *name##_gen_params(struct kunit *test,                                \
> +                                            const void *prev, char *desc)                      \
>         {                                                                                       \
>                 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
>                 if (__next - (array) < ARRAY_SIZE((array))) {                                   \
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index d80b5990d85d..f50ef82179c4 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -696,7 +696,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         /* Get initial param. */
>                         param_desc[0] = '\0';
>                         /* TODO: Make generate_params try-catch */
> -                       curr_param = test_case->generate_params(NULL, param_desc);
> +                       curr_param = test_case->generate_params(&test, NULL, param_desc);
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
>                                   "KTAP version 1\n");
> @@ -727,7 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
>
>                                 /* Get next param. */
>                                 param_desc[0] = '\0';
> -                               curr_param = test_case->generate_params(curr_param, param_desc);
> +                               curr_param = test_case->generate_params(&test, curr_param,
> +                                                                       param_desc);
>                         }
>                 }
>
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
                     ` (2 preceding siblings ...)
  2025-08-02  9:44   ` David Gow
@ 2025-08-05 15:18   ` Rae Moar
  2025-08-08  1:22     ` Marie Zhussupova
  3 siblings, 1 reply; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:18 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> KUnit parameterized tests currently support two
> primary methods for getting parameters:
> 1.  Defining custom logic within a `generate_params`
>     function.
> 2.  Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
>     macros with pre-defined static arrays.
>
> These methods present limitations when dealing with
> dynamically generated parameter arrays, or in scenarios
> where populating parameters sequentially via
> `generate_params` is inefficient or overly complex.
>
> This patch addresses these limitations by adding a new
> `params_data` field to `struct kunit`, of the type
> `kunit_params`. The struct `kunit_params` is designed to
> store the parameter array itself, along with essential metadata
> including the parameter count, parameter size, and a
> `get_description` function for providing custom descriptions
> for individual parameters.
>
> The `params_data` field can be populated by calling the new
> `kunit_register_params_array` macro from within a
> `param_init` function. By attaching the parameter array
> directly to the parent kunit test instance, these parameters
> can be iterated over in kunit_run_tests() behind the scenes.
>
> This modification provides greater flexibility to the
> KUnit framework, allowing testers to easily register and
> utilize both dynamic and static parameter arrays.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>

Hello!

Very excited by the prospect of setting up an array dynamically
instead of statically for parameterized tests. In general, I am happy
to see this framework is becoming more flexible and therefore more
tailored to our test author's needs.

I already commented on the modpost error but I have a few more
comments and ideas below. Let me know what you think.

Thanks!
-Rae

> ---
>  include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
>  lib/kunit/test.c     | 26 ++++++++++++++++++++-
>  2 files changed, 75 insertions(+), 5 deletions(-)
>
> diff --git a/include/kunit/test.h b/include/kunit/test.h
> index 4ba65dc35710..9143f0e22323 100644
> --- a/include/kunit/test.h
> +++ b/include/kunit/test.h
> @@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
>   */
>  #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
>                 { .run_case = test_name, .name = #test_name,                    \
> -                 .generate_params = gen_params,                                \
> +                 .generate_params = (gen_params)                               \
> +                  ?: kunit_get_next_param_and_desc,                            \
>                   .param_init = init, .param_exit = exit,                       \
>                   .module_name = KBUILD_MODNAME}
>
> @@ -294,6 +295,21 @@ struct kunit_suite_set {
>         struct kunit_suite * const *end;
>  };
>
> +/* Stores the pointer to the parameter array and its metadata. */
> +struct kunit_params {
> +       /*
> +        * Reference to the parameter array for the parameterized tests. This
> +        * is NULL if a parameter array wasn't directly passed to the
> +        * parent kunit struct via the kunit_register_params_array macro.
> +        */
> +       const void *params;
> +       /* Reference to a function that gets the description of a parameter. */
> +       void (*get_description)(const void *param, char *desc);
> +
> +       int num_params;

Since in some cases we know the number of params within a series/suite
of the parameterized tests, is it possible for us to print a test plan
line in KTAP when this number is known? This would be helpful for
reading test results but also the parser could verify the number of
subtests is the number expected.

> +       size_t elem_size;
> +};
> +
>  /**
>   * struct kunit - represents a running instance of a test.
>   *
> @@ -302,12 +318,14 @@ struct kunit_suite_set {
>   * @parent: for user to store data that they want to shared across
>   *         parameterized tests. Typically, the data is provided in
>   *         the param_init function (see &struct kunit_case).
> + * @params_data: for users to directly store the parameter array.
>   *
>   * Used to store information about the current context under which the test
>   * is running. Most of this data is private and should only be accessed
> - * indirectly via public functions; the two exceptions are @priv and @parent
> - * which can be used by the test writer to store arbitrary data or data that is
> - * available to all parameter test executions, respectively.
> + * indirectly via public functions. There are three exceptions to this: @priv,
> + * @parent, and @params_data. These members can be used by the test writer to
> + * store arbitrary data, data available to all parameter test executions, and
> + * the parameter array, respectively.
>   */
>  struct kunit {
>         void *priv;
> @@ -316,6 +334,8 @@ struct kunit {
>          * during parameterized testing.
>          */
>         struct kunit *parent;
> +       /* Stores the params array and all data related to it. */
> +       struct kunit_params params_data;

I might slightly prefer the term params_array rather than params_data.
Up to what you prefer.

>
>         /* private: internal use only. */
>         const char *name; /* Read only after initialization! */
> @@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
>  struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
>                 struct kunit_suite_set suite_set);
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);
> +
>  #if IS_BUILTIN(CONFIG_KUNIT)
>  int kunit_run_all_tests(void);
>  #else
> @@ -1735,6 +1757,30 @@ do {                                                                            \
>                 return NULL;                                                                    \
>         }
>
> +/**
> + * kunit_register_params_array() - Register parameters for a KUnit test.
> + * @test: The KUnit test structure to which parameters will be added.
> + * @params_arr: An array of test parameters.
> + * @param_cnt: Number of parameters.
> + * @get_desc: A pointer to a function that generates a string description for
> + * a given parameter element.
> + *
> + * This macro initializes the @test's parameter array data, storing information
> + * including the parameter array, its count, the element size, and the parameter
> + * description function within `test->params_data`. KUnit's built-in
> + * `kunit_get_next_param_and_desc` function will automatically read this
> + * data when a custom `generate_params` function isn't provided.
> + */
> +#define kunit_register_params_array(test, params_arr, param_cnt, get_desc)                     \

I also might slightly prefer params_array and param_count here instead
of params_arr and param_cnt. Again this is definitely a nitpick so up
to you.

> +       do {                                                                                    \
> +               struct kunit *_test = (test);                                           \
> +               const typeof((params_arr)[0]) * _params_ptr = &(params_arr)[0];                 \
> +               _test->params_data.params = _params_ptr;                                        \
> +               _test->params_data.num_params = (param_cnt);                                    \
> +               _test->params_data.elem_size = sizeof(*_params_ptr);                            \
> +               _test->params_data.get_description = (get_desc);                                \
> +       } while (0)
> +
>  // TODO(dlatypov@google.com): consider eventually migrating users to explicitly
>  // include resource.h themselves if they need it.
>  #include <kunit/resource.h>
> diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> index f50ef82179c4..2f4b7087db3f 100644
> --- a/lib/kunit/test.c
> +++ b/lib/kunit/test.c
> @@ -337,6 +337,13 @@ void __kunit_do_failed_assertion(struct kunit *test,
>  }
>  EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
>
> +static void __kunit_init_params(struct kunit *test)
> +{
> +       test->params_data.params = NULL;
> +       test->params_data.num_params = 0;
> +       test->params_data.elem_size = 0;
> +}
> +
>  void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
>  {
>         spin_lock_init(&test->lock);
> @@ -347,6 +354,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
>                 string_stream_clear(log);
>         test->status = KUNIT_SUCCESS;
>         test->status_comment[0] = '\0';
> +       __kunit_init_params(test);
>  }
>  EXPORT_SYMBOL_GPL(kunit_init_test);
>
> @@ -641,6 +649,22 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
>         total->total += add.total;
>  }
>
> +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc)
> +{
> +       struct kunit_params *params_arr = &test->params_data;
> +       const void *param;
> +
> +       if (test->param_index < params_arr->num_params) {
> +               param = (char *)params_arr->params
> +                       + test->param_index * params_arr->elem_size;
> +
> +               if (params_arr->get_description)
> +                       params_arr->get_description(param, desc);
> +               return param;
> +       }
> +       return NULL;
> +}

I also agree with David that it should definitely be considered: 1 -
whether to utilize struct kunit_params for the case of using
KUNIT_ARRAY_PARAM and 2 - whether the user should actively input this
function instead of setting generate_params to NULL.

Another idea that just popped into my head is if we have access to
struct kunit* test now in all of the generate_params functions,
instead of setting a "desc" could we just set the test->name field?

> +
>  static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
>  {
>         if (test_case->param_init) {
> @@ -687,7 +711,7 @@ int kunit_run_tests(struct kunit_suite *suite)
>                         /* Test marked as skip */
>                         test.status = KUNIT_SKIPPED;
>                         kunit_update_stats(&param_stats, test.status);
> -               } else if (!test_case->generate_params) {
> +               } else if (!test_case->generate_params && !test.params_data.params) {

I agree with David that it is helpful to have one check for whether a
test is a parameterized test rather than two. My instinct is that if
test_case->generate_params is NULL it should be safe to assume the
test isn't a parameterized test.

However, as an alternative or even as a helpful addition, I like the
idea of a simple kunit_test_is_param function that can pass in the
test and it will return a bool whether the test is parameterized or
not.




>                         /* Non-parameterised test. */
>                         test_case->status = KUNIT_SKIPPED;
>                         kunit_run_case_catch_errors(suite, test_case, &test);
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup
  2025-07-29 19:36 ` [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup Marie Zhussupova
  2025-07-30 12:25   ` kernel test robot
  2025-08-02  9:44   ` David Gow
@ 2025-08-05 15:19   ` Rae Moar
  2 siblings, 0 replies; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:19 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> Add `example_params_test_with_init` to illustrate how to manage
> shared resources across parameterized KUnit tests. This example
> showcases the use of the new `param_init` function and its registration
> to a test using the `KUNIT_CASE_PARAM_WITH_INIT` macro.
>
> Additionally, the test demonstrates:
> - How to directly assign a static parameter array to a test via
>   `kunit_register_params_array`.
> - Leveraging the Resource API for test resource management.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>

Hello!

I am always happy to see a new example test. I have a few tiny
nitpicky comments below. I would be happy for this to go in as-is or
just include the next test in the series as David suggested.

Reviewed-by: Rae Moar <rmoar@google.com>

Thanks!
-Rae

> ---
>  lib/kunit/kunit-example-test.c | 112 +++++++++++++++++++++++++++++++++
>  1 file changed, 112 insertions(+)
>
> diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> index 3056d6bc705d..5bf559e243f6 100644
> --- a/lib/kunit/kunit-example-test.c
> +++ b/lib/kunit/kunit-example-test.c
> @@ -277,6 +277,116 @@ static void example_slow_test(struct kunit *test)
>         KUNIT_EXPECT_EQ(test, 1 + 1, 2);
>  }
>
> +/*
> + * This custom function allocates memory for the kunit_resource data field.
> + * The function is passed to kunit_alloc_resource() and executed once
> + * by the internal helper __kunit_add_resource().
> + */

I don't think it is necessary to include that this function is
executed by an internal function: __kunit_add_resource(). Especially
since we have other example tests for the resource API.

> +static int example_resource_init(struct kunit_resource *res, void *context)
> +{
> +       int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> +
> +       if (!info)
> +               return -ENOMEM;
> +       *info = *(int *)context;
> +       res->data = info;
> +       return 0;
> +}
> +
> +/*
> + * This function deallocates memory for the 'kunit_resource' data field.
> + * The function is passed to kunit_alloc_resource() and automatically
> + * executes within kunit_release_resource() when the resource's reference
> + * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> + * counting to ensure that resources are not freed prematurely.
> + */

Similarly, I think this is a bit too much information since we have
other tests for the resource API. I would maybe shorten this by
removing the references to kunit_release_resource() and
kunit_put_resource().

> +static void example_resource_free(struct kunit_resource *res)
> +{
> +       kfree(res->data);
> +}
> +
> +/*
> + * This match function is invoked by kunit_find_resource() to locate
> + * a test resource based on defined criteria. The current example
> + * uniquely identifies the resource by its free function; however,
> + * alternative custom criteria can be implemented. Refer to
> + * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> + */

Again I would consider shortening this description.



> +static bool example_resource_alloc_match(struct kunit *test,
> +                                        struct kunit_resource *res,
> +                                        void *match_data)
> +{
> +       return res->data && res->free == example_resource_free;
> +}
> +
> +/*
> + * This is an example of a function that provides a description for each of the
> + * parameters.
> + */
> +static void example_param_array_get_desc(const void *p, char *desc)
> +{
> +       const struct example_param *param = p;
> +
> +       snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> +                "example check if %d is less than or equal to 3", param->value);
> +}
> +
> +/*
> + * Initializes the parent kunit struct for parameterized KUnit tests.
> + * This function enables sharing resources across all parameterized
> + * tests by adding them to the `parent` kunit test struct. It also supports
> + * registering either static or dynamic arrays of test parameters.
> + */
> +static int example_param_init(struct kunit *test)
> +{
> +       int ctx = 3; /* Data to be stored. */
> +       int arr_size = ARRAY_SIZE(example_params_array);
> +
> +       /*
> +        * This allocates a struct kunit_resource, sets its data field to
> +        * ctx, and adds it to the kunit struct's resources list. Note that
> +        * this is test managed so we don't need to have a custom exit function
> +        * to free it.
> +        */
> +       void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> +                                         GFP_KERNEL, &ctx);
> +
> +       if (!data)
> +               return -ENOMEM;
> +       /* Pass the static param array information to the parent struct kunit. */
> +       kunit_register_params_array(test, example_params_array, arr_size,
> +                                   example_param_array_get_desc);
> +       return 0;
> +}
> +
> +/*
> + * This is an example of a parameterized test that uses shared resources
> + * available from the struct kunit parent field of the kunit struct.
> + */
> +static void example_params_test_with_init(struct kunit *test)
> +{
> +       int threshold;
> +       struct kunit_resource *res;
> +       const struct example_param *param = test->param_value;
> +
> +       /* By design, param pointer will not be NULL. */
> +       KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +       /* Here we access the parent pointer of the test to find the shared resource. */
> +       res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> +
> +       KUNIT_ASSERT_NOT_NULL(test, res);
> +
> +       /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> +       threshold = *((int *)res->data);
> +
> +       /* Assert that the parameter is less than or equal to a certain threshold. */
> +       KUNIT_ASSERT_LE(test, param->value, threshold);
> +
> +       /* This decreases the reference count after calling kunit_find_resource(). */
> +       kunit_put_resource(res);
> +}
> +
>  /*
>   * Here we make a list of all the test cases we want to add to the test suite
>   * below.
> @@ -296,6 +406,8 @@ static struct kunit_case example_test_cases[] = {
>         KUNIT_CASE(example_static_stub_using_fn_ptr_test),
>         KUNIT_CASE(example_priv_test),
>         KUNIT_CASE_PARAM(example_params_test, example_gen_params),
> +       KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> +                                  example_param_init, NULL),
>         KUNIT_CASE_SLOW(example_slow_test),
>         {}
>  };
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 8/9] kunit: Add example parameterized test with direct dynamic parameter array setup
  2025-07-29 19:36 ` [PATCH 8/9] kunit: Add example parameterized test with direct dynamic " Marie Zhussupova
  2025-08-02  9:44   ` David Gow
@ 2025-08-05 15:19   ` Rae Moar
  1 sibling, 0 replies; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:19 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> Introduce `example_params_test_with_init_dynamic_arr`. This new
> KUnit test demonstrates directly assigning a dynamic parameter
> array using the `kunit_register_params_array` macro. It highlights the
> use of `param_init` and `param_exit` for proper initialization and
> cleanup, and their registration to the test with
> `KUNIT_CASE_PARAM_WITH_INIT`.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>

Hello!

This test is interesting and complex. I am very happy to see this test
accepted. I think it is a good demonstration of the new parameterized
test features.

Reviewed-by: Rae Moar <rmoar@google.com>

Thanks!

-Rae

> ---
>  lib/kunit/kunit-example-test.c | 95 ++++++++++++++++++++++++++++++++++
>  1 file changed, 95 insertions(+)
>
> diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> index 5bf559e243f6..3ab121d81bf6 100644
> --- a/lib/kunit/kunit-example-test.c
> +++ b/lib/kunit/kunit-example-test.c
> @@ -387,6 +387,98 @@ static void example_params_test_with_init(struct kunit *test)
>         kunit_put_resource(res);
>  }
>
> +/*
> + * Helper function to create a parameter array of Fibonacci numbers. This example
> + * highlights a parameter generation scenario that is:
> + * 1. Not feasible to fully pre-generate at compile time.
> + * 2. Challenging to implement with a standard 'generate_params' function,
> + * as it typically only provides the immediately 'prev' parameter, while
> + * Fibonacci requires access to two preceding values for calculation.
> + */
> +static void *make_fibonacci_params(int seq_size)
> +{
> +       int *seq;
> +
> +       if (seq_size <= 0)
> +               return NULL;
> +
> +       seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
> +
> +       if (!seq)
> +               return NULL;
> +
> +       if (seq_size >= 1)
> +               seq[0] = 0;
> +       if (seq_size >= 2)
> +               seq[1] = 1;
> +       for (int i = 2; i < seq_size; i++)
> +               seq[i] = seq[i - 1] + seq[i - 2];
> +       return seq;
> +}
> +
> +/*
> + * This is an example of a function that provides a description for each of the
> + * parameters.
> + */
> +static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
> +{
> +       const int *fib_num = p;
> +
> +       snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
> +}
> +
> +/*
> + * Example of a parameterized test init function that registers a dynamic array.
> + */
> +static int example_param_init_dynamic_arr(struct kunit *test)
> +{
> +       int seq_size = 6;
> +       int *fibonacci_params = make_fibonacci_params(seq_size);
> +
> +       if (!fibonacci_params)
> +               return -ENOMEM;
> +
> +       /*
> +        * Passes the dynamic parameter array information to the parent struct kunit.
> +        * The array and its metadata will be stored in test->parent->params_data.
> +        * The array itself will be located in params_data.params.
> +        */
> +       kunit_register_params_array(test, fibonacci_params, seq_size,
> +                                   example_param_dynamic_arr_get_desc);
> +       return 0;
> +}
> +
> +/**
> + * Function to clean up the parameterized test's parent kunit struct if
> + * there were custom allocations.
> + */
> +static void example_param_exit_dynamic_arr(struct kunit *test)
> +{
> +       /*
> +        * We allocated this array, so we need to free it.
> +        * Since the parent parameter instance is passed here,
> +        * we can directly access the array via `test->params_data.params`
> +        * instead of `test->parent->params_data.params`.
> +        */
> +       kfree(test->params_data.params);
> +}
> +
> +/*
> + * Example of test that uses the registered dynamic array to perform assertions
> + * and expectations.
> + */
> +static void example_params_test_with_init_dynamic_arr(struct kunit *test)
> +{
> +       const int *param = test->param_value;
> +       int param_val;
> +
> +       /* By design, param pointer will not be NULL. */
> +       KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +       param_val = *param;
> +       KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
> +}
> +
>  /*
>   * Here we make a list of all the test cases we want to add to the test suite
>   * below.
> @@ -408,6 +500,9 @@ static struct kunit_case example_test_cases[] = {
>         KUNIT_CASE_PARAM(example_params_test, example_gen_params),
>         KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
>                                    example_param_init, NULL),
> +       KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
> +                                  example_param_init_dynamic_arr,
> +                                  example_param_exit_dynamic_arr),
>         KUNIT_CASE_SLOW(example_slow_test),
>         {}
>  };
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 9/9] Documentation: kunit: Document new parameterized test features
  2025-07-29 19:36 ` [PATCH 9/9] Documentation: kunit: Document new parameterized test features Marie Zhussupova
  2025-08-02  9:45   ` David Gow
@ 2025-08-05 15:19   ` Rae Moar
  2025-08-08 13:05     ` Marie Zhussupova
  1 sibling, 1 reply; 41+ messages in thread
From: Rae Moar @ 2025-08-05 15:19 UTC (permalink / raw)
  To: Marie Zhussupova
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
>
> -Update the KUnit documentation to explain the concept
> of a parent parameterized test.
> -Add examples demonstrating different ways of passing
> parameters to parameterized tests and how to manage
> shared resources between them.
>
> Signed-off-by: Marie Zhussupova <marievic@google.com>

Hello!

This is amazing! I have a few comments below but I appreciate the
effort to document this new feature. It is always incredibly helpful
to have documentation to go along with the code.

Reviewed-by: Rae Moar <rmoar@google.com>

Thanks!
-Rae

> ---
>  Documentation/dev-tools/kunit/usage.rst | 455 +++++++++++++++++++++++-
>  1 file changed, 449 insertions(+), 6 deletions(-)
>
> diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
> index 066ecda1dd98..be1d656053cf 100644
> --- a/Documentation/dev-tools/kunit/usage.rst
> +++ b/Documentation/dev-tools/kunit/usage.rst
> @@ -542,11 +542,21 @@ There is more boilerplate code involved, but it can:
>  Parameterized Testing
>  ~~~~~~~~~~~~~~~~~~~~~
>
> -The table-driven testing pattern is common enough that KUnit has special
> -support for it.
> -
> -By reusing the same ``cases`` array from above, we can write the test as a
> -"parameterized test" with the following.
> +To efficiently and elegantly validate a test case against a variety of inputs,
> +KUnit also provides a parameterized testing framework. This feature formalizes
> +and extends the concept of table-driven tests discussed previously, offering
> +a more integrated and flexible way to handle multiple test scenarios with
> +minimal code duplication.
> +
> +Passing Parameters to the Test Cases
> +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> +There are three main ways to provide the parameters to a test case:
> +
> +Array Parameter Macros (``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC``):
> +   KUnit provides special support for the common table-driven testing pattern.
> +   By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
> +   ``cases`` array from the previous section, we can create a parameterized test
> +   as shown below:

Is it possible to bold the titles of the ways to pass in parameters:
Array Parameter Macros, etc.? I feel like they should stand out more
from the rest of the text. Also I think I would prefer if there was an
empty line between the title and the rest of the indented text, to
again further separate these titles from the rest of the text.

>
>  .. code-block:: c
>
> @@ -555,7 +565,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
>                 const char *str;
>                 const char *sha1;
>         };
> -       const struct sha1_test_case cases[] = {
> +       static const struct sha1_test_case cases[] = {
>                 {
>                         .str = "hello world",
>                         .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
> @@ -590,6 +600,439 @@ By reusing the same ``cases`` array from above, we can write the test as a
>                 {}
>         };
>
> +Custom Parameter Generator (``generate_params``):
> +   You can pass your own ``generate_params`` function to the ``KUNIT_CASE_PARAM``
> +   or ``KUNIT_CASE_PARAM_WITH_INIT`` macros. This function is responsible for
> +   generating parameters one by one. It receives the previously generated parameter
> +   as the ``prev`` argument (which is ``NULL`` on the first call) and can also
> +   access any context available from the parent ``struct kunit`` passed as the
> +   ``test`` argument. KUnit calls this function repeatedly until it returns
> +   ``NULL``. Below is an example of how it works:
> +
> +.. code-block:: c
> +
> +       #define MAX_TEST_BUFFER_SIZE 8
> +
> +       // Example generator function. It produces a sequence of buffer sizes that
> +       // are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
> +       static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
> +       {
> +               long prev_buffer_size = (long)prev;
> +               long next_buffer_size = 1; // Start with an initial size of 1.
> +
> +               // Stop generating parameters if the limit is reached or exceeded.
> +               if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
> +                       return NULL;
> +
> +               // For subsequent calls, calculate the next size by doubling the previous one.
> +               if (prev)
> +                       next_buffer_size = prev_buffer_size << 1;
> +
> +               return (void *)next_buffer_size;
> +       }
> +
> +       // Simple test to validate that kunit_kzalloc provides zeroed memory.
> +       static void buffer_zero_test(struct kunit *test)
> +       {
> +               long buffer_size = (long)test->param_value;
> +               // Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
> +               // memory "parameter managed," meaning it's automatically cleaned up at
> +               // the end of each parameter execution.
> +               int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
> +
> +               // Ensure the allocation was successful.
> +               KUNIT_ASSERT_NOT_NULL(test, buf);
> +
> +               // Loop through the buffer and confirm every element is zero.
> +               for (int i = 0; i < buffer_size; i++)
> +                       KUNIT_EXPECT_EQ(test, buf[i], 0);
> +       }
> +
> +       static struct kunit_case buffer_test_cases[] = {
> +               KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
> +               {}
> +       };
> +
> +Direct Registration in Parameter Init Function (using ``kunit_register_params_array``):
> +   For more complex scenarios, you can directly register a parameter array with
> +   a test case instead of using a ``generate_params`` function. This is done by
> +   passing the array to the ``kunit_register_params_array`` macro within an
> +   initialization function for the parameterized test series
> +   (i.e., a function named ``param_init``). To better understand this mechanism
> +   please refer to the "Adding Shared Resources" section below.
> +
> +   This method supports both dynamically built and static arrays.
> +
> +   As the following code shows, the ``example_param_init_dynamic_arr`` function
> +   utilizes ``make_fibonacci_params`` to create a dynamic array, which is then
> +   registered using ``kunit_register_params_array``. The corresponding exit
> +   function, ``example_param_exit``, is responsible for freeing this dynamically
> +   allocated params array after the parameterized test series ends.
> +
> +.. code-block:: c

As David mentioned, this example code is a bit long. I would also
prefer if this example had just the highlights and then a link to the
source code.

> +
> +       /*
> +        * Helper function to create a parameter array of Fibonacci numbers. This example
> +        * highlights a parameter generation scenario that is:
> +        * 1. Not feasible to fully pre-generate at compile time.
> +        * 2. Challenging to implement with a standard 'generate_params' function,
> +        * as it typically only provides the immediately 'prev' parameter, while
> +        * Fibonacci requires access to two preceding values for calculation.
> +        */
> +       static void *make_fibonacci_params(int seq_size)
> +       {
> +               int *seq;
> +
> +               if (seq_size <= 0)
> +                       return NULL;
> +
> +               seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
> +
> +               if (!seq)
> +                       return NULL;
> +
> +               if (seq_size >= 1)
> +                       seq[0] = 0;
> +               if (seq_size >= 2)
> +                       seq[1] = 1;
> +               for (int i = 2; i < seq_size; i++)
> +                       seq[i] = seq[i - 1] + seq[i - 2];
> +               return seq;
> +       }
> +
> +       // This is an example of a function that provides a description for each of the
> +       // parameters.
> +       static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
> +       {
> +               const int *fib_num = p;
> +
> +               snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
> +       }
> +
> +       // Example of a parameterized test init function that registers a dynamic array.
> +       static int example_param_init_dynamic_arr(struct kunit *test)
> +       {
> +               int seq_size = 6;
> +               int *fibonacci_params = make_fibonacci_params(seq_size);
> +
> +               if (!fibonacci_params)
> +                       return -ENOMEM;
> +
> +               /*
> +                * Passes the dynamic parameter array information to the parent struct kunit.
> +                * The array and its metadata will be stored in test->parent->params_data.
> +                * The array itself will be located in params_data.params.
> +                */
> +               kunit_register_params_array(test, fibonacci_params, seq_size,
> +                                           example_param_dynamic_arr_get_desc);
> +               return 0;
> +       }
> +
> +       // Function to clean up the parameterized test's parent kunit struct if
> +       // there were custom allocations.
> +       static void example_param_exit_dynamic_arr(struct kunit *test)
> +       {
> +               /*
> +                * We allocated this array, so we need to free it.
> +                * Since the parent parameter instance is passed here,
> +                * we can directly access the array via `test->params_data.params`
> +                * instead of `test->parent->params_data.params`.
> +                */
> +               kfree(test->params_data.params);
> +       }
> +
> +       /*
> +        * Example of test that uses the registered dynamic array to perform assertions
> +        * and expectations.
> +        */
> +       static void example_params_test_with_init_dynamic_arr(struct kunit *test)
> +       {
> +               const int *param = test->param_value;
> +               int param_val;
> +
> +               /* By design, param pointer will not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +               param_val = *param;
> +               KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
> +       }
> +
> +       static struct kunit_case example_tests[] = {
> +               // The NULL here stands in for the generate_params function
> +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
> +                                          example_param_init_dynamic_arr,
> +                                          example_param_exit_dynamic_arr),
> +               {}
> +       };
> +
> +
> +Adding Shared Resources
> +^^^^^^^^^^^^^^^^^^^^^^^
> +All parameterized test executions in this framework have a parent test of type
> +``struct kunit``. This parent is not used to execute any test logic itself;
> +instead, it serves as a container for shared context that can be accessed by
> +all its individual test executions (or parameters). Therefore, each individual
> +test execution holds a pointer to this parent, accessible via a field named
> +``parent``.
> +
> +It's possible to add resources to share between the individual test executions
> +within a parameterized test series by using the ``KUNIT_CASE_PARAM_WITH_INIT``
> +macro, to which you pass custom ``param_init`` and ``param_exit`` functions.
> +These functions run once before and once after the entire parameterized test
> +series, respectively. The ``param_init`` function can be used for adding any
> +resources to the resources field of a parent test and also provide an additional
> +way of setting the parameter array. The ``param_exit`` function can be used
> +release any resources that were not test managed i.e. not automatically cleaned
> +up after the test ends.
> +
> +.. note::
> +   If both a ``generate_params`` function is passed to ``KUNIT_CASE_PARAM_WITH_INIT``
> +   and an array is registered via ``kunit_register_params_array`` in
> +   ``param_init``, the ``generate_params`` function will be used to get
> +   the parameters.
> +
> +Both ``param_init`` and ``param_exit`` are passed the parent instance of a test
> +(parent ``struct kunit``) behind the scenes. However, the test case function
> +receives the individual instance of a test for each parameter. Therefore, to
> +manage and access shared resources from within a test case function, you must use
> +``test->parent``.
> +
> +.. note::
> +   The ``suite->init()`` function, which runs before each parameter execution,
> +   receives the individual instance of a test for each parameter. Therefore,
> +   resources set up in ``suite->init()`` are reset for each individual
> +   parameterized test execution and are only visible within that specific test.
> +
> +For instance, finding a shared resource allocated by the Resource API requires
> +passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
> +all other APIs that might be used in the test case function, including
> +``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
> +Documentation/dev-tools/kunit/api/test.rst and the
> +Documentation/dev-tools/kunit/api/resource.rst).
> +
> +The code below shows how you can add the shared resources. Note that this code
> +utilizes the Resource API, which you can read more about here:
> +Documentation/dev-tools/kunit/api/resource.rst.

It would be nice if these references to the Documentation files were
actual links to the webpages. This would look something like -
":ref:`kunit-resource`" and then also labeling that section: "..
_kunit-resource:".

> +
> +.. code-block:: c
> +
> +       /* An example parameter array. */
> +       static const struct example_param {
> +               int value;
> +       } example_params_array[] = {
> +               { .value = 3, },
> +               { .value = 2, },
> +               { .value = 1, },
> +               { .value = 0, },
> +       };
> +
> +       /*
> +        * This custom function allocates memory for the kunit_resource data field.
> +        * The function is passed to kunit_alloc_resource() and executed once
> +        * by the internal helper __kunit_add_resource().
> +        */
> +       static int example_resource_init(struct kunit_resource *res, void *context)
> +       {
> +               int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> +
> +               if (!info)
> +                       return -ENOMEM;
> +               *info = *(int *)context;
> +               res->data = info;
> +               return 0;
> +       }
> +
> +       /*
> +        * This function deallocates memory for the 'kunit_resource' data field.
> +        * The function is passed to kunit_alloc_resource() and automatically
> +        * executes within kunit_release_resource() when the resource's reference
> +        * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> +        * counting to ensure that resources are not freed prematurely.
> +        */
> +       static void example_resource_free(struct kunit_resource *res)
> +       {
> +               kfree(res->data);
> +       }
> +
> +       /*
> +        * This match function is invoked by kunit_find_resource() to locate
> +        * a test resource based on defined criteria. The current example
> +        * uniquely identifies the resource by its free function; however,
> +        * alternative custom criteria can be implemented. Refer to
> +        * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> +        */
> +       static bool example_resource_alloc_match(struct kunit *test,
> +                                                struct kunit_resource *res,
> +                                                void *match_data)
> +       {
> +               return res->data && res->free == example_resource_free;
> +       }
> +
> +       /*
> +        * This is an example of a function that provides a description for each of the
> +        * parameters.
> +       */
> +       static void example_param_array_get_desc(const void *p, char *desc)
> +       {
> +               const struct example_param *param = p;
> +
> +               snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> +                       "example check if %d is less than or equal to 3", param->value);
> +       }
> +
> +       /*
> +        * Initializes the parent kunit struct for parameterized KUnit tests.
> +        * This function enables sharing resources across all parameterized
> +        * tests by adding them to the `parent` kunit test struct. It also supports
> +        * registering either static or dynamic arrays of test parameters.
> +        */
> +       static int example_param_init(struct kunit *test)
> +       {
> +               int ctx = 3; /* Data to be stored. */
> +               int arr_size = ARRAY_SIZE(example_params_array);
> +
> +               /*
> +                * This allocates a struct kunit_resource, sets its data field to
> +                * ctx, and adds it to the kunit struct's resources list. Note that
> +                * this is test managed so we don't need to have a custom exit function
> +                * to free it.
> +                */
> +               void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> +                                                 GFP_KERNEL, &ctx);
> +
> +               if (!data)
> +                       return -ENOMEM;
> +               /* Pass the static param array information to the parent struct kunit. */
> +               kunit_register_params_array(test, example_params_array, arr_size,
> +                                           example_param_array_get_desc);
> +               return 0;
> +       }
> +
> +       /*
> +       * This is an example of a parameterized test that uses shared resources
> +       * available from the struct kunit parent field of the kunit struct.
> +       */
> +       static void example_params_test_with_init(struct kunit *test)
> +       {
> +               int threshold;
> +               struct kunit_resource *res;
> +               const struct example_param *param = test->param_value;
> +
> +               /* By design, param pointer will not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +               /* Here we need to access the parent pointer of the test to find the shared resource. */
> +               res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> +
> +               KUNIT_ASSERT_NOT_NULL(test, res);
> +
> +               /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> +               threshold = *((int *)res->data);
> +
> +               /* Assert that the parameter is less than or equal to a certain threshold. */
> +               KUNIT_ASSERT_LE(test, param->value, threshold);
> +
> +               /* This decreases the reference count after calling kunit_find_resource(). */
> +               kunit_put_resource(res);
> +       }
> +
> +
> +       static struct kunit_case example_tests[] = {
> +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> +                                          example_param_init, NULL),
> +               {}
> +       };
> +
> +As an alternative to using the KUnit Resource API for shared resources, you can
> +place them in ``test->parent->priv``. It can store data that needs to persist
> +and be accessible across all executions within a parameterized test series.
> +
> +As stated previously ``param_init`` and ``param_exit`` receive the parent
> +``struct kunit`` instance. So, you can directly use ``test->priv`` within them
> +to manage shared resources. However, from within the test case function, you must
> +navigate up to the parent i.e. use ``test->parent->priv`` to access those same
> +resources.
> +
> +The resources placed in ``test->parent-priv`` will also need to be allocated in

Nit: I think this is a typo for test->parent->priv.



> +memory to persist across the parameterized tests executions. If memory is
> +allocated using the memory allocation APIs provided by KUnit (described more in
> +the section below), you will not need to worry about deallocating them as they
> +will be managed by the parent parameterized test that gets automatically cleaned
> +up upon the end of the parameterized test series.
> +
> +The code below demonstrates example usage of the ``priv`` field for shared
> +resources:
> +
> +.. code-block:: c
> +
> +       /* An example parameter array. */
> +       static const struct example_param {
> +               int value;
> +       } example_params_array[] = {
> +               { .value = 3, },
> +               { .value = 2, },
> +               { .value = 1, },
> +               { .value = 0, },
> +       };
> +
> +       /*
> +        * Initializes the parent kunit struct for parameterized KUnit tests.
> +        * This function enables sharing resources across all parameterized
> +        * tests.
> +        */
> +       static int example_param_init_priv(struct kunit *test)
> +       {
> +               int ctx = 3; /* Data to be stored. */
> +               int arr_size = ARRAY_SIZE(example_params_array);
> +
> +               /*
> +                * Allocate memory using kunit_kzalloc(). Since the `param_init`
> +                * function receives the parent instance of test, this memory
> +                * allocation will be scoped to the lifetime of the whole
> +                * parameterized test series.
> +                */
> +               test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
> +
> +               /* Assign the context value to test->priv.*/
> +               *((int *)test->priv) = ctx;
> +
> +               /* Pass the static param array information to the parent struct kunit. */
> +               kunit_register_params_array(test, example_params_array, arr_size, NULL);
> +               return 0;
> +       }
> +
> +       /*
> +       * This is an example of a parameterized test that uses shared resources
> +       * available from the struct kunit parent field of the kunit struct.
> +       */
> +       static void example_params_test_with_init_priv(struct kunit *test)
> +       {
> +               int threshold;
> +               const struct example_param *param = test->param_value;
> +
> +               /* By design, param pointer will not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, param);
> +
> +               /* By design, test->parent will also not be NULL. */
> +               KUNIT_ASSERT_NOT_NULL(test, test->parent);
> +
> +               /* Assert that test->parent->priv has data. */
> +               KUNIT_ASSERT_NOT_NULL(test, test->parent->priv);
> +
> +               /* Here we need to use test->parent->priv to access the shared resource. */
> +               threshold = *(int *)test->parent->priv;
> +
> +               /* Assert that the parameter is less than or equal to a certain threshold. */
> +               KUNIT_ASSERT_LE(test, param->value, threshold);
> +       }
> +
> +
> +       static struct kunit_case example_tests[] = {
> +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv, NULL,
> +                                          example_param_init_priv, NULL),
> +               {}
> +       };
> +
>  Allocating Memory
>  -----------------
>
> --
> 2.50.1.552.g942d659e1b-goog
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup
  2025-08-02  9:44   ` David Gow
@ 2025-08-06 17:50     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-08-06 17:50 UTC (permalink / raw)
  To: David Gow
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Sat, Aug 2, 2025 at 5:45 AM David Gow <davidgow@google.com> wrote:
>
> On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
> >
> > Add `example_params_test_with_init` to illustrate how to manage
> > shared resources across parameterized KUnit tests. This example
> > showcases the use of the new `param_init` function and its registration
> > to a test using the `KUNIT_CASE_PARAM_WITH_INIT` macro.
> >
> > Additionally, the test demonstrates:
> > - How to directly assign a static parameter array to a test via
> >   `kunit_register_params_array`.
> > - Leveraging the Resource API for test resource management.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
> > ---
>
> Thanks for writing some examples! This is great, and makes the rest of
> the series much easier to understand.
>
> (It also reminds me how much I hate the verbose parts of the resource
> API, but it's definitely out of scope to refactor that here. :-))
>
> It does seem like this is a lot of effort to go through for one shared
> integer, though. In the real world, I'd suggest using
> kunit->parent->priv here. As an example, though, it's fine (though
> maybe using a named resource or even kunit_kzalloc() or similar would
> give a better example of how convenient this could be.
>
> It's also not entirely clear why we're using
> kunit_register_params_array() for a static array, when
> KUNIT_ARRAY_PARAM() exists. (This is clearly because the latter
> doesn't support init functions; and I see why we don't necessarily
> want to make the number of macros explode through adding
> KUNIT_ARRAY_PARAM_WITH_INIT() et al, but maybe we should note that in
> the commit description, either here or before.)
>
> Actual test looks fine, though:
>
> Reviewed-by: David Gow <davidgow@google.com>
>
> Cheers,
> -- David
>

Hello David,

I agree that using the Resource API for a single integer is a bit extra.
My idea behind this test was to demonstrate that the Resource API
could be used for managing shared resources and to have the
style of the existing example tests that do simple things with integers.

Using kunit_kzalloc() would be a great simplification. As for a named
resource, we don't have a function to allocate named resources yet
as would be needed here, but that sounds like a great future patch.

We can actually use the KUNIT_ARRAY_PARAM() macro with
KUNIT_CASE_PARAM_WITH_INIT(). We would just pass that created
`*_gen_params` function to KUNIT_CASE_PARAM_WITH_INIT()
instead of NULL. The reason I used kunit_register_params_array() with
the static array was to show that test users can pass a static array
this way, as well, and also to avoid making the test too long with the
dynamic array
creation. But I do like the consistency of using KUNIT_ARRAY_PARAM()
for static arrays and kunit_register_params_array() only for
dynamic ones.

Thank you,
-Marie

>
> >  lib/kunit/kunit-example-test.c | 112 +++++++++++++++++++++++++++++++++
> >  1 file changed, 112 insertions(+)
> >
> > diff --git a/lib/kunit/kunit-example-test.c b/lib/kunit/kunit-example-test.c
> > index 3056d6bc705d..5bf559e243f6 100644
> > --- a/lib/kunit/kunit-example-test.c
> > +++ b/lib/kunit/kunit-example-test.c
> > @@ -277,6 +277,116 @@ static void example_slow_test(struct kunit *test)
> >         KUNIT_EXPECT_EQ(test, 1 + 1, 2);
> >  }
> >
> > +/*
> > + * This custom function allocates memory for the kunit_resource data field.
> > + * The function is passed to kunit_alloc_resource() and executed once
> > + * by the internal helper __kunit_add_resource().
> > + */
> > +static int example_resource_init(struct kunit_resource *res, void *context)
> > +{
> > +       int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> > +
> > +       if (!info)
> > +               return -ENOMEM;
> > +       *info = *(int *)context;
> > +       res->data = info;
> > +       return 0;
> > +}
> > +
> > +/*
> > + * This function deallocates memory for the 'kunit_resource' data field.
> > + * The function is passed to kunit_alloc_resource() and automatically
> > + * executes within kunit_release_resource() when the resource's reference
> > + * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> > + * counting to ensure that resources are not freed prematurely.
> > + */
> > +static void example_resource_free(struct kunit_resource *res)
> > +{
> > +       kfree(res->data);
> > +}
> > +
> > +/*
> > + * This match function is invoked by kunit_find_resource() to locate
> > + * a test resource based on defined criteria. The current example
> > + * uniquely identifies the resource by its free function; however,
> > + * alternative custom criteria can be implemented. Refer to
> > + * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> > + */
> > +static bool example_resource_alloc_match(struct kunit *test,
> > +                                        struct kunit_resource *res,
> > +                                        void *match_data)
> > +{
> > +       return res->data && res->free == example_resource_free;
> > +}
> > +
> > +/*
> > + * This is an example of a function that provides a description for each of the
> > + * parameters.
> > + */
> > +static void example_param_array_get_desc(const void *p, char *desc)
> > +{
> > +       const struct example_param *param = p;
> > +
> > +       snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> > +                "example check if %d is less than or equal to 3", param->value);
> > +}
> > +
> > +/*
> > + * Initializes the parent kunit struct for parameterized KUnit tests.
> > + * This function enables sharing resources across all parameterized
> > + * tests by adding them to the `parent` kunit test struct. It also supports
> > + * registering either static or dynamic arrays of test parameters.
> > + */
> > +static int example_param_init(struct kunit *test)
> > +{
> > +       int ctx = 3; /* Data to be stored. */
> > +       int arr_size = ARRAY_SIZE(example_params_array);
> > +
> > +       /*
> > +        * This allocates a struct kunit_resource, sets its data field to
> > +        * ctx, and adds it to the kunit struct's resources list. Note that
> > +        * this is test managed so we don't need to have a custom exit function
> > +        * to free it.
> > +        */
> > +       void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> > +                                         GFP_KERNEL, &ctx);
> > +
> > +       if (!data)
> > +               return -ENOMEM;
> > +       /* Pass the static param array information to the parent struct kunit. */
> > +       kunit_register_params_array(test, example_params_array, arr_size,
> > +                                   example_param_array_get_desc);
> > +       return 0;
> > +}
> > +
> > +/*
> > + * This is an example of a parameterized test that uses shared resources
> > + * available from the struct kunit parent field of the kunit struct.
> > + */
> > +static void example_params_test_with_init(struct kunit *test)
> > +{
> > +       int threshold;
> > +       struct kunit_resource *res;
> > +       const struct example_param *param = test->param_value;
> > +
> > +       /* By design, param pointer will not be NULL. */
> > +       KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +       /* Here we access the parent pointer of the test to find the shared resource. */
> > +       res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> > +
> > +       KUNIT_ASSERT_NOT_NULL(test, res);
> > +
> > +       /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> > +       threshold = *((int *)res->data);
> > +
> > +       /* Assert that the parameter is less than or equal to a certain threshold. */
> > +       KUNIT_ASSERT_LE(test, param->value, threshold);
> > +
> > +       /* This decreases the reference count after calling kunit_find_resource(). */
> > +       kunit_put_resource(res);
> > +}
> > +
> >  /*
> >   * Here we make a list of all the test cases we want to add to the test suite
> >   * below.
> > @@ -296,6 +406,8 @@ static struct kunit_case example_test_cases[] = {
> >         KUNIT_CASE(example_static_stub_using_fn_ptr_test),
> >         KUNIT_CASE(example_priv_test),
> >         KUNIT_CASE_PARAM(example_params_test, example_gen_params),
> > +       KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> > +                                  example_param_init, NULL),
> >         KUNIT_CASE_SLOW(example_slow_test),
> >         {}
> >  };
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 5/9] drm/xe: Update parameter generator to new signature
  2025-08-02  9:44   ` David Gow
@ 2025-08-07 10:59     ` Lucas De Marchi
  0 siblings, 0 replies; 41+ messages in thread
From: Lucas De Marchi @ 2025-08-07 10:59 UTC (permalink / raw)
  To: David Gow
  Cc: Marie Zhussupova, rmoar, shuah, brendan.higgins, elver, dvyukov,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Sat, Aug 02, 2025 at 05:44:48PM +0800, David Gow wrote:
>On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
>>
>> This patch modifies `xe_pci_live_device_gen_param`
>> in xe_pci.c to accept an additional `struct kunit *test`
>> argument.
>>
>> Signed-off-by: Marie Zhussupova <marievic@google.com>
>> ---
>
>
>This is a pretty straightforward fix after patch 3. xe folks, would
>you prefer this kept as a separate patch, or squashed into patch 3
>(which changed the function signature)?

I don't like the breakage. Squashing would be much preferred.

thanks
Lucas De Marchi

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 4/9] kcsan: test: Update parameter generator to new signature
  2025-08-02  9:44   ` David Gow
@ 2025-08-07 11:11     ` Marco Elver
  0 siblings, 0 replies; 41+ messages in thread
From: Marco Elver @ 2025-08-07 11:11 UTC (permalink / raw)
  To: David Gow
  Cc: Marie Zhussupova, rmoar, shuah, brendan.higgins, dvyukov,
	lucas.demarchi, thomas.hellstrom, rodrigo.vivi, linux-kselftest,
	kunit-dev, kasan-dev, intel-xe, dri-devel, linux-kernel

On Sat, 2 Aug 2025 at 11:45, David Gow <davidgow@google.com> wrote:
>
> On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
> >
> > This patch modifies `nthreads_gen_params` in kcsan_test.c
> > to accept an additional `struct kunit *test` argument.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
> > ---
>
> This is a pretty straightforward fix after patch 3. KCSAN folks, would
> you prefer this kept as a separate patch, or squashed into the
> previous one (so there's no commit where this is broken)?

Normally patch series should be structured so that bisection does not
break. Having this fixup as a separate patch means that bisections
where the KCSAN test is enabled will break.

This is a tiny change, so I'd just squash it.


> Either way,
> Reviewed-by: David Gow <davidgow@google.com>
>
>
> -- David
>
> >  kernel/kcsan/kcsan_test.c | 2 +-
> >  1 file changed, 1 insertion(+), 1 deletion(-)
> >
> > diff --git a/kernel/kcsan/kcsan_test.c b/kernel/kcsan/kcsan_test.c
> > index c2871180edcc..fc76648525ac 100644
> > --- a/kernel/kcsan/kcsan_test.c
> > +++ b/kernel/kcsan/kcsan_test.c
> > @@ -1383,7 +1383,7 @@ static void test_atomic_builtins_missing_barrier(struct kunit *test)
> >   * The thread counts are chosen to cover potentially interesting boundaries and
> >   * corner cases (2 to 5), and then stress the system with larger counts.
> >   */
> > -static const void *nthreads_gen_params(const void *prev, char *desc)
> > +static const void *nthreads_gen_params(struct kunit *test, const void *prev, char *desc)
> >  {
> >         long nthreads = (long)prev;
> >
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test
  2025-08-05 15:18   ` Rae Moar
@ 2025-08-08  1:22     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-08-08  1:22 UTC (permalink / raw)
  To: Rae Moar
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Aug 5, 2025 at 11:18 AM Rae Moar <rmoar@google.com> wrote:
>
> On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
> >
> > KUnit parameterized tests currently support two
> > primary methods for getting parameters:
> > 1.  Defining custom logic within a `generate_params`
> >     function.
> > 2.  Using the KUNIT_ARRAY_PARAM and KUNIT_ARRAY_PARAM_DESC
> >     macros with pre-defined static arrays.
> >
> > These methods present limitations when dealing with
> > dynamically generated parameter arrays, or in scenarios
> > where populating parameters sequentially via
> > `generate_params` is inefficient or overly complex.
> >
> > This patch addresses these limitations by adding a new
> > `params_data` field to `struct kunit`, of the type
> > `kunit_params`. The struct `kunit_params` is designed to
> > store the parameter array itself, along with essential metadata
> > including the parameter count, parameter size, and a
> > `get_description` function for providing custom descriptions
> > for individual parameters.
> >
> > The `params_data` field can be populated by calling the new
> > `kunit_register_params_array` macro from within a
> > `param_init` function. By attaching the parameter array
> > directly to the parent kunit test instance, these parameters
> > can be iterated over in kunit_run_tests() behind the scenes.
> >
> > This modification provides greater flexibility to the
> > KUnit framework, allowing testers to easily register and
> > utilize both dynamic and static parameter arrays.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
>
> Hello!
>
> Very excited by the prospect of setting up an array dynamically
> instead of statically for parameterized tests. In general, I am happy
> to see this framework is becoming more flexible and therefore more
> tailored to our test author's needs.
>
> I already commented on the modpost error but I have a few more
> comments and ideas below. Let me know what you think.
>
> Thanks!
> -Rae
>

Hello Rae,

I have added replies to your comments below.

Thank you,
-Marie

> > ---
> >  include/kunit/test.h | 54 ++++++++++++++++++++++++++++++++++++++++----
> >  lib/kunit/test.c     | 26 ++++++++++++++++++++-
> >  2 files changed, 75 insertions(+), 5 deletions(-)
> >
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index 4ba65dc35710..9143f0e22323 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -245,7 +245,8 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
> >   */
> >  #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
> >                 { .run_case = test_name, .name = #test_name,                    \
> > -                 .generate_params = gen_params,                                \
> > +                 .generate_params = (gen_params)                               \
> > +                  ?: kunit_get_next_param_and_desc,                            \
> >                   .param_init = init, .param_exit = exit,                       \
> >                   .module_name = KBUILD_MODNAME}
> >
> > @@ -294,6 +295,21 @@ struct kunit_suite_set {
> >         struct kunit_suite * const *end;
> >  };
> >
> > +/* Stores the pointer to the parameter array and its metadata. */
> > +struct kunit_params {
> > +       /*
> > +        * Reference to the parameter array for the parameterized tests. This
> > +        * is NULL if a parameter array wasn't directly passed to the
> > +        * parent kunit struct via the kunit_register_params_array macro.
> > +        */
> > +       const void *params;
> > +       /* Reference to a function that gets the description of a parameter. */
> > +       void (*get_description)(const void *param, char *desc);
> > +
> > +       int num_params;
>
> Since in some cases we know the number of params within a series/suite
> of the parameterized tests, is it possible for us to print a test plan
> line in KTAP when this number is known? This would be helpful for
> reading test results but also the parser could verify the number of
> subtests is the number expected.

This sounds like a great idea. My initial worry was that the availability
of the test plan would be inconsistent since there would be no parameter
count information when the parameters are generated using a custom
generate_params function unless a test user provides them.
However, it is good to provide any information that we have.

>
> > +       size_t elem_size;
> > +};
> > +
> >  /**
> >   * struct kunit - represents a running instance of a test.
> >   *
> > @@ -302,12 +318,14 @@ struct kunit_suite_set {
> >   * @parent: for user to store data that they want to shared across
> >   *         parameterized tests. Typically, the data is provided in
> >   *         the param_init function (see &struct kunit_case).
> > + * @params_data: for users to directly store the parameter array.
> >   *
> >   * Used to store information about the current context under which the test
> >   * is running. Most of this data is private and should only be accessed
> > - * indirectly via public functions; the two exceptions are @priv and @parent
> > - * which can be used by the test writer to store arbitrary data or data that is
> > - * available to all parameter test executions, respectively.
> > + * indirectly via public functions. There are three exceptions to this: @priv,
> > + * @parent, and @params_data. These members can be used by the test writer to
> > + * store arbitrary data, data available to all parameter test executions, and
> > + * the parameter array, respectively.
> >   */
> >  struct kunit {
> >         void *priv;
> > @@ -316,6 +334,8 @@ struct kunit {
> >          * during parameterized testing.
> >          */
> >         struct kunit *parent;
> > +       /* Stores the params array and all data related to it. */
> > +       struct kunit_params params_data;
>
> I might slightly prefer the term params_array rather than params_data.
> Up to what you prefer.

I like params_array because it's a naming convention similar
to what object-oriented programming (OOP) languages like
Java use. For instance, an ArrayList object encapsulates
both an array and its size. I will make this change in v2.

>
> >
> >         /* private: internal use only. */
> >         const char *name; /* Read only after initialization! */
> > @@ -386,6 +406,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
> >  struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
> >                 struct kunit_suite_set suite_set);
> >
> > +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc);
> > +
> >  #if IS_BUILTIN(CONFIG_KUNIT)
> >  int kunit_run_all_tests(void);
> >  #else
> > @@ -1735,6 +1757,30 @@ do {                                                                            \
> >                 return NULL;                                                                    \
> >         }
> >
> > +/**
> > + * kunit_register_params_array() - Register parameters for a KUnit test.
> > + * @test: The KUnit test structure to which parameters will be added.
> > + * @params_arr: An array of test parameters.
> > + * @param_cnt: Number of parameters.
> > + * @get_desc: A pointer to a function that generates a string description for
> > + * a given parameter element.
> > + *
> > + * This macro initializes the @test's parameter array data, storing information
> > + * including the parameter array, its count, the element size, and the parameter
> > + * description function within `test->params_data`. KUnit's built-in
> > + * `kunit_get_next_param_and_desc` function will automatically read this
> > + * data when a custom `generate_params` function isn't provided.
> > + */
> > +#define kunit_register_params_array(test, params_arr, param_cnt, get_desc)                     \
>
> I also might slightly prefer params_array and param_count here instead
> of params_arr and param_cnt. Again this is definitely a nitpick so up
> to you.

I will do params_arr and param_cnt in v2, it definitely looks better.

>
> > +       do {                                                                                    \
> > +               struct kunit *_test = (test);                                           \
> > +               const typeof((params_arr)[0]) * _params_ptr = &(params_arr)[0];                 \
> > +               _test->params_data.params = _params_ptr;                                        \
> > +               _test->params_data.num_params = (param_cnt);                                    \
> > +               _test->params_data.elem_size = sizeof(*_params_ptr);                            \
> > +               _test->params_data.get_description = (get_desc);                                \
> > +       } while (0)
> > +
> >  // TODO(dlatypov@google.com): consider eventually migrating users to explicitly
> >  // include resource.h themselves if they need it.
> >  #include <kunit/resource.h>
> > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > index f50ef82179c4..2f4b7087db3f 100644
> > --- a/lib/kunit/test.c
> > +++ b/lib/kunit/test.c
> > @@ -337,6 +337,13 @@ void __kunit_do_failed_assertion(struct kunit *test,
> >  }
> >  EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
> >
> > +static void __kunit_init_params(struct kunit *test)
> > +{
> > +       test->params_data.params = NULL;
> > +       test->params_data.num_params = 0;
> > +       test->params_data.elem_size = 0;
> > +}
> > +
> >  void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
> >  {
> >         spin_lock_init(&test->lock);
> > @@ -347,6 +354,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
> >                 string_stream_clear(log);
> >         test->status = KUNIT_SUCCESS;
> >         test->status_comment[0] = '\0';
> > +       __kunit_init_params(test);
> >  }
> >  EXPORT_SYMBOL_GPL(kunit_init_test);
> >
> > @@ -641,6 +649,22 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
> >         total->total += add.total;
> >  }
> >
> > +const void *kunit_get_next_param_and_desc(struct kunit *test, const void *prev, char *desc)
> > +{
> > +       struct kunit_params *params_arr = &test->params_data;
> > +       const void *param;
> > +
> > +       if (test->param_index < params_arr->num_params) {
> > +               param = (char *)params_arr->params
> > +                       + test->param_index * params_arr->elem_size;
> > +
> > +               if (params_arr->get_description)
> > +                       params_arr->get_description(param, desc);
> > +               return param;
> > +       }
> > +       return NULL;
> > +}
>
> I also agree with David that it should definitely be considered: 1 -
> whether to utilize struct kunit_params for the case of using
> KUNIT_ARRAY_PARAM and 2 - whether the user should actively input this
> function instead of setting generate_params to NULL.
>
> Another idea that just popped into my head is if we have access to
> struct kunit* test now in all of the generate_params functions,
> instead of setting a "desc" could we just set the test->name field?
>

Utilizing struct kunit_params in KUNIT_ARRAY_PARAM is a great idea
as that would mean greater test plan availability as well as the consistency
of storing parameter array information in struct kunit_params.

To the second point, I think the user being asked to explicitly input
kunit_get_next_param_and_desc if they are registering a parameter
array in param_init is a good solution. It would make the code easier
as there would be no extra logic happening behind the scenes.
Moreover, we would always know that if it's a parameterized
test then the generate_params function is not NULL. The only thing
is that we would have to document it really well.

Another potential solution could be to assign the
kunit_get_next_param_and_desc function to generate_params
in the  __kunit_init_parent_test function that runs param_init, if we
keep its run before we determine that a test
is parameterized (I know you commented on this here:
https://lore.kernel.org/all/CA+GJov5R2GnBfxXR=28vS3F4b1E-=WLDXpgdJo0SpKAXb1dpsw@mail.gmail.com/).

__kunit_init_parent_test could set generate_params to
kunit_get_next_param_and_desc if generate_params is NULL
and kunit_params is non NULL, after the param_init call.
What are your thoughts?

Finally, utilizing test->name could be something to do in a future patch.
First, we wouldn't need to pass char *desc as an argument to
generate_params and second, the variable naming would be better
as we are using the test name for KTAP output for regular tests.
The downside would be having to change existing implementations
that use char *desc and to explicitly specify to use test->name to users.

> > +
> >  static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
> >  {
> >         if (test_case->param_init) {
> > @@ -687,7 +711,7 @@ int kunit_run_tests(struct kunit_suite *suite)
> >                         /* Test marked as skip */
> >                         test.status = KUNIT_SKIPPED;
> >                         kunit_update_stats(&param_stats, test.status);
> > -               } else if (!test_case->generate_params) {
> > +               } else if (!test_case->generate_params && !test.params_data.params) {
>
> I agree with David that it is helpful to have one check for whether a
> test is a parameterized test rather than two. My instinct is that if
> test_case->generate_params is NULL it should be safe to assume the
> test isn't a parameterized test.

Agreed.

>
> However, as an alternative or even as a helpful addition, I like the
> idea of a simple kunit_test_is_param function that can pass in the
> test and it will return a bool whether the test is parameterized or
> not.
>
That could be a good idea. At the same time, if it remains true
that generate_params being NULL implies that it's not a
parameterized test, I think a simple check would be enough.

>
>
>
> >                         /* Non-parameterised test. */
> >                         test_case->status = KUNIT_SKIPPED;
> >                         kunit_run_case_catch_errors(suite, test_case, &test);
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 1/9] kunit: Add parent kunit for parameterized test context
  2025-08-05 15:17   ` Rae Moar
@ 2025-08-08 12:20     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-08-08 12:20 UTC (permalink / raw)
  To: Rae Moar
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Aug 5, 2025 at 11:17 AM Rae Moar <rmoar@google.com> wrote:
>
> On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
> >
> > Currently, KUnit parameterized tests lack a mechanism
> > to share resources across individual test invocations
> > because the same `struct kunit` instance is reused for
> > each test.
> >
> > This patch refactors kunit_run_tests() to provide each
> > parameterized test with its own `struct kunit` instance.
> > A new parent pointer is added to `struct kunit`, allowing
> > individual parameterized tests to reference a shared
> > parent kunit instance. Resources added to this parent
> > will then be accessible to all individual parameter
> > test executions.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
>
> Hello!
>
> Thank you so much for sending out this series. I have wanted to see an
> update of our parameterized test framework for a while. I have a few
> comments below for this patch. But otherwise it is looking good.
>
> Reviewed-by: Rae Moar <rmoar@google.com>
>
> Thanks!
> -Rae
>
> > ---
> >  include/kunit/test.h | 12 ++++++++++--
> >  lib/kunit/test.c     | 32 +++++++++++++++++++-------------
> >  2 files changed, 29 insertions(+), 15 deletions(-)
> >
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index 39c768f87dc9..a42d0c8cb985 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -268,14 +268,22 @@ struct kunit_suite_set {
> >   *
> >   * @priv: for user to store arbitrary data. Commonly used to pass data
> >   *       created in the init function (see &struct kunit_suite).
> > + * @parent: for user to store data that they want to shared across
> > + *         parameterized tests.
> >   *
>
> As David mentioned, I would also prefer that this provides a more
> general description of the @parent field here. Although this is
> currently only used for parameterized tests, it could have other use
> cases in the future.
>

Will edit this in v2.

> >   * Used to store information about the current context under which the test
> >   * is running. Most of this data is private and should only be accessed
> > - * indirectly via public functions; the one exception is @priv which can be
> > - * used by the test writer to store arbitrary data.
> > + * indirectly via public functions; the two exceptions are @priv and @parent
> > + * which can be used by the test writer to store arbitrary data or data that is
> > + * available to all parameter test executions, respectively.
>
> In addition, I would prefer that the call out to @parent here is also
> changed to a more general description of the @parent field. However,
> feel free to also include the description of the use case for the
> parameterized tests.
>

I will edit this in v2, as well.

> >   */
> >  struct kunit {
> >         void *priv;
> > +       /*
> > +        * Reference to the parent struct kunit for storing shared resources
> > +        * during parameterized testing.
> > +        */
>
> I am more 50/50 on changing this description. Could change it just to:
> "Reference to the parent struct kunit for storing shared resources."

Thank you for the suggestion! The description would sound good.

>
> > +       struct kunit *parent;
> >
> >         /* private: internal use only. */
> >         const char *name; /* Read only after initialization! */
> > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > index f3c6b11f12b8..4d6a39eb2c80 100644
> > --- a/lib/kunit/test.c
> > +++ b/lib/kunit/test.c
> > @@ -647,6 +647,7 @@ int kunit_run_tests(struct kunit_suite *suite)
> >         struct kunit_case *test_case;
> >         struct kunit_result_stats suite_stats = { 0 };
> >         struct kunit_result_stats total_stats = { 0 };
> > +       const void *curr_param;
> >
> >         /* Taint the kernel so we know we've run tests. */
> >         add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
> > @@ -679,36 +680,39 @@ int kunit_run_tests(struct kunit_suite *suite)
> >                 } else {
> >                         /* Get initial param. */
> >                         param_desc[0] = '\0';
> > -                       test.param_value = test_case->generate_params(NULL, param_desc);
> > +                       /* TODO: Make generate_params try-catch */
> > +                       curr_param = test_case->generate_params(NULL, param_desc);
> >                         test_case->status = KUNIT_SKIPPED;
> >                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> >                                   "KTAP version 1\n");
> >                         kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> >                                   "# Subtest: %s", test_case->name);
> >
> > -                       while (test.param_value) {
> > -                               kunit_run_case_catch_errors(suite, test_case, &test);
> > +                       while (curr_param) {
> > +                               struct kunit param_test = {
> > +                                       .param_value = curr_param,
> > +                                       .param_index = ++test.param_index,
> > +                                       .parent = &test,
> > +                               };
> > +                               kunit_init_test(&param_test, test_case->name, test_case->log);
> > +                               kunit_run_case_catch_errors(suite, test_case, &param_test);
> >
> >                                 if (param_desc[0] == '\0') {
> >                                         snprintf(param_desc, sizeof(param_desc),
> >                                                  "param-%d", test.param_index);
>
> This probably doesn't matter too much either way but should this be
> param_test.param_index instead? This would cover the case where the
> param_index is changed during the test run even though it shouldn't.
>

Thank you for catching this!

> >                                 }
> >
> > -                               kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
> > -                                                     test.status,
> > -                                                     test.param_index + 1,
> > +                               kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
> > +                                                     param_test.status,
> > +                                                     param_test.param_index,
> >                                                       param_desc,
> > -                                                     test.status_comment);
> > +                                                     param_test.status_comment);
> >
> > -                               kunit_update_stats(&param_stats, test.status);
> > +                               kunit_update_stats(&param_stats, param_test.status);
> >
> >                                 /* Get next param. */
> >                                 param_desc[0] = '\0';
> > -                               test.param_value = test_case->generate_params(test.param_value, param_desc);
> > -                               test.param_index++;
> > -                               test.status = KUNIT_SUCCESS;
> > -                               test.status_comment[0] = '\0';
> > -                               test.priv = NULL;
> > +                               curr_param = test_case->generate_params(curr_param, param_desc);
> >                         }
> >                 }
> >
> > @@ -723,6 +727,8 @@ int kunit_run_tests(struct kunit_suite *suite)
> >
> >                 kunit_update_stats(&suite_stats, test_case->status);
> >                 kunit_accumulate_stats(&total_stats, param_stats);
> > +               /* TODO: Put this kunit_cleanup into a try-catch. */
> > +               kunit_cleanup(&test);
>
> I might be missing something here but why not do this cleanup before
> the printing stage and only if the test was a parent param test?
>

Thank you for catching this too, it should be only for the parent param test.

>
>
> >         }
> >
> >         if (suite->suite_exit)
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management
  2025-08-05 15:17   ` Rae Moar
@ 2025-08-08 13:01     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-08-08 13:01 UTC (permalink / raw)
  To: Rae Moar
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Aug 5, 2025 at 11:18 AM Rae Moar <rmoar@google.com> wrote:
>
> On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
> >
> > Add `param_init` and `param_exit` function pointers to
> > `struct kunit_case`. Users will be able to set them
> > via the new `KUNIT_CASE_PARAM_WITH_INIT` macro.
>
> Hello!
>
> Very intrigued by this idea to add an init and exit function for
> parameterized tests. In a way, this allows parameterized test series
> to act more like suites. Either way I am happy to see more flexibility
> being brought to the parameterized test framework.
>
> I have a few comments below that I would like to discuss before a
> final review. But this patch is looking good.
>
> Thanks!
> -Rae
>
> >
> > These functions are invoked by kunit_run_tests() once before
> > and once after the entire parameterized test series, respectively.
>
> This is a philosophical question but should we refer to a group of
> parameterized tests as a parameterized test series or a parameterized
> test suite? In the KTAP, the appearance is identical to a suite but in
> the running of the tests it acts distinct to a test case or suite.
> Curious on David's opinion here.
>

Thank you for bringing this up! Using the wording of the patch that
introduced the parameterized tests:
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/lib/kunit?id=fadb08e7c7501ed42949e646c6865ba4ec5dd948,
"parameterized test" will refer to a group of parameterized tests and
"parameter run" will refer to a single parameter execution. I will also
specify this terminology in the docs for v2.

> > They will receive the parent kunit test instance, allowing users
> > to register and manage shared resources. Resources added to this
> > parent kunit test will be accessible to all individual parameterized
> > tests, facilitating init and exit for shared state.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
> > ---
> >  include/kunit/test.h | 33 ++++++++++++++++++++++++++++++++-
> >  lib/kunit/test.c     | 23 ++++++++++++++++++++++-
> >  2 files changed, 54 insertions(+), 2 deletions(-)
> >
> > diff --git a/include/kunit/test.h b/include/kunit/test.h
> > index a42d0c8cb985..d8dac7efd745 100644
> > --- a/include/kunit/test.h
> > +++ b/include/kunit/test.h
> > @@ -92,6 +92,8 @@ struct kunit_attributes {
> >   * @name:     the name of the test case.
> >   * @generate_params: the generator function for parameterized tests.
> >   * @attr:     the attributes associated with the test
> > + * @param_init: The init function to run before parameterized tests.
> > + * @param_exit: The exit function to run after parameterized tests.
>
> If we decide on a terminology for the parameterized test group, it
> might be clearer to label these "The init function to run before
> parameterized test [suite/series]." and same for the exit function.
>

Will update this in v2.

> >   *
> >   * A test case is a function with the signature,
> >   * ``void (*)(struct kunit *)``
> > @@ -129,6 +131,13 @@ struct kunit_case {
> >         const void* (*generate_params)(const void *prev, char *desc);
> >         struct kunit_attributes attr;
> >
> > +       /*
> > +        * Optional user-defined functions: one to register shared resources once
> > +        * before the parameterized test series, and another to release them after.
> > +        */
> > +       int (*param_init)(struct kunit *test);
> > +       void (*param_exit)(struct kunit *test);
> > +
> >         /* private: internal use only. */
> >         enum kunit_status status;
> >         char *module_name;
> > @@ -218,6 +227,27 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
> >                   .generate_params = gen_params,                                \
> >                   .attr = attributes, .module_name = KBUILD_MODNAME}
> >
> > +/**
> > + * KUNIT_CASE_PARAM_WITH_INIT() - Define a parameterized KUnit test case with custom
> > + * init and exit functions.
> > + * @test_name: The function implementing the test case.
> > + * @gen_params: The function to generate parameters for the test case.
> > + * @init: The init function to run before parameterized tests.
> > + * @exit: The exit function to run after parameterized tests.
>
> If we do change the description above of param_init/param_exit, it
> might be nice to change it here too.
>

Will update this, as well.

> > + *
> > + * Provides the option to register init and exit functions that take in the
> > + * parent of the parameterized tests and run once before and once after the
> > + * parameterized test series. The init function can be used to add any resources
> > + * to share between the parameterized tests or to pass parameter arrays. The
> > + * exit function can be used to clean up any resources that are not managed by
> > + * the test.
> > + */
> > +#define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
> > +               { .run_case = test_name, .name = #test_name,                    \
> > +                 .generate_params = gen_params,                                \
> > +                 .param_init = init, .param_exit = exit,                       \
> > +                 .module_name = KBUILD_MODNAME}
> > +
> >  /**
> >   * struct kunit_suite - describes a related collection of &struct kunit_case
> >   *
> > @@ -269,7 +299,8 @@ struct kunit_suite_set {
> >   * @priv: for user to store arbitrary data. Commonly used to pass data
> >   *       created in the init function (see &struct kunit_suite).
> >   * @parent: for user to store data that they want to shared across
> > - *         parameterized tests.
> > + *         parameterized tests. Typically, the data is provided in
> > + *         the param_init function (see &struct kunit_case).
> >   *
> >   * Used to store information about the current context under which the test
> >   * is running. Most of this data is private and should only be accessed
> > diff --git a/lib/kunit/test.c b/lib/kunit/test.c
> > index 4d6a39eb2c80..d80b5990d85d 100644
> > --- a/lib/kunit/test.c
> > +++ b/lib/kunit/test.c
> > @@ -641,6 +641,19 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
> >         total->total += add.total;
> >  }
> >
> > +static void __kunit_init_parent_test(struct kunit_case *test_case, struct kunit *test)
>
> It would be nice to include "param" in this function name. Currently
> it sounds more like you are initializing the @parent field of struct
> kunit *test.

That is a great suggestion, I will incorporate it in v2.

>
> > +{
> > +       if (test_case->param_init) {
> > +               int err = test_case->param_init(test);
> > +
> > +               if (err) {
> > +                       kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
> > +                               "# failed to initialize parent parameter test.");
> > +                       test_case->status = KUNIT_FAILURE;
> > +               }
> > +       }
> > +}
> > +
> >  int kunit_run_tests(struct kunit_suite *suite)
> >  {
> >         char param_desc[KUNIT_PARAM_DESC_SIZE];
> > @@ -668,6 +681,8 @@ int kunit_run_tests(struct kunit_suite *suite)
> >                 struct kunit_result_stats param_stats = { 0 };
> >
> >                 kunit_init_test(&test, test_case->name, test_case->log);
> > +               __kunit_init_parent_test(test_case, &test);
> > +
>
> Is it possible to move this so this function is only called when it is
> a parameterized test? I see the check for KUNIT_FAILURE is useful but
> I think I would still prefer this within the section for parameterized
> tests.

Yes, I will do that, unless we decide to go with the route
to set generate_params to point to kunit_get_next_param_and_desc
in the __kunit_init_parent_test function.

>
> >                 if (test_case->status == KUNIT_SKIPPED) {
> >                         /* Test marked as skip */
> >                         test.status = KUNIT_SKIPPED;
> > @@ -677,7 +692,7 @@ int kunit_run_tests(struct kunit_suite *suite)
> >                         test_case->status = KUNIT_SKIPPED;
> >                         kunit_run_case_catch_errors(suite, test_case, &test);
> >                         kunit_update_stats(&param_stats, test.status);
> > -               } else {
> > +               } else if (test_case->status != KUNIT_FAILURE) {
> >                         /* Get initial param. */
> >                         param_desc[0] = '\0';
> >                         /* TODO: Make generate_params try-catch */
> > @@ -727,6 +742,12 @@ int kunit_run_tests(struct kunit_suite *suite)
> >
> >                 kunit_update_stats(&suite_stats, test_case->status);
> >                 kunit_accumulate_stats(&total_stats, param_stats);
> > +               /*
> > +                * TODO: Put into a try catch. Since we don't need suite->exit
> > +                * for it we can't reuse kunit_try_run_cleanup for this yet.
> > +                */
> > +               if (test_case->param_exit)
> > +                       test_case->param_exit(&test);
>
> Also here I am not sure why this is done outside of the check for if
> the test is parameterized? Either way this should definitely be done
> before the test stats and ok/not ok line are printed because if there
> is any log output during the param_exit function it is necessary to
> print that before the status line to identify that that log
> corresponds with that test.

Thank you for catching this! Yes, it should be inside the check for if
the test is parameterized.

>
> Also just curious why you chose to implement a function to perform the
> param_init but not the param_exit?

To be consistent with the existing style of "exit" functions in KUnit,
test->param_exit returns void. Therefore, similar to how it's done for
suite->suite_exit
(https://elixir.bootlin.com/linux/v6.16/source/lib/kunit/test.c#L685),
I didn't do extra error handling as the function itself doesn't
indicate an error and
therefore, didn't put it in a separate function. To do error handling
for it, it would
need to be in a try catch, then we could check if the cleanup timed out or
if there was an internal error.

>
>
>
> >                 /* TODO: Put this kunit_cleanup into a try-catch. */
> >                 kunit_cleanup(&test);
> >         }
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 9/9] Documentation: kunit: Document new parameterized test features
  2025-08-02  9:45   ` David Gow
@ 2025-08-08 13:02     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-08-08 13:02 UTC (permalink / raw)
  To: David Gow
  Cc: rmoar, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Sat, Aug 2, 2025 at 5:45 AM David Gow <davidgow@google.com> wrote:
>
> On Wed, 30 Jul 2025 at 03:37, Marie Zhussupova <marievic@google.com> wrote:
> >
> > -Update the KUnit documentation to explain the concept
> > of a parent parameterized test.
> > -Add examples demonstrating different ways of passing
> > parameters to parameterized tests and how to manage
> > shared resources between them.
> >
>
> Nit: We don't need the dot points ('-') here. Just make them paragraphs.

Will do!

>
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
> > ---
>
> Thanks very, very much for including such detailed documentation.
>
> I do think some of the examples could be trimmed / left in the
> kunit-example-test.c file and referenced, as they're long enough that
> it's difficult to focus on the essentials. But otherwise, this looks
> great.
>
> A few small notes below, but otherwise:
>
> Reviewed-by: David Gow <davidgow@google.com>
>
> Cheers,
> -- David
>
> >  Documentation/dev-tools/kunit/usage.rst | 455 +++++++++++++++++++++++-
> >  1 file changed, 449 insertions(+), 6 deletions(-)
> >
> > diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
> > index 066ecda1dd98..be1d656053cf 100644
> > --- a/Documentation/dev-tools/kunit/usage.rst
> > +++ b/Documentation/dev-tools/kunit/usage.rst
> > @@ -542,11 +542,21 @@ There is more boilerplate code involved, but it can:
> >  Parameterized Testing
> >  ~~~~~~~~~~~~~~~~~~~~~
> >
> > -The table-driven testing pattern is common enough that KUnit has special
> > -support for it.
> > -
> > -By reusing the same ``cases`` array from above, we can write the test as a
> > -"parameterized test" with the following.
> > +To efficiently and elegantly validate a test case against a variety of inputs,
> > +KUnit also provides a parameterized testing framework. This feature formalizes
> > +and extends the concept of table-driven tests discussed previously, offering
> > +a more integrated and flexible way to handle multiple test scenarios with
> > +minimal code duplication.
>
> Nit: maybe we can tone down the adjectives slightly here. I do like
> parameterised testing a lot, but it probably doesn't need to be
> "efficient", "elegant", "integrated", and "flexible".

Will do that in v2.

>
> > +
> > +Passing Parameters to the Test Cases
> > +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > +There are three main ways to provide the parameters to a test case:
> > +
> > +Array Parameter Macros (``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC``):
> > +   KUnit provides special support for the common table-driven testing pattern.
> > +   By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
> > +   ``cases`` array from the previous section, we can create a parameterized test
> > +   as shown below:
> >
> >  .. code-block:: c
> >
> > @@ -555,7 +565,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
> >                 const char *str;
> >                 const char *sha1;
> >         };
> > -       const struct sha1_test_case cases[] = {
> > +       static const struct sha1_test_case cases[] = {
> >                 {
> >                         .str = "hello world",
> >                         .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
> > @@ -590,6 +600,439 @@ By reusing the same ``cases`` array from above, we can write the test as a
> >                 {}
> >         };
> >
> > +Custom Parameter Generator (``generate_params``):
> > +   You can pass your own ``generate_params`` function to the ``KUNIT_CASE_PARAM``
> > +   or ``KUNIT_CASE_PARAM_WITH_INIT`` macros. This function is responsible for
> > +   generating parameters one by one. It receives the previously generated parameter
> > +   as the ``prev`` argument (which is ``NULL`` on the first call) and can also
> > +   access any context available from the parent ``struct kunit`` passed as the
> > +   ``test`` argument. KUnit calls this function repeatedly until it returns
> > +   ``NULL``. Below is an example of how it works:
> > +
> > +.. code-block:: c
> > +
> > +       #define MAX_TEST_BUFFER_SIZE 8
> > +
> > +       // Example generator function. It produces a sequence of buffer sizes that
> > +       // are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
> > +       static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
> > +       {
> > +               long prev_buffer_size = (long)prev;
> > +               long next_buffer_size = 1; // Start with an initial size of 1.
> > +
> > +               // Stop generating parameters if the limit is reached or exceeded.
> > +               if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
> > +                       return NULL;
> > +
> > +               // For subsequent calls, calculate the next size by doubling the previous one.
> > +               if (prev)
> > +                       next_buffer_size = prev_buffer_size << 1;
> > +
> > +               return (void *)next_buffer_size;
> > +       }
> > +
> > +       // Simple test to validate that kunit_kzalloc provides zeroed memory.
> > +       static void buffer_zero_test(struct kunit *test)
> > +       {
> > +               long buffer_size = (long)test->param_value;
> > +               // Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
> > +               // memory "parameter managed," meaning it's automatically cleaned up at
> > +               // the end of each parameter execution.
> > +               int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
> > +
> > +               // Ensure the allocation was successful.
> > +               KUNIT_ASSERT_NOT_NULL(test, buf);
> > +
> > +               // Loop through the buffer and confirm every element is zero.
> > +               for (int i = 0; i < buffer_size; i++)
> > +                       KUNIT_EXPECT_EQ(test, buf[i], 0);
> > +       }
> > +
> > +       static struct kunit_case buffer_test_cases[] = {
> > +               KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
> > +               {}
> > +       };
> > +
> > +Direct Registration in Parameter Init Function (using ``kunit_register_params_array``):
>
> Maybe we should highlight this as being array-based more explicitly.
> "Runtime Array Registration in the Init function" or similar?

That would make it clearer, will make this edit in v2.

>
> > +   For more complex scenarios, you can directly register a parameter array with
> > +   a test case instead of using a ``generate_params`` function. This is done by
> > +   passing the array to the ``kunit_register_params_array`` macro within an
> > +   initialization function for the parameterized test series
> > +   (i.e., a function named ``param_init``). To better understand this mechanism
> > +   please refer to the "Adding Shared Resources" section below.
> > +
> > +   This method supports both dynamically built and static arrays.
> > +
> > +   As the following code shows, the ``example_param_init_dynamic_arr`` function
> > +   utilizes ``make_fibonacci_params`` to create a dynamic array, which is then
> > +   registered using ``kunit_register_params_array``. The corresponding exit
> > +   function, ``example_param_exit``, is responsible for freeing this dynamically
> > +   allocated params array after the parameterized test series ends.
> > +
> > +.. code-block:: c
> > +
> > +       /*
> > +        * Helper function to create a parameter array of Fibonacci numbers. This example
> > +        * highlights a parameter generation scenario that is:
> > +        * 1. Not feasible to fully pre-generate at compile time.
> > +        * 2. Challenging to implement with a standard 'generate_params' function,
> > +        * as it typically only provides the immediately 'prev' parameter, while
> > +        * Fibonacci requires access to two preceding values for calculation.
> > +        */
> > +       static void *make_fibonacci_params(int seq_size)
> > +       {
> > +               int *seq;
> > +
> > +               if (seq_size <= 0)
> > +                       return NULL;
> > +
> > +               seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
> > +
> > +               if (!seq)
> > +                       return NULL;
> > +
> > +               if (seq_size >= 1)
> > +                       seq[0] = 0;
> > +               if (seq_size >= 2)
> > +                       seq[1] = 1;
> > +               for (int i = 2; i < seq_size; i++)
> > +                       seq[i] = seq[i - 1] + seq[i - 2];
> > +               return seq;
> > +       }
> > +
> > +       // This is an example of a function that provides a description for each of the
> > +       // parameters.
> > +       static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
> > +       {
> > +               const int *fib_num = p;
> > +
> > +               snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
> > +       }
> > +
> > +       // Example of a parameterized test init function that registers a dynamic array.
> > +       static int example_param_init_dynamic_arr(struct kunit *test)
> > +       {
> > +               int seq_size = 6;
> > +               int *fibonacci_params = make_fibonacci_params(seq_size);
> > +
> > +               if (!fibonacci_params)
> > +                       return -ENOMEM;
> > +
> > +               /*
> > +                * Passes the dynamic parameter array information to the parent struct kunit.
> > +                * The array and its metadata will be stored in test->parent->params_data.
> > +                * The array itself will be located in params_data.params.
> > +                */
> > +               kunit_register_params_array(test, fibonacci_params, seq_size,
> > +                                           example_param_dynamic_arr_get_desc);
> > +               return 0;
> > +       }
> > +
> > +       // Function to clean up the parameterized test's parent kunit struct if
> > +       // there were custom allocations.
> > +       static void example_param_exit_dynamic_arr(struct kunit *test)
> > +       {
> > +               /*
> > +                * We allocated this array, so we need to free it.
> > +                * Since the parent parameter instance is passed here,
> > +                * we can directly access the array via `test->params_data.params`
> > +                * instead of `test->parent->params_data.params`.
> > +                */
> > +               kfree(test->params_data.params);
> > +       }
> > +
> > +       /*
> > +        * Example of test that uses the registered dynamic array to perform assertions
> > +        * and expectations.
> > +        */
> > +       static void example_params_test_with_init_dynamic_arr(struct kunit *test)
> > +       {
> > +               const int *param = test->param_value;
> > +               int param_val;
> > +
> > +               /* By design, param pointer will not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +               param_val = *param;
> > +               KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
> > +       }
> > +
> > +       static struct kunit_case example_tests[] = {
> > +               // The NULL here stands in for the generate_params function
> > +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
> > +                                          example_param_init_dynamic_arr,
> > +                                          example_param_exit_dynamic_arr),
> > +               {}
> > +       };
> > +
>
> This is a long example, which already exists in the source code
> (kunit-example-test.c). Could we just include some highlights (e.g.,
> the init function and the KUNIT_CASE_PARAM_WITH_INIT call), and link
> to the source code for the rest?

Thank you for the suggestion! I will do that in v2.

>
> > +Adding Shared Resources
> > +^^^^^^^^^^^^^^^^^^^^^^^
> > +All parameterized test executions in this framework have a parent test of type
> > +``struct kunit``. This parent is not used to execute any test logic itself;
> > +instead, it serves as a container for shared context that can be accessed by
> > +all its individual test executions (or parameters). Therefore, each individual
> > +test execution holds a pointer to this parent, accessible via a field named
> > +``parent``.
> > +
> > +It's possible to add resources to share between the individual test executions
> > +within a parameterized test series by using the ``KUNIT_CASE_PARAM_WITH_INIT``
> > +macro, to which you pass custom ``param_init`` and ``param_exit`` functions.
> > +These functions run once before and once after the entire parameterized test
> > +series, respectively. The ``param_init`` function can be used for adding any
> > +resources to the resources field of a parent test and also provide an additional
> > +way of setting the parameter array. The ``param_exit`` function can be used
> > +release any resources that were not test managed i.e. not automatically cleaned
> > +up after the test ends.
> > +
> > +.. note::
> > +   If both a ``generate_params`` function is passed to ``KUNIT_CASE_PARAM_WITH_INIT``
> > +   and an array is registered via ``kunit_register_params_array`` in
> > +   ``param_init``, the ``generate_params`` function will be used to get
> > +   the parameters.
>
> Maybe note that the ``generate_params`` function can use the array
> passed, though?

Will make this edit in v2

>
> > +
> > +Both ``param_init`` and ``param_exit`` are passed the parent instance of a test
> > +(parent ``struct kunit``) behind the scenes. However, the test case function
> > +receives the individual instance of a test for each parameter. Therefore, to
> > +manage and access shared resources from within a test case function, you must use
> > +``test->parent``.
> > +
> > +.. note::
> > +   The ``suite->init()`` function, which runs before each parameter execution,
> > +   receives the individual instance of a test for each parameter. Therefore,
> > +   resources set up in ``suite->init()`` are reset for each individual
> > +   parameterized test execution and are only visible within that specific test.
> > +
> > +For instance, finding a shared resource allocated by the Resource API requires
> > +passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
> > +all other APIs that might be used in the test case function, including
> > +``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
> > +Documentation/dev-tools/kunit/api/test.rst and the
> > +Documentation/dev-tools/kunit/api/resource.rst).
> > +
> > +The code below shows how you can add the shared resources. Note that this code
> > +utilizes the Resource API, which you can read more about here:
> > +Documentation/dev-tools/kunit/api/resource.rst.
> > +
> > +.. code-block:: c
> > +
> > +       /* An example parameter array. */
> > +       static const struct example_param {
> > +               int value;
> > +       } example_params_array[] = {
> > +               { .value = 3, },
> > +               { .value = 2, },
> > +               { .value = 1, },
> > +               { .value = 0, },
> > +       };
> > +
> > +       /*
> > +        * This custom function allocates memory for the kunit_resource data field.
> > +        * The function is passed to kunit_alloc_resource() and executed once
> > +        * by the internal helper __kunit_add_resource().
> > +        */
> > +       static int example_resource_init(struct kunit_resource *res, void *context)
> > +       {
> > +               int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> > +
> > +               if (!info)
> > +                       return -ENOMEM;
> > +               *info = *(int *)context;
> > +               res->data = info;
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +        * This function deallocates memory for the 'kunit_resource' data field.
> > +        * The function is passed to kunit_alloc_resource() and automatically
> > +        * executes within kunit_release_resource() when the resource's reference
> > +        * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> > +        * counting to ensure that resources are not freed prematurely.
> > +        */
> > +       static void example_resource_free(struct kunit_resource *res)
> > +       {
> > +               kfree(res->data);
> > +       }
> > +
> > +       /*
> > +        * This match function is invoked by kunit_find_resource() to locate
> > +        * a test resource based on defined criteria. The current example
> > +        * uniquely identifies the resource by its free function; however,
> > +        * alternative custom criteria can be implemented. Refer to
> > +        * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> > +        */
> > +       static bool example_resource_alloc_match(struct kunit *test,
> > +                                                struct kunit_resource *res,
> > +                                                void *match_data)
> > +       {
> > +               return res->data && res->free == example_resource_free;
> > +       }
> > +
> > +       /*
> > +        * This is an example of a function that provides a description for each of the
> > +        * parameters.
> > +       */
> > +       static void example_param_array_get_desc(const void *p, char *desc)
> > +       {
> > +               const struct example_param *param = p;
> > +
> > +               snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> > +                       "example check if %d is less than or equal to 3", param->value);
> > +       }
> > +
> > +       /*
> > +        * Initializes the parent kunit struct for parameterized KUnit tests.
> > +        * This function enables sharing resources across all parameterized
> > +        * tests by adding them to the `parent` kunit test struct. It also supports
> > +        * registering either static or dynamic arrays of test parameters.
> > +        */
> > +       static int example_param_init(struct kunit *test)
> > +       {
> > +               int ctx = 3; /* Data to be stored. */
> > +               int arr_size = ARRAY_SIZE(example_params_array);
> > +
> > +               /*
> > +                * This allocates a struct kunit_resource, sets its data field to
> > +                * ctx, and adds it to the kunit struct's resources list. Note that
> > +                * this is test managed so we don't need to have a custom exit function
> > +                * to free it.
> > +                */
> > +               void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> > +                                                 GFP_KERNEL, &ctx);
> > +
> > +               if (!data)
> > +                       return -ENOMEM;
> > +               /* Pass the static param array information to the parent struct kunit. */
> > +               kunit_register_params_array(test, example_params_array, arr_size,
> > +                                           example_param_array_get_desc);
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +       * This is an example of a parameterized test that uses shared resources
> > +       * available from the struct kunit parent field of the kunit struct.
> > +       */
> > +       static void example_params_test_with_init(struct kunit *test)
> > +       {
> > +               int threshold;
> > +               struct kunit_resource *res;
> > +               const struct example_param *param = test->param_value;
> > +
> > +               /* By design, param pointer will not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +               /* Here we need to access the parent pointer of the test to find the shared resource. */
> > +               res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> > +
> > +               KUNIT_ASSERT_NOT_NULL(test, res);
> > +
> > +               /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> > +               threshold = *((int *)res->data);
> > +
> > +               /* Assert that the parameter is less than or equal to a certain threshold. */
> > +               KUNIT_ASSERT_LE(test, param->value, threshold);
> > +
> > +               /* This decreases the reference count after calling kunit_find_resource(). */
> > +               kunit_put_resource(res);
> > +       }
> > +
> > +
> > +       static struct kunit_case example_tests[] = {
> > +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> > +                                          example_param_init, NULL),
> > +               {}
> > +       };
> > +
>
> This is a really long example, which already exists in
> kunit-example-test.c. Can we either link to it there (and just include
> the most critical lines here), or have a smaller, less-complete
> example inline here?

Yes, I will do that in v2.

>
>
> > +As an alternative to using the KUnit Resource API for shared resources, you can
> > +place them in ``test->parent->priv``. It can store data that needs to persist
> > +and be accessible across all executions within a parameterized test series.
> > +
> > +As stated previously ``param_init`` and ``param_exit`` receive the parent
> > +``struct kunit`` instance. So, you can directly use ``test->priv`` within them
> > +to manage shared resources. However, from within the test case function, you must
> > +navigate up to the parent i.e. use ``test->parent->priv`` to access those same
> > +resources.
> > +
> > +The resources placed in ``test->parent-priv`` will also need to be allocated in
> > +memory to persist across the parameterized tests executions. If memory is
>
> Nit: 'parameterized test executions' singular?

Thank you for catching this! Though I will be changing all references to
parameter executions to "parameter runs" to be consistent with the
terminology in v2.

>
> > +allocated using the memory allocation APIs provided by KUnit (described more in
> > +the section below), you will not need to worry about deallocating them as they
> > +will be managed by the parent parameterized test that gets automatically cleaned
> > +up upon the end of the parameterized test series.
> > +
> > +The code below demonstrates example usage of the ``priv`` field for shared
> > +resources:
> > +
> > +.. code-block:: c
> > +
> > +       /* An example parameter array. */
> > +       static const struct example_param {
> > +               int value;
> > +       } example_params_array[] = {
> > +               { .value = 3, },
> > +               { .value = 2, },
> > +               { .value = 1, },
> > +               { .value = 0, },
> > +       };
> > +
> > +       /*
> > +        * Initializes the parent kunit struct for parameterized KUnit tests.
> > +        * This function enables sharing resources across all parameterized
> > +        * tests.
> > +        */
> > +       static int example_param_init_priv(struct kunit *test)
> > +       {
> > +               int ctx = 3; /* Data to be stored. */
> > +               int arr_size = ARRAY_SIZE(example_params_array);
> > +
> > +               /*
> > +                * Allocate memory using kunit_kzalloc(). Since the `param_init`
> > +                * function receives the parent instance of test, this memory
> > +                * allocation will be scoped to the lifetime of the whole
> > +                * parameterized test series.
> > +                */
> > +               test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
> > +
> > +               /* Assign the context value to test->priv.*/
> > +               *((int *)test->priv) = ctx;
> > +
> > +               /* Pass the static param array information to the parent struct kunit. */
> > +               kunit_register_params_array(test, example_params_array, arr_size, NULL);
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +       * This is an example of a parameterized test that uses shared resources
> > +       * available from the struct kunit parent field of the kunit struct.
> > +       */
> > +       static void example_params_test_with_init_priv(struct kunit *test)
> > +       {
> > +               int threshold;
> > +               const struct example_param *param = test->param_value;
> > +
> > +               /* By design, param pointer will not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +               /* By design, test->parent will also not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, test->parent);
> > +
> > +               /* Assert that test->parent->priv has data. */
> > +               KUNIT_ASSERT_NOT_NULL(test, test->parent->priv);
> > +
> > +               /* Here we need to use test->parent->priv to access the shared resource. */
> > +               threshold = *(int *)test->parent->priv;
> > +
> > +               /* Assert that the parameter is less than or equal to a certain threshold. */
> > +               KUNIT_ASSERT_LE(test, param->value, threshold);
> > +       }
> > +
> > +
> > +       static struct kunit_case example_tests[] = {
> > +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv, NULL,
> > +                                          example_param_init_priv, NULL),
> > +               {}
> > +       };
> > +
>
> Again, this is a little long, but it's not as bad as the others, and
> isn't in the example tests, so I'm okay with leaving it. Though maybe
> we could get rid of some of the asserts for the purpose of keeping the
> documentation focused and readable.

Sounds good!


>
>
> >  Allocating Memory
> >  -----------------
> >
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 9/9] Documentation: kunit: Document new parameterized test features
  2025-08-05 15:19   ` Rae Moar
@ 2025-08-08 13:05     ` Marie Zhussupova
  0 siblings, 0 replies; 41+ messages in thread
From: Marie Zhussupova @ 2025-08-08 13:05 UTC (permalink / raw)
  To: Rae Moar
  Cc: davidgow, shuah, brendan.higgins, elver, dvyukov, lucas.demarchi,
	thomas.hellstrom, rodrigo.vivi, linux-kselftest, kunit-dev,
	kasan-dev, intel-xe, dri-devel, linux-kernel

On Tue, Aug 5, 2025 at 11:19 AM Rae Moar <rmoar@google.com> wrote:
>
> On Tue, Jul 29, 2025 at 3:37 PM Marie Zhussupova <marievic@google.com> wrote:
> >
> > -Update the KUnit documentation to explain the concept
> > of a parent parameterized test.
> > -Add examples demonstrating different ways of passing
> > parameters to parameterized tests and how to manage
> > shared resources between them.
> >
> > Signed-off-by: Marie Zhussupova <marievic@google.com>
>
> Hello!
>
> This is amazing! I have a few comments below but I appreciate the
> effort to document this new feature. It is always incredibly helpful
> to have documentation to go along with the code.
>
> Reviewed-by: Rae Moar <rmoar@google.com>
>
> Thanks!
> -Rae
>
> > ---
> >  Documentation/dev-tools/kunit/usage.rst | 455 +++++++++++++++++++++++-
> >  1 file changed, 449 insertions(+), 6 deletions(-)
> >
> > diff --git a/Documentation/dev-tools/kunit/usage.rst b/Documentation/dev-tools/kunit/usage.rst
> > index 066ecda1dd98..be1d656053cf 100644
> > --- a/Documentation/dev-tools/kunit/usage.rst
> > +++ b/Documentation/dev-tools/kunit/usage.rst
> > @@ -542,11 +542,21 @@ There is more boilerplate code involved, but it can:
> >  Parameterized Testing
> >  ~~~~~~~~~~~~~~~~~~~~~
> >
> > -The table-driven testing pattern is common enough that KUnit has special
> > -support for it.
> > -
> > -By reusing the same ``cases`` array from above, we can write the test as a
> > -"parameterized test" with the following.
> > +To efficiently and elegantly validate a test case against a variety of inputs,
> > +KUnit also provides a parameterized testing framework. This feature formalizes
> > +and extends the concept of table-driven tests discussed previously, offering
> > +a more integrated and flexible way to handle multiple test scenarios with
> > +minimal code duplication.
> > +
> > +Passing Parameters to the Test Cases
> > +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> > +There are three main ways to provide the parameters to a test case:
> > +
> > +Array Parameter Macros (``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC``):
> > +   KUnit provides special support for the common table-driven testing pattern.
> > +   By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
> > +   ``cases`` array from the previous section, we can create a parameterized test
> > +   as shown below:
>
> Is it possible to bold the titles of the ways to pass in parameters:
> Array Parameter Macros, etc.? I feel like they should stand out more
> from the rest of the text. Also I think I would prefer if there was an
> empty line between the title and the rest of the indented text, to
> again further separate these titles from the rest of the text.

Bolding may clutter the title a bit for people reading the .rst file.
Adding an empty line is a great idea, I will do that in v2.

>
> >
> >  .. code-block:: c
> >
> > @@ -555,7 +565,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
> >                 const char *str;
> >                 const char *sha1;
> >         };
> > -       const struct sha1_test_case cases[] = {
> > +       static const struct sha1_test_case cases[] = {
> >                 {
> >                         .str = "hello world",
> >                         .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
> > @@ -590,6 +600,439 @@ By reusing the same ``cases`` array from above, we can write the test as a
> >                 {}
> >         };
> >
> > +Custom Parameter Generator (``generate_params``):
> > +   You can pass your own ``generate_params`` function to the ``KUNIT_CASE_PARAM``
> > +   or ``KUNIT_CASE_PARAM_WITH_INIT`` macros. This function is responsible for
> > +   generating parameters one by one. It receives the previously generated parameter
> > +   as the ``prev`` argument (which is ``NULL`` on the first call) and can also
> > +   access any context available from the parent ``struct kunit`` passed as the
> > +   ``test`` argument. KUnit calls this function repeatedly until it returns
> > +   ``NULL``. Below is an example of how it works:
> > +
> > +.. code-block:: c
> > +
> > +       #define MAX_TEST_BUFFER_SIZE 8
> > +
> > +       // Example generator function. It produces a sequence of buffer sizes that
> > +       // are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
> > +       static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
> > +       {
> > +               long prev_buffer_size = (long)prev;
> > +               long next_buffer_size = 1; // Start with an initial size of 1.
> > +
> > +               // Stop generating parameters if the limit is reached or exceeded.
> > +               if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
> > +                       return NULL;
> > +
> > +               // For subsequent calls, calculate the next size by doubling the previous one.
> > +               if (prev)
> > +                       next_buffer_size = prev_buffer_size << 1;
> > +
> > +               return (void *)next_buffer_size;
> > +       }
> > +
> > +       // Simple test to validate that kunit_kzalloc provides zeroed memory.
> > +       static void buffer_zero_test(struct kunit *test)
> > +       {
> > +               long buffer_size = (long)test->param_value;
> > +               // Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
> > +               // memory "parameter managed," meaning it's automatically cleaned up at
> > +               // the end of each parameter execution.
> > +               int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
> > +
> > +               // Ensure the allocation was successful.
> > +               KUNIT_ASSERT_NOT_NULL(test, buf);
> > +
> > +               // Loop through the buffer and confirm every element is zero.
> > +               for (int i = 0; i < buffer_size; i++)
> > +                       KUNIT_EXPECT_EQ(test, buf[i], 0);
> > +       }
> > +
> > +       static struct kunit_case buffer_test_cases[] = {
> > +               KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
> > +               {}
> > +       };
> > +
> > +Direct Registration in Parameter Init Function (using ``kunit_register_params_array``):
> > +   For more complex scenarios, you can directly register a parameter array with
> > +   a test case instead of using a ``generate_params`` function. This is done by
> > +   passing the array to the ``kunit_register_params_array`` macro within an
> > +   initialization function for the parameterized test series
> > +   (i.e., a function named ``param_init``). To better understand this mechanism
> > +   please refer to the "Adding Shared Resources" section below.
> > +
> > +   This method supports both dynamically built and static arrays.
> > +
> > +   As the following code shows, the ``example_param_init_dynamic_arr`` function
> > +   utilizes ``make_fibonacci_params`` to create a dynamic array, which is then
> > +   registered using ``kunit_register_params_array``. The corresponding exit
> > +   function, ``example_param_exit``, is responsible for freeing this dynamically
> > +   allocated params array after the parameterized test series ends.
> > +
> > +.. code-block:: c
>
> As David mentioned, this example code is a bit long. I would also
> prefer if this example had just the highlights and then a link to the
> source code.
>

I agree it's a bit long. I will do highlights and a link to the source code
in v2.

> > +
> > +       /*
> > +        * Helper function to create a parameter array of Fibonacci numbers. This example
> > +        * highlights a parameter generation scenario that is:
> > +        * 1. Not feasible to fully pre-generate at compile time.
> > +        * 2. Challenging to implement with a standard 'generate_params' function,
> > +        * as it typically only provides the immediately 'prev' parameter, while
> > +        * Fibonacci requires access to two preceding values for calculation.
> > +        */
> > +       static void *make_fibonacci_params(int seq_size)
> > +       {
> > +               int *seq;
> > +
> > +               if (seq_size <= 0)
> > +                       return NULL;
> > +
> > +               seq = kmalloc_array(seq_size, sizeof(int), GFP_KERNEL);
> > +
> > +               if (!seq)
> > +                       return NULL;
> > +
> > +               if (seq_size >= 1)
> > +                       seq[0] = 0;
> > +               if (seq_size >= 2)
> > +                       seq[1] = 1;
> > +               for (int i = 2; i < seq_size; i++)
> > +                       seq[i] = seq[i - 1] + seq[i - 2];
> > +               return seq;
> > +       }
> > +
> > +       // This is an example of a function that provides a description for each of the
> > +       // parameters.
> > +       static void example_param_dynamic_arr_get_desc(const void *p, char *desc)
> > +       {
> > +               const int *fib_num = p;
> > +
> > +               snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
> > +       }
> > +
> > +       // Example of a parameterized test init function that registers a dynamic array.
> > +       static int example_param_init_dynamic_arr(struct kunit *test)
> > +       {
> > +               int seq_size = 6;
> > +               int *fibonacci_params = make_fibonacci_params(seq_size);
> > +
> > +               if (!fibonacci_params)
> > +                       return -ENOMEM;
> > +
> > +               /*
> > +                * Passes the dynamic parameter array information to the parent struct kunit.
> > +                * The array and its metadata will be stored in test->parent->params_data.
> > +                * The array itself will be located in params_data.params.
> > +                */
> > +               kunit_register_params_array(test, fibonacci_params, seq_size,
> > +                                           example_param_dynamic_arr_get_desc);
> > +               return 0;
> > +       }
> > +
> > +       // Function to clean up the parameterized test's parent kunit struct if
> > +       // there were custom allocations.
> > +       static void example_param_exit_dynamic_arr(struct kunit *test)
> > +       {
> > +               /*
> > +                * We allocated this array, so we need to free it.
> > +                * Since the parent parameter instance is passed here,
> > +                * we can directly access the array via `test->params_data.params`
> > +                * instead of `test->parent->params_data.params`.
> > +                */
> > +               kfree(test->params_data.params);
> > +       }
> > +
> > +       /*
> > +        * Example of test that uses the registered dynamic array to perform assertions
> > +        * and expectations.
> > +        */
> > +       static void example_params_test_with_init_dynamic_arr(struct kunit *test)
> > +       {
> > +               const int *param = test->param_value;
> > +               int param_val;
> > +
> > +               /* By design, param pointer will not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +               param_val = *param;
> > +               KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
> > +       }
> > +
> > +       static struct kunit_case example_tests[] = {
> > +               // The NULL here stands in for the generate_params function
> > +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr, NULL,
> > +                                          example_param_init_dynamic_arr,
> > +                                          example_param_exit_dynamic_arr),
> > +               {}
> > +       };
> > +
> > +
> > +Adding Shared Resources
> > +^^^^^^^^^^^^^^^^^^^^^^^
> > +All parameterized test executions in this framework have a parent test of type
> > +``struct kunit``. This parent is not used to execute any test logic itself;
> > +instead, it serves as a container for shared context that can be accessed by
> > +all its individual test executions (or parameters). Therefore, each individual
> > +test execution holds a pointer to this parent, accessible via a field named
> > +``parent``.
> > +
> > +It's possible to add resources to share between the individual test executions
> > +within a parameterized test series by using the ``KUNIT_CASE_PARAM_WITH_INIT``
> > +macro, to which you pass custom ``param_init`` and ``param_exit`` functions.
> > +These functions run once before and once after the entire parameterized test
> > +series, respectively. The ``param_init`` function can be used for adding any
> > +resources to the resources field of a parent test and also provide an additional
> > +way of setting the parameter array. The ``param_exit`` function can be used
> > +release any resources that were not test managed i.e. not automatically cleaned
> > +up after the test ends.
> > +
> > +.. note::
> > +   If both a ``generate_params`` function is passed to ``KUNIT_CASE_PARAM_WITH_INIT``
> > +   and an array is registered via ``kunit_register_params_array`` in
> > +   ``param_init``, the ``generate_params`` function will be used to get
> > +   the parameters.
> > +
> > +Both ``param_init`` and ``param_exit`` are passed the parent instance of a test
> > +(parent ``struct kunit``) behind the scenes. However, the test case function
> > +receives the individual instance of a test for each parameter. Therefore, to
> > +manage and access shared resources from within a test case function, you must use
> > +``test->parent``.
> > +
> > +.. note::
> > +   The ``suite->init()`` function, which runs before each parameter execution,
> > +   receives the individual instance of a test for each parameter. Therefore,
> > +   resources set up in ``suite->init()`` are reset for each individual
> > +   parameterized test execution and are only visible within that specific test.
> > +
> > +For instance, finding a shared resource allocated by the Resource API requires
> > +passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
> > +all other APIs that might be used in the test case function, including
> > +``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
> > +Documentation/dev-tools/kunit/api/test.rst and the
> > +Documentation/dev-tools/kunit/api/resource.rst).
> > +
> > +The code below shows how you can add the shared resources. Note that this code
> > +utilizes the Resource API, which you can read more about here:
> > +Documentation/dev-tools/kunit/api/resource.rst.
>
> It would be nice if these references to the Documentation files were
> actual links to the webpages. This would look something like -
> ":ref:`kunit-resource`" and then also labeling that section: "..
> _kunit-resource:".

Thank you for this suggestion, I will do that in v2.

>
> > +
> > +.. code-block:: c
> > +
> > +       /* An example parameter array. */
> > +       static const struct example_param {
> > +               int value;
> > +       } example_params_array[] = {
> > +               { .value = 3, },
> > +               { .value = 2, },
> > +               { .value = 1, },
> > +               { .value = 0, },
> > +       };
> > +
> > +       /*
> > +        * This custom function allocates memory for the kunit_resource data field.
> > +        * The function is passed to kunit_alloc_resource() and executed once
> > +        * by the internal helper __kunit_add_resource().
> > +        */
> > +       static int example_resource_init(struct kunit_resource *res, void *context)
> > +       {
> > +               int *info = kmalloc(sizeof(*info), GFP_KERNEL);
> > +
> > +               if (!info)
> > +                       return -ENOMEM;
> > +               *info = *(int *)context;
> > +               res->data = info;
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +        * This function deallocates memory for the 'kunit_resource' data field.
> > +        * The function is passed to kunit_alloc_resource() and automatically
> > +        * executes within kunit_release_resource() when the resource's reference
> > +        * count, via kunit_put_resource(), drops to zero. KUnit uses reference
> > +        * counting to ensure that resources are not freed prematurely.
> > +        */
> > +       static void example_resource_free(struct kunit_resource *res)
> > +       {
> > +               kfree(res->data);
> > +       }
> > +
> > +       /*
> > +        * This match function is invoked by kunit_find_resource() to locate
> > +        * a test resource based on defined criteria. The current example
> > +        * uniquely identifies the resource by its free function; however,
> > +        * alternative custom criteria can be implemented. Refer to
> > +        * lib/kunit/platform.c and lib/kunit/static_stub.c for further examples.
> > +        */
> > +       static bool example_resource_alloc_match(struct kunit *test,
> > +                                                struct kunit_resource *res,
> > +                                                void *match_data)
> > +       {
> > +               return res->data && res->free == example_resource_free;
> > +       }
> > +
> > +       /*
> > +        * This is an example of a function that provides a description for each of the
> > +        * parameters.
> > +       */
> > +       static void example_param_array_get_desc(const void *p, char *desc)
> > +       {
> > +               const struct example_param *param = p;
> > +
> > +               snprintf(desc, KUNIT_PARAM_DESC_SIZE,
> > +                       "example check if %d is less than or equal to 3", param->value);
> > +       }
> > +
> > +       /*
> > +        * Initializes the parent kunit struct for parameterized KUnit tests.
> > +        * This function enables sharing resources across all parameterized
> > +        * tests by adding them to the `parent` kunit test struct. It also supports
> > +        * registering either static or dynamic arrays of test parameters.
> > +        */
> > +       static int example_param_init(struct kunit *test)
> > +       {
> > +               int ctx = 3; /* Data to be stored. */
> > +               int arr_size = ARRAY_SIZE(example_params_array);
> > +
> > +               /*
> > +                * This allocates a struct kunit_resource, sets its data field to
> > +                * ctx, and adds it to the kunit struct's resources list. Note that
> > +                * this is test managed so we don't need to have a custom exit function
> > +                * to free it.
> > +                */
> > +               void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
> > +                                                 GFP_KERNEL, &ctx);
> > +
> > +               if (!data)
> > +                       return -ENOMEM;
> > +               /* Pass the static param array information to the parent struct kunit. */
> > +               kunit_register_params_array(test, example_params_array, arr_size,
> > +                                           example_param_array_get_desc);
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +       * This is an example of a parameterized test that uses shared resources
> > +       * available from the struct kunit parent field of the kunit struct.
> > +       */
> > +       static void example_params_test_with_init(struct kunit *test)
> > +       {
> > +               int threshold;
> > +               struct kunit_resource *res;
> > +               const struct example_param *param = test->param_value;
> > +
> > +               /* By design, param pointer will not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +               /* Here we need to access the parent pointer of the test to find the shared resource. */
> > +               res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
> > +
> > +               KUNIT_ASSERT_NOT_NULL(test, res);
> > +
> > +               /* Since the data field in kunit_resource is a void pointer we need to typecast it. */
> > +               threshold = *((int *)res->data);
> > +
> > +               /* Assert that the parameter is less than or equal to a certain threshold. */
> > +               KUNIT_ASSERT_LE(test, param->value, threshold);
> > +
> > +               /* This decreases the reference count after calling kunit_find_resource(). */
> > +               kunit_put_resource(res);
> > +       }
> > +
> > +
> > +       static struct kunit_case example_tests[] = {
> > +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, NULL,
> > +                                          example_param_init, NULL),
> > +               {}
> > +       };
> > +
> > +As an alternative to using the KUnit Resource API for shared resources, you can
> > +place them in ``test->parent->priv``. It can store data that needs to persist
> > +and be accessible across all executions within a parameterized test series.
> > +
> > +As stated previously ``param_init`` and ``param_exit`` receive the parent
> > +``struct kunit`` instance. So, you can directly use ``test->priv`` within them
> > +to manage shared resources. However, from within the test case function, you must
> > +navigate up to the parent i.e. use ``test->parent->priv`` to access those same
> > +resources.
> > +
> > +The resources placed in ``test->parent-priv`` will also need to be allocated in
>
> Nit: I think this is a typo for test->parent->priv.
>
Thank you for catching this!

>
>
> > +memory to persist across the parameterized tests executions. If memory is
> > +allocated using the memory allocation APIs provided by KUnit (described more in
> > +the section below), you will not need to worry about deallocating them as they
> > +will be managed by the parent parameterized test that gets automatically cleaned
> > +up upon the end of the parameterized test series.
> > +
> > +The code below demonstrates example usage of the ``priv`` field for shared
> > +resources:
> > +
> > +.. code-block:: c
> > +
> > +       /* An example parameter array. */
> > +       static const struct example_param {
> > +               int value;
> > +       } example_params_array[] = {
> > +               { .value = 3, },
> > +               { .value = 2, },
> > +               { .value = 1, },
> > +               { .value = 0, },
> > +       };
> > +
> > +       /*
> > +        * Initializes the parent kunit struct for parameterized KUnit tests.
> > +        * This function enables sharing resources across all parameterized
> > +        * tests.
> > +        */
> > +       static int example_param_init_priv(struct kunit *test)
> > +       {
> > +               int ctx = 3; /* Data to be stored. */
> > +               int arr_size = ARRAY_SIZE(example_params_array);
> > +
> > +               /*
> > +                * Allocate memory using kunit_kzalloc(). Since the `param_init`
> > +                * function receives the parent instance of test, this memory
> > +                * allocation will be scoped to the lifetime of the whole
> > +                * parameterized test series.
> > +                */
> > +               test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
> > +
> > +               /* Assign the context value to test->priv.*/
> > +               *((int *)test->priv) = ctx;
> > +
> > +               /* Pass the static param array information to the parent struct kunit. */
> > +               kunit_register_params_array(test, example_params_array, arr_size, NULL);
> > +               return 0;
> > +       }
> > +
> > +       /*
> > +       * This is an example of a parameterized test that uses shared resources
> > +       * available from the struct kunit parent field of the kunit struct.
> > +       */
> > +       static void example_params_test_with_init_priv(struct kunit *test)
> > +       {
> > +               int threshold;
> > +               const struct example_param *param = test->param_value;
> > +
> > +               /* By design, param pointer will not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, param);
> > +
> > +               /* By design, test->parent will also not be NULL. */
> > +               KUNIT_ASSERT_NOT_NULL(test, test->parent);
> > +
> > +               /* Assert that test->parent->priv has data. */
> > +               KUNIT_ASSERT_NOT_NULL(test, test->parent->priv);
> > +
> > +               /* Here we need to use test->parent->priv to access the shared resource. */
> > +               threshold = *(int *)test->parent->priv;
> > +
> > +               /* Assert that the parameter is less than or equal to a certain threshold. */
> > +               KUNIT_ASSERT_LE(test, param->value, threshold);
> > +       }
> > +
> > +
> > +       static struct kunit_case example_tests[] = {
> > +               KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv, NULL,
> > +                                          example_param_init_priv, NULL),
> > +               {}
> > +       };
> > +
> >  Allocating Memory
> >  -----------------
> >
> > --
> > 2.50.1.552.g942d659e1b-goog
> >

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2025-08-08 13:05 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-29 19:36 [PATCH 0/9] kunit: Refactor and extend KUnit's Marie Zhussupova
2025-07-29 19:36 ` [PATCH 1/9] kunit: Add parent kunit for parameterized test context Marie Zhussupova
2025-08-02  9:44   ` David Gow
2025-08-05 15:17   ` Rae Moar
2025-08-08 12:20     ` Marie Zhussupova
2025-07-29 19:36 ` [PATCH 2/9] kunit: Introduce param_init/exit for parameterized test shared context management Marie Zhussupova
2025-07-30 13:50   ` kernel test robot
2025-08-02  9:44   ` David Gow
2025-08-05 15:17   ` Rae Moar
2025-08-08 13:01     ` Marie Zhussupova
2025-07-29 19:36 ` [PATCH 3/9] kunit: Pass additional context to generate_params for parameterized testing Marie Zhussupova
2025-07-30 14:32   ` kernel test robot
2025-08-02  9:44   ` David Gow
2025-08-05 15:18   ` Rae Moar
2025-07-29 19:36 ` [PATCH 4/9] kcsan: test: Update parameter generator to new signature Marie Zhussupova
2025-08-02  9:44   ` David Gow
2025-08-07 11:11     ` Marco Elver
2025-07-29 19:36 ` [PATCH 5/9] drm/xe: " Marie Zhussupova
2025-08-02  9:44   ` David Gow
2025-08-07 10:59     ` Lucas De Marchi
2025-07-29 19:36 ` [PATCH 6/9] kunit: Enable direct registration of parameter arrays to a KUnit test Marie Zhussupova
2025-07-29 20:14   ` Rae Moar
2025-07-29 20:26     ` Rae Moar
2025-07-31 15:58   ` Dan Carpenter
2025-07-31 20:01     ` Marie Zhussupova
2025-08-02  9:44   ` David Gow
2025-08-05 15:18   ` Rae Moar
2025-08-08  1:22     ` Marie Zhussupova
2025-07-29 19:36 ` [PATCH 7/9] kunit: Add example parameterized test with shared resources and direct static parameter array setup Marie Zhussupova
2025-07-30 12:25   ` kernel test robot
2025-08-02  9:44   ` David Gow
2025-08-06 17:50     ` Marie Zhussupova
2025-08-05 15:19   ` Rae Moar
2025-07-29 19:36 ` [PATCH 8/9] kunit: Add example parameterized test with direct dynamic " Marie Zhussupova
2025-08-02  9:44   ` David Gow
2025-08-05 15:19   ` Rae Moar
2025-07-29 19:36 ` [PATCH 9/9] Documentation: kunit: Document new parameterized test features Marie Zhussupova
2025-08-02  9:45   ` David Gow
2025-08-08 13:02     ` Marie Zhussupova
2025-08-05 15:19   ` Rae Moar
2025-08-08 13:05     ` Marie Zhussupova

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).