History

Merge tag 'linux_kselftest-kunit-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest

Pull kunit updates from Shuah Khan: - New parameterized test features KUnit parameterized tests supported two primary methods for getting parameters: - Defining custom logic within a generate_params() function. - 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. These limitations are fixed with a parameterized test method - Fix issues in kunit build artifacts cleanup - Fix parsing skipped test problem in kselftest framework - Enable PCI on UML without triggering WARN() - a few other fixes and adds support for new configs such as MIPS * tag 'linux_kselftest-kunit-6.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: kunit: Extend kconfig help text for KUNIT_UML_PCI rust: kunit: allow `cfg` on `test`s kunit: qemu_configs: Add MIPS configurations kunit: Enable PCI on UML without triggering WARN() Documentation: kunit: Document new parameterized test features kunit: Add example parameterized test with direct dynamic parameter array setup kunit: Add example parameterized test with shared resource management using the Resource API kunit: Enable direct registration of parameter arrays to a KUnit test kunit: Pass parameterized test context to generate_params() kunit: Introduce param_init/exit for parameterized test context management kunit: Add parent kunit for parameterized test context kunit: tool: Accept --raw_output=full as an alias of 'all' kunit: tool: Parse skipped tests from kselftest.h kunit: Always descend into kunit directory during build

Linus Torvalds committed 1 year ago

Commit 30bbcb42 parents d2b2fea 285cae520 files · +880 −63Browse files

Unified · Split

20 files
  1. Documentation/dev-tools/kunit/usage.rst +337 −5
  2. drivers/gpu/drm/xe/tests/xe_pci.c +7 −7
  3. drivers/gpu/drm/xe/tests/xe_pci_test.h +5 −4
  4. include/kunit/test.h +90 −5
  5. kernel/kcsan/kcsan_test.c +1 −1
  6. lib/Makefile +0 −4
  7. lib/kunit/Kconfig +11 −0
  8. lib/kunit/Makefile +1 −1
  9. lib/kunit/kunit-example-test.c +217 −0
  10. lib/kunit/test.c +79 −15
  11. rust/kernel/kunit.rs +11 −0
  12. rust/macros/kunit.rs +36 −12
  13. tools/testing/kunit/configs/arch_uml.config +2 −3
  14. tools/testing/kunit/kunit.py +2 −2
  15. tools/testing/kunit/kunit_parser.py +5 −3
  16. tools/testing/kunit/qemu_configs/mips.py +18 −0
  17. tools/testing/kunit/qemu_configs/mips64.py +19 −0
  18. tools/testing/kunit/qemu_configs/mips64el.py +19 −0
  19. tools/testing/kunit/qemu_configs/mipsel.py +18 −0
  20. tools/testing/kunit/test_data/test_is_test_passed-kselftest.log +2 −1
Documentation/dev-tools/kunit/usage.rst+337−5
@@ -542,11 +542,31 @@ There is more boilerplate code involved, but it can: · 541 unmodified lines
542542 Parameterized Testing
543543 ~~~~~~~~~~~~~~~~~~~~~
544544
545−The table-driven testing pattern is common enough that KUnit has special
546−support for it.
545+To run a test case against multiple inputs, KUnit provides a parameterized
546+testing framework. This feature formalizes and extends the concept of
547+table-driven tests discussed previously.
547548
548−By reusing the same ``cases`` array from above, we can write the test as a
549−"parameterized test" with the following.
549+A KUnit test is determined to be parameterized if a parameter generator function
550+is provided when registering the test case. A test user can either write their
551+own generator function or use one that is provided by KUnit. The generator
552+function is stored in ``kunit_case->generate_params`` and can be set using the
553+macros described in the section below.
554+
555+To establish the terminology, a "parameterized test" is a test which is run
556+multiple times (once per "parameter" or "parameter run"). Each parameter run has
557+both its own independent ``struct kunit`` (the "parameter run context") and
558+access to a shared parent ``struct kunit`` (the "parameterized test context").
559+
560+Passing Parameters to a Test
561+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
562+There are three ways to provide the parameters to a test:
563+
564+Array Parameter Macros:
565+
566+ KUnit provides special support for the common table-driven testing pattern.
567+ By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
568+ ``cases`` array from the previous section, we can create a parameterized test
569+ as shown below:
550570
551571 .. code-block:: c
552572
@@ -555,7 +575,7 @@ By reusing the same ``cases`` array from above, we can write the test as a · 2 unmodified lines
555575 const char *str;
556576 const char *sha1;
557577 };
558− const struct sha1_test_case cases[] = {
578+ static const struct sha1_test_case cases[] = {
559579 {
560580 .str = "hello world",
561581 .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
@@ -590,6 +610,318 @@ By reusing the same ``cases`` array from above, we can write the test as a · 28 unmodified lines
590610 {}
591611 };
592612
613+Custom Parameter Generator Function:
614+
615+ The generator function is responsible for generating parameters one-by-one
616+ and has the following signature:
617+ ``const void* (*)(struct kunit *test, const void *prev, char *desc)``.
618+ You can pass the generator function to the ``KUNIT_CASE_PARAM``
619+ or ``KUNIT_CASE_PARAM_WITH_INIT`` macros.
620+
621+ The function receives the previously generated parameter as the ``prev`` argument
622+ (which is ``NULL`` on the first call) and can also access the parameterized
623+ test context passed as the ``test`` argument. KUnit calls this function
624+ repeatedly until it returns ``NULL``, which signifies that a parameterized
625+ test ended.
626+
627+ Below is an example of how it works:
628+
629+.. code-block:: c
630+
631+ #define MAX_TEST_BUFFER_SIZE 8
632+
633+ // Example generator function. It produces a sequence of buffer sizes that
634+ // are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
635+ static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
636+ {
637+ long prev_buffer_size = (long)prev;
638+ long next_buffer_size = 1; // Start with an initial size of 1.
639+
640+ // Stop generating parameters if the limit is reached or exceeded.
641+ if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
642+ return NULL;
643+
644+ // For subsequent calls, calculate the next size by doubling the previous one.
645+ if (prev)
646+ next_buffer_size = prev_buffer_size << 1;
647+
648+ return (void *)next_buffer_size;
649+ }
650+
651+ // Simple test to validate that kunit_kzalloc provides zeroed memory.
652+ static void buffer_zero_test(struct kunit *test)
653+ {
654+ long buffer_size = (long)test->param_value;
655+ // Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
656+ // memory "parameter run managed," meaning it's automatically cleaned up at
657+ // the end of each parameter run.
658+ int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
659+
660+ // Ensure the allocation was successful.
661+ KUNIT_ASSERT_NOT_NULL(test, buf);
662+
663+ // Loop through the buffer and confirm every element is zero.
664+ for (int i = 0; i < buffer_size; i++)
665+ KUNIT_EXPECT_EQ(test, buf[i], 0);
666+ }
667+
668+ static struct kunit_case buffer_test_cases[] = {
669+ KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
670+ {}
671+ };
672+
673+Runtime Parameter Array Registration in the Init Function:
674+
675+ For scenarios where you might need to initialize a parameterized test, you
676+ can directly register a parameter array to the parameterized test context.
677+
678+ To do this, you must pass the parameterized test context, the array itself,
679+ the array size, and a ``get_description()`` function to the
680+ ``kunit_register_params_array()`` macro. This macro populates
681+ ``struct kunit_params`` within the parameterized test context, effectively
682+ storing a parameter array object. The ``get_description()`` function will
683+ be used for populating parameter descriptions and has the following signature:
684+ ``void (*)(struct kunit *test, const void *param, char *desc)``. Note that it
685+ also has access to the parameterized test context.
686+
687+ .. important::
688+ When using this way to register a parameter array, you will need to
689+ manually pass ``kunit_array_gen_params()`` as the generator function to
690+ ``KUNIT_CASE_PARAM_WITH_INIT``. ``kunit_array_gen_params()`` is a KUnit
691+ helper that will use the registered array to generate the parameters.
692+
693+ If needed, instead of passing the KUnit helper, you can also pass your
694+ own custom generator function that utilizes the parameter array. To
695+ access the parameter array from within the parameter generator
696+ function use ``test->params_array.params``.
697+
698+ The ``kunit_register_params_array()`` macro should be called within a
699+ ``param_init()`` function that initializes the parameterized test and has
700+ the following signature ``int (*)(struct kunit *test)``. For a detailed
701+ explanation of this mechanism please refer to the "Adding Shared Resources"
702+ section that is after this one. This method supports registering both
703+ dynamically built and static parameter arrays.
704+
705+ The code snippet below shows the ``example_param_init_dynamic_arr`` test that
706+ utilizes ``make_fibonacci_params()`` to create a dynamic array, which is then
707+ registered using ``kunit_register_params_array()``. To see the full code
708+ please refer to lib/kunit/kunit-example-test.c.
709+
710+.. code-block:: c
711+
712+ /*
713+ * Example of a parameterized test param_init() function that registers a dynamic
714+ * array of parameters.
715+ */
716+ static int example_param_init_dynamic_arr(struct kunit *test)
717+ {
718+ size_t seq_size;
719+ int *fibonacci_params;
720+
721+ kunit_info(test, "initializing parameterized test\n");
722+
723+ seq_size = 6;
724+ fibonacci_params = make_fibonacci_params(test, seq_size);
725+ if (!fibonacci_params)
726+ return -ENOMEM;
727+ /*
728+ * Passes the dynamic parameter array information to the parameterized test
729+ * context struct kunit. The array and its metadata will be stored in
730+ * test->parent->params_array. The array itself will be located in
731+ * params_data.params.
732+ */
733+ kunit_register_params_array(test, fibonacci_params, seq_size,
734+ example_param_dynamic_arr_get_desc);
735+ return 0;
736+ }
737+
738+ static struct kunit_case example_test_cases[] = {
739+ /*
740+ * Note how we pass kunit_array_gen_params() to use the array we
741+ * registered in example_param_init_dynamic_arr() to generate
742+ * parameters.
743+ */
744+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr,
745+ kunit_array_gen_params,
746+ example_param_init_dynamic_arr,
747+ example_param_exit_dynamic_arr),
748+ {}
749+ };
750+
751+Adding Shared Resources
752+^^^^^^^^^^^^^^^^^^^^^^^
753+All parameter runs in this framework hold a reference to the parameterized test
754+context, which can be accessed using the parent ``struct kunit`` pointer. The
755+parameterized test context is not used to execute any test logic itself; instead,
756+it serves as a container for shared resources.
757+
758+It's possible to add resources to share between parameter runs within a
759+parameterized test by using ``KUNIT_CASE_PARAM_WITH_INIT``, to which you pass
760+custom ``param_init()`` and ``param_exit()`` functions. These functions run once
761+before and once after the parameterized test, respectively.
762+
763+The ``param_init()`` function, with the signature ``int (*)(struct kunit *test)``,
764+can be used for adding resources to the ``resources`` or ``priv`` fields of
765+the parameterized test context, registering the parameter array, and any other
766+initialization logic.
767+
768+The ``param_exit()`` function, with the signature ``void (*)(struct kunit *test)``,
769+can be used to release any resources that were not parameterized test managed (i.e.
770+not automatically cleaned up after the parameterized test ends) and for any other
771+exit logic.
772+
773+Both ``param_init()`` and ``param_exit()`` are passed the parameterized test
774+context behind the scenes. However, the test case function receives the parameter
775+run context. Therefore, to manage and access shared resources from within a test
776+case function, you must use ``test->parent``.
777+
778+For instance, finding a shared resource allocated by the Resource API requires
779+passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
780+all other APIs that might be used in the test case function, including
781+``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
782+Documentation/dev-tools/kunit/api/test.rst and the
783+Documentation/dev-tools/kunit/api/resource.rst).
784+
785+.. note::
786+ The ``suite->init()`` function, which executes before each parameter run,
787+ receives the parameter run context. Therefore, any resources set up in
788+ ``suite->init()`` are cleaned up after each parameter run.
789+
790+The code below shows how you can add the shared resources. Note that this code
791+utilizes the Resource API, which you can read more about here:
792+Documentation/dev-tools/kunit/api/resource.rst. To see the full version of this
793+code please refer to lib/kunit/kunit-example-test.c.
794+
795+.. code-block:: c
796+
797+ static int example_resource_init(struct kunit_resource *res, void *context)
798+ {
799+ ... /* Code that allocates memory and stores context in res->data. */
800+ }
801+
802+ /* This function deallocates memory for the kunit_resource->data field. */
803+ static void example_resource_free(struct kunit_resource *res)
804+ {
805+ kfree(res->data);
806+ }
807+
808+ /* This match function locates a test resource based on defined criteria. */
809+ static bool example_resource_alloc_match(struct kunit *test, struct kunit_resource *res,
810+ void *match_data)
811+ {
812+ return res->data && res->free == example_resource_free;
813+ }
814+
815+ /* Function to initialize the parameterized test. */
816+ static int example_param_init(struct kunit *test)
817+ {
818+ int ctx = 3; /* Data to be stored. */
819+ void *data = kunit_alloc_resource(test, example_resource_init,
820+ example_resource_free,
821+ GFP_KERNEL, &ctx);
822+ if (!data)
823+ return -ENOMEM;
824+ kunit_register_params_array(test, example_params_array,
825+ ARRAY_SIZE(example_params_array));
826+ return 0;
827+ }
828+
829+ /* Example test that uses shared resources in test->resources. */
830+ static void example_params_test_with_init(struct kunit *test)
831+ {
832+ int threshold;
833+ const struct example_param *param = test->param_value;
834+ /* Here we pass test->parent to access the parameterized test context. */
835+ struct kunit_resource *res = kunit_find_resource(test->parent,
836+ example_resource_alloc_match,
837+ NULL);
838+
839+ threshold = *((int *)res->data);
840+ KUNIT_ASSERT_LE(test, param->value, threshold);
841+ kunit_put_resource(res);
842+ }
843+
844+ static struct kunit_case example_test_cases[] = {
845+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, kunit_array_gen_params,
846+ example_param_init, NULL),
847+ {}
848+ };
849+
850+As an alternative to using the KUnit Resource API for sharing resources, you can
851+place them in ``test->parent->priv``. This serves as a more lightweight method
852+for resource storage, best for scenarios where complex resource management is
853+not required.
854+
855+As stated previously ``param_init()`` and ``param_exit()`` get the parameterized
856+test context. So, you can directly use ``test->priv`` within ``param_init/exit``
857+to manage shared resources. However, from within the test case function, you must
858+navigate up to the parent ``struct kunit`` i.e. the parameterized test context.
859+Therefore, you need to use ``test->parent->priv`` to access those same
860+resources.
861+
862+The resources placed in ``test->parent->priv`` will need to be allocated in
863+memory to persist across the parameter runs. If memory is allocated using the
864+KUnit memory allocation APIs (described more in the "Allocating Memory" section
865+below), you won't need to worry about deallocation. The APIs will make the memory
866+parameterized test 'managed', ensuring that it will automatically get cleaned up
867+after the parameterized test concludes.
868+
869+The code below demonstrates example usage of the ``priv`` field for shared
870+resources:
871+
872+.. code-block:: c
873+
874+ static const struct example_param {
875+ int value;
876+ } example_params_array[] = {
877+ { .value = 3, },
878+ { .value = 2, },
879+ { .value = 1, },
880+ { .value = 0, },
881+ };
882+
883+ /* Initialize the parameterized test context. */
884+ static int example_param_init_priv(struct kunit *test)
885+ {
886+ int ctx = 3; /* Data to be stored. */
887+ int arr_size = ARRAY_SIZE(example_params_array);
888+
889+ /*
890+ * Allocate memory using kunit_kzalloc(). Since the `param_init`
891+ * function receives the parameterized test context, this memory
892+ * allocation will be scoped to the lifetime of the parameterized test.
893+ */
894+ test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
895+
896+ /* Assign the context value to test->priv.*/
897+ *((int *)test->priv) = ctx;
898+
899+ /* Register the parameter array. */
900+ kunit_register_params_array(test, example_params_array, arr_size, NULL);
901+ return 0;
902+ }
903+
904+ static void example_params_test_with_init_priv(struct kunit *test)
905+ {
906+ int threshold;
907+ const struct example_param *param = test->param_value;
908+
909+ /* By design, test->parent will not be NULL. */
910+ KUNIT_ASSERT_NOT_NULL(test, test->parent);
911+
912+ /* Here we use test->parent->priv to access the shared resource. */
913+ threshold = *(int *)test->parent->priv;
914+
915+ KUNIT_ASSERT_LE(test, param->value, threshold);
916+ }
917+
918+ static struct kunit_case example_tests[] = {
919+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv,
920+ kunit_array_gen_params,
921+ example_param_init_priv, NULL),
922+ {}
923+ };
924+
593925 Allocating Memory
594926 -----------------
595927
drivers/gpu/drm/xe/tests/xe_pci.c+7−7
@@ -44,9 +44,9 @@ KUNIT_ARRAY_PARAM(pci_id, pciidlist, xe_pci_id_kunit_desc); · 43 unmodified lines
4444 *
4545 * Return: pointer to the next parameter or NULL if no more parameters
4646 */
47−const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc)
47+const void *xe_pci_graphics_ip_gen_param(struct kunit *test, const void *prev, char *desc)
4848 {
49− return graphics_ip_gen_params(prev, desc);
49+ return graphics_ip_gen_params(test, prev, desc);
5050 }
5151 EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param);
5252
@@ -61,9 +61,9 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param); · 8 unmodified lines
6161 *
6262 * Return: pointer to the next parameter or NULL if no more parameters
6363 */
64−const void *xe_pci_media_ip_gen_param(const void *prev, char *desc)
64+const void *xe_pci_media_ip_gen_param(struct kunit *test, const void *prev, char *desc)
6565 {
66− return media_ip_gen_params(prev, desc);
66+ return media_ip_gen_params(test, prev, desc);
6767 }
6868 EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);
6969
@@ -78,9 +78,9 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param); · 8 unmodified lines
7878 *
7979 * Return: pointer to the next parameter or NULL if no more parameters
8080 */
81−const void *xe_pci_id_gen_param(const void *prev, char *desc)
81+const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc)
8282 {
83− const struct pci_device_id *pci = pci_id_gen_params(prev, desc);
83+ const struct pci_device_id *pci = pci_id_gen_params(test, prev, desc);
8484
8585 return pci->driver_data ? pci : NULL;
8686 }
@@ -159,7 +159,7 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_fake_device_init); · 72 unmodified lines
159159 * Return: pointer to the next &struct xe_device ready to be used as a parameter
160160 * or NULL if there are no more Xe devices on the system.
161161 */
162−const void *xe_pci_live_device_gen_param(const void *prev, char *desc)
162+const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc)
163163 {
164164 const struct xe_device *xe = prev;
165165 struct device *dev = xe ? xe->drm.dev : NULL;
drivers/gpu/drm/xe/tests/xe_pci_test.h+5−4
@@ -7,6 +7,7 @@ · 6 unmodified lines
77 #define _XE_PCI_TEST_H_
88
99 #include <linux/types.h>
10+#include <kunit/test.h>
1011
1112 #include "xe_platform_types.h"
1213 #include "xe_sriov_types.h"
@@ -25,9 +26,9 @@ struct xe_pci_fake_data { · 12 unmodified lines
2526
2627 int xe_pci_fake_device_init(struct xe_device *xe);
2728
28−const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc);
29−const void *xe_pci_media_ip_gen_param(const void *prev, char *desc);
30−const void *xe_pci_id_gen_param(const void *prev, char *desc);
31−const void *xe_pci_live_device_gen_param(const void *prev, char *desc);
29+const void *xe_pci_graphics_ip_gen_param(struct kunit *test, const void *prev, char *desc);
30+const void *xe_pci_media_ip_gen_param(struct kunit *test, const void *prev, char *desc);
31+const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc);
32+const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc);
3233
3334 #endif
include/kunit/test.h+90−5
@@ -92,6 +92,8 @@ struct kunit_attributes { · 91 unmodified lines
9292 * @name: the name of the test case.
9393 * @generate_params: the generator function for parameterized tests.
9494 * @attr: the attributes associated with the test
95+ * @param_init: The init function to run before a parameterized test.
96+ * @param_exit: The exit function to run after a parameterized test.
9597 *
9698 * A test case is a function with the signature,
9799 * ``void (*)(struct kunit *)``
@@ -126,8 +128,11 @@ struct kunit_attributes { · 28 unmodified lines
126128 struct kunit_case {
127129 void (*run_case)(struct kunit *test);
128130 const char *name;
129− const void* (*generate_params)(const void *prev, char *desc);
131+ const void* (*generate_params)(struct kunit *test,
132+ const void *prev, char *desc);
130133 struct kunit_attributes attr;
134+ int (*param_init)(struct kunit *test);
135+ void (*param_exit)(struct kunit *test);
131136
132137 /* private: internal use only. */
133138 enum kunit_status status;
@@ -218,6 +223,31 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) · 84 unmodified lines
218223 .generate_params = gen_params, \
219224 .attr = attributes, .module_name = KBUILD_MODNAME}
220225
226+/**
227+ * KUNIT_CASE_PARAM_WITH_INIT - Define a parameterized KUnit test case with custom
228+ * param_init() and param_exit() functions.
229+ * @test_name: The function implementing the test case.
230+ * @gen_params: The function to generate parameters for the test case.
231+ * @init: A reference to the param_init() function to run before a parameterized test.
232+ * @exit: A reference to the param_exit() function to run after a parameterized test.
233+ *
234+ * Provides the option to register param_init() and param_exit() functions.
235+ * param_init/exit will be passed the parameterized test context and run once
236+ * before and once after the parameterized test. The init function can be used
237+ * to add resources to share between parameter runs, pass parameter arrays,
238+ * and any other setup logic. The exit function can be used to clean up resources
239+ * that were not managed by the parameterized test, and any other teardown logic.
240+ *
241+ * Note: If you are registering a parameter array in param_init() with
242+ * kunit_register_param_array() then you need to pass kunit_array_gen_params()
243+ * to this as the generator function.
244+ */
245+#define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit) \
246+ { .run_case = test_name, .name = #test_name, \
247+ .generate_params = gen_params, \
248+ .param_init = init, .param_exit = exit, \
249+ .module_name = KBUILD_MODNAME}
250+
221251 /**
222252 * struct kunit_suite - describes a related collection of &struct kunit_case
223253 *
@@ -263,19 +293,39 @@ struct kunit_suite_set { · 39 unmodified lines
263293 struct kunit_suite * const *end;
264294 };
265295
296+/* Stores the pointer to the parameter array and its metadata. */
297+struct kunit_params {
298+ /*
299+ * Reference to the parameter array for a parameterized test. This
300+ * is NULL if a parameter array wasn't directly passed to the
301+ * parameterized test context struct kunit via kunit_register_params_array().
302+ */
303+ const void *params;
304+ /* Reference to a function that gets the description of a parameter. */
305+ void (*get_description)(struct kunit *test, const void *param, char *desc);
306+ size_t num_params;
307+ size_t elem_size;
308+};
309+
266310 /**
267311 * struct kunit - represents a running instance of a test.
268312 *
269313 * @priv: for user to store arbitrary data. Commonly used to pass data
270314 * created in the init function (see &struct kunit_suite).
315+ * @parent: reference to the parent context of type struct kunit that can
316+ * be used for storing shared resources.
317+ * @params_array: for storing the parameter array.
271318 *
272319 * Used to store information about the current context under which the test
273320 * is running. Most of this data is private and should only be accessed
274− * indirectly via public functions; the one exception is @priv which can be
275− * used by the test writer to store arbitrary data.
321+ * indirectly via public functions; the exceptions are @priv, @parent and
322+ * @params_array which can be used by the test writer to store arbitrary data,
323+ * access the parent context, and to store the parameter array, respectively.
276324 */
277325 struct kunit {
278326 void *priv;
327+ struct kunit *parent;
328+ struct kunit_params params_array;
279329
280330 /* private: internal use only. */
281331 const char *name; /* Read only after initialization! */
@@ -346,6 +396,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr) · 64 unmodified lines
346396 struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
347397 struct kunit_suite_set suite_set);
348398
399+const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc);
400+
349401 #if IS_BUILTIN(CONFIG_KUNIT)
350402 int kunit_run_all_tests(void);
351403 #else
@@ -1674,9 +1726,12 @@ do { \ · 1322 unmodified lines
16741726 * Define function @name_gen_params which uses @array to generate parameters.
16751727 */
16761728 #define KUNIT_ARRAY_PARAM(name, array, get_desc) \
1677− static const void *name##_gen_params(const void *prev, char *desc) \
1729+ static const void *name##_gen_params(struct kunit *test, \
1730+ const void *prev, char *desc) \
16781731 { \
16791732 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \
1733+ if (!prev) \
1734+ kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL); \
16801735 if (__next - (array) < ARRAY_SIZE((array))) { \
16811736 void (*__get_desc)(typeof(__next), char *) = get_desc; \
16821737 if (__get_desc) \
@@ -1695,9 +1750,12 @@ do { \ · 12 unmodified lines
16951750 * Define function @name_gen_params which uses @array to generate parameters.
16961751 */
16971752 #define KUNIT_ARRAY_PARAM_DESC(name, array, desc_member) \
1698− static const void *name##_gen_params(const void *prev, char *desc) \
1753+ static const void *name##_gen_params(struct kunit *test, \
1754+ const void *prev, char *desc) \
16991755 { \
17001756 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \
1757+ if (!prev) \
1758+ kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL); \
17011759 if (__next - (array) < ARRAY_SIZE((array))) { \
17021760 strscpy(desc, __next->desc_member, KUNIT_PARAM_DESC_SIZE); \
17031761 return __next; \
@@ -1705,6 +1763,33 @@ do { \ · 1 unmodified line
17051763 return NULL; \
17061764 }
17071765
1766+/**
1767+ * kunit_register_params_array() - Register parameter array for a KUnit test.
1768+ * @test: The KUnit test structure to which parameters will be added.
1769+ * @array: An array of test parameters.
1770+ * @param_count: Number of parameters.
1771+ * @get_desc: Function that generates a string description for a given parameter
1772+ * element.
1773+ *
1774+ * This macro initializes the @test's parameter array data, storing information
1775+ * including the parameter array, its count, the element size, and the parameter
1776+ * description function within `test->params_array`.
1777+ *
1778+ * Note: If using this macro in param_init(), kunit_array_gen_params()
1779+ * will then need to be manually provided as the parameter generator function to
1780+ * KUNIT_CASE_PARAM_WITH_INIT(). kunit_array_gen_params() is a KUnit
1781+ * function that uses the registered array to generate parameters
1782+ */
1783+#define kunit_register_params_array(test, array, param_count, get_desc) \
1784+ do { \
1785+ struct kunit *_test = (test); \
1786+ const typeof((array)[0]) * _params_ptr = &(array)[0]; \
1787+ _test->params_array.params = _params_ptr; \
1788+ _test->params_array.num_params = (param_count); \
1789+ _test->params_array.elem_size = sizeof(*_params_ptr); \
1790+ _test->params_array.get_description = (get_desc); \
1791+ } while (0)
1792+
17081793 // TODO(dlatypov@google.com): consider eventually migrating users to explicitly
17091794 // include resource.h themselves if they need it.
17101795 #include <kunit/resource.h>
kernel/kcsan/kcsan_test.c+1−1
@@ -1383,7 +1383,7 @@ static void test_atomic_builtins_missing_barrier(struct kunit *test) · 1382 unmodified lines
13831383 * The thread counts are chosen to cover potentially interesting boundaries and
13841384 * corner cases (2 to 5), and then stress the system with larger counts.
13851385 */
1386−static const void *nthreads_gen_params(const void *prev, char *desc)
1386+static const void *nthreads_gen_params(struct kunit *test, const void *prev, char *desc)
13871387 {
13881388 long nthreads = (long)prev;
13891389
lib/Makefile+0−4
@@ -109,11 +109,7 @@ test_fpu-y := test_fpu_glue.o test_fpu_impl.o · 108 unmodified lines
109109 CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU)
110110 CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
111111
112−# Some KUnit files (hooks.o) need to be built-in even when KUnit is a module,
113−# so we can't just use obj-$(CONFIG_KUNIT).
114−ifdef CONFIG_KUNIT
115112 obj-y += kunit/
116−endif
117113
118114 ifeq ($(CONFIG_DEBUG_KOBJECT),y)
119115 CFLAGS_kobject.o += -DDEBUG
lib/kunit/Kconfig+11−0
@@ -106,4 +106,15 @@ config KUNIT_DEFAULT_TIMEOUT · 105 unmodified lines
106106 If unsure, the default timeout of 300 seconds is suitable for most
107107 cases.
108108
109+config KUNIT_UML_PCI
110+ bool "KUnit UML PCI Support"
111+ depends on UML
112+ select UML_PCI
113+ help
114+ Enables the PCI subsystem on UML for use by KUnit tests.
115+ Some KUnit tests require the PCI core which is not enabled by
116+ default on UML.
117+
118+ If unsure, say N.
119+
109120 endif # KUNIT
lib/kunit/Makefile+1−1
@@ -17,7 +17,7 @@ kunit-objs += debugfs.o · 16 unmodified lines
1717 endif
1818
1919 # KUnit 'hooks' are built-in even when KUnit is built as a module.
20−obj-y += hooks.o
20+obj-$(if $(CONFIG_KUNIT),y) += hooks.o
2121
2222 obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
2323 obj-$(CONFIG_KUNIT_TEST) += platform-test.o
lib/kunit/kunit-example-test.c+217−0
@@ -277,6 +277,218 @@ static void example_slow_test(struct kunit *test) · 276 unmodified lines
277277 KUNIT_EXPECT_EQ(test, 1 + 1, 2);
278278 }
279279
280+/*
281+ * This custom function allocates memory and sets the information we want
282+ * stored in the kunit_resource->data field.
283+ */
284+static int example_resource_init(struct kunit_resource *res, void *context)
285+{
286+ int *info = kmalloc(sizeof(*info), GFP_KERNEL);
287+
288+ if (!info)
289+ return -ENOMEM;
290+ *info = *(int *)context;
291+ res->data = info;
292+ return 0;
293+}
294+
295+/*
296+ * This function deallocates memory for the kunit_resource->data field.
297+ */
298+static void example_resource_free(struct kunit_resource *res)
299+{
300+ kfree(res->data);
301+}
302+
303+/*
304+ * This match function is invoked by kunit_find_resource() to locate
305+ * a test resource based on certain criteria.
306+ */
307+static bool example_resource_alloc_match(struct kunit *test,
308+ struct kunit_resource *res,
309+ void *match_data)
310+{
311+ return res->data && res->free == example_resource_free;
312+}
313+
314+/*
315+ * This is an example of a function that provides a description for each of the
316+ * parameters in a parameterized test.
317+ */
318+static void example_param_array_get_desc(struct kunit *test, const void *p, char *desc)
319+{
320+ const struct example_param *param = p;
321+
322+ snprintf(desc, KUNIT_PARAM_DESC_SIZE,
323+ "example check if %d is less than or equal to 3", param->value);
324+}
325+
326+/*
327+ * This function gets passed in the parameterized test context i.e. the
328+ * struct kunit belonging to the parameterized test. You can use this function
329+ * to add resources you want shared across the whole parameterized test or
330+ * for additional setup.
331+ */
332+static int example_param_init(struct kunit *test)
333+{
334+ int ctx = 3; /* Data to be stored. */
335+ size_t arr_size = ARRAY_SIZE(example_params_array);
336+
337+ /*
338+ * This allocates a struct kunit_resource, sets its data field to
339+ * ctx, and adds it to the struct kunit's resources list. Note that
340+ * this is parameterized test managed. So, it doesn't need to have
341+ * a custom exit function to deallocation as it will get cleaned up at
342+ * the end of the parameterized test.
343+ */
344+ void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
345+ GFP_KERNEL, &ctx);
346+
347+ if (!data)
348+ return -ENOMEM;
349+ /*
350+ * Pass the parameter array information to the parameterized test context
351+ * struct kunit. Note that you will need to provide kunit_array_gen_params()
352+ * as the generator function to KUNIT_CASE_PARAM_WITH_INIT() when registering
353+ * a parameter array this route.
354+ */
355+ kunit_register_params_array(test, example_params_array, arr_size,
356+ example_param_array_get_desc);
357+ return 0;
358+}
359+
360+/*
361+ * This is an example of a test that uses shared resources available in the
362+ * parameterized test context.
363+ */
364+static void example_params_test_with_init(struct kunit *test)
365+{
366+ int threshold;
367+ struct kunit_resource *res;
368+ const struct example_param *param = test->param_value;
369+
370+ /* By design, param pointer will not be NULL. */
371+ KUNIT_ASSERT_NOT_NULL(test, param);
372+
373+ /*
374+ * Here we pass test->parent to search for shared resources in the
375+ * parameterized test context.
376+ */
377+ res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
378+
379+ KUNIT_ASSERT_NOT_NULL(test, res);
380+
381+ /* Since kunit_resource->data is a void pointer we need to typecast it. */
382+ threshold = *((int *)res->data);
383+
384+ /* Assert that the parameter is less than or equal to a certain threshold. */
385+ KUNIT_ASSERT_LE(test, param->value, threshold);
386+
387+ /* This decreases the reference count after calling kunit_find_resource(). */
388+ kunit_put_resource(res);
389+}
390+
391+/*
392+ * Helper function to create a parameter array of Fibonacci numbers. This example
393+ * highlights a parameter generation scenario that is:
394+ * 1. Not feasible to fully pre-generate at compile time.
395+ * 2. Challenging to implement with a standard generate_params() function,
396+ * as it only provides the previous parameter, while Fibonacci requires
397+ * access to two preceding values for calculation.
398+ */
399+static void *make_fibonacci_params(struct kunit *test, size_t seq_size)
400+{
401+ int *seq;
402+
403+ if (seq_size <= 0)
404+ return NULL;
405+ /*
406+ * Using kunit_kmalloc_array here ties the lifetime of the array to
407+ * the parameterized test i.e. it will get automatically cleaned up
408+ * by KUnit after the parameterized test finishes.
409+ */
410+ seq = kunit_kmalloc_array(test, seq_size, sizeof(int), GFP_KERNEL);
411+
412+ if (!seq)
413+ return NULL;
414+ if (seq_size >= 1)
415+ seq[0] = 0;
416+ if (seq_size >= 2)
417+ seq[1] = 1;
418+ for (int i = 2; i < seq_size; i++)
419+ seq[i] = seq[i - 1] + seq[i - 2];
420+ return seq;
421+}
422+
423+/*
424+ * This is an example of a function that provides a description for each of the
425+ * parameters.
426+ */
427+static void example_param_dynamic_arr_get_desc(struct kunit *test, const void *p, char *desc)
428+{
429+ const int *fib_num = p;
430+
431+ snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
432+}
433+
434+/*
435+ * Example of a parameterized test param_init() function that registers a dynamic
436+ * array of parameters.
437+ */
438+static int example_param_init_dynamic_arr(struct kunit *test)
439+{
440+ size_t seq_size;
441+ int *fibonacci_params;
442+
443+ kunit_info(test, "initializing parameterized test\n");
444+
445+ seq_size = 6;
446+ fibonacci_params = make_fibonacci_params(test, seq_size);
447+
448+ if (!fibonacci_params)
449+ return -ENOMEM;
450+
451+ /*
452+ * Passes the dynamic parameter array information to the parameterized test
453+ * context struct kunit. The array and its metadata will be stored in
454+ * test->parent->params_array. The array itself will be located in
455+ * params_data.params.
456+ *
457+ * Note that you will need to pass kunit_array_gen_params() as the
458+ * generator function to KUNIT_CASE_PARAM_WITH_INIT() when registering
459+ * a parameter array this route.
460+ */
461+ kunit_register_params_array(test, fibonacci_params, seq_size,
462+ example_param_dynamic_arr_get_desc);
463+ return 0;
464+}
465+
466+/*
467+ * Example of a parameterized test param_exit() function that outputs a log
468+ * at the end of the parameterized test. It could also be used for any other
469+ * teardown logic.
470+ */
471+static void example_param_exit_dynamic_arr(struct kunit *test)
472+{
473+ kunit_info(test, "exiting parameterized test\n");
474+}
475+
476+/*
477+ * Example of test that uses the registered dynamic array to perform assertions
478+ * and expectations.
479+ */
480+static void example_params_test_with_init_dynamic_arr(struct kunit *test)
481+{
482+ const int *param = test->param_value;
483+ int param_val;
484+
485+ /* By design, param pointer will not be NULL. */
486+ KUNIT_ASSERT_NOT_NULL(test, param);
487+
488+ param_val = *param;
489+ KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
490+}
491+
280492 /*
281493 * Here we make a list of all the test cases we want to add to the test suite
282494 * below.
@@ -296,6 +508,11 @@ static struct kunit_case example_test_cases[] = { · 13 unmodified lines
296508 KUNIT_CASE(example_static_stub_using_fn_ptr_test),
297509 KUNIT_CASE(example_priv_test),
298510 KUNIT_CASE_PARAM(example_params_test, example_gen_params),
511+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, kunit_array_gen_params,
512+ example_param_init, NULL),
513+ KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr,
514+ kunit_array_gen_params, example_param_init_dynamic_arr,
515+ example_param_exit_dynamic_arr),
299516 KUNIT_CASE_SLOW(example_slow_test),
300517 {}
301518 };
lib/kunit/test.c+79−15
@@ -337,6 +337,14 @@ void __kunit_do_failed_assertion(struct kunit *test, · 336 unmodified lines
337337 }
338338 EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
339339
340+static void kunit_init_params(struct kunit *test)
341+{
342+ test->params_array.params = NULL;
343+ test->params_array.get_description = NULL;
344+ test->params_array.num_params = 0;
345+ test->params_array.elem_size = 0;
346+}
347+
340348 void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
341349 {
342350 spin_lock_init(&test->lock);
@@ -347,6 +355,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream · 4 unmodified lines
347355 string_stream_clear(log);
348356 test->status = KUNIT_SUCCESS;
349357 test->status_comment[0] = '\0';
358+ kunit_init_params(test);
350359 }
351360 EXPORT_SYMBOL_GPL(kunit_init_test);
352361
@@ -641,12 +650,44 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total, · 288 unmodified lines
641650 total->total += add.total;
642651 }
643652
653+const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc)
654+{
655+ struct kunit_params *params_arr = &test->params_array;
656+ const void *param;
657+
658+ if (test->param_index < params_arr->num_params) {
659+ param = (char *)params_arr->params
660+ + test->param_index * params_arr->elem_size;
661+
662+ if (params_arr->get_description)
663+ params_arr->get_description(test, param, desc);
664+ return param;
665+ }
666+ return NULL;
667+}
668+EXPORT_SYMBOL_GPL(kunit_array_gen_params);
669+
670+static void kunit_init_parent_param_test(struct kunit_case *test_case, struct kunit *test)
671+{
672+ if (test_case->param_init) {
673+ int err = test_case->param_init(test);
674+
675+ if (err) {
676+ kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
677+ "# failed to initialize parent parameter test (%d)", err);
678+ test->status = KUNIT_FAILURE;
679+ test_case->status = KUNIT_FAILURE;
680+ }
681+ }
682+}
683+
644684 int kunit_run_tests(struct kunit_suite *suite)
645685 {
646686 char param_desc[KUNIT_PARAM_DESC_SIZE];
647687 struct kunit_case *test_case;
648688 struct kunit_result_stats suite_stats = { 0 };
649689 struct kunit_result_stats total_stats = { 0 };
690+ const void *curr_param;
650691
651692 /* Taint the kernel so we know we've run tests. */
652693 add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
@@ -677,41 +718,64 @@ int kunit_run_tests(struct kunit_suite *suite) · 24 unmodified lines
677718 kunit_run_case_catch_errors(suite, test_case, &test);
678719 kunit_update_stats(&param_stats, test.status);
679720 } else {
721+ kunit_init_parent_param_test(test_case, &test);
722+ if (test_case->status == KUNIT_FAILURE) {
723+ kunit_update_stats(&param_stats, test.status);
724+ goto test_case_end;
725+ }
680726 /* Get initial param. */
681727 param_desc[0] = '\0';
682− test.param_value = test_case->generate_params(NULL, param_desc);
728+ /* TODO: Make generate_params try-catch */
729+ curr_param = test_case->generate_params(&test, NULL, param_desc);
683730 test_case->status = KUNIT_SKIPPED;
684731 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
685732 "KTAP version 1\n");
686733 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
687734 "# Subtest: %s", test_case->name);
735+ if (test.params_array.params &&
736+ test_case->generate_params == kunit_array_gen_params) {
737+ kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT
738+ KUNIT_SUBTEST_INDENT "1..%zd\n",
739+ test.params_array.num_params);
740+ }
688741
689− while (test.param_value) {
690− kunit_run_case_catch_errors(suite, test_case, &test);
742+ while (curr_param) {
743+ struct kunit param_test = {
744+ .param_value = curr_param,
745+ .param_index = ++test.param_index,
746+ .parent = &test,
747+ };
748+ kunit_init_test(&param_test, test_case->name, test_case->log);
749+ kunit_run_case_catch_errors(suite, test_case, &param_test);
691750
692751 if (param_desc[0] == '\0') {
693752 snprintf(param_desc, sizeof(param_desc),
694− "param-%d", test.param_index);
753+ "param-%d", param_test.param_index);
695754 }
696755
697− kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
698− test.status,
699− test.param_index + 1,
756+ kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
757+ param_test.status,
758+ param_test.param_index,
700759 param_desc,
701− test.status_comment);
760+ param_test.status_comment);
702761
703− kunit_update_stats(&param_stats, test.status);
762+ kunit_update_stats(&param_stats, param_test.status);
704763
705764 /* Get next param. */
706765 param_desc[0] = '\0';
707− test.param_value = test_case->generate_params(test.param_value, param_desc);
708− test.param_index++;
709− test.status = KUNIT_SUCCESS;
710− test.status_comment[0] = '\0';
711− test.priv = NULL;
766+ curr_param = test_case->generate_params(&test, curr_param,
767+ param_desc);
712768 }
769+ /*
770+ * TODO: Put into a try catch. Since we don't need suite->exit
771+ * for it we can't reuse kunit_try_run_cleanup for this yet.
772+ */
773+ if (test_case->param_exit)
774+ test_case->param_exit(&test);
775+ /* TODO: Put this kunit_cleanup into a try-catch. */
776+ kunit_cleanup(&test);
713777 }
714−
778+test_case_end:
715779 kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
716780
717781 kunit_print_test_stats(&test, param_stats);
rust/kernel/kunit.rs+11−0
@@ -210,6 +210,8 @@ pub const fn kunit_case( · 209 unmodified lines
210210 status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
211211 module_name: core::ptr::null_mut(),
212212 log: core::ptr::null_mut(),
213+ param_init: None,
214+ param_exit: None,
213215 }
214216 }
215217
@@ -229,6 +231,8 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { · 13 unmodified lines
229231 status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
230232 module_name: core::ptr::null_mut(),
231233 log: core::ptr::null_mut(),
234+ param_init: None,
235+ param_exit: None,
232236 }
233237 }
234238
@@ -357,4 +361,11 @@ mod tests { · 122 unmodified lines
357361 fn rust_test_kunit_in_kunit_test() {
358362 assert!(in_kunit_test());
359363 }
364+
365+ #[test]
366+ #[cfg(not(all()))]
367+ fn rust_test_kunit_always_disabled_test() {
368+ // This test should never run because of the `cfg`.
369+ assert!(false);
370+ }
360371 }
rust/macros/kunit.rs+36−12
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 //! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com>
66
77 use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
8+use std::collections::HashMap;
89 use std::fmt::Write;
910
1011 pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
@@ -41,20 +42,32 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { · 30 unmodified lines
4142 // Get the functions set as tests. Search for `[test]` -> `fn`.
4243 let mut body_it = body.stream().into_iter();
4344 let mut tests = Vec::new();
45+ let mut attributes: HashMap<String, TokenStream> = HashMap::new();
4446 while let Some(token) = body_it.next() {
4547 match token {
46− TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() {
47− Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => {
48− let test_name = match body_it.next() {
49− Some(TokenTree::Ident(ident)) => ident.to_string(),
50− _ => continue,
51− };
52− tests.push(test_name);
48+ TokenTree::Punct(ref p) if p.as_char() == '#' => match body_it.next() {
49+ Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
50+ if let Some(TokenTree::Ident(name)) = g.stream().into_iter().next() {
51+ // Collect attributes because we need to find which are tests. We also
52+ // need to copy `cfg` attributes so tests can be conditionally enabled.
53+ attributes
54+ .entry(name.to_string())
55+ .or_default()
56+ .extend([token, TokenTree::Group(g)]);
57+ }
58+ continue;
5359 }
54− _ => continue,
60+ _ => (),
5561 },
62+ TokenTree::Ident(i) if i.to_string() == "fn" && attributes.contains_key("test") => {
63+ if let Some(TokenTree::Ident(test_name)) = body_it.next() {
64+ tests.push((test_name, attributes.remove("cfg").unwrap_or_default()))
65+ }
66+ }
67+
5668 _ => (),
5769 }
70+ attributes.clear();
5871 }
5972
6073 // Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration.
@@ -100,11 +113,22 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream { · 39 unmodified lines
100113 let mut test_cases = "".to_owned();
101114 let mut assert_macros = "".to_owned();
102115 let path = crate::helpers::file();
103− for test in &tests {
116+ let num_tests = tests.len();
117+ for (test, cfg_attr) in tests {
104118 let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}");
105− // An extra `use` is used here to reduce the length of the message.
119+ // Append any `cfg` attributes the user might have written on their tests so we don't
120+ // attempt to call them when they are `cfg`'d out. An extra `use` is used here to reduce
121+ // the length of the assert message.
106122 let kunit_wrapper = format!(
107− "unsafe extern \"C\" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit) {{ use ::kernel::kunit::is_test_result_ok; assert!(is_test_result_ok({test}())); }}",
123+ r#"unsafe extern "C" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit)
124+ {{
125+ (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED;
126+ {cfg_attr} {{
127+ (*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
128+ use ::kernel::kunit::is_test_result_ok;
129+ assert!(is_test_result_ok({test}()));
130+ }}
131+ }}"#,
108132 );
109133 writeln!(kunit_macros, "{kunit_wrapper}").unwrap();
110134 writeln!(
@@ -139,7 +163,7 @@ macro_rules! assert_eq {{ · 28 unmodified lines
139163 writeln!(
140164 kunit_macros,
141165 "static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];",
142− tests.len() + 1
166+ num_tests + 1
143167 )
144168 .unwrap();
145169
tools/testing/kunit/configs/arch_uml.config+2−3
@@ -1,8 +1,7 @@
11 # Config options which are added to UML builds by default
22
3−# Enable virtio/pci, as a lot of tests require it.
4−CONFIG_VIRTIO_UML=y
5−CONFIG_UML_PCI_OVER_VIRTIO=y
3+# Enable pci, as a lot of tests require it.
4+CONFIG_KUNIT_UML_PCI=y
65
76 # Enable FORTIFY_SOURCE for wider checking.
87 CONFIG_FORTIFY_SOURCE=y
tools/testing/kunit/kunit.py+2−2
@@ -228,7 +228,7 @@ def parse_tests(request: KunitParseRequest, metadata: kunit_json.Metadata, input · 227 unmodified lines
228228 fake_test.counts.passed = 1
229229
230230 output: Iterable[str] = input_data
231− if request.raw_output == 'all':
231+ if request.raw_output == 'all' or request.raw_output == 'full':
232232 pass
233233 elif request.raw_output == 'kunit':
234234 output = kunit_parser.extract_tap_lines(output)
@@ -425,7 +425,7 @@ def add_parse_opts(parser: argparse.ArgumentParser) -> None: · 190 unmodified lines
425425 parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. '
426426 'By default, filters to just KUnit output. Use '
427427 '--raw_output=all to show everything',
428− type=str, nargs='?', const='all', default=None, choices=['all', 'kunit'])
428+ type=str, nargs='?', const='all', default=None, choices=['all', 'full', 'kunit'])
429429 parser.add_argument('--json',
430430 nargs='?',
431431 help='Prints parsed test results as JSON to stdout or a file if '
tools/testing/kunit/kunit_parser.py+5−3
@@ -352,9 +352,9 @@ def parse_test_plan(lines: LineStream, test: Test) -> bool: · 351 unmodified lines
352352 lines.pop()
353353 return True
354354
355−TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?([^#]*)( # .*)?$')
355+TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) ?(- )?([^#]*)( # .*)?$')
356356
357−TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?(.*) # SKIP(.*)$')
357+TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) ?(- )?(.*) # SKIP ?(.*)$')
358358
359359 def peek_test_name_match(lines: LineStream, test: Test) -> bool:
360360 """
@@ -379,6 +379,8 @@ def peek_test_name_match(lines: LineStream, test: Test) -> bool: · 18 unmodified lines
379379 if not match:
380380 return False
381381 name = match.group(4)
382+ if not name:
383+ return False
382384 return name == test.name
383385
384386 def parse_test_result(lines: LineStream, test: Test,
@@ -416,7 +418,7 @@ def parse_test_result(lines: LineStream, test: Test, · 31 unmodified lines
416418
417419 # Set name of test object
418420 if skip_match:
419− test.name = skip_match.group(4)
421+ test.name = skip_match.group(4) or skip_match.group(5)
420422 else:
421423 test.name = match.group(4)
422424
tools/testing/kunit/qemu_configs/mips.py+18−0
@@ -0,0 +1,18 @@
1+# SPDX-License-Identifier: GPL-2.0
2+
3+from ..qemu_config import QemuArchParams
4+
5+QEMU_ARCH = QemuArchParams(linux_arch='mips',
6+ kconfig='''
7+CONFIG_32BIT=y
8+CONFIG_CPU_BIG_ENDIAN=y
9+CONFIG_MIPS_MALTA=y
10+CONFIG_SERIAL_8250=y
11+CONFIG_SERIAL_8250_CONSOLE=y
12+CONFIG_POWER_RESET=y
13+CONFIG_POWER_RESET_SYSCON=y
14+''',
15+ qemu_arch='mips',
16+ kernel_path='vmlinuz',
17+ kernel_command_line='console=ttyS0',
18+ extra_qemu_params=['-M', 'malta'])
tools/testing/kunit/qemu_configs/mips64.py+19−0
@@ -0,0 +1,19 @@
1+# SPDX-License-Identifier: GPL-2.0
2+
3+from ..qemu_config import QemuArchParams
4+
5+QEMU_ARCH = QemuArchParams(linux_arch='mips',
6+ kconfig='''
7+CONFIG_CPU_MIPS64_R2=y
8+CONFIG_64BIT=y
9+CONFIG_CPU_BIG_ENDIAN=y
10+CONFIG_MIPS_MALTA=y
11+CONFIG_SERIAL_8250=y
12+CONFIG_SERIAL_8250_CONSOLE=y
13+CONFIG_POWER_RESET=y
14+CONFIG_POWER_RESET_SYSCON=y
15+''',
16+ qemu_arch='mips64',
17+ kernel_path='vmlinuz',
18+ kernel_command_line='console=ttyS0',
19+ extra_qemu_params=['-M', 'malta', '-cpu', '5KEc'])
tools/testing/kunit/qemu_configs/mips64el.py+19−0
@@ -0,0 +1,19 @@
1+# SPDX-License-Identifier: GPL-2.0
2+
3+from ..qemu_config import QemuArchParams
4+
5+QEMU_ARCH = QemuArchParams(linux_arch='mips',
6+ kconfig='''
7+CONFIG_CPU_MIPS64_R2=y
8+CONFIG_64BIT=y
9+CONFIG_CPU_LITTLE_ENDIAN=y
10+CONFIG_MIPS_MALTA=y
11+CONFIG_SERIAL_8250=y
12+CONFIG_SERIAL_8250_CONSOLE=y
13+CONFIG_POWER_RESET=y
14+CONFIG_POWER_RESET_SYSCON=y
15+''',
16+ qemu_arch='mips64el',
17+ kernel_path='vmlinuz',
18+ kernel_command_line='console=ttyS0',
19+ extra_qemu_params=['-M', 'malta', '-cpu', '5KEc'])
tools/testing/kunit/qemu_configs/mipsel.py+18−0
@@ -0,0 +1,18 @@
1+# SPDX-License-Identifier: GPL-2.0
2+
3+from ..qemu_config import QemuArchParams
4+
5+QEMU_ARCH = QemuArchParams(linux_arch='mips',
6+ kconfig='''
7+CONFIG_32BIT=y
8+CONFIG_CPU_LITTLE_ENDIAN=y
9+CONFIG_MIPS_MALTA=y
10+CONFIG_SERIAL_8250=y
11+CONFIG_SERIAL_8250_CONSOLE=y
12+CONFIG_POWER_RESET=y
13+CONFIG_POWER_RESET_SYSCON=y
14+''',
15+ qemu_arch='mipsel',
16+ kernel_path='vmlinuz',
17+ kernel_command_line='console=ttyS0',
18+ extra_qemu_params=['-M', 'malta'])
tools/testing/kunit/test_data/test_is_test_passed-kselftest.log+2−1
@@ -1,5 +1,5 @@
11 TAP version 13
2−1..2
2+1..3
33 # selftests: membarrier: membarrier_test_single_thread
44 # TAP version 13
55 # 1..2
@@ -12,3 +12,4 @@ ok 1 selftests: membarrier: membarrier_test_single_thread · 6 unmodified lines
1212 # ok 1 sys_membarrier available
1313 # ok 2 sys membarrier invalid command test: command = -1, flags = 0, errno = 22. Failed as expected
1414 ok 2 selftests: membarrier: membarrier_test_multi_thread
15+ok 3 # SKIP selftests: membarrier: membarrier_test_multi_thread