linux-kselftest-kunit-5.10-rc1

This Kunit update for Linux 5.10-rc1 consists of:
 
 - add Kunit to kernel_init() and remove KUnit from init calls entirely.
   This addresses the concern Kunit would not work correctly during
   late init phase.
 - add a linker section where KUnit can put references to its test suites.
   This patch is the first step in transitioning to dispatching all KUnit
   tests from a centralized executor rather than having each as its own
   separate late_initcall.
 - add a centralized executor to dispatch tests rather than relying on
   late_initcall to schedule each test suite separately. Centralized
   execution is for built-in tests only; modules will execute tests when
   loaded.
 - convert bitfield test to use KUnit framework
 - Documentation updates for naming guidelines and how kunit_test_suite()
   works.
 - add test plan to KUnit TAP format
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEPZKym/RZuOCGeA/kCwJExA0NQxwFAl+Mr68ACgkQCwJExA0N
 Qxy7HxAAuToPP6uUHwTC3KzVVE4hjP9a3t4hiD7kP/gI0umN+2nrccm6Vx6E+r9t
 Jkjiv9Yxj3riOkE5jJ8KriAx228mwz3N1yBEDfpp+8iCWOK3iOuFKKTTWOoZY4hf
 Enlf7n4Yp2TOEmIH0xwh/H67zl0+3FwT3fGWC6DDPXHuw+X+mGphCl9XPB70rZcT
 q/s0dwx1CmWBm30MgFXN+SZ7CgLP13lRAvkVO4t56/O1SkTbpCe7U1zqT2p5UoOY
 x7qvzs3pdCaWbpCsAqFWr46iECDHuVQjIgLuddOF/OgWVcCZlv7T7ESd7IDPHUPx
 DD3zYG0ODV0jKZHmpwkSojSbu3z6v5FnfhLpAcaHoEMBeRu5UIar7EjPHwqrqiU7
 JqE7dBECmcD308sr9u0w44DK15nmsD3+njrBQ/AJmsWdg0wtnMvA01nAHKObbk0n
 33aIu4Iny1dH35/rt9dV2DKT09f5r0ANCjoJMX8gu/li66FHGfULOaqr6KLLqi5X
 VPgHCKzyT9nD+Bc2LYzRWmhhAj+5Iwyglgpe9ZiOlPQ5i+hLvfPPAZxVYSbVA1Sk
 aVZi+ibKUqHSBfXcaLf/OKX7Csf4zni3F+WfFT5ZIC4Y6iEF+0tvS2HW2/pcUAN/
 OSPYYmyqhwYIl8tvbQENgBsyU/K1rECxJpqWAznJLRCebkY5a/s=
 =0Sco
 -----END PGP SIGNATURE-----

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

Pull more Kunit updates from Shuah Khan:

 - add Kunit to kernel_init() and remove KUnit from init calls entirely.

   This addresses the concern that Kunit would not work correctly during
   late init phase.

 - add a linker section where KUnit can put references to its test
   suites.

   This is the first step in transitioning to dispatching all KUnit
   tests from a centralized executor rather than having each as its own
   separate late_initcall.

 - add a centralized executor to dispatch tests rather than relying on
   late_initcall to schedule each test suite separately. Centralized
   execution is for built-in tests only; modules will execute tests when
   loaded.

 - convert bitfield test to use KUnit framework

 - Documentation updates for naming guidelines and how
   kunit_test_suite() works.

 - add test plan to KUnit TAP format

* tag 'linux-kselftest-kunit-5.10-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest:
  lib: kunit: Fix compilation test when using TEST_BIT_FIELD_COMPILE
  lib: kunit: add bitfield test conversion to KUnit
  Documentation: kunit: add a brief blurb about kunit_test_suite
  kunit: test: add test plan to KUnit TAP format
  init: main: add KUnit to kernel init
  kunit: test: create a single centralized executor for all tests
  vmlinux.lds.h: add linker section for KUnit test suites
  Documentation: kunit: Add naming guidelines
This commit is contained in:
Linus Torvalds 2020-10-18 14:45:59 -07:00
commit 7cf726a594
16 changed files with 444 additions and 110 deletions

View File

@ -11,6 +11,7 @@ KUnit - Unit Testing for the Linux Kernel
usage usage
kunit-tool kunit-tool
api/index api/index
style
faq faq
What is KUnit? What is KUnit?

View File

@ -0,0 +1,205 @@
.. SPDX-License-Identifier: GPL-2.0
===========================
Test Style and Nomenclature
===========================
To make finding, writing, and using KUnit tests as simple as possible, it's
strongly encouraged that they are named and written according to the guidelines
below. While it's possible to write KUnit tests which do not follow these rules,
they may break some tooling, may conflict with other tests, and may not be run
automatically by testing systems.
It's recommended that you only deviate from these guidelines when:
1. Porting tests to KUnit which are already known with an existing name, or
2. Writing tests which would cause serious problems if automatically run (e.g.,
non-deterministically producing false positives or negatives, or taking an
extremely long time to run).
Subsystems, Suites, and Tests
=============================
In order to make tests as easy to find as possible, they're grouped into suites
and subsystems. A test suite is a group of tests which test a related area of
the kernel, and a subsystem is a set of test suites which test different parts
of the same kernel subsystem or driver.
Subsystems
----------
Every test suite must belong to a subsystem. A subsystem is a collection of one
or more KUnit test suites which test the same driver or part of the kernel. A
rule of thumb is that a test subsystem should match a single kernel module. If
the code being tested can't be compiled as a module, in many cases the subsystem
should correspond to a directory in the source tree or an entry in the
MAINTAINERS file. If unsure, follow the conventions set by tests in similar
areas.
Test subsystems should be named after the code being tested, either after the
module (wherever possible), or after the directory or files being tested. Test
subsystems should be named to avoid ambiguity where necessary.
If a test subsystem name has multiple components, they should be separated by
underscores. *Do not* include "test" or "kunit" directly in the subsystem name
unless you are actually testing other tests or the kunit framework itself.
Example subsystems could be:
``ext4``
Matches the module and filesystem name.
``apparmor``
Matches the module name and LSM name.
``kasan``
Common name for the tool, prominent part of the path ``mm/kasan``
``snd_hda_codec_hdmi``
Has several components (``snd``, ``hda``, ``codec``, ``hdmi``) separated by
underscores. Matches the module name.
Avoid names like these:
``linear-ranges``
Names should use underscores, not dashes, to separate words. Prefer
``linear_ranges``.
``qos-kunit-test``
As well as using underscores, this name should not have "kunit-test" as a
suffix, and ``qos`` is ambiguous as a subsystem name. ``power_qos`` would be a
better name.
``pc_parallel_port``
The corresponding module name is ``parport_pc``, so this subsystem should also
be named ``parport_pc``.
.. note::
The KUnit API and tools do not explicitly know about subsystems. They're
simply a way of categorising test suites and naming modules which
provides a simple, consistent way for humans to find and run tests. This
may change in the future, though.
Suites
------
KUnit tests are grouped into test suites, which cover a specific area of
functionality being tested. Test suites can have shared initialisation and
shutdown code which is run for all tests in the suite.
Not all subsystems will need to be split into multiple test suites (e.g. simple drivers).
Test suites are named after the subsystem they are part of. If a subsystem
contains several suites, the specific area under test should be appended to the
subsystem name, separated by an underscore.
In the event that there are multiple types of test using KUnit within a
subsystem (e.g., both unit tests and integration tests), they should be put into
separate suites, with the type of test as the last element in the suite name.
Unless these tests are actually present, avoid using ``_test``, ``_unittest`` or
similar in the suite name.
The full test suite name (including the subsystem name) should be specified as
the ``.name`` member of the ``kunit_suite`` struct, and forms the base for the
module name (see below).
Example test suites could include:
``ext4_inode``
Part of the ``ext4`` subsystem, testing the ``inode`` area.
``kunit_try_catch``
Part of the ``kunit`` implementation itself, testing the ``try_catch`` area.
``apparmor_property_entry``
Part of the ``apparmor`` subsystem, testing the ``property_entry`` area.
``kasan``
The ``kasan`` subsystem has only one suite, so the suite name is the same as
the subsystem name.
Avoid names like:
``ext4_ext4_inode``
There's no reason to state the subsystem twice.
``property_entry``
The suite name is ambiguous without the subsystem name.
``kasan_integration_test``
Because there is only one suite in the ``kasan`` subsystem, the suite should
just be called ``kasan``. There's no need to redundantly add
``integration_test``. Should a separate test suite with, for example, unit
tests be added, then that suite could be named ``kasan_unittest`` or similar.
Test Cases
----------
Individual tests consist of a single function which tests a constrained
codepath, property, or function. In the test output, individual tests' results
will show up as subtests of the suite's results.
Tests should be named after what they're testing. This is often the name of the
function being tested, with a description of the input or codepath being tested.
As tests are C functions, they should be named and written in accordance with
the kernel coding style.
.. note::
As tests are themselves functions, their names cannot conflict with
other C identifiers in the kernel. This may require some creative
naming. It's a good idea to make your test functions `static` to avoid
polluting the global namespace.
Example test names include:
``unpack_u32_with_null_name``
Tests the ``unpack_u32`` function when a NULL name is passed in.
``test_list_splice``
Tests the ``list_splice`` macro. It has the prefix ``test_`` to avoid a
name conflict with the macro itself.
Should it be necessary to refer to a test outside the context of its test suite,
the *fully-qualified* name of a test should be the suite name followed by the
test name, separated by a colon (i.e. ``suite:test``).
Test Kconfig Entries
====================
Every test suite should be tied to a Kconfig entry.
This Kconfig entry must:
* be named ``CONFIG_<name>_KUNIT_TEST``: where <name> is the name of the test
suite.
* be listed either alongside the config entries for the driver/subsystem being
tested, or be under [Kernel Hacking]→[Kernel Testing and Coverage]
* depend on ``CONFIG_KUNIT``
* be visible only if ``CONFIG_KUNIT_ALL_TESTS`` is not enabled.
* have a default value of ``CONFIG_KUNIT_ALL_TESTS``.
* have a brief description of KUnit in the help text
Unless there's a specific reason not to (e.g. the test is unable to be built as
a module), Kconfig entries for tests should be tristate.
An example Kconfig entry:
.. code-block:: none
config FOO_KUNIT_TEST
tristate "KUnit test for foo" if !KUNIT_ALL_TESTS
depends on KUNIT
default KUNIT_ALL_TESTS
help
This builds unit tests for foo.
For more information on KUnit and unit tests in general, please refer
to the KUnit documentation in Documentation/dev-tools/kunit
If unsure, say N
Test File and Module Names
==========================
KUnit tests can often be compiled as a module. These modules should be named
after the test suite, followed by ``_test``. If this is likely to conflict with
non-KUnit tests, the suffix ``_kunit`` can also be used.
The easiest way of achieving this is to name the file containing the test suite
``<suite>_test.c`` (or, as above, ``<suite>_kunit.c``). This file should be
placed next to the code under test.
If the suite name contains some or all of the name of the test's parent
directory, it may make sense to modify the source filename to reduce redundancy.
For example, a ``foo_firmware`` suite could be in the ``foo/firmware_test.c``
file.

View File

@ -211,6 +211,11 @@ KUnit test framework.
.. note:: .. note::
A test case will only be run if it is associated with a test suite. A test case will only be run if it is associated with a test suite.
``kunit_test_suite(...)`` is a macro which tells the linker to put the specified
test suite in a special linker section so that it can be run by KUnit either
after late_init, or when the test module is loaded (depending on whether the
test was built in or not).
For more information on these types of things see the :doc:`api/test`. For more information on these types of things see the :doc:`api/test`.
Isolating Behavior Isolating Behavior

View File

@ -734,7 +734,8 @@
THERMAL_TABLE(governor) \ THERMAL_TABLE(governor) \
EARLYCON_TABLE() \ EARLYCON_TABLE() \
LSM_TABLE() \ LSM_TABLE() \
EARLY_LSM_TABLE() EARLY_LSM_TABLE() \
KUNIT_TABLE()
#define INIT_TEXT \ #define INIT_TEXT \
*(.init.text .init.text.*) \ *(.init.text .init.text.*) \
@ -932,6 +933,13 @@
KEEP(*(.con_initcall.init)) \ KEEP(*(.con_initcall.init)) \
__con_initcall_end = .; __con_initcall_end = .;
/* Alignment must be consistent with (kunit_suite *) in include/kunit/test.h */
#define KUNIT_TABLE() \
. = ALIGN(8); \
__kunit_suites_start = .; \
KEEP(*(.kunit_test_suites)) \
__kunit_suites_end = .;
#ifdef CONFIG_BLK_DEV_INITRD #ifdef CONFIG_BLK_DEV_INITRD
#define INIT_RAM_FS \ #define INIT_RAM_FS \
. = ALIGN(4); \ . = ALIGN(4); \

View File

@ -239,10 +239,19 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite);
unsigned int kunit_test_case_num(struct kunit_suite *suite, unsigned int kunit_test_case_num(struct kunit_suite *suite,
struct kunit_case *test_case); struct kunit_case *test_case);
int __kunit_test_suites_init(struct kunit_suite **suites); int __kunit_test_suites_init(struct kunit_suite * const * const suites);
void __kunit_test_suites_exit(struct kunit_suite **suites); void __kunit_test_suites_exit(struct kunit_suite **suites);
#if IS_BUILTIN(CONFIG_KUNIT)
int kunit_run_all_tests(void);
#else
static inline int kunit_run_all_tests(void)
{
return 0;
}
#endif /* IS_BUILTIN(CONFIG_KUNIT) */
/** /**
* kunit_test_suites() - used to register one or more &struct kunit_suite * kunit_test_suites() - used to register one or more &struct kunit_suite
* with KUnit. * with KUnit.
@ -252,34 +261,57 @@ void __kunit_test_suites_exit(struct kunit_suite **suites);
* Registers @suites_list with the test framework. See &struct kunit_suite for * Registers @suites_list with the test framework. See &struct kunit_suite for
* more information. * more information.
* *
* When builtin, KUnit tests are all run as late_initcalls; this means * If a test suite is built-in, module_init() gets translated into
* that they cannot test anything where tests must run at a different init * an initcall which we don't want as the idea is that for builtins
* phase. One significant restriction resulting from this is that KUnit * the executor will manage execution. So ensure we do not define
* cannot reliably test anything that is initialize in the late_init phase; * module_{init|exit} functions for the builtin case when registering
* another is that KUnit is useless to test things that need to be run in * suites via kunit_test_suites() below.
* an earlier init phase.
*
* An alternative is to build the tests as a module. Because modules
* do not support multiple late_initcall()s, we need to initialize an
* array of suites for a module.
*
* TODO(brendanhiggins@google.com): Don't run all KUnit tests as
* late_initcalls. I have some future work planned to dispatch all KUnit
* tests from the same place, and at the very least to do so after
* everything else is definitely initialized.
*/ */
#define kunit_test_suites(suites_list...) \ #ifdef MODULE
static struct kunit_suite *suites[] = {suites_list, NULL}; \ #define kunit_test_suites_for_module(__suites) \
static int kunit_test_suites_init(void) \ static int __init kunit_test_suites_init(void) \
{ \ { \
return __kunit_test_suites_init(suites); \ return __kunit_test_suites_init(__suites); \
} \ } \
late_initcall(kunit_test_suites_init); \ module_init(kunit_test_suites_init); \
\
static void __exit kunit_test_suites_exit(void) \ static void __exit kunit_test_suites_exit(void) \
{ \ { \
return __kunit_test_suites_exit(suites); \ return __kunit_test_suites_exit(__suites); \
} \ } \
module_exit(kunit_test_suites_exit) module_exit(kunit_test_suites_exit)
#else
#define kunit_test_suites_for_module(__suites)
#endif /* MODULE */
#define __kunit_test_suites(unique_array, unique_suites, ...) \
static struct kunit_suite *unique_array[] = { __VA_ARGS__, NULL }; \
kunit_test_suites_for_module(unique_array); \
static struct kunit_suite **unique_suites \
__used __section(.kunit_test_suites) = unique_array
/**
* kunit_test_suites() - used to register one or more &struct kunit_suite
* with KUnit.
*
* @suites: a statically allocated list of &struct kunit_suite.
*
* Registers @suites with the test framework. See &struct kunit_suite for
* more information.
*
* When builtin, KUnit tests are all run via executor; this is done
* by placing the array of struct kunit_suite * in the .kunit_test_suites
* ELF section.
*
* An alternative is to build the tests as a module. Because modules do not
* support multiple initcall()s, we need to initialize an array of suites for a
* module.
*
*/
#define kunit_test_suites(...) \
__kunit_test_suites(__UNIQUE_ID(array), \
__UNIQUE_ID(suites), \
__VA_ARGS__)
#define kunit_test_suite(suite) kunit_test_suites(&suite) #define kunit_test_suite(suite) kunit_test_suites(&suite)

View File

@ -108,6 +108,8 @@
#define CREATE_TRACE_POINTS #define CREATE_TRACE_POINTS
#include <trace/events/initcall.h> #include <trace/events/initcall.h>
#include <kunit/test.h>
static int kernel_init(void *); static int kernel_init(void *);
extern void init_IRQ(void); extern void init_IRQ(void);
@ -1513,6 +1515,8 @@ static noinline void __init kernel_init_freeable(void)
do_basic_setup(); do_basic_setup();
kunit_run_all_tests();
console_on_rootfs(); console_on_rootfs();
/* /*

View File

@ -2063,13 +2063,6 @@ config TEST_BITMAP
If unsure, say N. If unsure, say N.
config TEST_BITFIELD
tristate "Test bitfield functions at runtime"
help
Enable this option to test the bitfield functions at boot.
If unsure, say N.
config TEST_UUID config TEST_UUID
tristate "Test functions located in the uuid module at runtime" tristate "Test functions located in the uuid module at runtime"
@ -2219,6 +2212,22 @@ config TEST_SYSCTL
If unsure, say N. If unsure, say N.
config BITFIELD_KUNIT
tristate "KUnit test bitfield functions at runtime"
depends on KUNIT
help
Enable this option to test the bitfield functions at boot.
KUnit tests run during boot and output the results to the debug log
in TAP format (http://testanything.org/). Only useful for kernel devs
running the KUnit test harness, and not intended for inclusion into a
production build.
For more information on KUnit and unit tests in general please refer
to the KUnit documentation in Documentation/dev-tools/kunit/.
If unsure, say N.
config SYSCTL_KUNIT_TEST config SYSCTL_KUNIT_TEST
tristate "KUnit test for sysctl" if !KUNIT_ALL_TESTS tristate "KUnit test for sysctl" if !KUNIT_ALL_TESTS
depends on KUNIT depends on KUNIT

View File

@ -87,7 +87,6 @@ obj-$(CONFIG_TEST_STATIC_KEYS) += test_static_key_base.o
obj-$(CONFIG_TEST_PRINTF) += test_printf.o obj-$(CONFIG_TEST_PRINTF) += test_printf.o
obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o obj-$(CONFIG_TEST_BITMAP) += test_bitmap.o
obj-$(CONFIG_TEST_STRSCPY) += test_strscpy.o obj-$(CONFIG_TEST_STRSCPY) += test_strscpy.o
obj-$(CONFIG_TEST_BITFIELD) += test_bitfield.o
obj-$(CONFIG_TEST_UUID) += test_uuid.o obj-$(CONFIG_TEST_UUID) += test_uuid.o
obj-$(CONFIG_TEST_XARRAY) += test_xarray.o obj-$(CONFIG_TEST_XARRAY) += test_xarray.o
obj-$(CONFIG_TEST_PARMAN) += test_parman.o obj-$(CONFIG_TEST_PARMAN) += test_parman.o
@ -349,6 +348,7 @@ obj-$(CONFIG_OBJAGG) += objagg.o
obj-$(CONFIG_PLDMFW) += pldmfw/ obj-$(CONFIG_PLDMFW) += pldmfw/
# KUnit tests # KUnit tests
obj-$(CONFIG_BITFIELD_KUNIT) += bitfield_kunit.o
obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o obj-$(CONFIG_LIST_KUNIT_TEST) += list-test.o
obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o obj-$(CONFIG_LINEAR_RANGES_TEST) += test_linear_ranges.o
obj-$(CONFIG_BITS_TEST) += test_bits.o obj-$(CONFIG_BITS_TEST) += test_bits.o

View File

@ -5,8 +5,7 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/kernel.h> #include <kunit/test.h>
#include <linux/module.h>
#include <linux/bitfield.h> #include <linux/bitfield.h>
#define CHECK_ENC_GET_U(tp, v, field, res) do { \ #define CHECK_ENC_GET_U(tp, v, field, res) do { \
@ -14,13 +13,11 @@
u##tp _res; \ u##tp _res; \
\ \
_res = u##tp##_encode_bits(v, field); \ _res = u##tp##_encode_bits(v, field); \
if (_res != res) { \ KUNIT_ASSERT_FALSE_MSG(context, _res != res, \
pr_warn("u" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != " #res "\n",\ "u" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != " #res "\n", \
(u64)_res); \ (u64)_res); \
return -EINVAL; \ KUNIT_ASSERT_FALSE(context, \
} \ u##tp##_get_bits(_res, field) != v); \
if (u##tp##_get_bits(_res, field) != v) \
return -EINVAL; \
} \ } \
} while (0) } while (0)
@ -29,14 +26,13 @@
__le##tp _res; \ __le##tp _res; \
\ \
_res = le##tp##_encode_bits(v, field); \ _res = le##tp##_encode_bits(v, field); \
if (_res != cpu_to_le##tp(res)) { \ KUNIT_ASSERT_FALSE_MSG(context, \
pr_warn("le" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx\n",\ _res != cpu_to_le##tp(res), \
(u64)le##tp##_to_cpu(_res), \ "le" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx",\
(u64)(res)); \ (u64)le##tp##_to_cpu(_res), \
return -EINVAL; \ (u64)(res)); \
} \ KUNIT_ASSERT_FALSE(context, \
if (le##tp##_get_bits(_res, field) != v) \ le##tp##_get_bits(_res, field) != v);\
return -EINVAL; \
} \ } \
} while (0) } while (0)
@ -45,14 +41,13 @@
__be##tp _res; \ __be##tp _res; \
\ \
_res = be##tp##_encode_bits(v, field); \ _res = be##tp##_encode_bits(v, field); \
if (_res != cpu_to_be##tp(res)) { \ KUNIT_ASSERT_FALSE_MSG(context, \
pr_warn("be" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx\n",\ _res != cpu_to_be##tp(res), \
(u64)be##tp##_to_cpu(_res), \ "be" #tp "_encode_bits(" #v ", " #field ") is 0x%llx != 0x%llx", \
(u64)(res)); \ (u64)be##tp##_to_cpu(_res), \
return -EINVAL; \ (u64)(res)); \
} \ KUNIT_ASSERT_FALSE(context, \
if (be##tp##_get_bits(_res, field) != v) \ be##tp##_get_bits(_res, field) != v);\
return -EINVAL; \
} \ } \
} while (0) } while (0)
@ -62,7 +57,7 @@
CHECK_ENC_GET_BE(tp, v, field, res); \ CHECK_ENC_GET_BE(tp, v, field, res); \
} while (0) } while (0)
static int test_constants(void) static void __init test_bitfields_constants(struct kunit *context)
{ {
/* /*
* NOTE * NOTE
@ -95,19 +90,17 @@ static int test_constants(void)
CHECK_ENC_GET(64, 7, 0x00f0000000000000ull, 0x0070000000000000ull); CHECK_ENC_GET(64, 7, 0x00f0000000000000ull, 0x0070000000000000ull);
CHECK_ENC_GET(64, 14, 0x0f00000000000000ull, 0x0e00000000000000ull); CHECK_ENC_GET(64, 14, 0x0f00000000000000ull, 0x0e00000000000000ull);
CHECK_ENC_GET(64, 15, 0xf000000000000000ull, 0xf000000000000000ull); CHECK_ENC_GET(64, 15, 0xf000000000000000ull, 0xf000000000000000ull);
return 0;
} }
#define CHECK(tp, mask) do { \ #define CHECK(tp, mask) do { \
u64 v; \ u64 v; \
\ \
for (v = 0; v < 1 << hweight32(mask); v++) \ for (v = 0; v < 1 << hweight32(mask); v++) \
if (tp##_encode_bits(v, mask) != v << __ffs64(mask)) \ KUNIT_ASSERT_FALSE(context, \
return -EINVAL; \ tp##_encode_bits(v, mask) != v << __ffs64(mask));\
} while (0) } while (0)
static int test_variables(void) static void __init test_bitfields_variables(struct kunit *context)
{ {
CHECK(u8, 0x0f); CHECK(u8, 0x0f);
CHECK(u8, 0xf0); CHECK(u8, 0xf0);
@ -130,39 +123,32 @@ static int test_variables(void)
CHECK(u64, 0x000000007f000000ull); CHECK(u64, 0x000000007f000000ull);
CHECK(u64, 0x0000000018000000ull); CHECK(u64, 0x0000000018000000ull);
CHECK(u64, 0x0000001f8000000ull); CHECK(u64, 0x0000001f8000000ull);
return 0;
} }
static int __init test_bitfields(void)
{
int ret = test_constants();
if (ret) {
pr_warn("constant tests failed!\n");
return ret;
}
ret = test_variables();
if (ret) {
pr_warn("variable tests failed!\n");
return ret;
}
#ifdef TEST_BITFIELD_COMPILE #ifdef TEST_BITFIELD_COMPILE
static void __init test_bitfields_compile(struct kunit *context)
{
/* these should fail compilation */ /* these should fail compilation */
CHECK_ENC_GET(16, 16, 0x0f00, 0x1000); CHECK_ENC_GET(16, 16, 0x0f00, 0x1000);
u32_encode_bits(7, 0x06000000); u32_encode_bits(7, 0x06000000);
/* this should at least give a warning */ /* this should at least give a warning */
u16_encode_bits(0, 0x60000); u16_encode_bits(0, 0x60000);
}
#endif #endif
pr_info("tests passed\n"); static struct kunit_case __refdata bitfields_test_cases[] = {
KUNIT_CASE(test_bitfields_constants),
KUNIT_CASE(test_bitfields_variables),
{}
};
return 0; static struct kunit_suite bitfields_test_suite = {
} .name = "bitfields",
module_init(test_bitfields) .test_cases = bitfields_test_cases,
};
kunit_test_suites(&bitfields_test_suite);
MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>"); MODULE_AUTHOR("Johannes Berg <johannes@sipsolutions.net>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -3,7 +3,8 @@ obj-$(CONFIG_KUNIT) += kunit.o
kunit-objs += test.o \ kunit-objs += test.o \
string-stream.o \ string-stream.o \
assert.o \ assert.o \
try-catch.o try-catch.o \
executor.o
ifeq ($(CONFIG_KUNIT_DEBUGFS),y) ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
kunit-objs += debugfs.o kunit-objs += debugfs.o

43
lib/kunit/executor.c Normal file
View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: GPL-2.0
#include <kunit/test.h>
/*
* These symbols point to the .kunit_test_suites section and are defined in
* include/asm-generic/vmlinux.lds.h, and consequently must be extern.
*/
extern struct kunit_suite * const * const __kunit_suites_start[];
extern struct kunit_suite * const * const __kunit_suites_end[];
#if IS_BUILTIN(CONFIG_KUNIT)
static void kunit_print_tap_header(void)
{
struct kunit_suite * const * const *suites, * const *subsuite;
int num_of_suites = 0;
for (suites = __kunit_suites_start;
suites < __kunit_suites_end;
suites++)
for (subsuite = *suites; *subsuite != NULL; subsuite++)
num_of_suites++;
pr_info("TAP version 14\n");
pr_info("1..%d\n", num_of_suites);
}
int kunit_run_all_tests(void)
{
struct kunit_suite * const * const *suites;
kunit_print_tap_header();
for (suites = __kunit_suites_start;
suites < __kunit_suites_end;
suites++)
__kunit_test_suites_init(*suites);
return 0;
}
#endif /* IS_BUILTIN(CONFIG_KUNIT) */

View File

@ -16,16 +16,6 @@
#include "string-stream.h" #include "string-stream.h"
#include "try-catch-impl.h" #include "try-catch-impl.h"
static void kunit_print_tap_version(void)
{
static bool kunit_has_printed_tap_version;
if (!kunit_has_printed_tap_version) {
pr_info("TAP version 14\n");
kunit_has_printed_tap_version = true;
}
}
/* /*
* Append formatted message to log, size of which is limited to * Append formatted message to log, size of which is limited to
* KUNIT_LOG_SIZE bytes (including null terminating byte). * KUNIT_LOG_SIZE bytes (including null terminating byte).
@ -65,7 +55,6 @@ EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
static void kunit_print_subtest_start(struct kunit_suite *suite) static void kunit_print_subtest_start(struct kunit_suite *suite)
{ {
kunit_print_tap_version();
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "# Subtest: %s", kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "# Subtest: %s",
suite->name); suite->name);
kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "1..%zd", kunit_log(KERN_INFO, suite, KUNIT_SUBTEST_INDENT "1..%zd",
@ -381,7 +370,7 @@ static void kunit_init_suite(struct kunit_suite *suite)
kunit_debugfs_create_suite(suite); kunit_debugfs_create_suite(suite);
} }
int __kunit_test_suites_init(struct kunit_suite **suites) int __kunit_test_suites_init(struct kunit_suite * const * const suites)
{ {
unsigned int i; unsigned int i;

View File

@ -45,10 +45,11 @@ class TestStatus(Enum):
FAILURE = auto() FAILURE = auto()
TEST_CRASHED = auto() TEST_CRASHED = auto()
NO_TESTS = auto() NO_TESTS = auto()
FAILURE_TO_PARSE_TESTS = auto()
kunit_start_re = re.compile(r'TAP version [0-9]+$') kunit_start_re = re.compile(r'TAP version [0-9]+$')
kunit_end_re = re.compile('(List of all partitions:|' kunit_end_re = re.compile('(List of all partitions:|'
'Kernel panic - not syncing: VFS:|reboot: System halted)') 'Kernel panic - not syncing: VFS:)')
def isolate_kunit_output(kernel_output): def isolate_kunit_output(kernel_output):
started = False started = False
@ -109,7 +110,7 @@ OkNotOkResult = namedtuple('OkNotOkResult', ['is_ok','description', 'text'])
OK_NOT_OK_SUBTEST = re.compile(r'^[\s]+(ok|not ok) [0-9]+ - (.*)$') OK_NOT_OK_SUBTEST = re.compile(r'^[\s]+(ok|not ok) [0-9]+ - (.*)$')
OK_NOT_OK_MODULE = re.compile(r'^(ok|not ok) [0-9]+ - (.*)$') OK_NOT_OK_MODULE = re.compile(r'^(ok|not ok) ([0-9]+) - (.*)$')
def parse_ok_not_ok_test_case(lines: List[str], test_case: TestCase) -> bool: def parse_ok_not_ok_test_case(lines: List[str], test_case: TestCase) -> bool:
save_non_diagnositic(lines, test_case) save_non_diagnositic(lines, test_case)
@ -197,7 +198,9 @@ def max_status(left: TestStatus, right: TestStatus) -> TestStatus:
else: else:
return TestStatus.SUCCESS return TestStatus.SUCCESS
def parse_ok_not_ok_test_suite(lines: List[str], test_suite: TestSuite) -> bool: def parse_ok_not_ok_test_suite(lines: List[str],
test_suite: TestSuite,
expected_suite_index: int) -> bool:
consume_non_diagnositic(lines) consume_non_diagnositic(lines)
if not lines: if not lines:
test_suite.status = TestStatus.TEST_CRASHED test_suite.status = TestStatus.TEST_CRASHED
@ -210,6 +213,12 @@ def parse_ok_not_ok_test_suite(lines: List[str], test_suite: TestSuite) -> bool:
test_suite.status = TestStatus.SUCCESS test_suite.status = TestStatus.SUCCESS
else: else:
test_suite.status = TestStatus.FAILURE test_suite.status = TestStatus.FAILURE
suite_index = int(match.group(2))
if suite_index != expected_suite_index:
print_with_timestamp(
red('[ERROR] ') + 'expected_suite_index ' +
str(expected_suite_index) + ', but got ' +
str(suite_index))
return True return True
else: else:
return False return False
@ -222,7 +231,7 @@ def bubble_up_test_case_errors(test_suite: TestSuite) -> TestStatus:
max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases) max_test_case_status = bubble_up_errors(lambda x: x.status, test_suite.cases)
return max_status(max_test_case_status, test_suite.status) return max_status(max_test_case_status, test_suite.status)
def parse_test_suite(lines: List[str]) -> TestSuite: def parse_test_suite(lines: List[str], expected_suite_index: int) -> TestSuite:
if not lines: if not lines:
return None return None
consume_non_diagnositic(lines) consume_non_diagnositic(lines)
@ -241,7 +250,7 @@ def parse_test_suite(lines: List[str]) -> TestSuite:
break break
test_suite.cases.append(test_case) test_suite.cases.append(test_case)
expected_test_case_num -= 1 expected_test_case_num -= 1
if parse_ok_not_ok_test_suite(lines, test_suite): if parse_ok_not_ok_test_suite(lines, test_suite, expected_suite_index):
test_suite.status = bubble_up_test_case_errors(test_suite) test_suite.status = bubble_up_test_case_errors(test_suite)
return test_suite return test_suite
elif not lines: elif not lines:
@ -261,6 +270,17 @@ def parse_tap_header(lines: List[str]) -> bool:
else: else:
return False return False
TEST_PLAN = re.compile(r'[0-9]+\.\.([0-9]+)')
def parse_test_plan(lines: List[str]) -> int:
consume_non_diagnositic(lines)
match = TEST_PLAN.match(lines[0])
if match:
lines.pop(0)
return int(match.group(1))
else:
return None
def bubble_up_suite_errors(test_suite_list: List[TestSuite]) -> TestStatus: def bubble_up_suite_errors(test_suite_list: List[TestSuite]) -> TestStatus:
return bubble_up_errors(lambda x: x.status, test_suite_list) return bubble_up_errors(lambda x: x.status, test_suite_list)
@ -268,20 +288,33 @@ def parse_test_result(lines: List[str]) -> TestResult:
consume_non_diagnositic(lines) consume_non_diagnositic(lines)
if not lines or not parse_tap_header(lines): if not lines or not parse_tap_header(lines):
return TestResult(TestStatus.NO_TESTS, [], lines) return TestResult(TestStatus.NO_TESTS, [], lines)
expected_test_suite_num = parse_test_plan(lines)
if not expected_test_suite_num:
return TestResult(TestStatus.FAILURE_TO_PARSE_TESTS, [], lines)
test_suites = [] test_suites = []
test_suite = parse_test_suite(lines) for i in range(1, expected_test_suite_num + 1):
while test_suite: test_suite = parse_test_suite(lines, i)
test_suites.append(test_suite) if test_suite:
test_suite = parse_test_suite(lines) test_suites.append(test_suite)
return TestResult(bubble_up_suite_errors(test_suites), test_suites, lines) else:
print_with_timestamp(
red('[ERROR] ') + ' expected ' +
str(expected_test_suite_num) +
' test suites, but got ' + str(i - 2))
break
test_suite = parse_test_suite(lines, -1)
if test_suite:
print_with_timestamp(red('[ERROR] ') +
'got unexpected test suite: ' + test_suite.name)
if test_suites:
return TestResult(bubble_up_suite_errors(test_suites), test_suites, lines)
else:
return TestResult(TestStatus.NO_TESTS, [], lines)
def parse_run_tests(kernel_output) -> TestResult: def print_and_count_results(test_result: TestResult) -> None:
total_tests = 0 total_tests = 0
failed_tests = 0 failed_tests = 0
crashed_tests = 0 crashed_tests = 0
test_result = parse_test_result(list(isolate_kunit_output(kernel_output)))
if test_result.status == TestStatus.NO_TESTS:
print_with_timestamp(red('[ERROR] ') + 'no kunit output detected')
for test_suite in test_result.suites: for test_suite in test_result.suites:
if test_suite.status == TestStatus.SUCCESS: if test_suite.status == TestStatus.SUCCESS:
print_suite_divider(green('[PASSED] ') + test_suite.name) print_suite_divider(green('[PASSED] ') + test_suite.name)
@ -303,6 +336,21 @@ def parse_run_tests(kernel_output) -> TestResult:
print_with_timestamp(red('[FAILED] ') + test_case.name) print_with_timestamp(red('[FAILED] ') + test_case.name)
print_log(map(yellow, test_case.log)) print_log(map(yellow, test_case.log))
print_with_timestamp('') print_with_timestamp('')
return total_tests, failed_tests, crashed_tests
def parse_run_tests(kernel_output) -> TestResult:
total_tests = 0
failed_tests = 0
crashed_tests = 0
test_result = parse_test_result(list(isolate_kunit_output(kernel_output)))
if test_result.status == TestStatus.NO_TESTS:
print(red('[ERROR] ') + yellow('no tests run!'))
elif test_result.status == TestStatus.FAILURE_TO_PARSE_TESTS:
print(red('[ERROR] ') + yellow('could not parse test results!'))
else:
(total_tests,
failed_tests,
crashed_tests) = print_and_count_results(test_result)
print_with_timestamp(DIVIDER) print_with_timestamp(DIVIDER)
fmt = green if test_result.status == TestStatus.SUCCESS else red fmt = green if test_result.status == TestStatus.SUCCESS else red
print_with_timestamp( print_with_timestamp(

View File

@ -1,4 +1,5 @@
TAP version 14 TAP version 14
1..2
# Subtest: sysctl_test # Subtest: sysctl_test
1..8 1..8
# sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed # sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed

View File

@ -1,6 +1,7 @@
printk: console [tty0] enabled printk: console [tty0] enabled
printk: console [mc-1] enabled printk: console [mc-1] enabled
TAP version 14 TAP version 14
1..2
# Subtest: sysctl_test # Subtest: sysctl_test
1..8 1..8
# sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed # sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed

View File

@ -1,4 +1,5 @@
TAP version 14 TAP version 14
1..2
# Subtest: sysctl_test # Subtest: sysctl_test
1..8 1..8
# sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed # sysctl_test_dointvec_null_tbl_data: sysctl_test_dointvec_null_tbl_data passed