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
542Parameterized Testing542Parameterized Testing
543~~~~~~~~~~~~~~~~~~~~~543~~~~~~~~~~~~~~~~~~~~~
544 544
545The table-driven testing pattern is common enough that KUnit has special545To run a test case against multiple inputs, KUnit provides a parameterized
546support for it.546testing framework. This feature formalizes and extends the concept of
547table-driven tests discussed previously.
547 548
548By reusing the same ``cases`` array from above, we can write the test as a549A KUnit test is determined to be parameterized if a parameter generator function
549"parameterized test" with the following.550is provided when registering the test case. A test user can either write their
551own generator function or use one that is provided by KUnit. The generator
552function is stored in ``kunit_case->generate_params`` and can be set using the
553macros described in the section below.
554
555To establish the terminology, a "parameterized test" is a test which is run
556multiple times (once per "parameter" or "parameter run"). Each parameter run has
557both its own independent ``struct kunit`` (the "parameter run context") and
558access to a shared parent ``struct kunit`` (the "parameterized test context").
559
560Passing Parameters to a Test
561^^^^^^^^^^^^^^^^^^^^^^^^^^^^
562There are three ways to provide the parameters to a test:
563
564Array 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:
550 570
551.. code-block:: c571.. code-block:: c
552 572
@@ -555,7 +575,7 @@ By reusing the same ``cases`` array from above, we can write the test as a · 2 unmodified lines
555 const char *str;575 const char *str;
556 const char *sha1;576 const char *sha1;
557 };577 };
558 const struct sha1_test_case cases[] = {578 static const struct sha1_test_case cases[] = {
559 {579 {
560 .str = "hello world",580 .str = "hello world",
561 .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",581 .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
@@ -590,6 +610,318 @@ By reusing the same ``cases`` array from above, we can write the test as a · 28 unmodified lines
590 {}610 {}
591 };611 };
592 612
613Custom 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
673Runtime 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
751Adding Shared Resources
752^^^^^^^^^^^^^^^^^^^^^^^
753All parameter runs in this framework hold a reference to the parameterized test
754context, which can be accessed using the parent ``struct kunit`` pointer. The
755parameterized test context is not used to execute any test logic itself; instead,
756it serves as a container for shared resources.
757
758It's possible to add resources to share between parameter runs within a
759parameterized test by using ``KUNIT_CASE_PARAM_WITH_INIT``, to which you pass
760custom ``param_init()`` and ``param_exit()`` functions. These functions run once
761before and once after the parameterized test, respectively.
762
763The ``param_init()`` function, with the signature ``int (*)(struct kunit *test)``,
764can be used for adding resources to the ``resources`` or ``priv`` fields of
765the parameterized test context, registering the parameter array, and any other
766initialization logic.
767
768The ``param_exit()`` function, with the signature ``void (*)(struct kunit *test)``,
769can be used to release any resources that were not parameterized test managed (i.e.
770not automatically cleaned up after the parameterized test ends) and for any other
771exit logic.
772
773Both ``param_init()`` and ``param_exit()`` are passed the parameterized test
774context behind the scenes. However, the test case function receives the parameter
775run context. Therefore, to manage and access shared resources from within a test
776case function, you must use ``test->parent``.
777
778For instance, finding a shared resource allocated by the Resource API requires
779passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
780all other APIs that might be used in the test case function, including
781``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
782Documentation/dev-tools/kunit/api/test.rst and the
783Documentation/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
790The code below shows how you can add the shared resources. Note that this code
791utilizes the Resource API, which you can read more about here:
792Documentation/dev-tools/kunit/api/resource.rst. To see the full version of this
793code 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
850As an alternative to using the KUnit Resource API for sharing resources, you can
851place them in ``test->parent->priv``. This serves as a more lightweight method
852for resource storage, best for scenarios where complex resource management is
853not required.
854
855As stated previously ``param_init()`` and ``param_exit()`` get the parameterized
856test context. So, you can directly use ``test->priv`` within ``param_init/exit``
857to manage shared resources. However, from within the test case function, you must
858navigate up to the parent ``struct kunit`` i.e. the parameterized test context.
859Therefore, you need to use ``test->parent->priv`` to access those same
860resources.
861
862The resources placed in ``test->parent->priv`` will need to be allocated in
863memory to persist across the parameter runs. If memory is allocated using the
864KUnit memory allocation APIs (described more in the "Allocating Memory" section
865below), you won't need to worry about deallocation. The APIs will make the memory
866parameterized test 'managed', ensuring that it will automatically get cleaned up
867after the parameterized test concludes.
868
869The code below demonstrates example usage of the ``priv`` field for shared
870resources:
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
593Allocating Memory925Allocating Memory
594-----------------926-----------------
595 927
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
44 *44 *
45 * Return: pointer to the next parameter or NULL if no more parameters45 * Return: pointer to the next parameter or NULL if no more parameters
46 */46 */
47const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc)47const void *xe_pci_graphics_ip_gen_param(struct kunit *test, const void *prev, char *desc)
48{48{
49 return graphics_ip_gen_params(prev, desc);49 return graphics_ip_gen_params(test, prev, desc);
50}50}
51EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param);51EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param);
52 52
@@ -61,9 +61,9 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param); · 8 unmodified lines
61 *61 *
62 * Return: pointer to the next parameter or NULL if no more parameters62 * Return: pointer to the next parameter or NULL if no more parameters
63 */63 */
64const void *xe_pci_media_ip_gen_param(const void *prev, char *desc)64const void *xe_pci_media_ip_gen_param(struct kunit *test, const void *prev, char *desc)
65{65{
66 return media_ip_gen_params(prev, desc);66 return media_ip_gen_params(test, prev, desc);
67}67}
68EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);68EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);
69 69
@@ -78,9 +78,9 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param); · 8 unmodified lines
78 *78 *
79 * Return: pointer to the next parameter or NULL if no more parameters79 * Return: pointer to the next parameter or NULL if no more parameters
80 */80 */
81const void *xe_pci_id_gen_param(const void *prev, char *desc)81const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc)
82{82{
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);
84 84
85 return pci->driver_data ? pci : NULL;85 return pci->driver_data ? pci : NULL;
86}86}
@@ -159,7 +159,7 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_fake_device_init); · 72 unmodified lines
159 * Return: pointer to the next &struct xe_device ready to be used as a parameter159 * Return: pointer to the next &struct xe_device ready to be used as a parameter
160 * or NULL if there are no more Xe devices on the system.160 * or NULL if there are no more Xe devices on the system.
161 */161 */
162const void *xe_pci_live_device_gen_param(const void *prev, char *desc)162const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc)
163{163{
164 const struct xe_device *xe = prev;164 const struct xe_device *xe = prev;
165 struct device *dev = xe ? xe->drm.dev : NULL;165 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
7#define _XE_PCI_TEST_H_7#define _XE_PCI_TEST_H_
8 8
9#include <linux/types.h>9#include <linux/types.h>
10#include <kunit/test.h>
10 11
11#include "xe_platform_types.h"12#include "xe_platform_types.h"
12#include "xe_sriov_types.h"13#include "xe_sriov_types.h"
@@ -25,9 +26,9 @@ struct xe_pci_fake_data { · 12 unmodified lines
25 26
26int xe_pci_fake_device_init(struct xe_device *xe);27int xe_pci_fake_device_init(struct xe_device *xe);
27 28
28const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc);29const void *xe_pci_graphics_ip_gen_param(struct kunit *test, const void *prev, char *desc);
29const void *xe_pci_media_ip_gen_param(const void *prev, char *desc);30const void *xe_pci_media_ip_gen_param(struct kunit *test, const void *prev, char *desc);
30const void *xe_pci_id_gen_param(const void *prev, char *desc);31const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc);
31const void *xe_pci_live_device_gen_param(const void *prev, char *desc);32const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc);
32 33
33#endif34#endif
include/kunit/test.h+90−5
@@ -92,6 +92,8 @@ struct kunit_attributes { · 91 unmodified lines
92 * @name: the name of the test case.92 * @name: the name of the test case.
93 * @generate_params: the generator function for parameterized tests.93 * @generate_params: the generator function for parameterized tests.
94 * @attr: the attributes associated with the test94 * @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.
95 *97 *
96 * A test case is a function with the signature,98 * A test case is a function with the signature,
97 * ``void (*)(struct kunit *)``99 * ``void (*)(struct kunit *)``
@@ -126,8 +128,11 @@ struct kunit_attributes { · 28 unmodified lines
126struct kunit_case {128struct kunit_case {
127 void (*run_case)(struct kunit *test);129 void (*run_case)(struct kunit *test);
128 const char *name;130 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);
130 struct kunit_attributes attr;133 struct kunit_attributes attr;
134 int (*param_init)(struct kunit *test);
135 void (*param_exit)(struct kunit *test);
131 136
132 /* private: internal use only. */137 /* private: internal use only. */
133 enum kunit_status status;138 enum kunit_status status;
@@ -218,6 +223,31 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status) · 84 unmodified lines
218 .generate_params = gen_params, \223 .generate_params = gen_params, \
219 .attr = attributes, .module_name = KBUILD_MODNAME}224 .attr = attributes, .module_name = KBUILD_MODNAME}
220 225
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
221/**251/**
222 * struct kunit_suite - describes a related collection of &struct kunit_case252 * struct kunit_suite - describes a related collection of &struct kunit_case
223 *253 *
@@ -263,19 +293,39 @@ struct kunit_suite_set { · 39 unmodified lines
263 struct kunit_suite * const *end;293 struct kunit_suite * const *end;
264};294};
265 295
296/* Stores the pointer to the parameter array and its metadata. */
297struct 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
266/**310/**
267 * struct kunit - represents a running instance of a test.311 * struct kunit - represents a running instance of a test.
268 *312 *
269 * @priv: for user to store arbitrary data. Commonly used to pass data313 * @priv: for user to store arbitrary data. Commonly used to pass data
270 * created in the init function (see &struct kunit_suite).314 * 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.
271 *318 *
272 * Used to store information about the current context under which the test319 * Used to store information about the current context under which the test
273 * is running. Most of this data is private and should only be accessed320 * 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 be321 * indirectly via public functions; the exceptions are @priv, @parent and
275 * used by the test writer to store arbitrary data.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.
276 */324 */
277struct kunit {325struct kunit {
278 void *priv;326 void *priv;
327 struct kunit *parent;
328 struct kunit_params params_array;
279 329
280 /* private: internal use only. */330 /* private: internal use only. */
281 const char *name; /* Read only after initialization! */331 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
346struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,396struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
347 struct kunit_suite_set suite_set);397 struct kunit_suite_set suite_set);
348 398
399const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc);
400
349#if IS_BUILTIN(CONFIG_KUNIT)401#if IS_BUILTIN(CONFIG_KUNIT)
350int kunit_run_all_tests(void);402int kunit_run_all_tests(void);
351#else403#else
@@ -1674,9 +1726,12 @@ do { \ · 1322 unmodified lines
1674 * Define function @name_gen_params which uses @array to generate parameters.1726 * Define function @name_gen_params which uses @array to generate parameters.
1675 */1727 */
1676#define KUNIT_ARRAY_PARAM(name, array, get_desc) \1728#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) \
1678 { \1731 { \
1679 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \1732 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \
1733 if (!prev) \
1734 kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL); \
1680 if (__next - (array) < ARRAY_SIZE((array))) { \1735 if (__next - (array) < ARRAY_SIZE((array))) { \
1681 void (*__get_desc)(typeof(__next), char *) = get_desc; \1736 void (*__get_desc)(typeof(__next), char *) = get_desc; \
1682 if (__get_desc) \1737 if (__get_desc) \
@@ -1695,9 +1750,12 @@ do { \ · 12 unmodified lines
1695 * Define function @name_gen_params which uses @array to generate parameters.1750 * Define function @name_gen_params which uses @array to generate parameters.
1696 */1751 */
1697#define KUNIT_ARRAY_PARAM_DESC(name, array, desc_member) \1752#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) \
1699 { \1755 { \
1700 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \1756 typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \
1757 if (!prev) \
1758 kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL); \
1701 if (__next - (array) < ARRAY_SIZE((array))) { \1759 if (__next - (array) < ARRAY_SIZE((array))) { \
1702 strscpy(desc, __next->desc_member, KUNIT_PARAM_DESC_SIZE); \1760 strscpy(desc, __next->desc_member, KUNIT_PARAM_DESC_SIZE); \
1703 return __next; \1761 return __next; \
@@ -1705,6 +1763,33 @@ do { \ · 1 unmodified line
1705 return NULL; \1763 return NULL; \
1706 }1764 }
1707 1765
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
1708// TODO(dlatypov@google.com): consider eventually migrating users to explicitly1793// TODO(dlatypov@google.com): consider eventually migrating users to explicitly
1709// include resource.h themselves if they need it.1794// include resource.h themselves if they need it.
1710#include <kunit/resource.h>1795#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
1383 * The thread counts are chosen to cover potentially interesting boundaries and1383 * The thread counts are chosen to cover potentially interesting boundaries and
1384 * corner cases (2 to 5), and then stress the system with larger counts.1384 * corner cases (2 to 5), and then stress the system with larger counts.
1385 */1385 */
1386static const void *nthreads_gen_params(const void *prev, char *desc)1386static const void *nthreads_gen_params(struct kunit *test, const void *prev, char *desc)
1387{1387{
1388 long nthreads = (long)prev;1388 long nthreads = (long)prev;
1389 1389
lib/Makefile+0−4
@@ -109,11 +109,7 @@ test_fpu-y := test_fpu_glue.o test_fpu_impl.o · 108 unmodified lines
109CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU)109CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU)
110CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)110CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
111 111
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).
114ifdef CONFIG_KUNIT
115obj-y += kunit/112obj-y += kunit/
116endif
117 113
118ifeq ($(CONFIG_DEBUG_KOBJECT),y)114ifeq ($(CONFIG_DEBUG_KOBJECT),y)
119CFLAGS_kobject.o += -DDEBUG115CFLAGS_kobject.o += -DDEBUG
lib/kunit/Kconfig+11−0
@@ -106,4 +106,15 @@ config KUNIT_DEFAULT_TIMEOUT · 105 unmodified lines
106 If unsure, the default timeout of 300 seconds is suitable for most106 If unsure, the default timeout of 300 seconds is suitable for most
107 cases.107 cases.
108 108
109config 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
109endif # KUNIT120endif # KUNIT
lib/kunit/Makefile+1−1
@@ -17,7 +17,7 @@ kunit-objs += debugfs.o · 16 unmodified lines
17endif17endif
18 18
19# KUnit 'hooks' are built-in even when KUnit is built as a module.19# KUnit 'hooks' are built-in even when KUnit is built as a module.
20obj-y += hooks.o20obj-$(if $(CONFIG_KUNIT),y) += hooks.o
21 21
22obj-$(CONFIG_KUNIT_TEST) += kunit-test.o22obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
23obj-$(CONFIG_KUNIT_TEST) += platform-test.o23obj-$(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
277 KUNIT_EXPECT_EQ(test, 1 + 1, 2);277 KUNIT_EXPECT_EQ(test, 1 + 1, 2);
278}278}
279 279
280/*
281 * This custom function allocates memory and sets the information we want
282 * stored in the kunit_resource->data field.
283 */
284static 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 */
298static 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 */
307static 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 */
318static 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 */
332static 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 */
364static 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 */
399static 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 */
427static 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 */
438static 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 */
471static 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 */
480static 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
280/*492/*
281 * Here we make a list of all the test cases we want to add to the test suite493 * Here we make a list of all the test cases we want to add to the test suite
282 * below.494 * below.
@@ -296,6 +508,11 @@ static struct kunit_case example_test_cases[] = { · 13 unmodified lines
296 KUNIT_CASE(example_static_stub_using_fn_ptr_test),508 KUNIT_CASE(example_static_stub_using_fn_ptr_test),
297 KUNIT_CASE(example_priv_test),509 KUNIT_CASE(example_priv_test),
298 KUNIT_CASE_PARAM(example_params_test, example_gen_params),510 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),
299 KUNIT_CASE_SLOW(example_slow_test),516 KUNIT_CASE_SLOW(example_slow_test),
300 {}517 {}
301};518};
lib/kunit/test.c+79−15
@@ -337,6 +337,14 @@ void __kunit_do_failed_assertion(struct kunit *test, · 336 unmodified lines
337}337}
338EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);338EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
339 339
340static 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
340void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)348void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
341{349{
342 spin_lock_init(&test->lock);350 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
347 string_stream_clear(log);355 string_stream_clear(log);
348 test->status = KUNIT_SUCCESS;356 test->status = KUNIT_SUCCESS;
349 test->status_comment[0] = '\0';357 test->status_comment[0] = '\0';
358 kunit_init_params(test);
350}359}
351EXPORT_SYMBOL_GPL(kunit_init_test);360EXPORT_SYMBOL_GPL(kunit_init_test);
352 361
@@ -641,12 +650,44 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total, · 288 unmodified lines
641 total->total += add.total;650 total->total += add.total;
642}651}
643 652
653const 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}
668EXPORT_SYMBOL_GPL(kunit_array_gen_params);
669
670static 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
644int kunit_run_tests(struct kunit_suite *suite)684int kunit_run_tests(struct kunit_suite *suite)
645{685{
646 char param_desc[KUNIT_PARAM_DESC_SIZE];686 char param_desc[KUNIT_PARAM_DESC_SIZE];
647 struct kunit_case *test_case;687 struct kunit_case *test_case;
648 struct kunit_result_stats suite_stats = { 0 };688 struct kunit_result_stats suite_stats = { 0 };
649 struct kunit_result_stats total_stats = { 0 };689 struct kunit_result_stats total_stats = { 0 };
690 const void *curr_param;
650 691
651 /* Taint the kernel so we know we've run tests. */692 /* Taint the kernel so we know we've run tests. */
652 add_taint(TAINT_TEST, LOCKDEP_STILL_OK);693 add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
@@ -677,41 +718,64 @@ int kunit_run_tests(struct kunit_suite *suite) · 24 unmodified lines
677 kunit_run_case_catch_errors(suite, test_case, &test);718 kunit_run_case_catch_errors(suite, test_case, &test);
678 kunit_update_stats(&param_stats, test.status);719 kunit_update_stats(&param_stats, test.status);
679 } else {720 } 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 }
680 /* Get initial param. */726 /* Get initial param. */
681 param_desc[0] = '\0';727 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);
683 test_case->status = KUNIT_SKIPPED;730 test_case->status = KUNIT_SKIPPED;
684 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT731 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
685 "KTAP version 1\n");732 "KTAP version 1\n");
686 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT733 kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
687 "# Subtest: %s", test_case->name);734 "# 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 }
688 741
689 while (test.param_value) {742 while (curr_param) {
690 kunit_run_case_catch_errors(suite, test_case, &test);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);
691 750
692 if (param_desc[0] == '\0') {751 if (param_desc[0] == '\0') {
693 snprintf(param_desc, sizeof(param_desc),752 snprintf(param_desc, sizeof(param_desc),
694 "param-%d", test.param_index);753 "param-%d", param_test.param_index);
695 }754 }
696 755
697 kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,756 kunit_print_ok_not_ok(&param_test, KUNIT_LEVEL_CASE_PARAM,
698 test.status,757 param_test.status,
699 test.param_index + 1,758 param_test.param_index,
700 param_desc,759 param_desc,
701 test.status_comment);760 param_test.status_comment);
702 761
703 kunit_update_stats(&param_stats, test.status);762 kunit_update_stats(&param_stats, param_test.status);
704 763
705 /* Get next param. */764 /* Get next param. */
706 param_desc[0] = '\0';765 param_desc[0] = '\0';
707 test.param_value = test_case->generate_params(test.param_value, param_desc);766 curr_param = test_case->generate_params(&test, curr_param,
708 test.param_index++;767 param_desc);
709 test.status = KUNIT_SUCCESS;
710 test.status_comment[0] = '\0';
711 test.priv = NULL;
712 }768 }
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);
713 }777 }
714 778test_case_end:
715 kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);779 kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
716 780
717 kunit_print_test_stats(&test, param_stats);781 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
210 status: kernel::bindings::kunit_status_KUNIT_SUCCESS,210 status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
211 module_name: core::ptr::null_mut(),211 module_name: core::ptr::null_mut(),
212 log: core::ptr::null_mut(),212 log: core::ptr::null_mut(),
213 param_init: None,
214 param_exit: None,
213 }215 }
214}216}
215 217
@@ -229,6 +231,8 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case { · 13 unmodified lines
229 status: kernel::bindings::kunit_status_KUNIT_SUCCESS,231 status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
230 module_name: core::ptr::null_mut(),232 module_name: core::ptr::null_mut(),
231 log: core::ptr::null_mut(),233 log: core::ptr::null_mut(),
234 param_init: None,
235 param_exit: None,
232 }236 }
233}237}
234 238
@@ -357,4 +361,11 @@ mod tests { · 122 unmodified lines
357 fn rust_test_kunit_in_kunit_test() {361 fn rust_test_kunit_in_kunit_test() {
358 assert!(in_kunit_test());362 assert!(in_kunit_test());
359 }363 }
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 }
360}371}
rust/macros/kunit.rs+36−12
@@ -5,6 +5,7 @@ · 4 unmodified lines
5//! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com>5//! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com>
6 6
7use proc_macro::{Delimiter, Group, TokenStream, TokenTree};7use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
8use std::collections::HashMap;
8use std::fmt::Write;9use std::fmt::Write;
9 10
10pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {11pub(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
41 // Get the functions set as tests. Search for `[test]` -> `fn`.42 // Get the functions set as tests. Search for `[test]` -> `fn`.
42 let mut body_it = body.stream().into_iter();43 let mut body_it = body.stream().into_iter();
43 let mut tests = Vec::new();44 let mut tests = Vec::new();
45 let mut attributes: HashMap<String, TokenStream> = HashMap::new();
44 while let Some(token) = body_it.next() {46 while let Some(token) = body_it.next() {
45 match token {47 match token {
46 TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() {48 TokenTree::Punct(ref p) if p.as_char() == '#' => match body_it.next() {
47 Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => {49 Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
48 let test_name = match body_it.next() {50 if let Some(TokenTree::Ident(name)) = g.stream().into_iter().next() {
49 Some(TokenTree::Ident(ident)) => ident.to_string(),51 // Collect attributes because we need to find which are tests. We also
50 _ => continue,52 // need to copy `cfg` attributes so tests can be conditionally enabled.
51 };53 attributes
52 tests.push(test_name);54 .entry(name.to_string())
55 .or_default()
56 .extend([token, TokenTree::Group(g)]);
57 }
58 continue;
53 }59 }
54 _ => continue,60 _ => (),
55 },61 },
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
56 _ => (),68 _ => (),
57 }69 }
70 attributes.clear();
58 }71 }
59 72
60 // Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration.73 // 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
100 let mut test_cases = "".to_owned();113 let mut test_cases = "".to_owned();
101 let mut assert_macros = "".to_owned();114 let mut assert_macros = "".to_owned();
102 let path = crate::helpers::file();115 let path = crate::helpers::file();
103 for test in &tests {116 let num_tests = tests.len();
117 for (test, cfg_attr) in tests {
104 let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}");118 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.
106 let kunit_wrapper = format!(122 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 }}"#,
108 );132 );
109 writeln!(kunit_macros, "{kunit_wrapper}").unwrap();133 writeln!(kunit_macros, "{kunit_wrapper}").unwrap();
110 writeln!(134 writeln!(
@@ -139,7 +163,7 @@ macro_rules! assert_eq {{ · 28 unmodified lines
139 writeln!(163 writeln!(
140 kunit_macros,164 kunit_macros,
141 "static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];",165 "static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];",
142 tests.len() + 1166 num_tests + 1
143 )167 )
144 .unwrap();168 .unwrap();
145 169
tools/testing/kunit/configs/arch_uml.config+2−3
@@ -1,8 +1,7 @@
1# Config options which are added to UML builds by default1# Config options which are added to UML builds by default
2 2
3# Enable virtio/pci, as a lot of tests require it.3# Enable pci, as a lot of tests require it.
4CONFIG_VIRTIO_UML=y4CONFIG_KUNIT_UML_PCI=y
5CONFIG_UML_PCI_OVER_VIRTIO=y
6 5
7# Enable FORTIFY_SOURCE for wider checking.6# Enable FORTIFY_SOURCE for wider checking.
8CONFIG_FORTIFY_SOURCE=y7CONFIG_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
228 fake_test.counts.passed = 1228 fake_test.counts.passed = 1
229 229
230 output: Iterable[str] = input_data230 output: Iterable[str] = input_data
231 if request.raw_output == 'all':231 if request.raw_output == 'all' or request.raw_output == 'full':
232 pass232 pass
233 elif request.raw_output == 'kunit':233 elif request.raw_output == 'kunit':
234 output = kunit_parser.extract_tap_lines(output)234 output = kunit_parser.extract_tap_lines(output)
@@ -425,7 +425,7 @@ def add_parse_opts(parser: argparse.ArgumentParser) -> None: · 190 unmodified lines
425 parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. '425 parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. '
426 'By default, filters to just KUnit output. Use '426 'By default, filters to just KUnit output. Use '
427 '--raw_output=all to show everything',427 '--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'])
429 parser.add_argument('--json',429 parser.add_argument('--json',
430 nargs='?',430 nargs='?',
431 help='Prints parsed test results as JSON to stdout or a file if '431 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
352 lines.pop()352 lines.pop()
353 return True353 return True
354 354
355TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?([^#]*)( # .*)?$')355TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) ?(- )?([^#]*)( # .*)?$')
356 356
357TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?(.*) # SKIP(.*)$')357TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) ?(- )?(.*) # SKIP ?(.*)$')
358 358
359def peek_test_name_match(lines: LineStream, test: Test) -> bool:359def peek_test_name_match(lines: LineStream, test: Test) -> bool:
360 """360 """
@@ -379,6 +379,8 @@ def peek_test_name_match(lines: LineStream, test: Test) -> bool: · 18 unmodified lines
379 if not match:379 if not match:
380 return False380 return False
381 name = match.group(4)381 name = match.group(4)
382 if not name:
383 return False
382 return name == test.name384 return name == test.name
383 385
384def parse_test_result(lines: LineStream, test: Test,386def parse_test_result(lines: LineStream, test: Test,
@@ -416,7 +418,7 @@ def parse_test_result(lines: LineStream, test: Test, · 31 unmodified lines
416 418
417 # Set name of test object419 # Set name of test object
418 if skip_match:420 if skip_match:
419 test.name = skip_match.group(4)421 test.name = skip_match.group(4) or skip_match.group(5)
420 else:422 else:
421 test.name = match.group(4)423 test.name = match.group(4)
422 424
tools/testing/kunit/qemu_configs/mips.py+18−0
@@ -0,0 +1,18 @@
1# SPDX-License-Identifier: GPL-2.0
2
3from ..qemu_config import QemuArchParams
4
5QEMU_ARCH = QemuArchParams(linux_arch='mips',
6 kconfig='''
7CONFIG_32BIT=y
8CONFIG_CPU_BIG_ENDIAN=y
9CONFIG_MIPS_MALTA=y
10CONFIG_SERIAL_8250=y
11CONFIG_SERIAL_8250_CONSOLE=y
12CONFIG_POWER_RESET=y
13CONFIG_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
3from ..qemu_config import QemuArchParams
4
5QEMU_ARCH = QemuArchParams(linux_arch='mips',
6 kconfig='''
7CONFIG_CPU_MIPS64_R2=y
8CONFIG_64BIT=y
9CONFIG_CPU_BIG_ENDIAN=y
10CONFIG_MIPS_MALTA=y
11CONFIG_SERIAL_8250=y
12CONFIG_SERIAL_8250_CONSOLE=y
13CONFIG_POWER_RESET=y
14CONFIG_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
3from ..qemu_config import QemuArchParams
4
5QEMU_ARCH = QemuArchParams(linux_arch='mips',
6 kconfig='''
7CONFIG_CPU_MIPS64_R2=y
8CONFIG_64BIT=y
9CONFIG_CPU_LITTLE_ENDIAN=y
10CONFIG_MIPS_MALTA=y
11CONFIG_SERIAL_8250=y
12CONFIG_SERIAL_8250_CONSOLE=y
13CONFIG_POWER_RESET=y
14CONFIG_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
3from ..qemu_config import QemuArchParams
4
5QEMU_ARCH = QemuArchParams(linux_arch='mips',
6 kconfig='''
7CONFIG_32BIT=y
8CONFIG_CPU_LITTLE_ENDIAN=y
9CONFIG_MIPS_MALTA=y
10CONFIG_SERIAL_8250=y
11CONFIG_SERIAL_8250_CONSOLE=y
12CONFIG_POWER_RESET=y
13CONFIG_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 @@
1TAP version 131TAP version 13
21..221..3
3# selftests: membarrier: membarrier_test_single_thread3# selftests: membarrier: membarrier_test_single_thread
4# TAP version 134# TAP version 13
5# 1..25# 1..2
@@ -12,3 +12,4 @@ ok 1 selftests: membarrier: membarrier_test_single_thread · 6 unmodified lines
12# ok 1 sys_membarrier available12# ok 1 sys_membarrier available
13# ok 2 sys membarrier invalid command test: command = -1, flags = 0, errno = 22. Failed as expected13# ok 2 sys membarrier invalid command test: command = -1, flags = 0, errno = 22. Failed as expected
14ok 2 selftests: membarrier: membarrier_test_multi_thread14ok 2 selftests: membarrier: membarrier_test_multi_thread
15ok 3 # SKIP selftests: membarrier: membarrier_test_multi_thread