]> www.infradead.org Git - users/hch/misc.git/commitdiff
kunit: Enable direct registration of parameter arrays to a KUnit test
authorMarie Zhussupova <marievic@google.com>
Tue, 26 Aug 2025 09:13:34 +0000 (17:13 +0800)
committerShuah Khan <skhan@linuxfoundation.org>
Wed, 27 Aug 2025 05:36:03 +0000 (23:36 -0600)
KUnit parameterized tests currently support two primary methods f
or getting parameters:
1.  Defining custom logic within a generate_params() function.
2.  Using the KUNIT_ARRAY_PARAM() and KUNIT_ARRAY_PARAM_DESC()
    macros with a pre-defined static array and passing
    the created *_gen_params() to KUNIT_CASE_PARAM().

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_array`
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_array` field can be populated by calling the new
kunit_register_params_array() macro from within a param_init() function.
This will register the array as part of the parameterized test context.
The user will then need to pass kunit_array_gen_params() to the
KUNIT_CASE_PARAM_WITH_INIT() macro as the generator function, if not
providing their own. kunit_array_gen_params() is a KUnit helper that will
use the registered array to generate parameters.

The arrays passed to KUNIT_ARRAY_PARAM(,DESC) will also be registered to
the parameterized test context for consistency as well as for higher
availability of the parameter count that will be used for outputting a KTAP
test plan for a parameterized test.

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

Link: https://lore.kernel.org/r/20250826091341.1427123-5-davidgow@google.com
Reviewed-by: David Gow <davidgow@google.com>
Reviewed-by: Rae Moar <rmoar@google.com>
Signed-off-by: Marie Zhussupova <marievic@google.com>
[Only output the test plan if using kunit_array_gen_params --David]
Signed-off-by: David Gow <davidgow@google.com>
Signed-off-by: Shuah Khan <skhan@linuxfoundation.org>
include/kunit/test.h
lib/kunit/test.c

index 8eba1b03c3e3921740fd760fc1993d26dd34787c..5ec5182b5e5751751045269e14150c7c96ddc06f 100644 (file)
@@ -234,9 +234,13 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
  * Provides the option to register param_init() and param_exit() functions.
  * param_init/exit will be passed the parameterized test context and run once
  * before and once after the parameterized test. The init function can be used
- * to add resources to share between parameter runs, and any other setup logic.
- * The exit function can be used to clean up resources that were not managed by
- * the parameterized test, and any other teardown logic.
+ * to add resources to share between parameter runs, pass parameter arrays,
+ * and any other setup logic. The exit function can be used to clean up resources
+ * that were not managed by the parameterized test, and any other teardown logic.
+ *
+ * Note: If you are registering a parameter array in param_init() with
+ * kunit_register_param_array() then you need to pass kunit_array_gen_params()
+ * to this as the generator function.
  */
 #define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit)          \
                { .run_case = test_name, .name = #test_name,                    \
@@ -289,6 +293,20 @@ 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 a parameterized test. This
+        * is NULL if a parameter array wasn't directly passed to the
+        * parameterized test context struct kunit via kunit_register_params_array().
+        */
+       const void *params;
+       /* Reference to a function that gets the description of a parameter. */
+       void (*get_description)(struct kunit *test, const void *param, char *desc);
+       size_t num_params;
+       size_t elem_size;
+};
+
 /**
  * struct kunit - represents a running instance of a test.
  *
@@ -296,16 +314,18 @@ struct kunit_suite_set {
  *       created in the init function (see &struct kunit_suite).
  * @parent: reference to the parent context of type struct kunit that can
  *         be used for storing shared resources.
+ * @params_array: for storing 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 and access the
- * parent context, respectively.
+ * indirectly via public functions; the exceptions are @priv, @parent and
+ * @params_array which can be used by the test writer to store arbitrary data,
+ * access the parent context, and to store the parameter array, respectively.
  */
 struct kunit {
        void *priv;
        struct kunit *parent;
+       struct kunit_params params_array;
 
        /* private: internal use only. */
        const char *name; /* Read only after initialization! */
@@ -376,6 +396,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_array_gen_params(struct kunit *test, const void *prev, char *desc);
+
 #if IS_BUILTIN(CONFIG_KUNIT)
 int kunit_run_all_tests(void);
 #else
@@ -1708,6 +1730,8 @@ do {                                                                             \
                                             const void *prev, char *desc)                      \
        {                                                                                       \
                typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
+               if (!prev)                                                                      \
+                       kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL);      \
                if (__next - (array) < ARRAY_SIZE((array))) {                                   \
                        void (*__get_desc)(typeof(__next), char *) = get_desc;                  \
                        if (__get_desc)                                                         \
@@ -1730,6 +1754,8 @@ do {                                                                             \
                                             const void *prev, char *desc)                      \
        {                                                                                       \
                typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array);      \
+               if (!prev)                                                                      \
+                       kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL);      \
                if (__next - (array) < ARRAY_SIZE((array))) {                                   \
                        strscpy(desc, __next->desc_member, KUNIT_PARAM_DESC_SIZE);              \
                        return __next;                                                          \
@@ -1737,6 +1763,33 @@ do {                                                                            \
                return NULL;                                                                    \
        }
 
+/**
+ * kunit_register_params_array() - Register parameter array for a KUnit test.
+ * @test: The KUnit test structure to which parameters will be added.
+ * @array: An array of test parameters.
+ * @param_count: Number of parameters.
+ * @get_desc: 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_array`.
+ *
+ * Note: If using this macro in param_init(), kunit_array_gen_params()
+ * will then need to be manually provided as the parameter generator function to
+ * KUNIT_CASE_PARAM_WITH_INIT(). kunit_array_gen_params() is a KUnit
+ * function that uses the registered array to generate parameters
+ */
+#define kunit_register_params_array(test, array, param_count, get_desc)                                \
+       do {                                                                                    \
+               struct kunit *_test = (test);                                                   \
+               const typeof((array)[0]) * _params_ptr = &(array)[0];                           \
+               _test->params_array.params = _params_ptr;                                       \
+               _test->params_array.num_params = (param_count);                                 \
+               _test->params_array.elem_size = sizeof(*_params_ptr);                           \
+               _test->params_array.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>
index 50705248abad36b21054b3a74ab2e96fea5018b1..bb66ea1a3eaccf6537b8985f36df58e930134a43 100644 (file)
@@ -337,6 +337,14 @@ 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_array.params = NULL;
+       test->params_array.get_description = NULL;
+       test->params_array.num_params = 0;
+       test->params_array.elem_size = 0;
+}
+
 void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
 {
        spin_lock_init(&test->lock);
@@ -347,6 +355,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 +650,23 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
        total->total += add.total;
 }
 
+const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc)
+{
+       struct kunit_params *params_arr = &test->params_array;
+       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(test, param, desc);
+               return param;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(kunit_array_gen_params);
+
 static void kunit_init_parent_param_test(struct kunit_case *test_case, struct kunit *test)
 {
        if (test_case->param_init) {
@@ -706,6 +732,12 @@ int kunit_run_tests(struct kunit_suite *suite)
                                  "KTAP version 1\n");
                        kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
                                  "# Subtest: %s", test_case->name);
+                       if (test.params_array.params &&
+                           test_case->generate_params == kunit_array_gen_params) {
+                               kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT
+                                         KUNIT_SUBTEST_INDENT "1..%zd\n",
+                                         test.params_array.num_params);
+                       }
 
                        while (curr_param) {
                                struct kunit param_test = {