linux_kselftest-kunit-6.18-rc1
- A seven patch series adds a new parameterized test features KUnit parameterized tests currently support two primary methods for getting parameters: 1. Defining custom logic within a generate_params() function. 2. Using the KUNIT_ARRAY_PARAM() and KUNIT_ARRAY_PARAM_DESC() macros with 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. - Fixes issues in kunit build artifacts cleanup, - Fixes parsing skipped test problem in kselftest framework, - Enables PCI on UML without triggering WARN() - a few other fixes and adds support for new configs such as MIPS -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAmjbB0QACgkQCwJExA0N QxxpUQ/8CslEjTv2+/LA122eGDtg8np61W1MTNEQslYyiobhZ9CXeFw4yJlTTb0w hShFK1pGAWgkVCVKIJOaeItY0hF3BIxyVtlQxDUKvukpMnLvZRI0KhG7p8aYnCq+ jfQRy4gqwjaHyLQekQ1v6vRdHdTfh5mB6OUOCYa7QPQuKhOkBOaq2ZJ+9eFnkPXl KUGTcC3pL0jQQmaSgo7zFUbdGSq0JZkNbpMj0fAYB0zs+MSpfkAnQYEw3qaxU1/Z lu+CQdom+77Kt0r7UyEb6xUBeRveqYAWv6oFKq3CBzhlEGCkqVv6zgNg48qMC0QO cVW0E61u8bVAalhb7hOT3QsEp1k01Lr2N9/VBLP4LRb2HMlD2qlXDo6ftBjNgx/y m5Kukh1wQoOTaTJekdDMlaRXwApdn3MegheXE83n4dr44C5oyhlR8fFk/0Y6gSEU RKUg8ZwUNUhCdw4VgBn8zoSkK18D74feRoustmuMS00I/8to3iGvQ7kn3nz2fPMb AaZ6a2K95gk9TrD4UZdHF1aPHDudn3e8g6LdSOV/NoRNI9H+fXfhvxcLwW8WCqOw u5qfTxNUk4mPQrCs9hcDnWm9Ppuu5YeY989rKkA7j4z71dhyMOPynpOM+vXRijB8 9zRcvyls87tgDAquIZvTp6Q/oEmi60OF40DRC9MjgOw0Iw7feIc= =VfHu -----END PGP SIGNATURE----- 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
This commit is contained in:
commit
30bbcb4470
|
@ -542,11 +542,31 @@ There is more boilerplate code involved, but it can:
|
|||
Parameterized Testing
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The table-driven testing pattern is common enough that KUnit has special
|
||||
support for it.
|
||||
To run a test case against multiple inputs, KUnit provides a parameterized
|
||||
testing framework. This feature formalizes and extends the concept of
|
||||
table-driven tests discussed previously.
|
||||
|
||||
By reusing the same ``cases`` array from above, we can write the test as a
|
||||
"parameterized test" with the following.
|
||||
A KUnit test is determined to be parameterized if a parameter generator function
|
||||
is provided when registering the test case. A test user can either write their
|
||||
own generator function or use one that is provided by KUnit. The generator
|
||||
function is stored in ``kunit_case->generate_params`` and can be set using the
|
||||
macros described in the section below.
|
||||
|
||||
To establish the terminology, a "parameterized test" is a test which is run
|
||||
multiple times (once per "parameter" or "parameter run"). Each parameter run has
|
||||
both its own independent ``struct kunit`` (the "parameter run context") and
|
||||
access to a shared parent ``struct kunit`` (the "parameterized test context").
|
||||
|
||||
Passing Parameters to a Test
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
There are three ways to provide the parameters to a test:
|
||||
|
||||
Array Parameter Macros:
|
||||
|
||||
KUnit provides special support for the common table-driven testing pattern.
|
||||
By applying either ``KUNIT_ARRAY_PARAM`` or ``KUNIT_ARRAY_PARAM_DESC`` to the
|
||||
``cases`` array from the previous section, we can create a parameterized test
|
||||
as shown below:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
|
@ -555,7 +575,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
|
|||
const char *str;
|
||||
const char *sha1;
|
||||
};
|
||||
const struct sha1_test_case cases[] = {
|
||||
static const struct sha1_test_case cases[] = {
|
||||
{
|
||||
.str = "hello world",
|
||||
.sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
|
||||
|
@ -590,6 +610,318 @@ By reusing the same ``cases`` array from above, we can write the test as a
|
|||
{}
|
||||
};
|
||||
|
||||
Custom Parameter Generator Function:
|
||||
|
||||
The generator function is responsible for generating parameters one-by-one
|
||||
and has the following signature:
|
||||
``const void* (*)(struct kunit *test, const void *prev, char *desc)``.
|
||||
You can pass the generator function to the ``KUNIT_CASE_PARAM``
|
||||
or ``KUNIT_CASE_PARAM_WITH_INIT`` macros.
|
||||
|
||||
The function receives the previously generated parameter as the ``prev`` argument
|
||||
(which is ``NULL`` on the first call) and can also access the parameterized
|
||||
test context passed as the ``test`` argument. KUnit calls this function
|
||||
repeatedly until it returns ``NULL``, which signifies that a parameterized
|
||||
test ended.
|
||||
|
||||
Below is an example of how it works:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#define MAX_TEST_BUFFER_SIZE 8
|
||||
|
||||
// Example generator function. It produces a sequence of buffer sizes that
|
||||
// are powers of two, starting at 1 (e.g., 1, 2, 4, 8).
|
||||
static const void *buffer_size_gen_params(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
long prev_buffer_size = (long)prev;
|
||||
long next_buffer_size = 1; // Start with an initial size of 1.
|
||||
|
||||
// Stop generating parameters if the limit is reached or exceeded.
|
||||
if (prev_buffer_size >= MAX_TEST_BUFFER_SIZE)
|
||||
return NULL;
|
||||
|
||||
// For subsequent calls, calculate the next size by doubling the previous one.
|
||||
if (prev)
|
||||
next_buffer_size = prev_buffer_size << 1;
|
||||
|
||||
return (void *)next_buffer_size;
|
||||
}
|
||||
|
||||
// Simple test to validate that kunit_kzalloc provides zeroed memory.
|
||||
static void buffer_zero_test(struct kunit *test)
|
||||
{
|
||||
long buffer_size = (long)test->param_value;
|
||||
// Use kunit_kzalloc to allocate a zero-initialized buffer. This makes the
|
||||
// memory "parameter run managed," meaning it's automatically cleaned up at
|
||||
// the end of each parameter run.
|
||||
int *buf = kunit_kzalloc(test, buffer_size * sizeof(int), GFP_KERNEL);
|
||||
|
||||
// Ensure the allocation was successful.
|
||||
KUNIT_ASSERT_NOT_NULL(test, buf);
|
||||
|
||||
// Loop through the buffer and confirm every element is zero.
|
||||
for (int i = 0; i < buffer_size; i++)
|
||||
KUNIT_EXPECT_EQ(test, buf[i], 0);
|
||||
}
|
||||
|
||||
static struct kunit_case buffer_test_cases[] = {
|
||||
KUNIT_CASE_PARAM(buffer_zero_test, buffer_size_gen_params),
|
||||
{}
|
||||
};
|
||||
|
||||
Runtime Parameter Array Registration in the Init Function:
|
||||
|
||||
For scenarios where you might need to initialize a parameterized test, you
|
||||
can directly register a parameter array to the parameterized test context.
|
||||
|
||||
To do this, you must pass the parameterized test context, the array itself,
|
||||
the array size, and a ``get_description()`` function to the
|
||||
``kunit_register_params_array()`` macro. This macro populates
|
||||
``struct kunit_params`` within the parameterized test context, effectively
|
||||
storing a parameter array object. The ``get_description()`` function will
|
||||
be used for populating parameter descriptions and has the following signature:
|
||||
``void (*)(struct kunit *test, const void *param, char *desc)``. Note that it
|
||||
also has access to the parameterized test context.
|
||||
|
||||
.. important::
|
||||
When using this way to register a parameter array, you will need to
|
||||
manually pass ``kunit_array_gen_params()`` as the generator function to
|
||||
``KUNIT_CASE_PARAM_WITH_INIT``. ``kunit_array_gen_params()`` is a KUnit
|
||||
helper that will use the registered array to generate the parameters.
|
||||
|
||||
If needed, instead of passing the KUnit helper, you can also pass your
|
||||
own custom generator function that utilizes the parameter array. To
|
||||
access the parameter array from within the parameter generator
|
||||
function use ``test->params_array.params``.
|
||||
|
||||
The ``kunit_register_params_array()`` macro should be called within a
|
||||
``param_init()`` function that initializes the parameterized test and has
|
||||
the following signature ``int (*)(struct kunit *test)``. For a detailed
|
||||
explanation of this mechanism please refer to the "Adding Shared Resources"
|
||||
section that is after this one. This method supports registering both
|
||||
dynamically built and static parameter arrays.
|
||||
|
||||
The code snippet below shows the ``example_param_init_dynamic_arr`` test that
|
||||
utilizes ``make_fibonacci_params()`` to create a dynamic array, which is then
|
||||
registered using ``kunit_register_params_array()``. To see the full code
|
||||
please refer to lib/kunit/kunit-example-test.c.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
/*
|
||||
* Example of a parameterized test param_init() function that registers a dynamic
|
||||
* array of parameters.
|
||||
*/
|
||||
static int example_param_init_dynamic_arr(struct kunit *test)
|
||||
{
|
||||
size_t seq_size;
|
||||
int *fibonacci_params;
|
||||
|
||||
kunit_info(test, "initializing parameterized test\n");
|
||||
|
||||
seq_size = 6;
|
||||
fibonacci_params = make_fibonacci_params(test, seq_size);
|
||||
if (!fibonacci_params)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Passes the dynamic parameter array information to the parameterized test
|
||||
* context struct kunit. The array and its metadata will be stored in
|
||||
* test->parent->params_array. The array itself will be located in
|
||||
* params_data.params.
|
||||
*/
|
||||
kunit_register_params_array(test, fibonacci_params, seq_size,
|
||||
example_param_dynamic_arr_get_desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct kunit_case example_test_cases[] = {
|
||||
/*
|
||||
* Note how we pass kunit_array_gen_params() to use the array we
|
||||
* registered in example_param_init_dynamic_arr() to generate
|
||||
* parameters.
|
||||
*/
|
||||
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr,
|
||||
kunit_array_gen_params,
|
||||
example_param_init_dynamic_arr,
|
||||
example_param_exit_dynamic_arr),
|
||||
{}
|
||||
};
|
||||
|
||||
Adding Shared Resources
|
||||
^^^^^^^^^^^^^^^^^^^^^^^
|
||||
All parameter runs in this framework hold a reference to the parameterized test
|
||||
context, which can be accessed using the parent ``struct kunit`` pointer. The
|
||||
parameterized test context is not used to execute any test logic itself; instead,
|
||||
it serves as a container for shared resources.
|
||||
|
||||
It's possible to add resources to share between parameter runs within a
|
||||
parameterized test by using ``KUNIT_CASE_PARAM_WITH_INIT``, to which you pass
|
||||
custom ``param_init()`` and ``param_exit()`` functions. These functions run once
|
||||
before and once after the parameterized test, respectively.
|
||||
|
||||
The ``param_init()`` function, with the signature ``int (*)(struct kunit *test)``,
|
||||
can be used for adding resources to the ``resources`` or ``priv`` fields of
|
||||
the parameterized test context, registering the parameter array, and any other
|
||||
initialization logic.
|
||||
|
||||
The ``param_exit()`` function, with the signature ``void (*)(struct kunit *test)``,
|
||||
can be used to release any resources that were not parameterized test managed (i.e.
|
||||
not automatically cleaned up after the parameterized test ends) and for any other
|
||||
exit logic.
|
||||
|
||||
Both ``param_init()`` and ``param_exit()`` are passed the parameterized test
|
||||
context behind the scenes. However, the test case function receives the parameter
|
||||
run context. Therefore, to manage and access shared resources from within a test
|
||||
case function, you must use ``test->parent``.
|
||||
|
||||
For instance, finding a shared resource allocated by the Resource API requires
|
||||
passing ``test->parent`` to ``kunit_find_resource()``. This principle extends to
|
||||
all other APIs that might be used in the test case function, including
|
||||
``kunit_kzalloc()``, ``kunit_kmalloc_array()``, and others (see
|
||||
Documentation/dev-tools/kunit/api/test.rst and the
|
||||
Documentation/dev-tools/kunit/api/resource.rst).
|
||||
|
||||
.. note::
|
||||
The ``suite->init()`` function, which executes before each parameter run,
|
||||
receives the parameter run context. Therefore, any resources set up in
|
||||
``suite->init()`` are cleaned up after each parameter run.
|
||||
|
||||
The code below shows how you can add the shared resources. Note that this code
|
||||
utilizes the Resource API, which you can read more about here:
|
||||
Documentation/dev-tools/kunit/api/resource.rst. To see the full version of this
|
||||
code please refer to lib/kunit/kunit-example-test.c.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static int example_resource_init(struct kunit_resource *res, void *context)
|
||||
{
|
||||
... /* Code that allocates memory and stores context in res->data. */
|
||||
}
|
||||
|
||||
/* This function deallocates memory for the kunit_resource->data field. */
|
||||
static void example_resource_free(struct kunit_resource *res)
|
||||
{
|
||||
kfree(res->data);
|
||||
}
|
||||
|
||||
/* This match function locates a test resource based on defined criteria. */
|
||||
static bool example_resource_alloc_match(struct kunit *test, struct kunit_resource *res,
|
||||
void *match_data)
|
||||
{
|
||||
return res->data && res->free == example_resource_free;
|
||||
}
|
||||
|
||||
/* Function to initialize the parameterized test. */
|
||||
static int example_param_init(struct kunit *test)
|
||||
{
|
||||
int ctx = 3; /* Data to be stored. */
|
||||
void *data = kunit_alloc_resource(test, example_resource_init,
|
||||
example_resource_free,
|
||||
GFP_KERNEL, &ctx);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
kunit_register_params_array(test, example_params_array,
|
||||
ARRAY_SIZE(example_params_array));
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Example test that uses shared resources in test->resources. */
|
||||
static void example_params_test_with_init(struct kunit *test)
|
||||
{
|
||||
int threshold;
|
||||
const struct example_param *param = test->param_value;
|
||||
/* Here we pass test->parent to access the parameterized test context. */
|
||||
struct kunit_resource *res = kunit_find_resource(test->parent,
|
||||
example_resource_alloc_match,
|
||||
NULL);
|
||||
|
||||
threshold = *((int *)res->data);
|
||||
KUNIT_ASSERT_LE(test, param->value, threshold);
|
||||
kunit_put_resource(res);
|
||||
}
|
||||
|
||||
static struct kunit_case example_test_cases[] = {
|
||||
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, kunit_array_gen_params,
|
||||
example_param_init, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
As an alternative to using the KUnit Resource API for sharing resources, you can
|
||||
place them in ``test->parent->priv``. This serves as a more lightweight method
|
||||
for resource storage, best for scenarios where complex resource management is
|
||||
not required.
|
||||
|
||||
As stated previously ``param_init()`` and ``param_exit()`` get the parameterized
|
||||
test context. So, you can directly use ``test->priv`` within ``param_init/exit``
|
||||
to manage shared resources. However, from within the test case function, you must
|
||||
navigate up to the parent ``struct kunit`` i.e. the parameterized test context.
|
||||
Therefore, you need to use ``test->parent->priv`` to access those same
|
||||
resources.
|
||||
|
||||
The resources placed in ``test->parent->priv`` will need to be allocated in
|
||||
memory to persist across the parameter runs. If memory is allocated using the
|
||||
KUnit memory allocation APIs (described more in the "Allocating Memory" section
|
||||
below), you won't need to worry about deallocation. The APIs will make the memory
|
||||
parameterized test 'managed', ensuring that it will automatically get cleaned up
|
||||
after the parameterized test concludes.
|
||||
|
||||
The code below demonstrates example usage of the ``priv`` field for shared
|
||||
resources:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static const struct example_param {
|
||||
int value;
|
||||
} example_params_array[] = {
|
||||
{ .value = 3, },
|
||||
{ .value = 2, },
|
||||
{ .value = 1, },
|
||||
{ .value = 0, },
|
||||
};
|
||||
|
||||
/* Initialize the parameterized test context. */
|
||||
static int example_param_init_priv(struct kunit *test)
|
||||
{
|
||||
int ctx = 3; /* Data to be stored. */
|
||||
int arr_size = ARRAY_SIZE(example_params_array);
|
||||
|
||||
/*
|
||||
* Allocate memory using kunit_kzalloc(). Since the `param_init`
|
||||
* function receives the parameterized test context, this memory
|
||||
* allocation will be scoped to the lifetime of the parameterized test.
|
||||
*/
|
||||
test->priv = kunit_kzalloc(test, sizeof(int), GFP_KERNEL);
|
||||
|
||||
/* Assign the context value to test->priv.*/
|
||||
*((int *)test->priv) = ctx;
|
||||
|
||||
/* Register the parameter array. */
|
||||
kunit_register_params_array(test, example_params_array, arr_size, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void example_params_test_with_init_priv(struct kunit *test)
|
||||
{
|
||||
int threshold;
|
||||
const struct example_param *param = test->param_value;
|
||||
|
||||
/* By design, test->parent will not be NULL. */
|
||||
KUNIT_ASSERT_NOT_NULL(test, test->parent);
|
||||
|
||||
/* Here we use test->parent->priv to access the shared resource. */
|
||||
threshold = *(int *)test->parent->priv;
|
||||
|
||||
KUNIT_ASSERT_LE(test, param->value, threshold);
|
||||
}
|
||||
|
||||
static struct kunit_case example_tests[] = {
|
||||
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_priv,
|
||||
kunit_array_gen_params,
|
||||
example_param_init_priv, NULL),
|
||||
{}
|
||||
};
|
||||
|
||||
Allocating Memory
|
||||
-----------------
|
||||
|
||||
|
|
|
@ -44,9 +44,9 @@ KUNIT_ARRAY_PARAM(pci_id, pciidlist, xe_pci_id_kunit_desc);
|
|||
*
|
||||
* Return: pointer to the next parameter or NULL if no more parameters
|
||||
*/
|
||||
const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc)
|
||||
const void *xe_pci_graphics_ip_gen_param(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
return graphics_ip_gen_params(prev, desc);
|
||||
return graphics_ip_gen_params(test, prev, desc);
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param);
|
||||
|
||||
|
@ -61,9 +61,9 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_graphics_ip_gen_param);
|
|||
*
|
||||
* Return: pointer to the next parameter or NULL if no more parameters
|
||||
*/
|
||||
const void *xe_pci_media_ip_gen_param(const void *prev, char *desc)
|
||||
const void *xe_pci_media_ip_gen_param(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
return media_ip_gen_params(prev, desc);
|
||||
return media_ip_gen_params(test, prev, desc);
|
||||
}
|
||||
EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);
|
||||
|
||||
|
@ -78,9 +78,9 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_media_ip_gen_param);
|
|||
*
|
||||
* Return: pointer to the next parameter or NULL if no more parameters
|
||||
*/
|
||||
const void *xe_pci_id_gen_param(const void *prev, char *desc)
|
||||
const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
const struct pci_device_id *pci = pci_id_gen_params(prev, desc);
|
||||
const struct pci_device_id *pci = pci_id_gen_params(test, prev, desc);
|
||||
|
||||
return pci->driver_data ? pci : NULL;
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ EXPORT_SYMBOL_IF_KUNIT(xe_pci_fake_device_init);
|
|||
* Return: pointer to the next &struct xe_device ready to be used as a parameter
|
||||
* or NULL if there are no more Xe devices on the system.
|
||||
*/
|
||||
const void *xe_pci_live_device_gen_param(const void *prev, char *desc)
|
||||
const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
const struct xe_device *xe = prev;
|
||||
struct device *dev = xe ? xe->drm.dev : NULL;
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#define _XE_PCI_TEST_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <kunit/test.h>
|
||||
|
||||
#include "xe_platform_types.h"
|
||||
#include "xe_sriov_types.h"
|
||||
|
@ -25,9 +26,9 @@ struct xe_pci_fake_data {
|
|||
|
||||
int xe_pci_fake_device_init(struct xe_device *xe);
|
||||
|
||||
const void *xe_pci_graphics_ip_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_media_ip_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_id_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_live_device_gen_param(const void *prev, char *desc);
|
||||
const void *xe_pci_graphics_ip_gen_param(struct kunit *test, const void *prev, char *desc);
|
||||
const void *xe_pci_media_ip_gen_param(struct kunit *test, const void *prev, char *desc);
|
||||
const void *xe_pci_id_gen_param(struct kunit *test, const void *prev, char *desc);
|
||||
const void *xe_pci_live_device_gen_param(struct kunit *test, const void *prev, char *desc);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -92,6 +92,8 @@ struct kunit_attributes {
|
|||
* @name: the name of the test case.
|
||||
* @generate_params: the generator function for parameterized tests.
|
||||
* @attr: the attributes associated with the test
|
||||
* @param_init: The init function to run before a parameterized test.
|
||||
* @param_exit: The exit function to run after a parameterized test.
|
||||
*
|
||||
* A test case is a function with the signature,
|
||||
* ``void (*)(struct kunit *)``
|
||||
|
@ -126,8 +128,11 @@ struct kunit_attributes {
|
|||
struct kunit_case {
|
||||
void (*run_case)(struct kunit *test);
|
||||
const char *name;
|
||||
const void* (*generate_params)(const void *prev, char *desc);
|
||||
const void* (*generate_params)(struct kunit *test,
|
||||
const void *prev, char *desc);
|
||||
struct kunit_attributes attr;
|
||||
int (*param_init)(struct kunit *test);
|
||||
void (*param_exit)(struct kunit *test);
|
||||
|
||||
/* private: internal use only. */
|
||||
enum kunit_status status;
|
||||
|
@ -218,6 +223,31 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
|
|||
.generate_params = gen_params, \
|
||||
.attr = attributes, .module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* KUNIT_CASE_PARAM_WITH_INIT - Define a parameterized KUnit test case with custom
|
||||
* param_init() and param_exit() functions.
|
||||
* @test_name: The function implementing the test case.
|
||||
* @gen_params: The function to generate parameters for the test case.
|
||||
* @init: A reference to the param_init() function to run before a parameterized test.
|
||||
* @exit: A reference to the param_exit() function to run after a parameterized test.
|
||||
*
|
||||
* Provides the option to register param_init() and param_exit() functions.
|
||||
* param_init/exit will be passed the parameterized test context and run once
|
||||
* before and once after the parameterized test. The init function can be used
|
||||
* to add resources to share between parameter runs, pass parameter arrays,
|
||||
* and any other setup logic. The exit function can be used to clean up resources
|
||||
* that were not managed by the parameterized test, and any other teardown logic.
|
||||
*
|
||||
* Note: If you are registering a parameter array in param_init() with
|
||||
* kunit_register_param_array() then you need to pass kunit_array_gen_params()
|
||||
* to this as the generator function.
|
||||
*/
|
||||
#define KUNIT_CASE_PARAM_WITH_INIT(test_name, gen_params, init, exit) \
|
||||
{ .run_case = test_name, .name = #test_name, \
|
||||
.generate_params = gen_params, \
|
||||
.param_init = init, .param_exit = exit, \
|
||||
.module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* struct kunit_suite - describes a related collection of &struct kunit_case
|
||||
*
|
||||
|
@ -263,19 +293,39 @@ struct kunit_suite_set {
|
|||
struct kunit_suite * const *end;
|
||||
};
|
||||
|
||||
/* Stores the pointer to the parameter array and its metadata. */
|
||||
struct kunit_params {
|
||||
/*
|
||||
* Reference to the parameter array for a parameterized test. This
|
||||
* is NULL if a parameter array wasn't directly passed to the
|
||||
* parameterized test context struct kunit via kunit_register_params_array().
|
||||
*/
|
||||
const void *params;
|
||||
/* Reference to a function that gets the description of a parameter. */
|
||||
void (*get_description)(struct kunit *test, const void *param, char *desc);
|
||||
size_t num_params;
|
||||
size_t elem_size;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kunit - represents a running instance of a test.
|
||||
*
|
||||
* @priv: for user to store arbitrary data. Commonly used to pass data
|
||||
* created in the init function (see &struct kunit_suite).
|
||||
* @parent: reference to the parent context of type struct kunit that can
|
||||
* be used for storing shared resources.
|
||||
* @params_array: for storing the parameter array.
|
||||
*
|
||||
* Used to store information about the current context under which the test
|
||||
* is running. Most of this data is private and should only be accessed
|
||||
* indirectly via public functions; the one exception is @priv which can be
|
||||
* used by the test writer to store arbitrary data.
|
||||
* indirectly via public functions; the exceptions are @priv, @parent and
|
||||
* @params_array which can be used by the test writer to store arbitrary data,
|
||||
* access the parent context, and to store the parameter array, respectively.
|
||||
*/
|
||||
struct kunit {
|
||||
void *priv;
|
||||
struct kunit *parent;
|
||||
struct kunit_params params_array;
|
||||
|
||||
/* private: internal use only. */
|
||||
const char *name; /* Read only after initialization! */
|
||||
|
@ -346,6 +396,8 @@ void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
|
|||
struct kunit_suite_set kunit_merge_suite_sets(struct kunit_suite_set init_suite_set,
|
||||
struct kunit_suite_set suite_set);
|
||||
|
||||
const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc);
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KUNIT)
|
||||
int kunit_run_all_tests(void);
|
||||
#else
|
||||
|
@ -1674,9 +1726,12 @@ do { \
|
|||
* Define function @name_gen_params which uses @array to generate parameters.
|
||||
*/
|
||||
#define KUNIT_ARRAY_PARAM(name, array, get_desc) \
|
||||
static const void *name##_gen_params(const void *prev, char *desc) \
|
||||
static const void *name##_gen_params(struct kunit *test, \
|
||||
const void *prev, char *desc) \
|
||||
{ \
|
||||
typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \
|
||||
if (!prev) \
|
||||
kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL); \
|
||||
if (__next - (array) < ARRAY_SIZE((array))) { \
|
||||
void (*__get_desc)(typeof(__next), char *) = get_desc; \
|
||||
if (__get_desc) \
|
||||
|
@ -1695,9 +1750,12 @@ do { \
|
|||
* Define function @name_gen_params which uses @array to generate parameters.
|
||||
*/
|
||||
#define KUNIT_ARRAY_PARAM_DESC(name, array, desc_member) \
|
||||
static const void *name##_gen_params(const void *prev, char *desc) \
|
||||
static const void *name##_gen_params(struct kunit *test, \
|
||||
const void *prev, char *desc) \
|
||||
{ \
|
||||
typeof((array)[0]) *__next = prev ? ((typeof(__next)) prev) + 1 : (array); \
|
||||
if (!prev) \
|
||||
kunit_register_params_array(test, array, ARRAY_SIZE(array), NULL); \
|
||||
if (__next - (array) < ARRAY_SIZE((array))) { \
|
||||
strscpy(desc, __next->desc_member, KUNIT_PARAM_DESC_SIZE); \
|
||||
return __next; \
|
||||
|
@ -1705,6 +1763,33 @@ do { \
|
|||
return NULL; \
|
||||
}
|
||||
|
||||
/**
|
||||
* kunit_register_params_array() - Register parameter array for a KUnit test.
|
||||
* @test: The KUnit test structure to which parameters will be added.
|
||||
* @array: An array of test parameters.
|
||||
* @param_count: Number of parameters.
|
||||
* @get_desc: Function that generates a string description for a given parameter
|
||||
* element.
|
||||
*
|
||||
* This macro initializes the @test's parameter array data, storing information
|
||||
* including the parameter array, its count, the element size, and the parameter
|
||||
* description function within `test->params_array`.
|
||||
*
|
||||
* Note: If using this macro in param_init(), kunit_array_gen_params()
|
||||
* will then need to be manually provided as the parameter generator function to
|
||||
* KUNIT_CASE_PARAM_WITH_INIT(). kunit_array_gen_params() is a KUnit
|
||||
* function that uses the registered array to generate parameters
|
||||
*/
|
||||
#define kunit_register_params_array(test, array, param_count, get_desc) \
|
||||
do { \
|
||||
struct kunit *_test = (test); \
|
||||
const typeof((array)[0]) * _params_ptr = &(array)[0]; \
|
||||
_test->params_array.params = _params_ptr; \
|
||||
_test->params_array.num_params = (param_count); \
|
||||
_test->params_array.elem_size = sizeof(*_params_ptr); \
|
||||
_test->params_array.get_description = (get_desc); \
|
||||
} while (0)
|
||||
|
||||
// TODO(dlatypov@google.com): consider eventually migrating users to explicitly
|
||||
// include resource.h themselves if they need it.
|
||||
#include <kunit/resource.h>
|
||||
|
|
|
@ -1383,7 +1383,7 @@ static void test_atomic_builtins_missing_barrier(struct kunit *test)
|
|||
* The thread counts are chosen to cover potentially interesting boundaries and
|
||||
* corner cases (2 to 5), and then stress the system with larger counts.
|
||||
*/
|
||||
static const void *nthreads_gen_params(const void *prev, char *desc)
|
||||
static const void *nthreads_gen_params(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
long nthreads = (long)prev;
|
||||
|
||||
|
|
|
@ -109,11 +109,7 @@ test_fpu-y := test_fpu_glue.o test_fpu_impl.o
|
|||
CFLAGS_test_fpu_impl.o += $(CC_FLAGS_FPU)
|
||||
CFLAGS_REMOVE_test_fpu_impl.o += $(CC_FLAGS_NO_FPU)
|
||||
|
||||
# Some KUnit files (hooks.o) need to be built-in even when KUnit is a module,
|
||||
# so we can't just use obj-$(CONFIG_KUNIT).
|
||||
ifdef CONFIG_KUNIT
|
||||
obj-y += kunit/
|
||||
endif
|
||||
|
||||
ifeq ($(CONFIG_DEBUG_KOBJECT),y)
|
||||
CFLAGS_kobject.o += -DDEBUG
|
||||
|
|
|
@ -106,4 +106,15 @@ config KUNIT_DEFAULT_TIMEOUT
|
|||
If unsure, the default timeout of 300 seconds is suitable for most
|
||||
cases.
|
||||
|
||||
config KUNIT_UML_PCI
|
||||
bool "KUnit UML PCI Support"
|
||||
depends on UML
|
||||
select UML_PCI
|
||||
help
|
||||
Enables the PCI subsystem on UML for use by KUnit tests.
|
||||
Some KUnit tests require the PCI core which is not enabled by
|
||||
default on UML.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
endif # KUNIT
|
||||
|
|
|
@ -17,7 +17,7 @@ kunit-objs += debugfs.o
|
|||
endif
|
||||
|
||||
# KUnit 'hooks' are built-in even when KUnit is built as a module.
|
||||
obj-y += hooks.o
|
||||
obj-$(if $(CONFIG_KUNIT),y) += hooks.o
|
||||
|
||||
obj-$(CONFIG_KUNIT_TEST) += kunit-test.o
|
||||
obj-$(CONFIG_KUNIT_TEST) += platform-test.o
|
||||
|
|
|
@ -277,6 +277,218 @@ static void example_slow_test(struct kunit *test)
|
|||
KUNIT_EXPECT_EQ(test, 1 + 1, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* This custom function allocates memory and sets the information we want
|
||||
* stored in the kunit_resource->data field.
|
||||
*/
|
||||
static int example_resource_init(struct kunit_resource *res, void *context)
|
||||
{
|
||||
int *info = kmalloc(sizeof(*info), GFP_KERNEL);
|
||||
|
||||
if (!info)
|
||||
return -ENOMEM;
|
||||
*info = *(int *)context;
|
||||
res->data = info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This function deallocates memory for the kunit_resource->data field.
|
||||
*/
|
||||
static void example_resource_free(struct kunit_resource *res)
|
||||
{
|
||||
kfree(res->data);
|
||||
}
|
||||
|
||||
/*
|
||||
* This match function is invoked by kunit_find_resource() to locate
|
||||
* a test resource based on certain criteria.
|
||||
*/
|
||||
static bool example_resource_alloc_match(struct kunit *test,
|
||||
struct kunit_resource *res,
|
||||
void *match_data)
|
||||
{
|
||||
return res->data && res->free == example_resource_free;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an example of a function that provides a description for each of the
|
||||
* parameters in a parameterized test.
|
||||
*/
|
||||
static void example_param_array_get_desc(struct kunit *test, const void *p, char *desc)
|
||||
{
|
||||
const struct example_param *param = p;
|
||||
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE,
|
||||
"example check if %d is less than or equal to 3", param->value);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function gets passed in the parameterized test context i.e. the
|
||||
* struct kunit belonging to the parameterized test. You can use this function
|
||||
* to add resources you want shared across the whole parameterized test or
|
||||
* for additional setup.
|
||||
*/
|
||||
static int example_param_init(struct kunit *test)
|
||||
{
|
||||
int ctx = 3; /* Data to be stored. */
|
||||
size_t arr_size = ARRAY_SIZE(example_params_array);
|
||||
|
||||
/*
|
||||
* This allocates a struct kunit_resource, sets its data field to
|
||||
* ctx, and adds it to the struct kunit's resources list. Note that
|
||||
* this is parameterized test managed. So, it doesn't need to have
|
||||
* a custom exit function to deallocation as it will get cleaned up at
|
||||
* the end of the parameterized test.
|
||||
*/
|
||||
void *data = kunit_alloc_resource(test, example_resource_init, example_resource_free,
|
||||
GFP_KERNEL, &ctx);
|
||||
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* Pass the parameter array information to the parameterized test context
|
||||
* struct kunit. Note that you will need to provide kunit_array_gen_params()
|
||||
* as the generator function to KUNIT_CASE_PARAM_WITH_INIT() when registering
|
||||
* a parameter array this route.
|
||||
*/
|
||||
kunit_register_params_array(test, example_params_array, arr_size,
|
||||
example_param_array_get_desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an example of a test that uses shared resources available in the
|
||||
* parameterized test context.
|
||||
*/
|
||||
static void example_params_test_with_init(struct kunit *test)
|
||||
{
|
||||
int threshold;
|
||||
struct kunit_resource *res;
|
||||
const struct example_param *param = test->param_value;
|
||||
|
||||
/* By design, param pointer will not be NULL. */
|
||||
KUNIT_ASSERT_NOT_NULL(test, param);
|
||||
|
||||
/*
|
||||
* Here we pass test->parent to search for shared resources in the
|
||||
* parameterized test context.
|
||||
*/
|
||||
res = kunit_find_resource(test->parent, example_resource_alloc_match, NULL);
|
||||
|
||||
KUNIT_ASSERT_NOT_NULL(test, res);
|
||||
|
||||
/* Since kunit_resource->data is a void pointer we need to typecast it. */
|
||||
threshold = *((int *)res->data);
|
||||
|
||||
/* Assert that the parameter is less than or equal to a certain threshold. */
|
||||
KUNIT_ASSERT_LE(test, param->value, threshold);
|
||||
|
||||
/* This decreases the reference count after calling kunit_find_resource(). */
|
||||
kunit_put_resource(res);
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to create a parameter array of Fibonacci numbers. This example
|
||||
* highlights a parameter generation scenario that is:
|
||||
* 1. Not feasible to fully pre-generate at compile time.
|
||||
* 2. Challenging to implement with a standard generate_params() function,
|
||||
* as it only provides the previous parameter, while Fibonacci requires
|
||||
* access to two preceding values for calculation.
|
||||
*/
|
||||
static void *make_fibonacci_params(struct kunit *test, size_t seq_size)
|
||||
{
|
||||
int *seq;
|
||||
|
||||
if (seq_size <= 0)
|
||||
return NULL;
|
||||
/*
|
||||
* Using kunit_kmalloc_array here ties the lifetime of the array to
|
||||
* the parameterized test i.e. it will get automatically cleaned up
|
||||
* by KUnit after the parameterized test finishes.
|
||||
*/
|
||||
seq = kunit_kmalloc_array(test, seq_size, sizeof(int), GFP_KERNEL);
|
||||
|
||||
if (!seq)
|
||||
return NULL;
|
||||
if (seq_size >= 1)
|
||||
seq[0] = 0;
|
||||
if (seq_size >= 2)
|
||||
seq[1] = 1;
|
||||
for (int i = 2; i < seq_size; i++)
|
||||
seq[i] = seq[i - 1] + seq[i - 2];
|
||||
return seq;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is an example of a function that provides a description for each of the
|
||||
* parameters.
|
||||
*/
|
||||
static void example_param_dynamic_arr_get_desc(struct kunit *test, const void *p, char *desc)
|
||||
{
|
||||
const int *fib_num = p;
|
||||
|
||||
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "fibonacci param: %d", *fib_num);
|
||||
}
|
||||
|
||||
/*
|
||||
* Example of a parameterized test param_init() function that registers a dynamic
|
||||
* array of parameters.
|
||||
*/
|
||||
static int example_param_init_dynamic_arr(struct kunit *test)
|
||||
{
|
||||
size_t seq_size;
|
||||
int *fibonacci_params;
|
||||
|
||||
kunit_info(test, "initializing parameterized test\n");
|
||||
|
||||
seq_size = 6;
|
||||
fibonacci_params = make_fibonacci_params(test, seq_size);
|
||||
|
||||
if (!fibonacci_params)
|
||||
return -ENOMEM;
|
||||
|
||||
/*
|
||||
* Passes the dynamic parameter array information to the parameterized test
|
||||
* context struct kunit. The array and its metadata will be stored in
|
||||
* test->parent->params_array. The array itself will be located in
|
||||
* params_data.params.
|
||||
*
|
||||
* Note that you will need to pass kunit_array_gen_params() as the
|
||||
* generator function to KUNIT_CASE_PARAM_WITH_INIT() when registering
|
||||
* a parameter array this route.
|
||||
*/
|
||||
kunit_register_params_array(test, fibonacci_params, seq_size,
|
||||
example_param_dynamic_arr_get_desc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Example of a parameterized test param_exit() function that outputs a log
|
||||
* at the end of the parameterized test. It could also be used for any other
|
||||
* teardown logic.
|
||||
*/
|
||||
static void example_param_exit_dynamic_arr(struct kunit *test)
|
||||
{
|
||||
kunit_info(test, "exiting parameterized test\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Example of test that uses the registered dynamic array to perform assertions
|
||||
* and expectations.
|
||||
*/
|
||||
static void example_params_test_with_init_dynamic_arr(struct kunit *test)
|
||||
{
|
||||
const int *param = test->param_value;
|
||||
int param_val;
|
||||
|
||||
/* By design, param pointer will not be NULL. */
|
||||
KUNIT_ASSERT_NOT_NULL(test, param);
|
||||
|
||||
param_val = *param;
|
||||
KUNIT_EXPECT_EQ(test, param_val - param_val, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we make a list of all the test cases we want to add to the test suite
|
||||
* below.
|
||||
|
@ -296,6 +508,11 @@ static struct kunit_case example_test_cases[] = {
|
|||
KUNIT_CASE(example_static_stub_using_fn_ptr_test),
|
||||
KUNIT_CASE(example_priv_test),
|
||||
KUNIT_CASE_PARAM(example_params_test, example_gen_params),
|
||||
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init, kunit_array_gen_params,
|
||||
example_param_init, NULL),
|
||||
KUNIT_CASE_PARAM_WITH_INIT(example_params_test_with_init_dynamic_arr,
|
||||
kunit_array_gen_params, example_param_init_dynamic_arr,
|
||||
example_param_exit_dynamic_arr),
|
||||
KUNIT_CASE_SLOW(example_slow_test),
|
||||
{}
|
||||
};
|
||||
|
|
|
@ -337,6 +337,14 @@ void __kunit_do_failed_assertion(struct kunit *test,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(__kunit_do_failed_assertion);
|
||||
|
||||
static void kunit_init_params(struct kunit *test)
|
||||
{
|
||||
test->params_array.params = NULL;
|
||||
test->params_array.get_description = NULL;
|
||||
test->params_array.num_params = 0;
|
||||
test->params_array.elem_size = 0;
|
||||
}
|
||||
|
||||
void kunit_init_test(struct kunit *test, const char *name, struct string_stream *log)
|
||||
{
|
||||
spin_lock_init(&test->lock);
|
||||
|
@ -347,6 +355,7 @@ void kunit_init_test(struct kunit *test, const char *name, struct string_stream
|
|||
string_stream_clear(log);
|
||||
test->status = KUNIT_SUCCESS;
|
||||
test->status_comment[0] = '\0';
|
||||
kunit_init_params(test);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_init_test);
|
||||
|
||||
|
@ -641,12 +650,44 @@ static void kunit_accumulate_stats(struct kunit_result_stats *total,
|
|||
total->total += add.total;
|
||||
}
|
||||
|
||||
const void *kunit_array_gen_params(struct kunit *test, const void *prev, char *desc)
|
||||
{
|
||||
struct kunit_params *params_arr = &test->params_array;
|
||||
const void *param;
|
||||
|
||||
if (test->param_index < params_arr->num_params) {
|
||||
param = (char *)params_arr->params
|
||||
+ test->param_index * params_arr->elem_size;
|
||||
|
||||
if (params_arr->get_description)
|
||||
params_arr->get_description(test, param, desc);
|
||||
return param;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_array_gen_params);
|
||||
|
||||
static void kunit_init_parent_param_test(struct kunit_case *test_case, struct kunit *test)
|
||||
{
|
||||
if (test_case->param_init) {
|
||||
int err = test_case->param_init(test);
|
||||
|
||||
if (err) {
|
||||
kunit_err(test_case, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
|
||||
"# failed to initialize parent parameter test (%d)", err);
|
||||
test->status = KUNIT_FAILURE;
|
||||
test_case->status = KUNIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int kunit_run_tests(struct kunit_suite *suite)
|
||||
{
|
||||
char param_desc[KUNIT_PARAM_DESC_SIZE];
|
||||
struct kunit_case *test_case;
|
||||
struct kunit_result_stats suite_stats = { 0 };
|
||||
struct kunit_result_stats total_stats = { 0 };
|
||||
const void *curr_param;
|
||||
|
||||
/* Taint the kernel so we know we've run tests. */
|
||||
add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
|
||||
|
@ -677,41 +718,64 @@ int kunit_run_tests(struct kunit_suite *suite)
|
|||
kunit_run_case_catch_errors(suite, test_case, &test);
|
||||
kunit_update_stats(¶m_stats, test.status);
|
||||
} else {
|
||||
kunit_init_parent_param_test(test_case, &test);
|
||||
if (test_case->status == KUNIT_FAILURE) {
|
||||
kunit_update_stats(¶m_stats, test.status);
|
||||
goto test_case_end;
|
||||
}
|
||||
/* Get initial param. */
|
||||
param_desc[0] = '\0';
|
||||
test.param_value = test_case->generate_params(NULL, param_desc);
|
||||
/* TODO: Make generate_params try-catch */
|
||||
curr_param = test_case->generate_params(&test, NULL, param_desc);
|
||||
test_case->status = KUNIT_SKIPPED;
|
||||
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
|
||||
"KTAP version 1\n");
|
||||
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
|
||||
"# Subtest: %s", test_case->name);
|
||||
if (test.params_array.params &&
|
||||
test_case->generate_params == kunit_array_gen_params) {
|
||||
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT
|
||||
KUNIT_SUBTEST_INDENT "1..%zd\n",
|
||||
test.params_array.num_params);
|
||||
}
|
||||
|
||||
while (test.param_value) {
|
||||
kunit_run_case_catch_errors(suite, test_case, &test);
|
||||
while (curr_param) {
|
||||
struct kunit param_test = {
|
||||
.param_value = curr_param,
|
||||
.param_index = ++test.param_index,
|
||||
.parent = &test,
|
||||
};
|
||||
kunit_init_test(¶m_test, test_case->name, test_case->log);
|
||||
kunit_run_case_catch_errors(suite, test_case, ¶m_test);
|
||||
|
||||
if (param_desc[0] == '\0') {
|
||||
snprintf(param_desc, sizeof(param_desc),
|
||||
"param-%d", test.param_index);
|
||||
"param-%d", param_test.param_index);
|
||||
}
|
||||
|
||||
kunit_print_ok_not_ok(&test, KUNIT_LEVEL_CASE_PARAM,
|
||||
test.status,
|
||||
test.param_index + 1,
|
||||
kunit_print_ok_not_ok(¶m_test, KUNIT_LEVEL_CASE_PARAM,
|
||||
param_test.status,
|
||||
param_test.param_index,
|
||||
param_desc,
|
||||
test.status_comment);
|
||||
param_test.status_comment);
|
||||
|
||||
kunit_update_stats(¶m_stats, test.status);
|
||||
kunit_update_stats(¶m_stats, param_test.status);
|
||||
|
||||
/* Get next param. */
|
||||
param_desc[0] = '\0';
|
||||
test.param_value = test_case->generate_params(test.param_value, param_desc);
|
||||
test.param_index++;
|
||||
test.status = KUNIT_SUCCESS;
|
||||
test.status_comment[0] = '\0';
|
||||
test.priv = NULL;
|
||||
curr_param = test_case->generate_params(&test, curr_param,
|
||||
param_desc);
|
||||
}
|
||||
/*
|
||||
* TODO: Put into a try catch. Since we don't need suite->exit
|
||||
* for it we can't reuse kunit_try_run_cleanup for this yet.
|
||||
*/
|
||||
if (test_case->param_exit)
|
||||
test_case->param_exit(&test);
|
||||
/* TODO: Put this kunit_cleanup into a try-catch. */
|
||||
kunit_cleanup(&test);
|
||||
}
|
||||
|
||||
test_case_end:
|
||||
kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
|
||||
|
||||
kunit_print_test_stats(&test, param_stats);
|
||||
|
|
|
@ -210,6 +210,8 @@ pub const fn kunit_case(
|
|||
status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
|
||||
module_name: core::ptr::null_mut(),
|
||||
log: core::ptr::null_mut(),
|
||||
param_init: None,
|
||||
param_exit: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -229,6 +231,8 @@ pub const fn kunit_case_null() -> kernel::bindings::kunit_case {
|
|||
status: kernel::bindings::kunit_status_KUNIT_SUCCESS,
|
||||
module_name: core::ptr::null_mut(),
|
||||
log: core::ptr::null_mut(),
|
||||
param_init: None,
|
||||
param_exit: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -357,4 +361,11 @@ mod tests {
|
|||
fn rust_test_kunit_in_kunit_test() {
|
||||
assert!(in_kunit_test());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(all()))]
|
||||
fn rust_test_kunit_always_disabled_test() {
|
||||
// This test should never run because of the `cfg`.
|
||||
assert!(false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
//! Copyright (c) 2023 José Expósito <jose.exposito89@gmail.com>
|
||||
|
||||
use proc_macro::{Delimiter, Group, TokenStream, TokenTree};
|
||||
use std::collections::HashMap;
|
||||
use std::fmt::Write;
|
||||
|
||||
pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
|
||||
|
@ -41,20 +42,32 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
|
|||
// Get the functions set as tests. Search for `[test]` -> `fn`.
|
||||
let mut body_it = body.stream().into_iter();
|
||||
let mut tests = Vec::new();
|
||||
let mut attributes: HashMap<String, TokenStream> = HashMap::new();
|
||||
while let Some(token) = body_it.next() {
|
||||
match token {
|
||||
TokenTree::Group(ident) if ident.to_string() == "[test]" => match body_it.next() {
|
||||
Some(TokenTree::Ident(ident)) if ident.to_string() == "fn" => {
|
||||
let test_name = match body_it.next() {
|
||||
Some(TokenTree::Ident(ident)) => ident.to_string(),
|
||||
_ => continue,
|
||||
};
|
||||
tests.push(test_name);
|
||||
TokenTree::Punct(ref p) if p.as_char() == '#' => match body_it.next() {
|
||||
Some(TokenTree::Group(g)) if g.delimiter() == Delimiter::Bracket => {
|
||||
if let Some(TokenTree::Ident(name)) = g.stream().into_iter().next() {
|
||||
// Collect attributes because we need to find which are tests. We also
|
||||
// need to copy `cfg` attributes so tests can be conditionally enabled.
|
||||
attributes
|
||||
.entry(name.to_string())
|
||||
.or_default()
|
||||
.extend([token, TokenTree::Group(g)]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
_ => continue,
|
||||
_ => (),
|
||||
},
|
||||
TokenTree::Ident(i) if i.to_string() == "fn" && attributes.contains_key("test") => {
|
||||
if let Some(TokenTree::Ident(test_name)) = body_it.next() {
|
||||
tests.push((test_name, attributes.remove("cfg").unwrap_or_default()))
|
||||
}
|
||||
}
|
||||
|
||||
_ => (),
|
||||
}
|
||||
attributes.clear();
|
||||
}
|
||||
|
||||
// Add `#[cfg(CONFIG_KUNIT="y")]` before the module declaration.
|
||||
|
@ -100,11 +113,22 @@ pub(crate) fn kunit_tests(attr: TokenStream, ts: TokenStream) -> TokenStream {
|
|||
let mut test_cases = "".to_owned();
|
||||
let mut assert_macros = "".to_owned();
|
||||
let path = crate::helpers::file();
|
||||
for test in &tests {
|
||||
let num_tests = tests.len();
|
||||
for (test, cfg_attr) in tests {
|
||||
let kunit_wrapper_fn_name = format!("kunit_rust_wrapper_{test}");
|
||||
// An extra `use` is used here to reduce the length of the message.
|
||||
// Append any `cfg` attributes the user might have written on their tests so we don't
|
||||
// attempt to call them when they are `cfg`'d out. An extra `use` is used here to reduce
|
||||
// the length of the assert message.
|
||||
let kunit_wrapper = format!(
|
||||
"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}())); }}",
|
||||
r#"unsafe extern "C" fn {kunit_wrapper_fn_name}(_test: *mut ::kernel::bindings::kunit)
|
||||
{{
|
||||
(*_test).status = ::kernel::bindings::kunit_status_KUNIT_SKIPPED;
|
||||
{cfg_attr} {{
|
||||
(*_test).status = ::kernel::bindings::kunit_status_KUNIT_SUCCESS;
|
||||
use ::kernel::kunit::is_test_result_ok;
|
||||
assert!(is_test_result_ok({test}()));
|
||||
}}
|
||||
}}"#,
|
||||
);
|
||||
writeln!(kunit_macros, "{kunit_wrapper}").unwrap();
|
||||
writeln!(
|
||||
|
@ -139,7 +163,7 @@ macro_rules! assert_eq {{
|
|||
writeln!(
|
||||
kunit_macros,
|
||||
"static mut TEST_CASES: [::kernel::bindings::kunit_case; {}] = [\n{test_cases} ::kernel::kunit::kunit_case_null(),\n];",
|
||||
tests.len() + 1
|
||||
num_tests + 1
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# Config options which are added to UML builds by default
|
||||
|
||||
# Enable virtio/pci, as a lot of tests require it.
|
||||
CONFIG_VIRTIO_UML=y
|
||||
CONFIG_UML_PCI_OVER_VIRTIO=y
|
||||
# Enable pci, as a lot of tests require it.
|
||||
CONFIG_KUNIT_UML_PCI=y
|
||||
|
||||
# Enable FORTIFY_SOURCE for wider checking.
|
||||
CONFIG_FORTIFY_SOURCE=y
|
||||
|
|
|
@ -228,7 +228,7 @@ def parse_tests(request: KunitParseRequest, metadata: kunit_json.Metadata, input
|
|||
fake_test.counts.passed = 1
|
||||
|
||||
output: Iterable[str] = input_data
|
||||
if request.raw_output == 'all':
|
||||
if request.raw_output == 'all' or request.raw_output == 'full':
|
||||
pass
|
||||
elif request.raw_output == 'kunit':
|
||||
output = kunit_parser.extract_tap_lines(output)
|
||||
|
@ -425,7 +425,7 @@ def add_parse_opts(parser: argparse.ArgumentParser) -> None:
|
|||
parser.add_argument('--raw_output', help='If set don\'t parse output from kernel. '
|
||||
'By default, filters to just KUnit output. Use '
|
||||
'--raw_output=all to show everything',
|
||||
type=str, nargs='?', const='all', default=None, choices=['all', 'kunit'])
|
||||
type=str, nargs='?', const='all', default=None, choices=['all', 'full', 'kunit'])
|
||||
parser.add_argument('--json',
|
||||
nargs='?',
|
||||
help='Prints parsed test results as JSON to stdout or a file if '
|
||||
|
|
|
@ -352,9 +352,9 @@ def parse_test_plan(lines: LineStream, test: Test) -> bool:
|
|||
lines.pop()
|
||||
return True
|
||||
|
||||
TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?([^#]*)( # .*)?$')
|
||||
TEST_RESULT = re.compile(r'^\s*(ok|not ok) ([0-9]+) ?(- )?([^#]*)( # .*)?$')
|
||||
|
||||
TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) (- )?(.*) # SKIP(.*)$')
|
||||
TEST_RESULT_SKIP = re.compile(r'^\s*(ok|not ok) ([0-9]+) ?(- )?(.*) # SKIP ?(.*)$')
|
||||
|
||||
def peek_test_name_match(lines: LineStream, test: Test) -> bool:
|
||||
"""
|
||||
|
@ -379,6 +379,8 @@ def peek_test_name_match(lines: LineStream, test: Test) -> bool:
|
|||
if not match:
|
||||
return False
|
||||
name = match.group(4)
|
||||
if not name:
|
||||
return False
|
||||
return name == test.name
|
||||
|
||||
def parse_test_result(lines: LineStream, test: Test,
|
||||
|
@ -416,7 +418,7 @@ def parse_test_result(lines: LineStream, test: Test,
|
|||
|
||||
# Set name of test object
|
||||
if skip_match:
|
||||
test.name = skip_match.group(4)
|
||||
test.name = skip_match.group(4) or skip_match.group(5)
|
||||
else:
|
||||
test.name = match.group(4)
|
||||
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
from ..qemu_config import QemuArchParams
|
||||
|
||||
QEMU_ARCH = QemuArchParams(linux_arch='mips',
|
||||
kconfig='''
|
||||
CONFIG_32BIT=y
|
||||
CONFIG_CPU_BIG_ENDIAN=y
|
||||
CONFIG_MIPS_MALTA=y
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_SYSCON=y
|
||||
''',
|
||||
qemu_arch='mips',
|
||||
kernel_path='vmlinuz',
|
||||
kernel_command_line='console=ttyS0',
|
||||
extra_qemu_params=['-M', 'malta'])
|
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
from ..qemu_config import QemuArchParams
|
||||
|
||||
QEMU_ARCH = QemuArchParams(linux_arch='mips',
|
||||
kconfig='''
|
||||
CONFIG_CPU_MIPS64_R2=y
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_CPU_BIG_ENDIAN=y
|
||||
CONFIG_MIPS_MALTA=y
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_SYSCON=y
|
||||
''',
|
||||
qemu_arch='mips64',
|
||||
kernel_path='vmlinuz',
|
||||
kernel_command_line='console=ttyS0',
|
||||
extra_qemu_params=['-M', 'malta', '-cpu', '5KEc'])
|
|
@ -0,0 +1,19 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
from ..qemu_config import QemuArchParams
|
||||
|
||||
QEMU_ARCH = QemuArchParams(linux_arch='mips',
|
||||
kconfig='''
|
||||
CONFIG_CPU_MIPS64_R2=y
|
||||
CONFIG_64BIT=y
|
||||
CONFIG_CPU_LITTLE_ENDIAN=y
|
||||
CONFIG_MIPS_MALTA=y
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_SYSCON=y
|
||||
''',
|
||||
qemu_arch='mips64el',
|
||||
kernel_path='vmlinuz',
|
||||
kernel_command_line='console=ttyS0',
|
||||
extra_qemu_params=['-M', 'malta', '-cpu', '5KEc'])
|
|
@ -0,0 +1,18 @@
|
|||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
from ..qemu_config import QemuArchParams
|
||||
|
||||
QEMU_ARCH = QemuArchParams(linux_arch='mips',
|
||||
kconfig='''
|
||||
CONFIG_32BIT=y
|
||||
CONFIG_CPU_LITTLE_ENDIAN=y
|
||||
CONFIG_MIPS_MALTA=y
|
||||
CONFIG_SERIAL_8250=y
|
||||
CONFIG_SERIAL_8250_CONSOLE=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_SYSCON=y
|
||||
''',
|
||||
qemu_arch='mipsel',
|
||||
kernel_path='vmlinuz',
|
||||
kernel_command_line='console=ttyS0',
|
||||
extra_qemu_params=['-M', 'malta'])
|
|
@ -1,5 +1,5 @@
|
|||
TAP version 13
|
||||
1..2
|
||||
1..3
|
||||
# selftests: membarrier: membarrier_test_single_thread
|
||||
# TAP version 13
|
||||
# 1..2
|
||||
|
@ -12,3 +12,4 @@ ok 1 selftests: membarrier: membarrier_test_single_thread
|
|||
# ok 1 sys_membarrier available
|
||||
# ok 2 sys membarrier invalid command test: command = -1, flags = 0, errno = 22. Failed as expected
|
||||
ok 2 selftests: membarrier: membarrier_test_multi_thread
|
||||
ok 3 # SKIP selftests: membarrier: membarrier_test_multi_thread
|
||||
|
|
Loading…
Reference in New Issue