Commit 665fe72a authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'linux-kselftest-kunit-5.20-rc1' of...

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

Pull KUnit updates from Shuah Khan:
 "This consists of several fixes and an important feature to discourage
  running KUnit tests on production systems. Running tests on a
  production system could leave the system in a bad state.

  Summary:

   - Add a new taint type, TAINT_TEST to signal that a test has been
     run.

     This should discourage people from running these tests on
     production systems, and to make it easier to tell if tests have
     been run accidentally (by loading the wrong configuration, etc)

   - Several documentation and tool enhancements and fixes"

* tag 'linux-kselftest-kunit-5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (29 commits)
  Documentation: KUnit: Fix example with compilation error
  Documentation: kunit: Add CLI args for kunit_tool
  kcsan: test: Add a .kunitconfig to run KCSAN tests
  kunit: executor: Fix a memory leak on failure in kunit_filter_tests
  clk: explicitly disable CONFIG_UML_PCI_OVER_VIRTIO in .kunitconfig
  mmc: sdhci-of-aspeed: test: Use kunit_test_suite() macro
  nitro_enclaves: test: Use kunit_test_suite() macro
  thunderbolt: test: Use kunit_test_suite() macro
  kunit: flatten kunit_suite*** to kunit_suite** in .kunit_test_suites
  kunit: unify module and builtin suite definitions
  selftest: Taint kernel when test module loaded
  module: panic: Taint the kernel when selftest modules load
  Documentation: kunit: fix example run_kunit func to allow spaces in args
  Documentation: kunit: Cleanup run_wrapper, fix x-ref
  kunit: test.h: fix a kernel-doc markup
  kunit: tool: Enable virtio/PCI by default on UML
  kunit: tool: make --kunitconfig repeatable, blindly concat
  kunit: add coverage_uml.config to enable GCOV on UML
  kunit: tool: refactor internal kconfig handling, allow overriding
  kunit: tool: introduce --qemu_args
  ...
parents aad26f55 4c392516
...@@ -100,6 +100,7 @@ Bit Log Number Reason that got the kernel tainted ...@@ -100,6 +100,7 @@ Bit Log Number Reason that got the kernel tainted
15 _/K 32768 kernel has been live patched 15 _/K 32768 kernel has been live patched
16 _/X 65536 auxiliary taint, defined for and used by distros 16 _/X 65536 auxiliary taint, defined for and used by distros
17 _/T 131072 kernel was built with the struct randomization plugin 17 _/T 131072 kernel was built with the struct randomization plugin
18 _/N 262144 an in-kernel test has been run
=== === ====== ======================================================== === === ====== ========================================================
Note: The character ``_`` is representing a blank in this table to make reading Note: The character ``_`` is representing a blank in this table to make reading
......
...@@ -275,6 +275,14 @@ assist writing kernel modules that are for use with kselftest: ...@@ -275,6 +275,14 @@ assist writing kernel modules that are for use with kselftest:
- ``tools/testing/selftests/kselftest_module.h`` - ``tools/testing/selftests/kselftest_module.h``
- ``tools/testing/selftests/kselftest/module.sh`` - ``tools/testing/selftests/kselftest/module.sh``
Note that test modules should taint the kernel with TAINT_TEST. This will
happen automatically for modules which are in the ``tools/testing/``
directory, or for modules which use the ``kselftest_module.h`` header above.
Otherwise, you'll need to add ``MODULE_INFO(test, "Y")`` to your module
source. selftests which do not load modules typically should not taint the
kernel, but in cases where a non-test module is loaded, TEST_TAINT can be
applied from userspace by writing to ``/proc/sys/kernel/tainted``.
How to use How to use
---------- ----------
...@@ -333,6 +341,7 @@ A bare bones test module might look like this: ...@@ -333,6 +341,7 @@ A bare bones test module might look like this:
KSTM_MODULE_LOADERS(test_foo); KSTM_MODULE_LOADERS(test_foo);
MODULE_AUTHOR("John Developer <jd@fooman.org>"); MODULE_AUTHOR("John Developer <jd@fooman.org>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_INFO(test, "Y");
Example test script Example test script
------------------- -------------------
......
...@@ -192,6 +192,21 @@ via UML. To run tests on qemu, by default it requires two flags: ...@@ -192,6 +192,21 @@ via UML. To run tests on qemu, by default it requires two flags:
if we have downloaded the microblaze toolchain from the 0-day if we have downloaded the microblaze toolchain from the 0-day
website to a directory in our home directory called toolchains. website to a directory in our home directory called toolchains.
This means that for most architectures, running under qemu is as simple as:
.. code-block:: bash
./tools/testing/kunit/kunit.py run --arch=x86_64
When cross-compiling, we'll likely need to specify a different toolchain, for
example:
.. code-block:: bash
./tools/testing/kunit/kunit.py run \
--arch=s390 \
--cross_compile=s390x-linux-gnu-
If we want to run KUnit tests on an architecture not supported by If we want to run KUnit tests on an architecture not supported by
the ``--arch`` flag, or want to run KUnit tests on qemu using a the ``--arch`` flag, or want to run KUnit tests on qemu using a
non-default configuration; then we can write our own``QemuConfig``. non-default configuration; then we can write our own``QemuConfig``.
...@@ -214,14 +229,11 @@ as ...@@ -214,14 +229,11 @@ as
--jobs=12 \ --jobs=12 \
--qemu_config=./tools/testing/kunit/qemu_configs/x86_64.py --qemu_config=./tools/testing/kunit/qemu_configs/x86_64.py
To run existing KUnit tests on non-UML architectures, see:
Documentation/dev-tools/kunit/non_uml.rst.
Command-Line Arguments Command-Line Arguments
====================== ======================
kunit_tool has a number of other command-line arguments which can kunit_tool has a number of other command-line arguments which can
be useful for our test environment. Below the most commonly used be useful for our test environment. Below are the most commonly used
command line arguments: command line arguments:
- ``--help``: Lists all available options. To list common options, - ``--help``: Lists all available options. To list common options,
...@@ -245,3 +257,64 @@ command line arguments: ...@@ -245,3 +257,64 @@ command line arguments:
added or modified. Instead, enable all tests added or modified. Instead, enable all tests
which have satisfied dependencies by adding which have satisfied dependencies by adding
``CONFIG_KUNIT_ALL_TESTS=y`` to your ``.kunitconfig``. ``CONFIG_KUNIT_ALL_TESTS=y`` to your ``.kunitconfig``.
- ``--kunitconfig``: Specifies the path or the directory of the ``.kunitconfig``
file. For example:
- ``lib/kunit/.kunitconfig`` can be the path of the file.
- ``lib/kunit`` can be the directory in which the file is located.
This file is used to build and run with a predefined set of tests
and their dependencies. For example, to run tests for a given subsystem.
- ``--kconfig_add``: Specifies additional configuration options to be
appended to the ``.kunitconfig`` file. For example:
.. code-block::
./tools/testing/kunit/kunit.py run --kconfig_add CONFIG_KASAN=y
- ``--arch``: Runs tests on the specified architecture. The architecture
argument is same as the Kbuild ARCH environment variable.
For example, i386, x86_64, arm, um, etc. Non-UML architectures run on qemu.
Default is `um`.
- ``--cross_compile``: Specifies the Kbuild toolchain. It passes the
same argument as passed to the ``CROSS_COMPILE`` variable used by
Kbuild. This will be the prefix for the toolchain
binaries such as GCC. For example:
- ``sparc64-linux-gnu-`` if we have the sparc toolchain installed on
our system.
- ``$HOME/toolchains/microblaze/gcc-9.2.0-nolibc/microblaze-linux/bin/microblaze-linux``
if we have downloaded the microblaze toolchain from the 0-day
website to a specified path in our home directory called toolchains.
- ``--qemu_config``: Specifies the path to a file containing a
custom qemu architecture definition. This should be a python file
containing a `QemuArchParams` object.
- ``--qemu_args``: Specifies additional qemu arguments, for example, ``-smp 8``.
- ``--jobs``: Specifies the number of jobs (commands) to run simultaneously.
By default, this is set to the number of cores on your system.
- ``--timeout``: Specifies the maximum number of seconds allowed for all tests to run.
This does not include the time taken to build the tests.
- ``--kernel_args``: Specifies additional kernel command-line arguments. May be repeated.
- ``--run_isolated``: If set, boots the kernel for each individual suite/test.
This is useful for debugging a non-hermetic test, one that
might pass/fail based on what ran before it.
- ``--raw_output``: If set, generates unformatted output from kernel. Possible options are:
- ``all``: To view the full kernel output, use ``--raw_output=all``.
- ``kunit``: This is the default option and filters to KUnit output. Use ``--raw_output`` or ``--raw_output=kunit``.
- ``--json``: If set, stores the test results in a JSON format and prints to `stdout` or
saves to a file if a filename is specified.
...@@ -15,7 +15,7 @@ It can be handy to create a bash function like: ...@@ -15,7 +15,7 @@ It can be handy to create a bash function like:
.. code-block:: bash .. code-block:: bash
function run_kunit() { function run_kunit() {
( cd "$(git rev-parse --show-toplevel)" && ./tools/testing/kunit/kunit.py run $@ ) ( cd "$(git rev-parse --show-toplevel)" && ./tools/testing/kunit/kunit.py run "$@" )
} }
.. note:: .. note::
...@@ -123,8 +123,7 @@ Putting it together into a copy-pastable sequence of commands: ...@@ -123,8 +123,7 @@ Putting it together into a copy-pastable sequence of commands:
.. code-block:: bash .. code-block:: bash
# Append coverage options to the current config # Append coverage options to the current config
$ echo -e "CONFIG_DEBUG_KERNEL=y\nCONFIG_DEBUG_INFO=y\nCONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y\nCONFIG_GCOV=y" >> .kunit/.kunitconfig $ ./tools/testing/kunit/kunit.py run --kunitconfig=.kunit/ --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
$ ./tools/testing/kunit/kunit.py run
# Extract the coverage information from the build dir (.kunit/) # Extract the coverage information from the build dir (.kunit/)
$ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/ $ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/
......
...@@ -505,7 +505,7 @@ By reusing the same ``cases`` array from above, we can write the test as a ...@@ -505,7 +505,7 @@ By reusing the same ``cases`` array from above, we can write the test as a
const char *str; const char *str;
const char *sha1; const char *sha1;
}; };
struct sha1_test_case cases[] = { const struct sha1_test_case cases[] = {
{ {
.str = "hello world", .str = "hello world",
.sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", .sha1 = "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
......
...@@ -2,3 +2,4 @@ CONFIG_KUNIT=y ...@@ -2,3 +2,4 @@ CONFIG_KUNIT=y
CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK=y
CONFIG_CLK_KUNIT_TEST=y CONFIG_CLK_KUNIT_TEST=y
CONFIG_CLK_GATE_KUNIT_TEST=y CONFIG_CLK_GATE_KUNIT_TEST=y
CONFIG_UML_PCI_OVER_VIRTIO=n
...@@ -169,8 +169,9 @@ config MMC_SDHCI_OF_ASPEED ...@@ -169,8 +169,9 @@ config MMC_SDHCI_OF_ASPEED
If unsure, say N. If unsure, say N.
config MMC_SDHCI_OF_ASPEED_TEST config MMC_SDHCI_OF_ASPEED_TEST
bool "Tests for the ASPEED SDHCI driver" bool "Tests for the ASPEED SDHCI driver" if !KUNIT_ALL_TESTS
depends on MMC_SDHCI_OF_ASPEED && KUNIT=y depends on MMC_SDHCI_OF_ASPEED && KUNIT
default KUNIT_ALL_TESTS
help help
Enable KUnit tests for the ASPEED SDHCI driver. Select this Enable KUnit tests for the ASPEED SDHCI driver. Select this
option only if you will boot the kernel for the purpose of running option only if you will boot the kernel for the purpose of running
......
...@@ -96,10 +96,4 @@ static struct kunit_suite aspeed_sdhci_test_suite = { ...@@ -96,10 +96,4 @@ static struct kunit_suite aspeed_sdhci_test_suite = {
.test_cases = aspeed_sdhci_test_cases, .test_cases = aspeed_sdhci_test_cases,
}; };
static struct kunit_suite *aspeed_sdc_test_suite_array[] = { kunit_test_suite(aspeed_sdhci_test_suite);
&aspeed_sdhci_test_suite,
NULL,
};
static struct kunit_suite **aspeed_sdc_test_suites
__used __section(".kunit_test_suites") = aspeed_sdc_test_suite_array;
...@@ -606,25 +606,6 @@ static struct platform_driver aspeed_sdc_driver = { ...@@ -606,25 +606,6 @@ static struct platform_driver aspeed_sdc_driver = {
#if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST) #if defined(CONFIG_MMC_SDHCI_OF_ASPEED_TEST)
#include "sdhci-of-aspeed-test.c" #include "sdhci-of-aspeed-test.c"
static inline int aspeed_sdc_tests_init(void)
{
return __kunit_test_suites_init(aspeed_sdc_test_suites);
}
static inline void aspeed_sdc_tests_exit(void)
{
__kunit_test_suites_exit(aspeed_sdc_test_suites);
}
#else
static inline int aspeed_sdc_tests_init(void)
{
return 0;
}
static inline void aspeed_sdc_tests_exit(void)
{
}
#endif #endif
static int __init aspeed_sdc_init(void) static int __init aspeed_sdc_init(void)
...@@ -637,18 +618,7 @@ static int __init aspeed_sdc_init(void) ...@@ -637,18 +618,7 @@ static int __init aspeed_sdc_init(void)
rc = platform_driver_register(&aspeed_sdc_driver); rc = platform_driver_register(&aspeed_sdc_driver);
if (rc < 0) if (rc < 0)
goto cleanup_sdhci; platform_driver_unregister(&aspeed_sdhci_driver);
rc = aspeed_sdc_tests_init();
if (rc < 0) {
platform_driver_unregister(&aspeed_sdc_driver);
goto cleanup_sdhci;
}
return 0;
cleanup_sdhci:
platform_driver_unregister(&aspeed_sdhci_driver);
return rc; return rc;
} }
...@@ -656,8 +626,6 @@ module_init(aspeed_sdc_init); ...@@ -656,8 +626,6 @@ module_init(aspeed_sdc_init);
static void __exit aspeed_sdc_exit(void) static void __exit aspeed_sdc_exit(void)
{ {
aspeed_sdc_tests_exit();
platform_driver_unregister(&aspeed_sdc_driver); platform_driver_unregister(&aspeed_sdc_driver);
platform_driver_unregister(&aspeed_sdhci_driver); platform_driver_unregister(&aspeed_sdhci_driver);
} }
......
...@@ -28,8 +28,10 @@ config USB4_DEBUGFS_WRITE ...@@ -28,8 +28,10 @@ config USB4_DEBUGFS_WRITE
this for production systems or distro kernels. this for production systems or distro kernels.
config USB4_KUNIT_TEST config USB4_KUNIT_TEST
bool "KUnit tests" bool "KUnit tests" if !KUNIT_ALL_TESTS
depends on KUNIT=y depends on (USB4=m || KUNIT=y)
depends on KUNIT
default KUNIT_ALL_TESTS
config USB4_DMA_TEST config USB4_DMA_TEST
tristate "DMA traffic test driver" tristate "DMA traffic test driver"
......
...@@ -872,7 +872,6 @@ int tb_domain_init(void) ...@@ -872,7 +872,6 @@ int tb_domain_init(void)
{ {
int ret; int ret;
tb_test_init();
tb_debugfs_init(); tb_debugfs_init();
tb_acpi_init(); tb_acpi_init();
...@@ -890,7 +889,6 @@ int tb_domain_init(void) ...@@ -890,7 +889,6 @@ int tb_domain_init(void)
err_acpi: err_acpi:
tb_acpi_exit(); tb_acpi_exit();
tb_debugfs_exit(); tb_debugfs_exit();
tb_test_exit();
return ret; return ret;
} }
...@@ -903,5 +901,4 @@ void tb_domain_exit(void) ...@@ -903,5 +901,4 @@ void tb_domain_exit(void)
tb_xdomain_exit(); tb_xdomain_exit();
tb_acpi_exit(); tb_acpi_exit();
tb_debugfs_exit(); tb_debugfs_exit();
tb_test_exit();
} }
...@@ -1271,12 +1271,4 @@ static inline void tb_service_debugfs_init(struct tb_service *svc) { } ...@@ -1271,12 +1271,4 @@ static inline void tb_service_debugfs_init(struct tb_service *svc) { }
static inline void tb_service_debugfs_remove(struct tb_service *svc) { } static inline void tb_service_debugfs_remove(struct tb_service *svc) { }
#endif #endif
#ifdef CONFIG_USB4_KUNIT_TEST
int tb_test_init(void);
void tb_test_exit(void);
#else
static inline int tb_test_init(void) { return 0; }
static inline void tb_test_exit(void) { }
#endif
#endif #endif
...@@ -2817,14 +2817,4 @@ static struct kunit_suite tb_test_suite = { ...@@ -2817,14 +2817,4 @@ static struct kunit_suite tb_test_suite = {
.test_cases = tb_test_cases, .test_cases = tb_test_cases,
}; };
static struct kunit_suite *tb_test_suites[] = { &tb_test_suite, NULL }; kunit_test_suite(tb_test_suite);
int tb_test_init(void)
{
return __kunit_test_suites_init(tb_test_suites);
}
void tb_test_exit(void)
{
return __kunit_test_suites_exit(tb_test_suites);
}
...@@ -16,8 +16,9 @@ config NITRO_ENCLAVES ...@@ -16,8 +16,9 @@ config NITRO_ENCLAVES
The module will be called nitro_enclaves. The module will be called nitro_enclaves.
config NITRO_ENCLAVES_MISC_DEV_TEST config NITRO_ENCLAVES_MISC_DEV_TEST
bool "Tests for the misc device functionality of the Nitro Enclaves" bool "Tests for the misc device functionality of the Nitro Enclaves" if !KUNIT_ALL_TESTS
depends on NITRO_ENCLAVES && KUNIT=y depends on NITRO_ENCLAVES && KUNIT
default KUNIT_ALL_TESTS
help help
Enable KUnit tests for the misc device functionality of the Nitro Enable KUnit tests for the misc device functionality of the Nitro
Enclaves. Select this option only if you will boot the kernel for Enclaves. Select this option only if you will boot the kernel for
......
...@@ -1759,35 +1759,10 @@ static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ...@@ -1759,35 +1759,10 @@ static long ne_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
#if defined(CONFIG_NITRO_ENCLAVES_MISC_DEV_TEST) #if defined(CONFIG_NITRO_ENCLAVES_MISC_DEV_TEST)
#include "ne_misc_dev_test.c" #include "ne_misc_dev_test.c"
static inline int ne_misc_dev_test_init(void)
{
return __kunit_test_suites_init(ne_misc_dev_test_suites);
}
static inline void ne_misc_dev_test_exit(void)
{
__kunit_test_suites_exit(ne_misc_dev_test_suites);
}
#else
static inline int ne_misc_dev_test_init(void)
{
return 0;
}
static inline void ne_misc_dev_test_exit(void)
{
}
#endif #endif
static int __init ne_init(void) static int __init ne_init(void)
{ {
int rc = 0;
rc = ne_misc_dev_test_init();
if (rc < 0)
return rc;
mutex_init(&ne_cpu_pool.mutex); mutex_init(&ne_cpu_pool.mutex);
return pci_register_driver(&ne_pci_driver); return pci_register_driver(&ne_pci_driver);
...@@ -1798,8 +1773,6 @@ static void __exit ne_exit(void) ...@@ -1798,8 +1773,6 @@ static void __exit ne_exit(void)
pci_unregister_driver(&ne_pci_driver); pci_unregister_driver(&ne_pci_driver);
ne_teardown_cpu_pool(); ne_teardown_cpu_pool();
ne_misc_dev_test_exit();
} }
module_init(ne_init); module_init(ne_init);
......
...@@ -151,7 +151,4 @@ static struct kunit_suite ne_misc_dev_test_suite = { ...@@ -151,7 +151,4 @@ static struct kunit_suite ne_misc_dev_test_suite = {
.test_cases = ne_misc_dev_test_cases, .test_cases = ne_misc_dev_test_cases,
}; };
static struct kunit_suite *ne_misc_dev_test_suites[] = { kunit_test_suite(ne_misc_dev_test_suite);
&ne_misc_dev_test_suite,
NULL
};
...@@ -237,9 +237,9 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite); ...@@ -237,9 +237,9 @@ 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 * const * const suites); int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites);
void __kunit_test_suites_exit(struct kunit_suite **suites); void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites);
#if IS_BUILTIN(CONFIG_KUNIT) #if IS_BUILTIN(CONFIG_KUNIT)
int kunit_run_all_tests(void); int kunit_run_all_tests(void);
...@@ -250,43 +250,11 @@ static inline int kunit_run_all_tests(void) ...@@ -250,43 +250,11 @@ static inline int kunit_run_all_tests(void)
} }
#endif /* IS_BUILTIN(CONFIG_KUNIT) */ #endif /* IS_BUILTIN(CONFIG_KUNIT) */
#ifdef MODULE #define __kunit_test_suites(unique_array, ...) \
/** MODULE_INFO(test, "Y"); \
* kunit_test_suites_for_module() - used to register one or more static struct kunit_suite *unique_array[] \
* &struct kunit_suite with KUnit. __aligned(sizeof(struct kunit_suite *)) \
* __used __section(".kunit_test_suites") = { __VA_ARGS__ }
* @__suites: a statically allocated list of &struct kunit_suite.
*
* Registers @__suites with the test framework. See &struct kunit_suite for
* more information.
*
* If a test suite is built-in, module_init() gets translated into
* an initcall which we don't want as the idea is that for builtins
* the executor will manage execution. So ensure we do not define
* module_{init|exit} functions for the builtin case when registering
* suites via kunit_test_suites() below.
*/
#define kunit_test_suites_for_module(__suites) \
static int __init kunit_test_suites_init(void) \
{ \
return __kunit_test_suites_init(__suites); \
} \
module_init(kunit_test_suites_init); \
\
static void __exit kunit_test_suites_exit(void) \
{ \
return __kunit_test_suites_exit(__suites); \
} \
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 * kunit_test_suites() - used to register one or more &struct kunit_suite
...@@ -294,21 +262,16 @@ static inline int kunit_run_all_tests(void) ...@@ -294,21 +262,16 @@ static inline int kunit_run_all_tests(void)
* *
* @__suites: a statically allocated list of &struct kunit_suite. * @__suites: a statically allocated list of &struct kunit_suite.
* *
* Registers @suites with the test framework. See &struct kunit_suite for * Registers @suites with the test framework.
* more information. * This is done by placing the array of struct kunit_suite * in the
* * .kunit_test_suites ELF section.
* 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 * When builtin, KUnit tests are all run via the executor at boot, and when
* support multiple initcall()s, we need to initialize an array of suites for a * built as a module, they run on module load.
* module.
* *
*/ */
#define kunit_test_suites(__suites...) \ #define kunit_test_suites(__suites...) \
__kunit_test_suites(__UNIQUE_ID(array), \ __kunit_test_suites(__UNIQUE_ID(array), \
__UNIQUE_ID(suites), \
##__suites) ##__suites)
#define kunit_test_suite(suite) kunit_test_suites(&suite) #define kunit_test_suite(suite) kunit_test_suites(&suite)
...@@ -320,7 +283,7 @@ static inline int kunit_run_all_tests(void) ...@@ -320,7 +283,7 @@ static inline int kunit_run_all_tests(void)
* *
* @__suites: a statically allocated list of &struct kunit_suite. * @__suites: a statically allocated list of &struct kunit_suite.
* *
* This functions identically as &kunit_test_suites() except that it suppresses * This functions identically as kunit_test_suites() except that it suppresses
* modpost warnings for referencing functions marked __init or data marked * modpost warnings for referencing functions marked __init or data marked
* __initdata; this is OK because currently KUnit only runs tests upon boot * __initdata; this is OK because currently KUnit only runs tests upon boot
* during the init phase or upon loading a module during the init phase. * during the init phase or upon loading a module during the init phase.
......
...@@ -505,6 +505,11 @@ struct module { ...@@ -505,6 +505,11 @@ struct module {
int num_static_call_sites; int num_static_call_sites;
struct static_call_site *static_call_sites; struct static_call_site *static_call_sites;
#endif #endif
#if IS_ENABLED(CONFIG_KUNIT)
int num_kunit_suites;
struct kunit_suite **kunit_suites;
#endif
#ifdef CONFIG_LIVEPATCH #ifdef CONFIG_LIVEPATCH
bool klp; /* Is this a livepatch module? */ bool klp; /* Is this a livepatch module? */
......
...@@ -68,7 +68,8 @@ static inline void set_arch_panic_timeout(int timeout, int arch_default_timeout) ...@@ -68,7 +68,8 @@ static inline void set_arch_panic_timeout(int timeout, int arch_default_timeout)
#define TAINT_LIVEPATCH 15 #define TAINT_LIVEPATCH 15
#define TAINT_AUX 16 #define TAINT_AUX 16
#define TAINT_RANDSTRUCT 17 #define TAINT_RANDSTRUCT 17
#define TAINT_FLAGS_COUNT 18 #define TAINT_TEST 18
#define TAINT_FLAGS_COUNT 19
#define TAINT_FLAGS_MAX ((1UL << TAINT_FLAGS_COUNT) - 1) #define TAINT_FLAGS_MAX ((1UL << TAINT_FLAGS_COUNT) - 1)
struct taint_flag { struct taint_flag {
......
# Note that the KCSAN tests need to run on an SMP setup.
# Under kunit_tool, this can be done by using the --qemu_args
# option to configure a machine with several cores. For example:
# ./tools/testing/kunit/kunit.py run --kunitconfig=kernel/kcsan \
# --arch=x86_64 --qemu_args="-smp 8"
CONFIG_KUNIT=y
CONFIG_DEBUG_KERNEL=y
# Need some level of concurrency to test a concurrency sanitizer.
CONFIG_SMP=y
CONFIG_KCSAN=y
CONFIG_KCSAN_KUNIT_TEST=y
# Set these if you want to run test_barrier_nothreads
#CONFIG_KCSAN_STRICT=y
#CONFIG_KCSAN_WEAK_MEMORY=y
# This prevents the test from timing out on many setups. Feel free to remove
# (or alter) this, in conjunction with setting a different test timeout with,
# for example, the --timeout kunit_tool option.
CONFIG_KCSAN_REPORT_ONCE_IN_MS=100
...@@ -1988,6 +1988,13 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags) ...@@ -1988,6 +1988,13 @@ static int check_modinfo(struct module *mod, struct load_info *info, int flags)
/* Set up license info based on the info section */ /* Set up license info based on the info section */
set_license(mod, get_modinfo(info, "license")); set_license(mod, get_modinfo(info, "license"));
if (get_modinfo(info, "test")) {
if (!test_taint(TAINT_TEST))
pr_warn("%s: loading test module taints kernel.\n",
mod->name);
add_taint_module(mod, TAINT_TEST, LOCKDEP_STILL_OK);
}
return 0; return 0;
} }
...@@ -2087,6 +2094,12 @@ static int find_module_sections(struct module *mod, struct load_info *info) ...@@ -2087,6 +2094,12 @@ static int find_module_sections(struct module *mod, struct load_info *info)
sizeof(*mod->static_call_sites), sizeof(*mod->static_call_sites),
&mod->num_static_call_sites); &mod->num_static_call_sites);
#endif #endif
#ifdef CONFIG_KUNIT
mod->kunit_suites = section_objs(info, ".kunit_test_suites",
sizeof(*mod->kunit_suites),
&mod->num_kunit_suites);
#endif
mod->extable = section_objs(info, "__ex_table", mod->extable = section_objs(info, "__ex_table",
sizeof(*mod->extable), &mod->num_exentries); sizeof(*mod->extable), &mod->num_exentries);
......
...@@ -428,6 +428,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = { ...@@ -428,6 +428,7 @@ const struct taint_flag taint_flags[TAINT_FLAGS_COUNT] = {
[ TAINT_LIVEPATCH ] = { 'K', ' ', true }, [ TAINT_LIVEPATCH ] = { 'K', ' ', true },
[ TAINT_AUX ] = { 'X', ' ', true }, [ TAINT_AUX ] = { 'X', ' ', true },
[ TAINT_RANDSTRUCT ] = { 'T', ' ', true }, [ TAINT_RANDSTRUCT ] = { 'T', ' ', true },
[ TAINT_TEST ] = { 'N', ' ', true },
}; };
/** /**
......
...@@ -9,8 +9,8 @@ ...@@ -9,8 +9,8 @@
* These symbols point to the .kunit_test_suites section and are defined in * These symbols point to the .kunit_test_suites section and are defined in
* include/asm-generic/vmlinux.lds.h, and consequently must be extern. * 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 __kunit_suites_start[];
extern struct kunit_suite * const * const __kunit_suites_end[]; extern struct kunit_suite * const __kunit_suites_end[];
#if IS_BUILTIN(CONFIG_KUNIT) #if IS_BUILTIN(CONFIG_KUNIT)
...@@ -55,7 +55,7 @@ static void kunit_parse_filter_glob(struct kunit_test_filter *parsed, ...@@ -55,7 +55,7 @@ static void kunit_parse_filter_glob(struct kunit_test_filter *parsed,
/* Create a copy of suite with only tests that match test_glob. */ /* Create a copy of suite with only tests that match test_glob. */
static struct kunit_suite * static struct kunit_suite *
kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob) kunit_filter_tests(const struct kunit_suite *const suite, const char *test_glob)
{ {
int n = 0; int n = 0;
struct kunit_case *filtered, *test_case; struct kunit_case *filtered, *test_case;
...@@ -69,15 +69,15 @@ kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob) ...@@ -69,15 +69,15 @@ kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob)
if (n == 0) if (n == 0)
return NULL; return NULL;
/* Use memcpy to workaround copy->name being const. */ copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
copy = kmalloc(sizeof(*copy), GFP_KERNEL);
if (!copy) if (!copy)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
memcpy(copy, suite, sizeof(*copy));
filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL); filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
if (!filtered) if (!filtered) {
kfree(copy);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
}
n = 0; n = 0;
kunit_suite_for_each_test_case(suite, test_case) { kunit_suite_for_each_test_case(suite, test_case) {
...@@ -92,62 +92,18 @@ kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob) ...@@ -92,62 +92,18 @@ kunit_filter_tests(struct kunit_suite *const suite, const char *test_glob)
static char *kunit_shutdown; static char *kunit_shutdown;
core_param(kunit_shutdown, kunit_shutdown, charp, 0644); core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
static struct kunit_suite * const * /* Stores an array of suites, end points one past the end */
kunit_filter_subsuite(struct kunit_suite * const * const subsuite,
struct kunit_test_filter *filter)
{
int i, n = 0;
struct kunit_suite **filtered, *filtered_suite;
n = 0;
for (i = 0; subsuite[i]; ++i) {
if (glob_match(filter->suite_glob, subsuite[i]->name))
++n;
}
if (n == 0)
return NULL;
filtered = kmalloc_array(n + 1, sizeof(*filtered), GFP_KERNEL);
if (!filtered)
return ERR_PTR(-ENOMEM);
n = 0;
for (i = 0; subsuite[i] != NULL; ++i) {
if (!glob_match(filter->suite_glob, subsuite[i]->name))
continue;
filtered_suite = kunit_filter_tests(subsuite[i], filter->test_glob);
if (IS_ERR(filtered_suite))
return ERR_CAST(filtered_suite);
else if (filtered_suite)
filtered[n++] = filtered_suite;
}
filtered[n] = NULL;
return filtered;
}
struct suite_set { struct suite_set {
struct kunit_suite * const * const *start; struct kunit_suite * const *start;
struct kunit_suite * const * const *end; struct kunit_suite * const *end;
}; };
static void kunit_free_subsuite(struct kunit_suite * const *subsuite)
{
unsigned int i;
for (i = 0; subsuite[i]; i++)
kfree(subsuite[i]);
kfree(subsuite);
}
static void kunit_free_suite_set(struct suite_set suite_set) static void kunit_free_suite_set(struct suite_set suite_set)
{ {
struct kunit_suite * const * const *suites; struct kunit_suite * const *suites;
for (suites = suite_set.start; suites < suite_set.end; suites++) for (suites = suite_set.start; suites < suite_set.end; suites++)
kunit_free_subsuite(*suites); kfree(*suites);
kfree(suite_set.start); kfree(suite_set.start);
} }
...@@ -156,7 +112,7 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, ...@@ -156,7 +112,7 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
int *err) int *err)
{ {
int i; int i;
struct kunit_suite * const **copy, * const *filtered_subsuite; struct kunit_suite **copy, *filtered_suite;
struct suite_set filtered; struct suite_set filtered;
struct kunit_test_filter filter; struct kunit_test_filter filter;
...@@ -171,14 +127,19 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set, ...@@ -171,14 +127,19 @@ static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
kunit_parse_filter_glob(&filter, filter_glob); kunit_parse_filter_glob(&filter, filter_glob);
for (i = 0; i < max; ++i) { for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
filtered_subsuite = kunit_filter_subsuite(suite_set->start[i], &filter); if (!glob_match(filter.suite_glob, suite_set->start[i]->name))
if (IS_ERR(filtered_subsuite)) { continue;
*err = PTR_ERR(filtered_subsuite);
filtered_suite = kunit_filter_tests(suite_set->start[i], filter.test_glob);
if (IS_ERR(filtered_suite)) {
*err = PTR_ERR(filtered_suite);
return filtered; return filtered;
} }
if (filtered_subsuite) if (!filtered_suite)
*copy++ = filtered_subsuite; continue;
*copy++ = filtered_suite;
} }
filtered.end = copy; filtered.end = copy;
...@@ -201,52 +162,33 @@ static void kunit_handle_shutdown(void) ...@@ -201,52 +162,33 @@ static void kunit_handle_shutdown(void)
} }
static void kunit_print_tap_header(struct suite_set *suite_set)
{
struct kunit_suite * const * const *suites, * const *subsuite;
int num_of_suites = 0;
for (suites = suite_set->start; suites < suite_set->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);
}
static void kunit_exec_run_tests(struct suite_set *suite_set) static void kunit_exec_run_tests(struct suite_set *suite_set)
{ {
struct kunit_suite * const * const *suites; size_t num_suites = suite_set->end - suite_set->start;
kunit_print_tap_header(suite_set); pr_info("TAP version 14\n");
pr_info("1..%zu\n", num_suites);
for (suites = suite_set->start; suites < suite_set->end; suites++) __kunit_test_suites_init(suite_set->start, num_suites);
__kunit_test_suites_init(*suites);
} }
static void kunit_exec_list_tests(struct suite_set *suite_set) static void kunit_exec_list_tests(struct suite_set *suite_set)
{ {
unsigned int i; struct kunit_suite * const *suites;
struct kunit_suite * const * const *suites;
struct kunit_case *test_case; struct kunit_case *test_case;
/* Hack: print a tap header so kunit.py can find the start of KUnit output. */ /* Hack: print a tap header so kunit.py can find the start of KUnit output. */
pr_info("TAP version 14\n"); pr_info("TAP version 14\n");
for (suites = suite_set->start; suites < suite_set->end; suites++) for (suites = suite_set->start; suites < suite_set->end; suites++)
for (i = 0; (*suites)[i] != NULL; i++) { kunit_suite_for_each_test_case((*suites), test_case) {
kunit_suite_for_each_test_case((*suites)[i], test_case) { pr_info("%s.%s\n", (*suites)->name, test_case->name);
pr_info("%s.%s\n", (*suites)[i]->name, test_case->name);
}
} }
} }
int kunit_run_all_tests(void) int kunit_run_all_tests(void)
{ {
struct suite_set suite_set = { struct suite_set suite_set = {__kunit_suites_start, __kunit_suites_end};
.start = __kunit_suites_start,
.end = __kunit_suites_end,
};
int err = 0; int err = 0;
if (filter_glob_param) { if (filter_glob_param) {
...@@ -264,11 +206,10 @@ int kunit_run_all_tests(void) ...@@ -264,11 +206,10 @@ int kunit_run_all_tests(void)
else else
pr_err("kunit executor: unknown action '%s'\n", action_param); pr_err("kunit executor: unknown action '%s'\n", action_param);
if (filter_glob_param) { /* a copy was made of each array */ if (filter_glob_param) { /* a copy was made of each suite */
kunit_free_suite_set(suite_set); kunit_free_suite_set(suite_set);
} }
out: out:
kunit_handle_shutdown(); kunit_handle_shutdown();
return err; return err;
......
...@@ -9,8 +9,6 @@ ...@@ -9,8 +9,6 @@
#include <kunit/test.h> #include <kunit/test.h>
static void kfree_at_end(struct kunit *test, const void *to_free); static void kfree_at_end(struct kunit *test, const void *to_free);
static void free_subsuite_at_end(struct kunit *test,
struct kunit_suite *const *to_free);
static struct kunit_suite *alloc_fake_suite(struct kunit *test, static struct kunit_suite *alloc_fake_suite(struct kunit *test,
const char *suite_name, const char *suite_name,
struct kunit_case *test_cases); struct kunit_case *test_cases);
...@@ -41,126 +39,80 @@ static void parse_filter_test(struct kunit *test) ...@@ -41,126 +39,80 @@ static void parse_filter_test(struct kunit *test)
kfree(filter.test_glob); kfree(filter.test_glob);
} }
static void filter_subsuite_test(struct kunit *test) static void filter_suites_test(struct kunit *test)
{ {
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite *subsuite[3] = {NULL, NULL};
struct kunit_suite * const *filtered; struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
struct kunit_test_filter filter = { struct suite_set got;
.suite_glob = "suite2", int err = 0;
.test_glob = NULL,
};
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
/* Want: suite1, suite2, NULL -> suite2, NULL */ /* Want: suite1, suite2, NULL -> suite2, NULL */
filtered = kunit_filter_subsuite(subsuite, &filter); got = kunit_filter_suites(&suite_set, "suite2", &err);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
free_subsuite_at_end(test, filtered); KUNIT_ASSERT_EQ(test, err, 0);
kfree_at_end(test, got.start);
/* Validate we just have suite2 */ /* Validate we just have suite2 */
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]);
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->name, "suite2");
KUNIT_EXPECT_FALSE(test, filtered[1]);
/* Contains one element (end is 1 past end) */
KUNIT_ASSERT_EQ(test, got.end - got.start, 1);
} }
static void filter_subsuite_test_glob_test(struct kunit *test) static void filter_suites_test_glob_test(struct kunit *test)
{ {
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite *subsuite[3] = {NULL, NULL};
struct kunit_suite * const *filtered; struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
struct kunit_test_filter filter = { struct suite_set got;
.suite_glob = "suite2", int err = 0;
.test_glob = "test2",
};
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
/* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */ /* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */
filtered = kunit_filter_subsuite(subsuite, &filter); got = kunit_filter_suites(&suite_set, "suite2.test2", &err);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
free_subsuite_at_end(test, filtered); KUNIT_ASSERT_EQ(test, err, 0);
kfree_at_end(test, got.start);
/* Validate we just have suite2 */ /* Validate we just have suite2 */
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]);
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->name, "suite2"); KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->name, "suite2");
KUNIT_EXPECT_FALSE(test, filtered[1]); KUNIT_ASSERT_EQ(test, got.end - got.start, 1);
/* Now validate we just have test2 */ /* Now validate we just have test2 */
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered[0]->test_cases); KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases);
KUNIT_EXPECT_STREQ(test, (const char *)filtered[0]->test_cases[0].name, "test2"); KUNIT_EXPECT_STREQ(test, (const char *)got.start[0]->test_cases[0].name, "test2");
KUNIT_EXPECT_FALSE(test, filtered[0]->test_cases[1].name); KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].name);
} }
static void filter_subsuite_to_empty_test(struct kunit *test) static void filter_suites_to_empty_test(struct kunit *test)
{ {
struct kunit_suite *subsuite[3] = {NULL, NULL, NULL}; struct kunit_suite *subsuite[3] = {NULL, NULL};
struct kunit_suite * const *filtered; struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
struct kunit_test_filter filter = { struct suite_set got;
.suite_glob = "not_found", int err = 0;
.test_glob = NULL,
};
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases); subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases); subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
filtered = kunit_filter_subsuite(subsuite, &filter); got = kunit_filter_suites(&suite_set, "not_found", &err);
free_subsuite_at_end(test, filtered); /* just in case */ KUNIT_ASSERT_EQ(test, err, 0);
kfree_at_end(test, got.start); /* just in case */
KUNIT_EXPECT_FALSE_MSG(test, filtered, KUNIT_EXPECT_PTR_EQ_MSG(test, got.start, got.end,
"should be NULL to indicate no match"); "should be empty to indicate no match");
}
static void kfree_subsuites_at_end(struct kunit *test, struct suite_set *suite_set)
{
struct kunit_suite * const * const *suites;
kfree_at_end(test, suite_set->start);
for (suites = suite_set->start; suites < suite_set->end; suites++)
free_subsuite_at_end(test, *suites);
}
static void filter_suites_test(struct kunit *test)
{
/* Suites per-file are stored as a NULL terminated array */
struct kunit_suite *subsuites[2][2] = {
{NULL, NULL},
{NULL, NULL},
};
/* Match the memory layout of suite_set */
struct kunit_suite * const * const suites[2] = {
subsuites[0], subsuites[1],
};
const struct suite_set suite_set = {
.start = suites,
.end = suites + 2,
};
struct suite_set filtered = {.start = NULL, .end = NULL};
int err = 0;
/* Emulate two files, each having one suite */
subsuites[0][0] = alloc_fake_suite(test, "suite0", dummy_test_cases);
subsuites[1][0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
/* Filter out suite1 */
filtered = kunit_filter_suites(&suite_set, "suite0", &err);
kfree_subsuites_at_end(test, &filtered); /* let us use ASSERTs without leaking */
KUNIT_EXPECT_EQ(test, err, 0);
KUNIT_ASSERT_EQ(test, filtered.end - filtered.start, (ptrdiff_t)1);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0]);
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, filtered.start[0][0]);
KUNIT_EXPECT_STREQ(test, (const char *)filtered.start[0][0]->name, "suite0");
} }
static struct kunit_case executor_test_cases[] = { static struct kunit_case executor_test_cases[] = {
KUNIT_CASE(parse_filter_test), KUNIT_CASE(parse_filter_test),
KUNIT_CASE(filter_subsuite_test),
KUNIT_CASE(filter_subsuite_test_glob_test),
KUNIT_CASE(filter_subsuite_to_empty_test),
KUNIT_CASE(filter_suites_test), KUNIT_CASE(filter_suites_test),
KUNIT_CASE(filter_suites_test_glob_test),
KUNIT_CASE(filter_suites_to_empty_test),
{} {}
}; };
...@@ -190,20 +142,6 @@ static void kfree_at_end(struct kunit *test, const void *to_free) ...@@ -190,20 +142,6 @@ static void kfree_at_end(struct kunit *test, const void *to_free)
(void *)to_free); (void *)to_free);
} }
static void free_subsuite_res_free(struct kunit_resource *res)
{
kunit_free_subsuite(res->data);
}
static void free_subsuite_at_end(struct kunit *test,
struct kunit_suite *const *to_free)
{
if (IS_ERR_OR_NULL(to_free))
return;
kunit_alloc_resource(test, NULL, free_subsuite_res_free,
GFP_KERNEL, (void *)to_free);
}
static struct kunit_suite *alloc_fake_suite(struct kunit *test, static struct kunit_suite *alloc_fake_suite(struct kunit *test,
const char *suite_name, const char *suite_name,
struct kunit_case *test_cases) struct kunit_case *test_cases)
......
...@@ -10,7 +10,9 @@ ...@@ -10,7 +10,9 @@
#include <kunit/test.h> #include <kunit/test.h>
#include <kunit/test-bug.h> #include <kunit/test-bug.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/panic.h>
#include <linux/sched/debug.h> #include <linux/sched/debug.h>
#include <linux/sched.h> #include <linux/sched.h>
...@@ -501,6 +503,9 @@ int kunit_run_tests(struct kunit_suite *suite) ...@@ -501,6 +503,9 @@ int kunit_run_tests(struct kunit_suite *suite)
struct kunit_result_stats suite_stats = { 0 }; struct kunit_result_stats suite_stats = { 0 };
struct kunit_result_stats total_stats = { 0 }; struct kunit_result_stats total_stats = { 0 };
/* Taint the kernel so we know we've run tests. */
add_taint(TAINT_TEST, LOCKDEP_STILL_OK);
if (suite->suite_init) { if (suite->suite_init) {
suite->suite_init_err = suite->suite_init(suite); suite->suite_init_err = suite->suite_init(suite);
if (suite->suite_init_err) { if (suite->suite_init_err) {
...@@ -581,11 +586,11 @@ static void kunit_init_suite(struct kunit_suite *suite) ...@@ -581,11 +586,11 @@ static void kunit_init_suite(struct kunit_suite *suite)
suite->suite_init_err = 0; suite->suite_init_err = 0;
} }
int __kunit_test_suites_init(struct kunit_suite * const * const suites) int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites)
{ {
unsigned int i; unsigned int i;
for (i = 0; suites[i] != NULL; i++) { for (i = 0; i < num_suites; i++) {
kunit_init_suite(suites[i]); kunit_init_suite(suites[i]);
kunit_run_tests(suites[i]); kunit_run_tests(suites[i]);
} }
...@@ -598,17 +603,54 @@ static void kunit_exit_suite(struct kunit_suite *suite) ...@@ -598,17 +603,54 @@ static void kunit_exit_suite(struct kunit_suite *suite)
kunit_debugfs_destroy_suite(suite); kunit_debugfs_destroy_suite(suite);
} }
void __kunit_test_suites_exit(struct kunit_suite **suites) void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites)
{ {
unsigned int i; unsigned int i;
for (i = 0; suites[i] != NULL; i++) for (i = 0; i < num_suites; i++)
kunit_exit_suite(suites[i]); kunit_exit_suite(suites[i]);
kunit_suite_counter = 1; kunit_suite_counter = 1;
} }
EXPORT_SYMBOL_GPL(__kunit_test_suites_exit); EXPORT_SYMBOL_GPL(__kunit_test_suites_exit);
#ifdef CONFIG_MODULES
static void kunit_module_init(struct module *mod)
{
__kunit_test_suites_init(mod->kunit_suites, mod->num_kunit_suites);
}
static void kunit_module_exit(struct module *mod)
{
__kunit_test_suites_exit(mod->kunit_suites, mod->num_kunit_suites);
}
static int kunit_module_notify(struct notifier_block *nb, unsigned long val,
void *data)
{
struct module *mod = data;
switch (val) {
case MODULE_STATE_LIVE:
kunit_module_init(mod);
break;
case MODULE_STATE_GOING:
kunit_module_exit(mod);
break;
case MODULE_STATE_COMING:
case MODULE_STATE_UNFORMED:
break;
}
return 0;
}
static struct notifier_block kunit_mod_nb = {
.notifier_call = kunit_module_notify,
.priority = 0,
};
#endif
struct kunit_kmalloc_array_params { struct kunit_kmalloc_array_params {
size_t n; size_t n;
size_t size; size_t size;
...@@ -703,13 +745,19 @@ EXPORT_SYMBOL_GPL(kunit_cleanup); ...@@ -703,13 +745,19 @@ EXPORT_SYMBOL_GPL(kunit_cleanup);
static int __init kunit_init(void) static int __init kunit_init(void)
{ {
kunit_debugfs_init(); kunit_debugfs_init();
#ifdef CONFIG_MODULES
return register_module_notifier(&kunit_mod_nb);
#else
return 0; return 0;
#endif
} }
late_initcall(kunit_init); late_initcall(kunit_init);
static void __exit kunit_exit(void) static void __exit kunit_exit(void)
{ {
#ifdef CONFIG_MODULES
unregister_module_notifier(&kunit_mod_nb);
#endif
kunit_debugfs_cleanup(); kunit_debugfs_cleanup();
} }
module_exit(kunit_exit); module_exit(kunit_exit);
......
...@@ -2191,6 +2191,9 @@ static void add_header(struct buffer *b, struct module *mod) ...@@ -2191,6 +2191,9 @@ static void add_header(struct buffer *b, struct module *mod)
if (strstarts(mod->name, "drivers/staging")) if (strstarts(mod->name, "drivers/staging"))
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n"); buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
if (strstarts(mod->name, "tools/testing"))
buf_printf(b, "\nMODULE_INFO(test, \"Y\");\n");
} }
static void add_exported_symbols(struct buffer *buf, struct module *mod) static void add_exported_symbols(struct buffer *buf, struct module *mod)
......
...@@ -177,7 +177,7 @@ static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test) ...@@ -177,7 +177,7 @@ static void policy_unpack_test_unpack_array_out_of_bounds(struct kunit *test)
array_size = unpack_array(puf->e, name); array_size = unpack_array(puf->e, name);
KUNIT_EXPECT_EQ(test, array_size, (u16)0); KUNIT_EXPECT_EQ(test, array_size, 0);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, KUNIT_EXPECT_PTR_EQ(test, puf->e->pos,
puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET); puf->e->start + TEST_NAMED_ARRAY_BUF_OFFSET);
} }
...@@ -391,10 +391,10 @@ static void policy_unpack_test_unpack_u16_chunk_basic(struct kunit *test) ...@@ -391,10 +391,10 @@ static void policy_unpack_test_unpack_u16_chunk_basic(struct kunit *test)
size = unpack_u16_chunk(puf->e, &chunk); size = unpack_u16_chunk(puf->e, &chunk);
KUNIT_EXPECT_PTR_EQ(test, (void *)chunk, KUNIT_EXPECT_PTR_EQ(test, chunk,
puf->e->start + TEST_U16_OFFSET + 2); puf->e->start + TEST_U16_OFFSET + 2);
KUNIT_EXPECT_EQ(test, size, (size_t)TEST_U16_DATA); KUNIT_EXPECT_EQ(test, size, TEST_U16_DATA);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, (void *)(chunk + TEST_U16_DATA)); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, (chunk + TEST_U16_DATA));
} }
static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_1( static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_1(
...@@ -408,7 +408,7 @@ static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_1( ...@@ -408,7 +408,7 @@ static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_1(
size = unpack_u16_chunk(puf->e, &chunk); size = unpack_u16_chunk(puf->e, &chunk);
KUNIT_EXPECT_EQ(test, size, (size_t)0); KUNIT_EXPECT_EQ(test, size, 0);
KUNIT_EXPECT_NULL(test, chunk); KUNIT_EXPECT_NULL(test, chunk);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->end - 1); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->end - 1);
} }
...@@ -430,7 +430,7 @@ static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_2( ...@@ -430,7 +430,7 @@ static void policy_unpack_test_unpack_u16_chunk_out_of_bounds_2(
size = unpack_u16_chunk(puf->e, &chunk); size = unpack_u16_chunk(puf->e, &chunk);
KUNIT_EXPECT_EQ(test, size, (size_t)0); KUNIT_EXPECT_EQ(test, size, 0);
KUNIT_EXPECT_NULL(test, chunk); KUNIT_EXPECT_NULL(test, chunk);
KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->start + TEST_U16_OFFSET); KUNIT_EXPECT_PTR_EQ(test, puf->e->pos, puf->e->start + TEST_U16_OFFSET);
} }
......
# 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
# This config fragment enables coverage on UML, which is different from the
# normal gcov used in other arches (no debugfs).
# Example usage:
# ./tools/testing/kunit/kunit.py run \
# --kunitconfig=tools/testing/kunit/configs/all_tests_uml.config \
# --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_GCOV=y
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
import argparse import argparse
import os import os
import re import re
import shlex
import sys import sys
import time import time
...@@ -22,6 +23,7 @@ from typing import Iterable, List, Optional, Sequence, Tuple ...@@ -22,6 +23,7 @@ from typing import Iterable, List, Optional, Sequence, Tuple
import kunit_json import kunit_json
import kunit_kernel import kunit_kernel
import kunit_parser import kunit_parser
from kunit_printer import stdout
class KunitStatus(Enum): class KunitStatus(Enum):
SUCCESS = auto() SUCCESS = auto()
...@@ -72,7 +74,7 @@ def get_kernel_root_path() -> str: ...@@ -72,7 +74,7 @@ def get_kernel_root_path() -> str:
def config_tests(linux: kunit_kernel.LinuxSourceTree, def config_tests(linux: kunit_kernel.LinuxSourceTree,
request: KunitConfigRequest) -> KunitResult: request: KunitConfigRequest) -> KunitResult:
kunit_parser.print_with_timestamp('Configuring KUnit Kernel ...') stdout.print_with_timestamp('Configuring KUnit Kernel ...')
config_start = time.time() config_start = time.time()
success = linux.build_reconfig(request.build_dir, request.make_options) success = linux.build_reconfig(request.build_dir, request.make_options)
...@@ -85,7 +87,7 @@ def config_tests(linux: kunit_kernel.LinuxSourceTree, ...@@ -85,7 +87,7 @@ def config_tests(linux: kunit_kernel.LinuxSourceTree,
def build_tests(linux: kunit_kernel.LinuxSourceTree, def build_tests(linux: kunit_kernel.LinuxSourceTree,
request: KunitBuildRequest) -> KunitResult: request: KunitBuildRequest) -> KunitResult:
kunit_parser.print_with_timestamp('Building KUnit Kernel ...') stdout.print_with_timestamp('Building KUnit Kernel ...')
build_start = time.time() build_start = time.time()
success = linux.build_kernel(request.alltests, success = linux.build_kernel(request.alltests,
...@@ -158,7 +160,7 @@ def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) - ...@@ -158,7 +160,7 @@ def exec_tests(linux: kunit_kernel.LinuxSourceTree, request: KunitExecRequest) -
test_counts = kunit_parser.TestCounts() test_counts = kunit_parser.TestCounts()
exec_time = 0.0 exec_time = 0.0
for i, filter_glob in enumerate(filter_globs): for i, filter_glob in enumerate(filter_globs):
kunit_parser.print_with_timestamp('Starting KUnit Kernel ({}/{})...'.format(i+1, len(filter_globs))) stdout.print_with_timestamp('Starting KUnit Kernel ({}/{})...'.format(i+1, len(filter_globs)))
test_start = time.time() test_start = time.time()
run_result = linux.run_kernel( run_result = linux.run_kernel(
...@@ -221,7 +223,7 @@ def parse_tests(request: KunitParseRequest, metadata: kunit_json.Metadata, input ...@@ -221,7 +223,7 @@ def parse_tests(request: KunitParseRequest, metadata: kunit_json.Metadata, input
else: else:
with open(request.json, 'w') as f: with open(request.json, 'w') as f:
f.write(json_str) f.write(json_str)
kunit_parser.print_with_timestamp("Test results stored in %s" % stdout.print_with_timestamp("Test results stored in %s" %
os.path.abspath(request.json)) os.path.abspath(request.json))
if test_result.status != kunit_parser.TestStatus.SUCCESS: if test_result.status != kunit_parser.TestStatus.SUCCESS:
...@@ -245,7 +247,7 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree, ...@@ -245,7 +247,7 @@ def run_tests(linux: kunit_kernel.LinuxSourceTree,
run_end = time.time() run_end = time.time()
kunit_parser.print_with_timestamp(( stdout.print_with_timestamp((
'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' + 'Elapsed time: %.3fs total, %.3fs configuring, %.3fs ' +
'building, %.3fs running\n') % ( 'building, %.3fs running\n') % (
run_end - run_start, run_end - run_start,
...@@ -291,8 +293,9 @@ def add_common_opts(parser) -> None: ...@@ -291,8 +293,9 @@ def add_common_opts(parser) -> None:
parser.add_argument('--kunitconfig', parser.add_argument('--kunitconfig',
help='Path to Kconfig fragment that enables KUnit tests.' help='Path to Kconfig fragment that enables KUnit tests.'
' If given a directory, (e.g. lib/kunit), "/.kunitconfig" ' ' If given a directory, (e.g. lib/kunit), "/.kunitconfig" '
'will get automatically appended.', 'will get automatically appended. If repeated, the files '
metavar='PATH') 'blindly concatenated, which might not work in all cases.',
action='append', metavar='PATHS')
parser.add_argument('--kconfig_add', parser.add_argument('--kconfig_add',
help='Additional Kconfig options to append to the ' help='Additional Kconfig options to append to the '
'.kunitconfig, e.g. CONFIG_KASAN=y. Can be repeated.', '.kunitconfig, e.g. CONFIG_KASAN=y. Can be repeated.',
...@@ -323,6 +326,10 @@ def add_common_opts(parser) -> None: ...@@ -323,6 +326,10 @@ def add_common_opts(parser) -> None:
'a QemuArchParams object.'), 'a QemuArchParams object.'),
type=str, metavar='FILE') type=str, metavar='FILE')
parser.add_argument('--qemu_args',
help='Additional QEMU arguments, e.g. "-smp 8"',
action='append', metavar='')
def add_build_opts(parser) -> None: def add_build_opts(parser) -> None:
parser.add_argument('--jobs', parser.add_argument('--jobs',
help='As in the make command, "Specifies the number of ' help='As in the make command, "Specifies the number of '
...@@ -365,7 +372,25 @@ def add_parse_opts(parser) -> None: ...@@ -365,7 +372,25 @@ def add_parse_opts(parser) -> None:
'filename is specified', 'filename is specified',
type=str, const='stdout', default=None, metavar='FILE') type=str, const='stdout', default=None, metavar='FILE')
def main(argv, linux=None):
def tree_from_args(cli_args: argparse.Namespace) -> kunit_kernel.LinuxSourceTree:
"""Returns a LinuxSourceTree based on the user's arguments."""
# Allow users to specify multiple arguments in one string, e.g. '-smp 8'
qemu_args: List[str] = []
if cli_args.qemu_args:
for arg in cli_args.qemu_args:
qemu_args.extend(shlex.split(arg))
return kunit_kernel.LinuxSourceTree(cli_args.build_dir,
kunitconfig_paths=cli_args.kunitconfig,
kconfig_add=cli_args.kconfig_add,
arch=cli_args.arch,
cross_compile=cli_args.cross_compile,
qemu_config_path=cli_args.qemu_config,
extra_qemu_args=qemu_args)
def main(argv):
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description='Helps writing and running KUnit tests.') description='Helps writing and running KUnit tests.')
subparser = parser.add_subparsers(dest='subcommand') subparser = parser.add_subparsers(dest='subcommand')
...@@ -412,14 +437,7 @@ def main(argv, linux=None): ...@@ -412,14 +437,7 @@ def main(argv, linux=None):
if not os.path.exists(cli_args.build_dir): if not os.path.exists(cli_args.build_dir):
os.mkdir(cli_args.build_dir) os.mkdir(cli_args.build_dir)
if not linux: linux = tree_from_args(cli_args)
linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir,
kunitconfig_path=cli_args.kunitconfig,
kconfig_add=cli_args.kconfig_add,
arch=cli_args.arch,
cross_compile=cli_args.cross_compile,
qemu_config_path=cli_args.qemu_config)
request = KunitRequest(build_dir=cli_args.build_dir, request = KunitRequest(build_dir=cli_args.build_dir,
make_options=cli_args.make_options, make_options=cli_args.make_options,
jobs=cli_args.jobs, jobs=cli_args.jobs,
...@@ -438,50 +456,29 @@ def main(argv, linux=None): ...@@ -438,50 +456,29 @@ def main(argv, linux=None):
not os.path.exists(cli_args.build_dir)): not os.path.exists(cli_args.build_dir)):
os.mkdir(cli_args.build_dir) os.mkdir(cli_args.build_dir)
if not linux: linux = tree_from_args(cli_args)
linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir,
kunitconfig_path=cli_args.kunitconfig,
kconfig_add=cli_args.kconfig_add,
arch=cli_args.arch,
cross_compile=cli_args.cross_compile,
qemu_config_path=cli_args.qemu_config)
request = KunitConfigRequest(build_dir=cli_args.build_dir, request = KunitConfigRequest(build_dir=cli_args.build_dir,
make_options=cli_args.make_options) make_options=cli_args.make_options)
result = config_tests(linux, request) result = config_tests(linux, request)
kunit_parser.print_with_timestamp(( stdout.print_with_timestamp((
'Elapsed time: %.3fs\n') % ( 'Elapsed time: %.3fs\n') % (
result.elapsed_time)) result.elapsed_time))
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
elif cli_args.subcommand == 'build': elif cli_args.subcommand == 'build':
if not linux: linux = tree_from_args(cli_args)
linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir,
kunitconfig_path=cli_args.kunitconfig,
kconfig_add=cli_args.kconfig_add,
arch=cli_args.arch,
cross_compile=cli_args.cross_compile,
qemu_config_path=cli_args.qemu_config)
request = KunitBuildRequest(build_dir=cli_args.build_dir, request = KunitBuildRequest(build_dir=cli_args.build_dir,
make_options=cli_args.make_options, make_options=cli_args.make_options,
jobs=cli_args.jobs, jobs=cli_args.jobs,
alltests=cli_args.alltests) alltests=cli_args.alltests)
result = config_and_build_tests(linux, request) result = config_and_build_tests(linux, request)
kunit_parser.print_with_timestamp(( stdout.print_with_timestamp((
'Elapsed time: %.3fs\n') % ( 'Elapsed time: %.3fs\n') % (
result.elapsed_time)) result.elapsed_time))
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
elif cli_args.subcommand == 'exec': elif cli_args.subcommand == 'exec':
if not linux: linux = tree_from_args(cli_args)
linux = kunit_kernel.LinuxSourceTree(cli_args.build_dir,
kunitconfig_path=cli_args.kunitconfig,
kconfig_add=cli_args.kconfig_add,
arch=cli_args.arch,
cross_compile=cli_args.cross_compile,
qemu_config_path=cli_args.qemu_config)
exec_request = KunitExecRequest(raw_output=cli_args.raw_output, exec_request = KunitExecRequest(raw_output=cli_args.raw_output,
build_dir=cli_args.build_dir, build_dir=cli_args.build_dir,
json=cli_args.json, json=cli_args.json,
...@@ -491,7 +488,7 @@ def main(argv, linux=None): ...@@ -491,7 +488,7 @@ def main(argv, linux=None):
kernel_args=cli_args.kernel_args, kernel_args=cli_args.kernel_args,
run_isolated=cli_args.run_isolated) run_isolated=cli_args.run_isolated)
result = exec_tests(linux, exec_request) result = exec_tests(linux, exec_request)
kunit_parser.print_with_timestamp(( stdout.print_with_timestamp((
'Elapsed time: %.3fs\n') % (result.elapsed_time)) 'Elapsed time: %.3fs\n') % (result.elapsed_time))
if result.status != KunitStatus.SUCCESS: if result.status != KunitStatus.SUCCESS:
sys.exit(1) sys.exit(1)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
from dataclasses import dataclass from dataclasses import dataclass
import re import re
from typing import List, Set from typing import Dict, Iterable, List, Set, Tuple
CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$' CONFIG_IS_NOT_SET_PATTERN = r'^# CONFIG_(\w+) is not set$'
CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$' CONFIG_PATTERN = r'^CONFIG_(\w+)=(\S+|".*")$'
...@@ -32,35 +32,51 @@ class Kconfig: ...@@ -32,35 +32,51 @@ class Kconfig:
"""Represents defconfig or .config specified using the Kconfig language.""" """Represents defconfig or .config specified using the Kconfig language."""
def __init__(self) -> None: def __init__(self) -> None:
self._entries = [] # type: List[KconfigEntry] self._entries = {} # type: Dict[str, str]
def entries(self) -> Set[KconfigEntry]: def __eq__(self, other) -> bool:
return set(self._entries) if not isinstance(other, self.__class__):
return False
return self._entries == other._entries
def add_entry(self, entry: KconfigEntry) -> None: def __repr__(self) -> str:
self._entries.append(entry) return ','.join(str(e) for e in self.as_entries())
def as_entries(self) -> Iterable[KconfigEntry]:
for name, value in self._entries.items():
yield KconfigEntry(name, value)
def add_entry(self, name: str, value: str) -> None:
self._entries[name] = value
def is_subset_of(self, other: 'Kconfig') -> bool: def is_subset_of(self, other: 'Kconfig') -> bool:
other_dict = {e.name: e.value for e in other.entries()} for name, value in self._entries.items():
for a in self.entries(): b = other._entries.get(name)
b = other_dict.get(a.name)
if b is None: if b is None:
if a.value == 'n': if value == 'n':
continue continue
return False return False
if a.value != b: if value != b:
return False return False
return True return True
def conflicting_options(self, other: 'Kconfig') -> List[Tuple[KconfigEntry, KconfigEntry]]:
diff = [] # type: List[Tuple[KconfigEntry, KconfigEntry]]
for name, value in self._entries.items():
b = other._entries.get(name)
if b and value != b:
pair = (KconfigEntry(name, value), KconfigEntry(name, b))
diff.append(pair)
return diff
def merge_in_entries(self, other: 'Kconfig') -> None: def merge_in_entries(self, other: 'Kconfig') -> None:
if other.is_subset_of(self): for name, value in other._entries.items():
return self._entries[name] = value
self._entries = list(self.entries().union(other.entries()))
def write_to_file(self, path: str) -> None: def write_to_file(self, path: str) -> None:
with open(path, 'a+') as f: with open(path, 'a+') as f:
for entry in self.entries(): for e in self.as_entries():
f.write(str(entry) + '\n') f.write(str(e) + '\n')
def parse_file(path: str) -> Kconfig: def parse_file(path: str) -> Kconfig:
with open(path, 'r') as f: with open(path, 'r') as f:
...@@ -78,14 +94,12 @@ def parse_from_string(blob: str) -> Kconfig: ...@@ -78,14 +94,12 @@ def parse_from_string(blob: str) -> Kconfig:
match = config_matcher.match(line) match = config_matcher.match(line)
if match: if match:
entry = KconfigEntry(match.group(1), match.group(2)) kconfig.add_entry(match.group(1), match.group(2))
kconfig.add_entry(entry)
continue continue
empty_match = is_not_set_matcher.match(line) empty_match = is_not_set_matcher.match(line)
if empty_match: if empty_match:
entry = KconfigEntry(empty_match.group(1), 'n') kconfig.add_entry(empty_match.group(1), 'n')
kconfig.add_entry(entry)
continue continue
if line[0] == '#': if line[0] == '#':
......
This diff is collapsed.
...@@ -13,10 +13,11 @@ from __future__ import annotations ...@@ -13,10 +13,11 @@ from __future__ import annotations
import re import re
import sys import sys
import datetime
from enum import Enum, auto from enum import Enum, auto
from typing import Iterable, Iterator, List, Optional, Tuple from typing import Iterable, Iterator, List, Optional, Tuple
from kunit_printer import stdout
class Test: class Test:
""" """
A class to represent a test parsed from KTAP results. All KTAP A class to represent a test parsed from KTAP results. All KTAP
...@@ -55,7 +56,7 @@ class Test: ...@@ -55,7 +56,7 @@ class Test:
def add_error(self, error_message: str) -> None: def add_error(self, error_message: str) -> None:
"""Records an error that occurred while parsing this test.""" """Records an error that occurred while parsing this test."""
self.counts.errors += 1 self.counts.errors += 1
print_with_timestamp(red('[ERROR]') + f' Test: {self.name}: {error_message}') stdout.print_with_timestamp(stdout.red('[ERROR]') + f' Test: {self.name}: {error_message}')
class TestStatus(Enum): class TestStatus(Enum):
"""An enumeration class to represent the status of a test.""" """An enumeration class to represent the status of a test."""
...@@ -461,32 +462,6 @@ def parse_diagnostic(lines: LineStream) -> List[str]: ...@@ -461,32 +462,6 @@ def parse_diagnostic(lines: LineStream) -> List[str]:
DIVIDER = '=' * 60 DIVIDER = '=' * 60
RESET = '\033[0;0m'
def red(text: str) -> str:
"""Returns inputted string with red color code."""
if not sys.stdout.isatty():
return text
return '\033[1;31m' + text + RESET
def yellow(text: str) -> str:
"""Returns inputted string with yellow color code."""
if not sys.stdout.isatty():
return text
return '\033[1;33m' + text + RESET
def green(text: str) -> str:
"""Returns inputted string with green color code."""
if not sys.stdout.isatty():
return text
return '\033[1;32m' + text + RESET
ANSI_LEN = len(red(''))
def print_with_timestamp(message: str) -> None:
"""Prints message with timestamp at beginning."""
print('[%s] %s' % (datetime.datetime.now().strftime('%H:%M:%S'), message))
def format_test_divider(message: str, len_message: int) -> str: def format_test_divider(message: str, len_message: int) -> str:
""" """
Returns string with message centered in fixed width divider. Returns string with message centered in fixed width divider.
...@@ -529,12 +504,12 @@ def print_test_header(test: Test) -> None: ...@@ -529,12 +504,12 @@ def print_test_header(test: Test) -> None:
message += ' (1 subtest)' message += ' (1 subtest)'
else: else:
message += f' ({test.expected_count} subtests)' message += f' ({test.expected_count} subtests)'
print_with_timestamp(format_test_divider(message, len(message))) stdout.print_with_timestamp(format_test_divider(message, len(message)))
def print_log(log: Iterable[str]) -> None: def print_log(log: Iterable[str]) -> None:
"""Prints all strings in saved log for test in yellow.""" """Prints all strings in saved log for test in yellow."""
for m in log: for m in log:
print_with_timestamp(yellow(m)) stdout.print_with_timestamp(stdout.yellow(m))
def format_test_result(test: Test) -> str: def format_test_result(test: Test) -> str:
""" """
...@@ -551,16 +526,16 @@ def format_test_result(test: Test) -> str: ...@@ -551,16 +526,16 @@ def format_test_result(test: Test) -> str:
String containing formatted test result String containing formatted test result
""" """
if test.status == TestStatus.SUCCESS: if test.status == TestStatus.SUCCESS:
return green('[PASSED] ') + test.name return stdout.green('[PASSED] ') + test.name
if test.status == TestStatus.SKIPPED: if test.status == TestStatus.SKIPPED:
return yellow('[SKIPPED] ') + test.name return stdout.yellow('[SKIPPED] ') + test.name
if test.status == TestStatus.NO_TESTS: if test.status == TestStatus.NO_TESTS:
return yellow('[NO TESTS RUN] ') + test.name return stdout.yellow('[NO TESTS RUN] ') + test.name
if test.status == TestStatus.TEST_CRASHED: if test.status == TestStatus.TEST_CRASHED:
print_log(test.log) print_log(test.log)
return red('[CRASHED] ') + test.name return stdout.red('[CRASHED] ') + test.name
print_log(test.log) print_log(test.log)
return red('[FAILED] ') + test.name return stdout.red('[FAILED] ') + test.name
def print_test_result(test: Test) -> None: def print_test_result(test: Test) -> None:
""" """
...@@ -572,7 +547,7 @@ def print_test_result(test: Test) -> None: ...@@ -572,7 +547,7 @@ def print_test_result(test: Test) -> None:
Parameters: Parameters:
test - Test object representing current test being printed test - Test object representing current test being printed
""" """
print_with_timestamp(format_test_result(test)) stdout.print_with_timestamp(format_test_result(test))
def print_test_footer(test: Test) -> None: def print_test_footer(test: Test) -> None:
""" """
...@@ -585,8 +560,8 @@ def print_test_footer(test: Test) -> None: ...@@ -585,8 +560,8 @@ def print_test_footer(test: Test) -> None:
test - Test object representing current test being printed test - Test object representing current test being printed
""" """
message = format_test_result(test) message = format_test_result(test)
print_with_timestamp(format_test_divider(message, stdout.print_with_timestamp(format_test_divider(message,
len(message) - ANSI_LEN)) len(message) - stdout.color_len()))
def print_summary_line(test: Test) -> None: def print_summary_line(test: Test) -> None:
""" """
...@@ -603,12 +578,12 @@ def print_summary_line(test: Test) -> None: ...@@ -603,12 +578,12 @@ def print_summary_line(test: Test) -> None:
test - Test object representing current test being printed test - Test object representing current test being printed
""" """
if test.status == TestStatus.SUCCESS: if test.status == TestStatus.SUCCESS:
color = green color = stdout.green
elif test.status in (TestStatus.SKIPPED, TestStatus.NO_TESTS): elif test.status in (TestStatus.SKIPPED, TestStatus.NO_TESTS):
color = yellow color = stdout.yellow
else: else:
color = red color = stdout.red
print_with_timestamp(color(f'Testing complete. {test.counts}')) stdout.print_with_timestamp(color(f'Testing complete. {test.counts}'))
# Other methods: # Other methods:
...@@ -762,7 +737,7 @@ def parse_run_tests(kernel_output: Iterable[str]) -> Test: ...@@ -762,7 +737,7 @@ def parse_run_tests(kernel_output: Iterable[str]) -> Test:
Return: Return:
Test - the main test object with all subtests. Test - the main test object with all subtests.
""" """
print_with_timestamp(DIVIDER) stdout.print_with_timestamp(DIVIDER)
lines = extract_tap_lines(kernel_output) lines = extract_tap_lines(kernel_output)
test = Test() test = Test()
if not lines: if not lines:
...@@ -773,6 +748,6 @@ def parse_run_tests(kernel_output: Iterable[str]) -> Test: ...@@ -773,6 +748,6 @@ def parse_run_tests(kernel_output: Iterable[str]) -> Test:
test = parse_test(lines, 0, []) test = parse_test(lines, 0, [])
if test.status != TestStatus.NO_TESTS: if test.status != TestStatus.NO_TESTS:
test.status = test.counts.get_status() test.status = test.counts.get_status()
print_with_timestamp(DIVIDER) stdout.print_with_timestamp(DIVIDER)
print_summary_line(test) print_summary_line(test)
return test return test
#!/usr/bin/env python3
# SPDX-License-Identifier: GPL-2.0
#
# Utilities for printing and coloring output.
#
# Copyright (C) 2022, Google LLC.
# Author: Daniel Latypov <dlatypov@google.com>
import datetime
import sys
import typing
_RESET = '\033[0;0m'
class Printer:
"""Wraps a file object, providing utilities for coloring output, etc."""
def __init__(self, output: typing.IO):
self._output = output
self._use_color = output.isatty()
def print(self, message: str) -> None:
print(message, file=self._output)
def print_with_timestamp(self, message: str) -> None:
ts = datetime.datetime.now().strftime('%H:%M:%S')
self.print(f'[{ts}] {message}')
def _color(self, code: str, text: str) -> str:
if not self._use_color:
return text
return code + text + _RESET
def red(self, text: str) -> str:
return self._color('\033[1;31m', text)
def yellow(self, text: str) -> str:
return self._color('\033[1;33m', text)
def green(self, text: str) -> str:
return self._color('\033[1;32m', text)
def color_len(self) -> int:
"""Returns the length of the color escape codes."""
return len(self.red(''))
# Provides a default instance that prints to stdout
stdout = Printer(sys.stdout)
This diff is collapsed.
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
#define __KSELFTEST_MODULE_H #define __KSELFTEST_MODULE_H
#include <linux/module.h> #include <linux/module.h>
#include <linux/panic.h>
/* /*
* Test framework for writing test modules to be loaded by kselftest. * Test framework for writing test modules to be loaded by kselftest.
...@@ -41,6 +42,7 @@ static inline int kstm_report(unsigned int total_tests, unsigned int failed_test ...@@ -41,6 +42,7 @@ static inline int kstm_report(unsigned int total_tests, unsigned int failed_test
static int __init __module##_init(void) \ static int __init __module##_init(void) \
{ \ { \
pr_info("loaded.\n"); \ pr_info("loaded.\n"); \
add_taint(TAINT_TEST, LOCKDEP_STILL_OK); \
selftest(); \ selftest(); \
return kstm_report(total_tests, failed_tests, skipped_tests); \ return kstm_report(total_tests, failed_tests, skipped_tests); \
} \ } \
...@@ -51,4 +53,6 @@ static void __exit __module##_exit(void) \ ...@@ -51,4 +53,6 @@ static void __exit __module##_exit(void) \
module_init(__module##_init); \ module_init(__module##_init); \
module_exit(__module##_exit) module_exit(__module##_exit)
MODULE_INFO(test, "Y");
#endif /* __KSELFTEST_MODULE_H */ #endif /* __KSELFTEST_MODULE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment