mirror of
https://github.com/armbian/linux-cix.git
synced 2026-01-06 12:30:45 -08:00
Merge tag 'linux-kselftest-kunit-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
Pull kunit updates from Shuah Khan: - add support for running Rust documentation tests as KUnit tests - make init, str, sync, types doctests compilable/testable - add support for attributes API which include speed, modules attributes, ability to filter and report attributes - add support for marking tests slow using attributes API - add attributes API documentation - fix a wild-memory-access bug in kunit_filter_suites() and a possible memory leak in kunit_filter_suites() - add support for counting number of test suites in a module, list action to kunit test modules, and test filtering on module tests * tag 'linux-kselftest-kunit-6.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest: (25 commits) kunit: fix struct kunit_attr header kunit: replace KUNIT_TRIGGER_STATIC_STUB maro with KUNIT_STATIC_STUB_REDIRECT kunit: Allow kunit test modules to use test filtering kunit: Make 'list' action available to kunit test modules kunit: Report the count of test suites in a module kunit: fix uninitialized variables bug in attributes filtering kunit: fix possible memory leak in kunit_filter_suites() kunit: fix wild-memory-access bug in kunit_filter_suites() kunit: Add documentation of KUnit test attributes kunit: add tests for filtering attributes kunit: time: Mark test as slow using test attributes kunit: memcpy: Mark tests as slow using test attributes kunit: tool: Add command line interface to filter and report attributes kunit: Add ability to filter attributes kunit: Add module attribute kunit: Add speed attribute kunit: Add test attributes API structure MAINTAINERS: add Rust KUnit files to the KUnit entry rust: support running Rust documentation tests as KUnit ones rust: types: make doctests compilable/testable ...
This commit is contained in:
@@ -321,3 +321,15 @@ command line arguments:
|
||||
|
||||
- ``--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.
|
||||
|
||||
- ``--filter``: Specifies filters on test attributes, for example, ``speed!=slow``.
|
||||
Multiple filters can be used by wrapping input in quotes and separating filters
|
||||
by commas. Example: ``--filter "speed>slow, module=example"``.
|
||||
|
||||
- ``--filter_action``: If set to ``skip``, filtered tests will be shown as skipped
|
||||
in the output rather than showing no output.
|
||||
|
||||
- ``--list_tests``: If set, lists all tests that will be run.
|
||||
|
||||
- ``--list_tests_attr``: If set, lists all tests that will be run and all of their
|
||||
attributes.
|
||||
|
||||
@@ -262,3 +262,169 @@ other code executed during boot, e.g.
|
||||
# Reset coverage counters before running the test.
|
||||
$ echo 0 > /sys/kernel/debug/gcov/reset
|
||||
$ modprobe kunit-example-test
|
||||
|
||||
|
||||
Test Attributes and Filtering
|
||||
=============================
|
||||
|
||||
Test suites and cases can be marked with test attributes, such as speed of
|
||||
test. These attributes will later be printed in test output and can be used to
|
||||
filter test execution.
|
||||
|
||||
Marking Test Attributes
|
||||
-----------------------
|
||||
|
||||
Tests are marked with an attribute by including a ``kunit_attributes`` object
|
||||
in the test definition.
|
||||
|
||||
Test cases can be marked using the ``KUNIT_CASE_ATTR(test_name, attributes)``
|
||||
macro to define the test case instead of ``KUNIT_CASE(test_name)``.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static const struct kunit_attributes example_attr = {
|
||||
.speed = KUNIT_VERY_SLOW,
|
||||
};
|
||||
|
||||
static struct kunit_case example_test_cases[] = {
|
||||
KUNIT_CASE_ATTR(example_test, example_attr),
|
||||
};
|
||||
|
||||
.. note::
|
||||
To mark a test case as slow, you can also use ``KUNIT_CASE_SLOW(test_name)``.
|
||||
This is a helpful macro as the slow attribute is the most commonly used.
|
||||
|
||||
Test suites can be marked with an attribute by setting the "attr" field in the
|
||||
suite definition.
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
static const struct kunit_attributes example_attr = {
|
||||
.speed = KUNIT_VERY_SLOW,
|
||||
};
|
||||
|
||||
static struct kunit_suite example_test_suite = {
|
||||
...,
|
||||
.attr = example_attr,
|
||||
};
|
||||
|
||||
.. note::
|
||||
Not all attributes need to be set in a ``kunit_attributes`` object. Unset
|
||||
attributes will remain uninitialized and act as though the attribute is set
|
||||
to 0 or NULL. Thus, if an attribute is set to 0, it is treated as unset.
|
||||
These unset attributes will not be reported and may act as a default value
|
||||
for filtering purposes.
|
||||
|
||||
Reporting Attributes
|
||||
--------------------
|
||||
|
||||
When a user runs tests, attributes will be present in the raw kernel output (in
|
||||
KTAP format). Note that attributes will be hidden by default in kunit.py output
|
||||
for all passing tests but the raw kernel output can be accessed using the
|
||||
``--raw_output`` flag. This is an example of how test attributes for test cases
|
||||
will be formatted in kernel output:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
# example_test.speed: slow
|
||||
ok 1 example_test
|
||||
|
||||
This is an example of how test attributes for test suites will be formatted in
|
||||
kernel output:
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
KTAP version 2
|
||||
# Subtest: example_suite
|
||||
# module: kunit_example_test
|
||||
1..3
|
||||
...
|
||||
ok 1 example_suite
|
||||
|
||||
Additionally, users can output a full attribute report of tests with their
|
||||
attributes, using the command line flag ``--list_tests_attr``:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
kunit.py run "example" --list_tests_attr
|
||||
|
||||
.. note::
|
||||
This report can be accessed when running KUnit manually by passing in the
|
||||
module_param ``kunit.action=list_attr``.
|
||||
|
||||
Filtering
|
||||
---------
|
||||
|
||||
Users can filter tests using the ``--filter`` command line flag when running
|
||||
tests. As an example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
kunit.py run --filter speed=slow
|
||||
|
||||
|
||||
You can also use the following operations on filters: "<", ">", "<=", ">=",
|
||||
"!=", and "=". Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
kunit.py run --filter "speed>slow"
|
||||
|
||||
This example will run all tests with speeds faster than slow. Note that the
|
||||
characters < and > are often interpreted by the shell, so they may need to be
|
||||
quoted or escaped, as above.
|
||||
|
||||
Additionally, you can use multiple filters at once. Simply separate filters
|
||||
using commas. Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
kunit.py run --filter "speed>slow, module=kunit_example_test"
|
||||
|
||||
.. note::
|
||||
You can use this filtering feature when running KUnit manually by passing
|
||||
the filter as a module param: ``kunit.filter="speed>slow, speed<=normal"``.
|
||||
|
||||
Filtered tests will not run or show up in the test output. You can use the
|
||||
``--filter_action=skip`` flag to skip filtered tests instead. These tests will be
|
||||
shown in the test output in the test but will not run. To use this feature when
|
||||
running KUnit manually, use the module param ``kunit.filter_action=skip``.
|
||||
|
||||
Rules of Filtering Procedure
|
||||
----------------------------
|
||||
|
||||
Since both suites and test cases can have attributes, there may be conflicts
|
||||
between attributes during filtering. The process of filtering follows these
|
||||
rules:
|
||||
|
||||
- Filtering always operates at a per-test level.
|
||||
|
||||
- If a test has an attribute set, then the test's value is filtered on.
|
||||
|
||||
- Otherwise, the value falls back to the suite's value.
|
||||
|
||||
- If neither are set, the attribute has a global "default" value, which is used.
|
||||
|
||||
List of Current Attributes
|
||||
--------------------------
|
||||
|
||||
``speed``
|
||||
|
||||
This attribute indicates the speed of a test's execution (how slow or fast the
|
||||
test is).
|
||||
|
||||
This attribute is saved as an enum with the following categories: "normal",
|
||||
"slow", or "very_slow". The assumed default speed for tests is "normal". This
|
||||
indicates that the test takes a relatively trivial amount of time (less than
|
||||
1 second), regardless of the machine it is running on. Any test slower than
|
||||
this could be marked as "slow" or "very_slow".
|
||||
|
||||
The macro ``KUNIT_CASE_SLOW(test_name)`` can be easily used to set the speed
|
||||
of a test case to "slow".
|
||||
|
||||
``module``
|
||||
|
||||
This attribute indicates the name of the module associated with the test.
|
||||
|
||||
This attribute is automatically saved as a string and is printed for each suite.
|
||||
Tests can also be filtered using this attribute.
|
||||
|
||||
@@ -11394,6 +11394,8 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest.git k
|
||||
F: Documentation/dev-tools/kunit/
|
||||
F: include/kunit/
|
||||
F: lib/kunit/
|
||||
F: rust/kernel/kunit.rs
|
||||
F: scripts/rustdoc_test_*
|
||||
F: tools/testing/kunit/
|
||||
|
||||
KERNEL USERMODE HELPER
|
||||
|
||||
50
include/kunit/attributes.h
Normal file
50
include/kunit/attributes.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* KUnit API to save and access test attributes
|
||||
*
|
||||
* Copyright (C) 2023, Google LLC.
|
||||
* Author: Rae Moar <rmoar@google.com>
|
||||
*/
|
||||
|
||||
#ifndef _KUNIT_ATTRIBUTES_H
|
||||
#define _KUNIT_ATTRIBUTES_H
|
||||
|
||||
/*
|
||||
* struct kunit_attr_filter - representation of attributes filter with the
|
||||
* attribute object and string input
|
||||
*/
|
||||
struct kunit_attr_filter {
|
||||
struct kunit_attr *attr;
|
||||
char *input;
|
||||
};
|
||||
|
||||
/*
|
||||
* Returns the name of the filter's attribute.
|
||||
*/
|
||||
const char *kunit_attr_filter_name(struct kunit_attr_filter filter);
|
||||
|
||||
/*
|
||||
* Print all test attributes for a test case or suite.
|
||||
* Output format for test cases: "# <test_name>.<attribute>: <value>"
|
||||
* Output format for test suites: "# <attribute>: <value>"
|
||||
*/
|
||||
void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level);
|
||||
|
||||
/*
|
||||
* Returns the number of fitlers in input.
|
||||
*/
|
||||
int kunit_get_filter_count(char *input);
|
||||
|
||||
/*
|
||||
* Parse attributes filter input and return an objects containing the
|
||||
* attribute object and the string input of the next filter.
|
||||
*/
|
||||
struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err);
|
||||
|
||||
/*
|
||||
* Returns a copy of the suite containing only tests that pass the filter.
|
||||
*/
|
||||
struct kunit_suite *kunit_filter_attr_tests(const struct kunit_suite *const suite,
|
||||
struct kunit_attr_filter filter, char *action, int *err);
|
||||
|
||||
#endif /* _KUNIT_ATTRIBUTES_H */
|
||||
@@ -11,7 +11,7 @@
|
||||
#if !IS_ENABLED(CONFIG_KUNIT)
|
||||
|
||||
/* If CONFIG_KUNIT is not enabled, these stubs quietly disappear. */
|
||||
#define KUNIT_TRIGGER_STATIC_STUB(real_fn_name, args...) do {} while (0)
|
||||
#define KUNIT_STATIC_STUB_REDIRECT(real_fn_name, args...) do {} while (0)
|
||||
|
||||
#else
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* This is a function prologue which is used to allow calls to the current
|
||||
* function to be redirected by a KUnit test. KUnit tests can call
|
||||
* kunit_activate_static_stub() to pass a replacement function in. The
|
||||
* replacement function will be called by KUNIT_TRIGGER_STATIC_STUB(), which
|
||||
* replacement function will be called by KUNIT_STATIC_STUB_REDIRECT(), which
|
||||
* will then return from the function. If the caller is not in a KUnit context,
|
||||
* the function will continue execution as normal.
|
||||
*
|
||||
@@ -87,7 +87,7 @@ void __kunit_activate_static_stub(struct kunit *test,
|
||||
* When activated, calls to real_fn_addr from within this test (even if called
|
||||
* indirectly) will instead call replacement_addr. The function pointed to by
|
||||
* real_fn_addr must begin with the static stub prologue in
|
||||
* KUNIT_TRIGGER_STATIC_STUB() for this to work. real_fn_addr and
|
||||
* KUNIT_STATIC_STUB_REDIRECT() for this to work. real_fn_addr and
|
||||
* replacement_addr must have the same type.
|
||||
*
|
||||
* The redirection can be disabled again with kunit_deactivate_static_stub().
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
#ifndef _KUNIT_TEST_BUG_H
|
||||
#define _KUNIT_TEST_BUG_H
|
||||
|
||||
#include <linux/stddef.h> /* for NULL */
|
||||
|
||||
#if IS_ENABLED(CONFIG_KUNIT)
|
||||
|
||||
#include <linux/jump_label.h> /* For static branch */
|
||||
|
||||
@@ -63,12 +63,35 @@ enum kunit_status {
|
||||
KUNIT_SKIPPED,
|
||||
};
|
||||
|
||||
/* Attribute struct/enum definitions */
|
||||
|
||||
/*
|
||||
* Speed Attribute is stored as an enum and separated into categories of
|
||||
* speed: very_slowm, slow, and normal. These speeds are relative to
|
||||
* other KUnit tests.
|
||||
*
|
||||
* Note: unset speed attribute acts as default of KUNIT_SPEED_NORMAL.
|
||||
*/
|
||||
enum kunit_speed {
|
||||
KUNIT_SPEED_UNSET,
|
||||
KUNIT_SPEED_VERY_SLOW,
|
||||
KUNIT_SPEED_SLOW,
|
||||
KUNIT_SPEED_NORMAL,
|
||||
KUNIT_SPEED_MAX = KUNIT_SPEED_NORMAL,
|
||||
};
|
||||
|
||||
/* Holds attributes for each test case and suite */
|
||||
struct kunit_attributes {
|
||||
enum kunit_speed speed;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kunit_case - represents an individual test case.
|
||||
*
|
||||
* @run_case: the function representing the actual test case.
|
||||
* @name: the name of the test case.
|
||||
* @generate_params: the generator function for parameterized tests.
|
||||
* @attr: the attributes associated with the test
|
||||
*
|
||||
* A test case is a function with the signature,
|
||||
* ``void (*)(struct kunit *)``
|
||||
@@ -104,9 +127,11 @@ struct kunit_case {
|
||||
void (*run_case)(struct kunit *test);
|
||||
const char *name;
|
||||
const void* (*generate_params)(const void *prev, char *desc);
|
||||
struct kunit_attributes attr;
|
||||
|
||||
/* private: internal use only. */
|
||||
enum kunit_status status;
|
||||
char *module_name;
|
||||
char *log;
|
||||
};
|
||||
|
||||
@@ -131,7 +156,32 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
|
||||
* &struct kunit_case object from it. See the documentation for
|
||||
* &struct kunit_case for an example on how to use it.
|
||||
*/
|
||||
#define KUNIT_CASE(test_name) { .run_case = test_name, .name = #test_name }
|
||||
#define KUNIT_CASE(test_name) \
|
||||
{ .run_case = test_name, .name = #test_name, \
|
||||
.module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* KUNIT_CASE_ATTR - A helper for creating a &struct kunit_case
|
||||
* with attributes
|
||||
*
|
||||
* @test_name: a reference to a test case function.
|
||||
* @attributes: a reference to a struct kunit_attributes object containing
|
||||
* test attributes
|
||||
*/
|
||||
#define KUNIT_CASE_ATTR(test_name, attributes) \
|
||||
{ .run_case = test_name, .name = #test_name, \
|
||||
.attr = attributes, .module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* KUNIT_CASE_SLOW - A helper for creating a &struct kunit_case
|
||||
* with the slow attribute
|
||||
*
|
||||
* @test_name: a reference to a test case function.
|
||||
*/
|
||||
|
||||
#define KUNIT_CASE_SLOW(test_name) \
|
||||
{ .run_case = test_name, .name = #test_name, \
|
||||
.attr.speed = KUNIT_SPEED_SLOW, .module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* KUNIT_CASE_PARAM - A helper for creation a parameterized &struct kunit_case
|
||||
@@ -152,7 +202,21 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
|
||||
*/
|
||||
#define KUNIT_CASE_PARAM(test_name, gen_params) \
|
||||
{ .run_case = test_name, .name = #test_name, \
|
||||
.generate_params = gen_params }
|
||||
.generate_params = gen_params, .module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* KUNIT_CASE_PARAM_ATTR - A helper for creating a parameterized &struct
|
||||
* kunit_case with attributes
|
||||
*
|
||||
* @test_name: a reference to a test case function.
|
||||
* @gen_params: a reference to a parameter generator function.
|
||||
* @attributes: a reference to a struct kunit_attributes object containing
|
||||
* test attributes
|
||||
*/
|
||||
#define KUNIT_CASE_PARAM_ATTR(test_name, gen_params, attributes) \
|
||||
{ .run_case = test_name, .name = #test_name, \
|
||||
.generate_params = gen_params, \
|
||||
.attr = attributes, .module_name = KBUILD_MODNAME}
|
||||
|
||||
/**
|
||||
* struct kunit_suite - describes a related collection of &struct kunit_case
|
||||
@@ -163,6 +227,7 @@ static inline char *kunit_status_to_ok_not_ok(enum kunit_status status)
|
||||
* @init: called before every test case.
|
||||
* @exit: called after every test case.
|
||||
* @test_cases: a null terminated array of test cases.
|
||||
* @attr: the attributes associated with the test suite
|
||||
*
|
||||
* A kunit_suite is a collection of related &struct kunit_case s, such that
|
||||
* @init is called before every test case and @exit is called after every
|
||||
@@ -182,6 +247,7 @@ struct kunit_suite {
|
||||
int (*init)(struct kunit *test);
|
||||
void (*exit)(struct kunit *test);
|
||||
struct kunit_case *test_cases;
|
||||
struct kunit_attributes attr;
|
||||
|
||||
/* private: internal use only */
|
||||
char status_comment[KUNIT_STATUS_COMMENT_SIZE];
|
||||
@@ -190,6 +256,12 @@ struct kunit_suite {
|
||||
int suite_init_err;
|
||||
};
|
||||
|
||||
/* Stores an array of suites, end points one past the end */
|
||||
struct kunit_suite_set {
|
||||
struct kunit_suite * const *start;
|
||||
struct kunit_suite * const *end;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kunit - represents a running instance of a test.
|
||||
*
|
||||
@@ -237,6 +309,10 @@ static inline void kunit_set_failure(struct kunit *test)
|
||||
}
|
||||
|
||||
bool kunit_enabled(void);
|
||||
const char *kunit_action(void);
|
||||
const char *kunit_filter_glob(void);
|
||||
char *kunit_filter(void);
|
||||
char *kunit_filter_action(void);
|
||||
|
||||
void kunit_init_test(struct kunit *test, const char *name, char *log);
|
||||
|
||||
@@ -247,10 +323,21 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite);
|
||||
unsigned int kunit_test_case_num(struct kunit_suite *suite,
|
||||
struct kunit_case *test_case);
|
||||
|
||||
struct kunit_suite_set
|
||||
kunit_filter_suites(const struct kunit_suite_set *suite_set,
|
||||
const char *filter_glob,
|
||||
char *filters,
|
||||
char *filter_action,
|
||||
int *err);
|
||||
void kunit_free_suite_set(struct kunit_suite_set suite_set);
|
||||
|
||||
int __kunit_test_suites_init(struct kunit_suite * const * const suites, int num_suites);
|
||||
|
||||
void __kunit_test_suites_exit(struct kunit_suite **suites, int num_suites);
|
||||
|
||||
void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin);
|
||||
void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr);
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KUNIT)
|
||||
int kunit_run_all_tests(void);
|
||||
#else
|
||||
|
||||
@@ -86,7 +86,7 @@ static void time64_to_tm_test_date_range(struct kunit *test)
|
||||
}
|
||||
|
||||
static struct kunit_case time_test_cases[] = {
|
||||
KUNIT_CASE(time64_to_tm_test_date_range),
|
||||
KUNIT_CASE_SLOW(time64_to_tm_test_date_range),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -2696,6 +2696,9 @@ config MEMCPY_SLOW_KUNIT_TEST
|
||||
and bit ranges. These can be very slow, so they are split out
|
||||
as a separate config, in case they need to be disabled.
|
||||
|
||||
Note this config option will be replaced by the use of KUnit test
|
||||
attributes.
|
||||
|
||||
config IS_SIGNED_TYPE_KUNIT_TEST
|
||||
tristate "Test is_signed_type() macro" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT
|
||||
@@ -3005,6 +3008,19 @@ config RUST_BUILD_ASSERT_ALLOW
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config RUST_KERNEL_DOCTESTS
|
||||
bool "Doctests for the `kernel` crate" if !KUNIT_ALL_TESTS
|
||||
depends on RUST && KUNIT=y
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This builds the documentation tests of the `kernel` crate
|
||||
as KUnit tests.
|
||||
|
||||
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.
|
||||
|
||||
endmenu # "Rust"
|
||||
|
||||
endmenu # Kernel hacking
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
menuconfig KUNIT
|
||||
tristate "KUnit - Enable support for unit tests"
|
||||
select GLOB if KUNIT=y
|
||||
select GLOB
|
||||
help
|
||||
Enables support for kernel unit tests (KUnit), a lightweight unit
|
||||
testing and mocking framework for the Linux kernel. These tests are
|
||||
|
||||
@@ -6,7 +6,8 @@ kunit-objs += test.o \
|
||||
string-stream.o \
|
||||
assert.o \
|
||||
try-catch.o \
|
||||
executor.o
|
||||
executor.o \
|
||||
attributes.o
|
||||
|
||||
ifeq ($(CONFIG_KUNIT_DEBUGFS),y)
|
||||
kunit-objs += debugfs.o
|
||||
|
||||
414
lib/kunit/attributes.c
Normal file
414
lib/kunit/attributes.c
Normal file
@@ -0,0 +1,414 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* KUnit API to save and access test attributes
|
||||
*
|
||||
* Copyright (C) 2023, Google LLC.
|
||||
* Author: Rae Moar <rmoar@google.com>
|
||||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/attributes.h>
|
||||
|
||||
/* Options for printing attributes:
|
||||
* PRINT_ALWAYS - attribute is printed for every test case and suite if set
|
||||
* PRINT_SUITE - attribute is printed for every suite if set but not for test cases
|
||||
* PRINT_NEVER - attribute is never printed
|
||||
*/
|
||||
enum print_ops {
|
||||
PRINT_ALWAYS,
|
||||
PRINT_SUITE,
|
||||
PRINT_NEVER,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct kunit_attr - represents a test attribute and holds flexible
|
||||
* helper functions to interact with attribute.
|
||||
*
|
||||
* @name: name of test attribute, eg. speed
|
||||
* @get_attr: function to return attribute value given a test
|
||||
* @to_string: function to return string representation of given
|
||||
* attribute value
|
||||
* @filter: function to indicate whether a given attribute value passes a
|
||||
* filter
|
||||
* @attr_default: default attribute value used during filtering
|
||||
* @print: value of enum print_ops to indicate when to print attribute
|
||||
*/
|
||||
struct kunit_attr {
|
||||
const char *name;
|
||||
void *(*get_attr)(void *test_or_suite, bool is_test);
|
||||
const char *(*to_string)(void *attr, bool *to_free);
|
||||
int (*filter)(void *attr, const char *input, int *err);
|
||||
void *attr_default;
|
||||
enum print_ops print;
|
||||
};
|
||||
|
||||
/* String Lists for enum Attributes */
|
||||
|
||||
static const char * const speed_str_list[] = {"unset", "very_slow", "slow", "normal"};
|
||||
|
||||
/* To String Methods */
|
||||
|
||||
static const char *attr_enum_to_string(void *attr, const char * const str_list[], bool *to_free)
|
||||
{
|
||||
long val = (long)attr;
|
||||
|
||||
*to_free = false;
|
||||
if (!val)
|
||||
return NULL;
|
||||
return str_list[val];
|
||||
}
|
||||
|
||||
static const char *attr_speed_to_string(void *attr, bool *to_free)
|
||||
{
|
||||
return attr_enum_to_string(attr, speed_str_list, to_free);
|
||||
}
|
||||
|
||||
static const char *attr_string_to_string(void *attr, bool *to_free)
|
||||
{
|
||||
*to_free = false;
|
||||
return (char *) attr;
|
||||
}
|
||||
|
||||
/* Filter Methods */
|
||||
|
||||
static const char op_list[] = "<>!=";
|
||||
|
||||
/*
|
||||
* Returns whether the inputted integer value matches the filter given
|
||||
* by the operation string and inputted integer.
|
||||
*/
|
||||
static int int_filter(long val, const char *op, int input, int *err)
|
||||
{
|
||||
if (!strncmp(op, "<=", 2))
|
||||
return (val <= input);
|
||||
else if (!strncmp(op, ">=", 2))
|
||||
return (val >= input);
|
||||
else if (!strncmp(op, "!=", 2))
|
||||
return (val != input);
|
||||
else if (!strncmp(op, ">", 1))
|
||||
return (val > input);
|
||||
else if (!strncmp(op, "<", 1))
|
||||
return (val < input);
|
||||
else if (!strncmp(op, "=", 1))
|
||||
return (val == input);
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: invalid filter operation: %s\n", op);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the inputted enum value "attr" matches the filter given
|
||||
* by the input string. Note: the str_list includes the corresponding string
|
||||
* list to the enum values.
|
||||
*/
|
||||
static int attr_enum_filter(void *attr, const char *input, int *err,
|
||||
const char * const str_list[], int max)
|
||||
{
|
||||
int i, j, input_int = -1;
|
||||
long test_val = (long)attr;
|
||||
const char *input_val = NULL;
|
||||
|
||||
for (i = 0; input[i]; i++) {
|
||||
if (!strchr(op_list, input[i])) {
|
||||
input_val = input + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!input_val) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: filter value not found: %s\n", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (j = 0; j <= max; j++) {
|
||||
if (!strcmp(input_val, str_list[j]))
|
||||
input_int = j;
|
||||
}
|
||||
|
||||
if (input_int < 0) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: invalid filter input: %s\n", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
return int_filter(test_val, input, input_int, err);
|
||||
}
|
||||
|
||||
static int attr_speed_filter(void *attr, const char *input, int *err)
|
||||
{
|
||||
return attr_enum_filter(attr, input, err, speed_str_list, KUNIT_SPEED_MAX);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns whether the inputted string value (attr) matches the filter given
|
||||
* by the input string.
|
||||
*/
|
||||
static int attr_string_filter(void *attr, const char *input, int *err)
|
||||
{
|
||||
char *str = attr;
|
||||
|
||||
if (!strncmp(input, "<", 1)) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: invalid filter input: %s\n", input);
|
||||
return false;
|
||||
} else if (!strncmp(input, ">", 1)) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: invalid filter input: %s\n", input);
|
||||
return false;
|
||||
} else if (!strncmp(input, "!=", 2)) {
|
||||
return (strcmp(input + 2, str) != 0);
|
||||
} else if (!strncmp(input, "=", 1)) {
|
||||
return (strcmp(input + 1, str) == 0);
|
||||
}
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: invalid filter operation: %s\n", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* Get Attribute Methods */
|
||||
|
||||
static void *attr_speed_get(void *test_or_suite, bool is_test)
|
||||
{
|
||||
struct kunit_suite *suite = is_test ? NULL : test_or_suite;
|
||||
struct kunit_case *test = is_test ? test_or_suite : NULL;
|
||||
|
||||
if (test)
|
||||
return ((void *) test->attr.speed);
|
||||
else
|
||||
return ((void *) suite->attr.speed);
|
||||
}
|
||||
|
||||
static void *attr_module_get(void *test_or_suite, bool is_test)
|
||||
{
|
||||
struct kunit_suite *suite = is_test ? NULL : test_or_suite;
|
||||
struct kunit_case *test = is_test ? test_or_suite : NULL;
|
||||
|
||||
// Suites get their module attribute from their first test_case
|
||||
if (test)
|
||||
return ((void *) test->module_name);
|
||||
else if (kunit_suite_num_test_cases(suite) > 0)
|
||||
return ((void *) suite->test_cases[0].module_name);
|
||||
else
|
||||
return (void *) "";
|
||||
}
|
||||
|
||||
/* List of all Test Attributes */
|
||||
|
||||
static struct kunit_attr kunit_attr_list[] = {
|
||||
{
|
||||
.name = "speed",
|
||||
.get_attr = attr_speed_get,
|
||||
.to_string = attr_speed_to_string,
|
||||
.filter = attr_speed_filter,
|
||||
.attr_default = (void *)KUNIT_SPEED_NORMAL,
|
||||
.print = PRINT_ALWAYS,
|
||||
},
|
||||
{
|
||||
.name = "module",
|
||||
.get_attr = attr_module_get,
|
||||
.to_string = attr_string_to_string,
|
||||
.filter = attr_string_filter,
|
||||
.attr_default = (void *)"",
|
||||
.print = PRINT_SUITE,
|
||||
}
|
||||
};
|
||||
|
||||
/* Helper Functions to Access Attributes */
|
||||
|
||||
const char *kunit_attr_filter_name(struct kunit_attr_filter filter)
|
||||
{
|
||||
return filter.attr->name;
|
||||
}
|
||||
|
||||
void kunit_print_attr(void *test_or_suite, bool is_test, unsigned int test_level)
|
||||
{
|
||||
int i;
|
||||
bool to_free = false;
|
||||
void *attr;
|
||||
const char *attr_name, *attr_str;
|
||||
struct kunit_suite *suite = is_test ? NULL : test_or_suite;
|
||||
struct kunit_case *test = is_test ? test_or_suite : NULL;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kunit_attr_list); i++) {
|
||||
if (kunit_attr_list[i].print == PRINT_NEVER ||
|
||||
(test && kunit_attr_list[i].print == PRINT_SUITE))
|
||||
continue;
|
||||
attr = kunit_attr_list[i].get_attr(test_or_suite, is_test);
|
||||
if (attr) {
|
||||
attr_name = kunit_attr_list[i].name;
|
||||
attr_str = kunit_attr_list[i].to_string(attr, &to_free);
|
||||
if (test) {
|
||||
kunit_log(KERN_INFO, test, "%*s# %s.%s: %s",
|
||||
KUNIT_INDENT_LEN * test_level, "", test->name,
|
||||
attr_name, attr_str);
|
||||
} else {
|
||||
kunit_log(KERN_INFO, suite, "%*s# %s: %s",
|
||||
KUNIT_INDENT_LEN * test_level, "", attr_name, attr_str);
|
||||
}
|
||||
|
||||
/* Free to_string of attribute if needed */
|
||||
if (to_free)
|
||||
kfree(attr_str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Helper Functions to Filter Attributes */
|
||||
|
||||
int kunit_get_filter_count(char *input)
|
||||
{
|
||||
int i, comma_index = 0, count = 0;
|
||||
|
||||
for (i = 0; input[i]; i++) {
|
||||
if (input[i] == ',') {
|
||||
if ((i - comma_index) > 1)
|
||||
count++;
|
||||
comma_index = i;
|
||||
}
|
||||
}
|
||||
if ((i - comma_index) > 0)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
struct kunit_attr_filter kunit_next_attr_filter(char **filters, int *err)
|
||||
{
|
||||
struct kunit_attr_filter filter = {};
|
||||
int i, j, comma_index = 0, new_start_index = 0;
|
||||
int op_index = -1, attr_index = -1;
|
||||
char op;
|
||||
char *input = *filters;
|
||||
|
||||
/* Parse input until operation */
|
||||
for (i = 0; input[i]; i++) {
|
||||
if (op_index < 0 && strchr(op_list, input[i])) {
|
||||
op_index = i;
|
||||
} else if (!comma_index && input[i] == ',') {
|
||||
comma_index = i;
|
||||
} else if (comma_index && input[i] != ' ') {
|
||||
new_start_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (op_index <= 0) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: filter operation not found: %s\n", input);
|
||||
return filter;
|
||||
}
|
||||
|
||||
/* Temporarily set operator to \0 character. */
|
||||
op = input[op_index];
|
||||
input[op_index] = '\0';
|
||||
|
||||
/* Find associated kunit_attr object */
|
||||
for (j = 0; j < ARRAY_SIZE(kunit_attr_list); j++) {
|
||||
if (!strcmp(input, kunit_attr_list[j].name)) {
|
||||
attr_index = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
input[op_index] = op;
|
||||
|
||||
if (attr_index < 0) {
|
||||
*err = -EINVAL;
|
||||
pr_err("kunit executor: attribute not found: %s\n", input);
|
||||
} else {
|
||||
filter.attr = &kunit_attr_list[attr_index];
|
||||
}
|
||||
|
||||
if (comma_index > 0) {
|
||||
input[comma_index] = '\0';
|
||||
filter.input = input + op_index;
|
||||
input = input + new_start_index;
|
||||
} else {
|
||||
filter.input = input + op_index;
|
||||
input = NULL;
|
||||
}
|
||||
|
||||
*filters = input;
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
struct kunit_suite *kunit_filter_attr_tests(const struct kunit_suite *const suite,
|
||||
struct kunit_attr_filter filter, char *action, int *err)
|
||||
{
|
||||
int n = 0;
|
||||
struct kunit_case *filtered, *test_case;
|
||||
struct kunit_suite *copy;
|
||||
void *suite_val, *test_val;
|
||||
bool suite_result, test_result, default_result, result;
|
||||
|
||||
/* Allocate memory for new copy of suite and list of test cases */
|
||||
copy = kmemdup(suite, sizeof(*copy), GFP_KERNEL);
|
||||
if (!copy)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
kunit_suite_for_each_test_case(suite, test_case) { n++; }
|
||||
|
||||
filtered = kcalloc(n + 1, sizeof(*filtered), GFP_KERNEL);
|
||||
if (!filtered) {
|
||||
kfree(copy);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
n = 0;
|
||||
|
||||
/* Save filtering result on default value */
|
||||
default_result = filter.attr->filter(filter.attr->attr_default, filter.input, err);
|
||||
if (*err)
|
||||
goto err;
|
||||
|
||||
/* Save suite attribute value and filtering result on that value */
|
||||
suite_val = filter.attr->get_attr((void *)suite, false);
|
||||
suite_result = filter.attr->filter(suite_val, filter.input, err);
|
||||
if (*err)
|
||||
goto err;
|
||||
|
||||
/* For each test case, save test case if passes filtering. */
|
||||
kunit_suite_for_each_test_case(suite, test_case) {
|
||||
test_val = filter.attr->get_attr((void *) test_case, true);
|
||||
test_result = filter.attr->filter(filter.attr->get_attr(test_case, true),
|
||||
filter.input, err);
|
||||
if (*err)
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* If attribute value of test case is set, filter on that value.
|
||||
* If not, filter on suite value if set. If not, filter on
|
||||
* default value.
|
||||
*/
|
||||
result = false;
|
||||
if (test_val) {
|
||||
if (test_result)
|
||||
result = true;
|
||||
} else if (suite_val) {
|
||||
if (suite_result)
|
||||
result = true;
|
||||
} else if (default_result) {
|
||||
result = true;
|
||||
}
|
||||
|
||||
if (result) {
|
||||
filtered[n++] = *test_case;
|
||||
} else if (action && strcmp(action, "skip") == 0) {
|
||||
test_case->status = KUNIT_SKIPPED;
|
||||
filtered[n++] = *test_case;
|
||||
}
|
||||
}
|
||||
|
||||
err:
|
||||
if (n == 0 || *err) {
|
||||
kfree(copy);
|
||||
kfree(filtered);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
copy->test_cases = filtered;
|
||||
|
||||
return copy;
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
#include <linux/reboot.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/attributes.h>
|
||||
#include <linux/glob.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
@@ -12,28 +13,59 @@
|
||||
extern struct kunit_suite * const __kunit_suites_start[];
|
||||
extern struct kunit_suite * const __kunit_suites_end[];
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KUNIT)
|
||||
|
||||
static char *filter_glob_param;
|
||||
static char *action_param;
|
||||
|
||||
module_param_named(filter_glob, filter_glob_param, charp, 0);
|
||||
MODULE_PARM_DESC(filter_glob,
|
||||
"Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test");
|
||||
module_param_named(action, action_param, charp, 0);
|
||||
module_param_named(action, action_param, charp, 0400);
|
||||
MODULE_PARM_DESC(action,
|
||||
"Changes KUnit executor behavior, valid values are:\n"
|
||||
"<none>: run the tests like normal\n"
|
||||
"'list' to list test names instead of running them.\n");
|
||||
"'list' to list test names instead of running them.\n"
|
||||
"'list_attr' to list test names and attributes instead of running them.\n");
|
||||
|
||||
const char *kunit_action(void)
|
||||
{
|
||||
return action_param;
|
||||
}
|
||||
|
||||
static char *filter_glob_param;
|
||||
static char *filter_param;
|
||||
static char *filter_action_param;
|
||||
|
||||
module_param_named(filter_glob, filter_glob_param, charp, 0400);
|
||||
MODULE_PARM_DESC(filter_glob,
|
||||
"Filter which KUnit test suites/tests run at boot-time, e.g. list* or list*.*del_test");
|
||||
module_param_named(filter, filter_param, charp, 0400);
|
||||
MODULE_PARM_DESC(filter,
|
||||
"Filter which KUnit test suites/tests run at boot-time using attributes, e.g. speed>slow");
|
||||
module_param_named(filter_action, filter_action_param, charp, 0400);
|
||||
MODULE_PARM_DESC(filter_action,
|
||||
"Changes behavior of filtered tests using attributes, valid values are:\n"
|
||||
"<none>: do not run filtered tests as normal\n"
|
||||
"'skip': skip all filtered tests instead so tests will appear in output\n");
|
||||
|
||||
const char *kunit_filter_glob(void)
|
||||
{
|
||||
return filter_glob_param;
|
||||
}
|
||||
|
||||
char *kunit_filter(void)
|
||||
{
|
||||
return filter_param;
|
||||
}
|
||||
|
||||
char *kunit_filter_action(void)
|
||||
{
|
||||
return filter_action_param;
|
||||
}
|
||||
|
||||
/* glob_match() needs NULL terminated strings, so we need a copy of filter_glob_param. */
|
||||
struct kunit_test_filter {
|
||||
struct kunit_glob_filter {
|
||||
char *suite_glob;
|
||||
char *test_glob;
|
||||
};
|
||||
|
||||
/* Split "suite_glob.test_glob" into two. Assumes filter_glob is not empty. */
|
||||
static void kunit_parse_filter_glob(struct kunit_test_filter *parsed,
|
||||
static void kunit_parse_glob_filter(struct kunit_glob_filter *parsed,
|
||||
const char *filter_glob)
|
||||
{
|
||||
const int len = strlen(filter_glob);
|
||||
@@ -55,7 +87,7 @@ static void kunit_parse_filter_glob(struct kunit_test_filter *parsed,
|
||||
|
||||
/* Create a copy of suite with only tests that match test_glob. */
|
||||
static struct kunit_suite *
|
||||
kunit_filter_tests(const struct kunit_suite *const suite, const char *test_glob)
|
||||
kunit_filter_glob_tests(const struct kunit_suite *const suite, const char *test_glob)
|
||||
{
|
||||
int n = 0;
|
||||
struct kunit_case *filtered, *test_case;
|
||||
@@ -89,16 +121,7 @@ kunit_filter_tests(const struct kunit_suite *const suite, const char *test_glob)
|
||||
return copy;
|
||||
}
|
||||
|
||||
static char *kunit_shutdown;
|
||||
core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
|
||||
|
||||
/* Stores an array of suites, end points one past the end */
|
||||
struct suite_set {
|
||||
struct kunit_suite * const *start;
|
||||
struct kunit_suite * const *end;
|
||||
};
|
||||
|
||||
static void kunit_free_suite_set(struct suite_set suite_set)
|
||||
void kunit_free_suite_set(struct kunit_suite_set suite_set)
|
||||
{
|
||||
struct kunit_suite * const *suites;
|
||||
|
||||
@@ -107,47 +130,144 @@ static void kunit_free_suite_set(struct suite_set suite_set)
|
||||
kfree(suite_set.start);
|
||||
}
|
||||
|
||||
static struct suite_set kunit_filter_suites(const struct suite_set *suite_set,
|
||||
const char *filter_glob,
|
||||
int *err)
|
||||
struct kunit_suite_set
|
||||
kunit_filter_suites(const struct kunit_suite_set *suite_set,
|
||||
const char *filter_glob,
|
||||
char *filters,
|
||||
char *filter_action,
|
||||
int *err)
|
||||
{
|
||||
int i;
|
||||
struct kunit_suite **copy, *filtered_suite;
|
||||
struct suite_set filtered;
|
||||
struct kunit_test_filter filter;
|
||||
int i, j, k;
|
||||
int filter_count = 0;
|
||||
struct kunit_suite **copy, **copy_start, *filtered_suite, *new_filtered_suite;
|
||||
struct kunit_suite_set filtered = {NULL, NULL};
|
||||
struct kunit_glob_filter parsed_glob;
|
||||
struct kunit_attr_filter *parsed_filters = NULL;
|
||||
|
||||
const size_t max = suite_set->end - suite_set->start;
|
||||
|
||||
copy = kmalloc_array(max, sizeof(*filtered.start), GFP_KERNEL);
|
||||
filtered.start = copy;
|
||||
if (!copy) { /* won't be able to run anything, return an empty set */
|
||||
filtered.end = copy;
|
||||
return filtered;
|
||||
}
|
||||
copy_start = copy;
|
||||
|
||||
kunit_parse_filter_glob(&filter, filter_glob);
|
||||
if (filter_glob)
|
||||
kunit_parse_glob_filter(&parsed_glob, filter_glob);
|
||||
|
||||
for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
|
||||
if (!glob_match(filter.suite_glob, suite_set->start[i]->name))
|
||||
continue;
|
||||
|
||||
filtered_suite = kunit_filter_tests(suite_set->start[i], filter.test_glob);
|
||||
if (IS_ERR(filtered_suite)) {
|
||||
*err = PTR_ERR(filtered_suite);
|
||||
/* Parse attribute filters */
|
||||
if (filters) {
|
||||
filter_count = kunit_get_filter_count(filters);
|
||||
parsed_filters = kcalloc(filter_count, sizeof(*parsed_filters), GFP_KERNEL);
|
||||
if (!parsed_filters) {
|
||||
kfree(copy);
|
||||
return filtered;
|
||||
}
|
||||
for (j = 0; j < filter_count; j++)
|
||||
parsed_filters[j] = kunit_next_attr_filter(&filters, err);
|
||||
if (*err)
|
||||
goto err;
|
||||
}
|
||||
|
||||
for (i = 0; &suite_set->start[i] != suite_set->end; i++) {
|
||||
filtered_suite = suite_set->start[i];
|
||||
if (filter_glob) {
|
||||
if (!glob_match(parsed_glob.suite_glob, filtered_suite->name))
|
||||
continue;
|
||||
filtered_suite = kunit_filter_glob_tests(filtered_suite,
|
||||
parsed_glob.test_glob);
|
||||
if (IS_ERR(filtered_suite)) {
|
||||
*err = PTR_ERR(filtered_suite);
|
||||
goto err;
|
||||
}
|
||||
}
|
||||
if (filter_count > 0 && parsed_filters != NULL) {
|
||||
for (k = 0; k < filter_count; k++) {
|
||||
new_filtered_suite = kunit_filter_attr_tests(filtered_suite,
|
||||
parsed_filters[k], filter_action, err);
|
||||
|
||||
/* Free previous copy of suite */
|
||||
if (k > 0 || filter_glob) {
|
||||
kfree(filtered_suite->test_cases);
|
||||
kfree(filtered_suite);
|
||||
}
|
||||
|
||||
filtered_suite = new_filtered_suite;
|
||||
|
||||
if (*err)
|
||||
goto err;
|
||||
if (IS_ERR(filtered_suite)) {
|
||||
*err = PTR_ERR(filtered_suite);
|
||||
goto err;
|
||||
}
|
||||
if (!filtered_suite)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!filtered_suite)
|
||||
continue;
|
||||
|
||||
*copy++ = filtered_suite;
|
||||
}
|
||||
filtered.start = copy_start;
|
||||
filtered.end = copy;
|
||||
|
||||
kfree(filter.suite_glob);
|
||||
kfree(filter.test_glob);
|
||||
err:
|
||||
if (*err)
|
||||
kfree(copy);
|
||||
|
||||
if (filter_glob) {
|
||||
kfree(parsed_glob.suite_glob);
|
||||
kfree(parsed_glob.test_glob);
|
||||
}
|
||||
|
||||
if (filter_count)
|
||||
kfree(parsed_filters);
|
||||
|
||||
return filtered;
|
||||
}
|
||||
|
||||
void kunit_exec_run_tests(struct kunit_suite_set *suite_set, bool builtin)
|
||||
{
|
||||
size_t num_suites = suite_set->end - suite_set->start;
|
||||
|
||||
if (builtin || num_suites) {
|
||||
pr_info("KTAP version 1\n");
|
||||
pr_info("1..%zu\n", num_suites);
|
||||
}
|
||||
|
||||
__kunit_test_suites_init(suite_set->start, num_suites);
|
||||
}
|
||||
|
||||
void kunit_exec_list_tests(struct kunit_suite_set *suite_set, bool include_attr)
|
||||
{
|
||||
struct kunit_suite * const *suites;
|
||||
struct kunit_case *test_case;
|
||||
|
||||
/* Hack: print a ktap header so kunit.py can find the start of KUnit output. */
|
||||
pr_info("KTAP version 1\n");
|
||||
|
||||
for (suites = suite_set->start; suites < suite_set->end; suites++) {
|
||||
/* Print suite name and suite attributes */
|
||||
pr_info("%s\n", (*suites)->name);
|
||||
if (include_attr)
|
||||
kunit_print_attr((void *)(*suites), false, 0);
|
||||
|
||||
/* Print test case name and attributes in suite */
|
||||
kunit_suite_for_each_test_case((*suites), test_case) {
|
||||
pr_info("%s.%s\n", (*suites)->name, test_case->name);
|
||||
if (include_attr)
|
||||
kunit_print_attr((void *)test_case, true, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if IS_BUILTIN(CONFIG_KUNIT)
|
||||
|
||||
static char *kunit_shutdown;
|
||||
core_param(kunit_shutdown, kunit_shutdown, charp, 0644);
|
||||
|
||||
static void kunit_handle_shutdown(void)
|
||||
{
|
||||
if (!kunit_shutdown)
|
||||
@@ -162,41 +282,20 @@ static void kunit_handle_shutdown(void)
|
||||
|
||||
}
|
||||
|
||||
static void kunit_exec_run_tests(struct suite_set *suite_set)
|
||||
{
|
||||
size_t num_suites = suite_set->end - suite_set->start;
|
||||
|
||||
pr_info("KTAP version 1\n");
|
||||
pr_info("1..%zu\n", num_suites);
|
||||
|
||||
__kunit_test_suites_init(suite_set->start, num_suites);
|
||||
}
|
||||
|
||||
static void kunit_exec_list_tests(struct suite_set *suite_set)
|
||||
{
|
||||
struct kunit_suite * const *suites;
|
||||
struct kunit_case *test_case;
|
||||
|
||||
/* Hack: print a ktap header so kunit.py can find the start of KUnit output. */
|
||||
pr_info("KTAP version 1\n");
|
||||
|
||||
for (suites = suite_set->start; suites < suite_set->end; suites++)
|
||||
kunit_suite_for_each_test_case((*suites), test_case) {
|
||||
pr_info("%s.%s\n", (*suites)->name, test_case->name);
|
||||
}
|
||||
}
|
||||
|
||||
int kunit_run_all_tests(void)
|
||||
{
|
||||
struct suite_set suite_set = {__kunit_suites_start, __kunit_suites_end};
|
||||
struct kunit_suite_set suite_set = {
|
||||
__kunit_suites_start, __kunit_suites_end,
|
||||
};
|
||||
int err = 0;
|
||||
if (!kunit_enabled()) {
|
||||
pr_info("kunit: disabled\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (filter_glob_param) {
|
||||
suite_set = kunit_filter_suites(&suite_set, filter_glob_param, &err);
|
||||
if (filter_glob_param || filter_param) {
|
||||
suite_set = kunit_filter_suites(&suite_set, filter_glob_param,
|
||||
filter_param, filter_action_param, &err);
|
||||
if (err) {
|
||||
pr_err("kunit executor: error filtering suites: %d\n", err);
|
||||
goto out;
|
||||
@@ -204,13 +303,15 @@ int kunit_run_all_tests(void)
|
||||
}
|
||||
|
||||
if (!action_param)
|
||||
kunit_exec_run_tests(&suite_set);
|
||||
kunit_exec_run_tests(&suite_set, true);
|
||||
else if (strcmp(action_param, "list") == 0)
|
||||
kunit_exec_list_tests(&suite_set);
|
||||
kunit_exec_list_tests(&suite_set, false);
|
||||
else if (strcmp(action_param, "list_attr") == 0)
|
||||
kunit_exec_list_tests(&suite_set, true);
|
||||
else
|
||||
pr_err("kunit executor: unknown action '%s'\n", action_param);
|
||||
|
||||
if (filter_glob_param) { /* a copy was made of each suite */
|
||||
if (filter_glob_param || filter_param) { /* a copy was made of each suite */
|
||||
kunit_free_suite_set(suite_set);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/attributes.h>
|
||||
|
||||
static void kfree_at_end(struct kunit *test, const void *to_free);
|
||||
static struct kunit_suite *alloc_fake_suite(struct kunit *test,
|
||||
@@ -24,15 +25,15 @@ static struct kunit_case dummy_test_cases[] = {
|
||||
|
||||
static void parse_filter_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_test_filter filter = {NULL, NULL};
|
||||
struct kunit_glob_filter filter = {NULL, NULL};
|
||||
|
||||
kunit_parse_filter_glob(&filter, "suite");
|
||||
kunit_parse_glob_filter(&filter, "suite");
|
||||
KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite");
|
||||
KUNIT_EXPECT_FALSE(test, filter.test_glob);
|
||||
kfree(filter.suite_glob);
|
||||
kfree(filter.test_glob);
|
||||
|
||||
kunit_parse_filter_glob(&filter, "suite.test");
|
||||
kunit_parse_glob_filter(&filter, "suite.test");
|
||||
KUNIT_EXPECT_STREQ(test, filter.suite_glob, "suite");
|
||||
KUNIT_EXPECT_STREQ(test, filter.test_glob, "test");
|
||||
kfree(filter.suite_glob);
|
||||
@@ -42,15 +43,17 @@ static void parse_filter_test(struct kunit *test)
|
||||
static void filter_suites_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_suite *subsuite[3] = {NULL, NULL};
|
||||
struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
|
||||
struct suite_set got;
|
||||
struct kunit_suite_set suite_set = {
|
||||
.start = subsuite, .end = &subsuite[2],
|
||||
};
|
||||
struct kunit_suite_set got;
|
||||
int err = 0;
|
||||
|
||||
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
|
||||
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
|
||||
|
||||
/* Want: suite1, suite2, NULL -> suite2, NULL */
|
||||
got = kunit_filter_suites(&suite_set, "suite2", &err);
|
||||
got = kunit_filter_suites(&suite_set, "suite2", NULL, NULL, &err);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
kfree_at_end(test, got.start);
|
||||
@@ -66,15 +69,17 @@ static void filter_suites_test(struct kunit *test)
|
||||
static void filter_suites_test_glob_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_suite *subsuite[3] = {NULL, NULL};
|
||||
struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
|
||||
struct suite_set got;
|
||||
struct kunit_suite_set suite_set = {
|
||||
.start = subsuite, .end = &subsuite[2],
|
||||
};
|
||||
struct kunit_suite_set got;
|
||||
int err = 0;
|
||||
|
||||
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
|
||||
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
|
||||
|
||||
/* Want: suite1, suite2, NULL -> suite2 (just test1), NULL */
|
||||
got = kunit_filter_suites(&suite_set, "suite2.test2", &err);
|
||||
got = kunit_filter_suites(&suite_set, "suite2.test2", NULL, NULL, &err);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
kfree_at_end(test, got.start);
|
||||
@@ -93,14 +98,16 @@ static void filter_suites_test_glob_test(struct kunit *test)
|
||||
static void filter_suites_to_empty_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_suite *subsuite[3] = {NULL, NULL};
|
||||
struct suite_set suite_set = {.start = subsuite, .end = &subsuite[2]};
|
||||
struct suite_set got;
|
||||
struct kunit_suite_set suite_set = {
|
||||
.start = subsuite, .end = &subsuite[2],
|
||||
};
|
||||
struct kunit_suite_set got;
|
||||
int err = 0;
|
||||
|
||||
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_test_cases);
|
||||
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_test_cases);
|
||||
|
||||
got = kunit_filter_suites(&suite_set, "not_found", &err);
|
||||
got = kunit_filter_suites(&suite_set, "not_found", NULL, NULL, &err);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
kfree_at_end(test, got.start); /* just in case */
|
||||
|
||||
@@ -108,11 +115,132 @@ static void filter_suites_to_empty_test(struct kunit *test)
|
||||
"should be empty to indicate no match");
|
||||
}
|
||||
|
||||
static void parse_filter_attr_test(struct kunit *test)
|
||||
{
|
||||
int j, filter_count;
|
||||
struct kunit_attr_filter *parsed_filters;
|
||||
char *filters = "speed>slow, module!=example";
|
||||
int err = 0;
|
||||
|
||||
filter_count = kunit_get_filter_count(filters);
|
||||
KUNIT_EXPECT_EQ(test, filter_count, 2);
|
||||
|
||||
parsed_filters = kunit_kcalloc(test, filter_count, sizeof(*parsed_filters),
|
||||
GFP_KERNEL);
|
||||
for (j = 0; j < filter_count; j++) {
|
||||
parsed_filters[j] = kunit_next_attr_filter(&filters, &err);
|
||||
KUNIT_ASSERT_EQ_MSG(test, err, 0, "failed to parse filter '%s'", filters[j]);
|
||||
}
|
||||
|
||||
KUNIT_EXPECT_STREQ(test, kunit_attr_filter_name(parsed_filters[0]), "speed");
|
||||
KUNIT_EXPECT_STREQ(test, parsed_filters[0].input, ">slow");
|
||||
|
||||
KUNIT_EXPECT_STREQ(test, kunit_attr_filter_name(parsed_filters[1]), "module");
|
||||
KUNIT_EXPECT_STREQ(test, parsed_filters[1].input, "!=example");
|
||||
}
|
||||
|
||||
static struct kunit_case dummy_attr_test_cases[] = {
|
||||
/* .run_case is not important, just needs to be non-NULL */
|
||||
{ .name = "slow", .run_case = dummy_test, .module_name = "dummy",
|
||||
.attr.speed = KUNIT_SPEED_SLOW },
|
||||
{ .name = "normal", .run_case = dummy_test, .module_name = "dummy" },
|
||||
{},
|
||||
};
|
||||
|
||||
static void filter_attr_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_suite *subsuite[3] = {NULL, NULL};
|
||||
struct kunit_suite_set suite_set = {
|
||||
.start = subsuite, .end = &subsuite[2],
|
||||
};
|
||||
struct kunit_suite_set got;
|
||||
int err = 0;
|
||||
|
||||
subsuite[0] = alloc_fake_suite(test, "normal_suite", dummy_attr_test_cases);
|
||||
subsuite[1] = alloc_fake_suite(test, "slow_suite", dummy_attr_test_cases);
|
||||
subsuite[1]->attr.speed = KUNIT_SPEED_SLOW; // Set suite attribute
|
||||
|
||||
/*
|
||||
* Want: normal_suite(slow, normal), slow_suite(slow, normal),
|
||||
* NULL -> normal_suite(normal), NULL
|
||||
*
|
||||
* The normal test in slow_suite is filtered out because the speed
|
||||
* attribute is unset and thus, the filtering is based on the parent attribute
|
||||
* of slow.
|
||||
*/
|
||||
got = kunit_filter_suites(&suite_set, NULL, "speed>slow", NULL, &err);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
kfree_at_end(test, got.start);
|
||||
|
||||
/* Validate we just have normal_suite */
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]);
|
||||
KUNIT_EXPECT_STREQ(test, got.start[0]->name, "normal_suite");
|
||||
KUNIT_ASSERT_EQ(test, got.end - got.start, 1);
|
||||
|
||||
/* Now validate we just have normal test case */
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases);
|
||||
KUNIT_EXPECT_STREQ(test, got.start[0]->test_cases[0].name, "normal");
|
||||
KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].name);
|
||||
}
|
||||
|
||||
static void filter_attr_empty_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_suite *subsuite[3] = {NULL, NULL};
|
||||
struct kunit_suite_set suite_set = {
|
||||
.start = subsuite, .end = &subsuite[2],
|
||||
};
|
||||
struct kunit_suite_set got;
|
||||
int err = 0;
|
||||
|
||||
subsuite[0] = alloc_fake_suite(test, "suite1", dummy_attr_test_cases);
|
||||
subsuite[1] = alloc_fake_suite(test, "suite2", dummy_attr_test_cases);
|
||||
|
||||
got = kunit_filter_suites(&suite_set, NULL, "module!=dummy", NULL, &err);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
kfree_at_end(test, got.start); /* just in case */
|
||||
|
||||
KUNIT_EXPECT_PTR_EQ_MSG(test, got.start, got.end,
|
||||
"should be empty to indicate no match");
|
||||
}
|
||||
|
||||
static void filter_attr_skip_test(struct kunit *test)
|
||||
{
|
||||
struct kunit_suite *subsuite[2] = {NULL};
|
||||
struct kunit_suite_set suite_set = {
|
||||
.start = subsuite, .end = &subsuite[1],
|
||||
};
|
||||
struct kunit_suite_set got;
|
||||
int err = 0;
|
||||
|
||||
subsuite[0] = alloc_fake_suite(test, "suite", dummy_attr_test_cases);
|
||||
|
||||
/* Want: suite(slow, normal), NULL -> suite(slow with SKIP, normal), NULL */
|
||||
got = kunit_filter_suites(&suite_set, NULL, "speed>slow", "skip", &err);
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start);
|
||||
KUNIT_ASSERT_EQ(test, err, 0);
|
||||
kfree_at_end(test, got.start);
|
||||
|
||||
/* Validate we have both the slow and normal test */
|
||||
KUNIT_ASSERT_NOT_ERR_OR_NULL(test, got.start[0]->test_cases);
|
||||
KUNIT_ASSERT_EQ(test, kunit_suite_num_test_cases(got.start[0]), 2);
|
||||
KUNIT_EXPECT_STREQ(test, got.start[0]->test_cases[0].name, "slow");
|
||||
KUNIT_EXPECT_STREQ(test, got.start[0]->test_cases[1].name, "normal");
|
||||
|
||||
/* Now ensure slow is skipped and normal is not */
|
||||
KUNIT_EXPECT_EQ(test, got.start[0]->test_cases[0].status, KUNIT_SKIPPED);
|
||||
KUNIT_EXPECT_FALSE(test, got.start[0]->test_cases[1].status);
|
||||
}
|
||||
|
||||
static struct kunit_case executor_test_cases[] = {
|
||||
KUNIT_CASE(parse_filter_test),
|
||||
KUNIT_CASE(filter_suites_test),
|
||||
KUNIT_CASE(filter_suites_test_glob_test),
|
||||
KUNIT_CASE(filter_suites_to_empty_test),
|
||||
KUNIT_CASE(parse_filter_attr_test),
|
||||
KUNIT_CASE(filter_attr_test),
|
||||
KUNIT_CASE(filter_attr_empty_test),
|
||||
KUNIT_CASE(filter_attr_skip_test),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -220,6 +220,14 @@ static void example_params_test(struct kunit *test)
|
||||
KUNIT_EXPECT_EQ(test, param->value % param->value, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This test should always pass. Can be used to practice filtering attributes.
|
||||
*/
|
||||
static void example_slow_test(struct kunit *test)
|
||||
{
|
||||
KUNIT_EXPECT_EQ(test, 1 + 1, 2);
|
||||
}
|
||||
|
||||
/*
|
||||
* Here we make a list of all the test cases we want to add to the test suite
|
||||
* below.
|
||||
@@ -237,6 +245,7 @@ static struct kunit_case example_test_cases[] = {
|
||||
KUNIT_CASE(example_all_expect_macros_test),
|
||||
KUNIT_CASE(example_static_stub_test),
|
||||
KUNIT_CASE_PARAM(example_params_test, example_gen_params),
|
||||
KUNIT_CASE_SLOW(example_slow_test),
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <kunit/resource.h>
|
||||
#include <kunit/test.h>
|
||||
#include <kunit/test-bug.h>
|
||||
#include <kunit/attributes.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
@@ -168,6 +169,13 @@ size_t kunit_suite_num_test_cases(struct kunit_suite *suite)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(kunit_suite_num_test_cases);
|
||||
|
||||
/* Currently supported test levels */
|
||||
enum {
|
||||
KUNIT_LEVEL_SUITE = 0,
|
||||
KUNIT_LEVEL_CASE,
|
||||
KUNIT_LEVEL_CASE_PARAM,
|
||||
};
|
||||
|
||||
static void kunit_print_suite_start(struct kunit_suite *suite)
|
||||
{
|
||||
/*
|
||||
@@ -181,17 +189,11 @@ static void kunit_print_suite_start(struct kunit_suite *suite)
|
||||
pr_info(KUNIT_SUBTEST_INDENT "KTAP version 1\n");
|
||||
pr_info(KUNIT_SUBTEST_INDENT "# Subtest: %s\n",
|
||||
suite->name);
|
||||
kunit_print_attr((void *)suite, false, KUNIT_LEVEL_CASE);
|
||||
pr_info(KUNIT_SUBTEST_INDENT "1..%zd\n",
|
||||
kunit_suite_num_test_cases(suite));
|
||||
}
|
||||
|
||||
/* Currently supported test levels */
|
||||
enum {
|
||||
KUNIT_LEVEL_SUITE = 0,
|
||||
KUNIT_LEVEL_CASE,
|
||||
KUNIT_LEVEL_CASE_PARAM,
|
||||
};
|
||||
|
||||
static void kunit_print_ok_not_ok(struct kunit *test,
|
||||
unsigned int test_level,
|
||||
enum kunit_status status,
|
||||
@@ -611,18 +613,22 @@ int kunit_run_tests(struct kunit_suite *suite)
|
||||
kunit_suite_for_each_test_case(suite, test_case) {
|
||||
struct kunit test = { .param_value = NULL, .param_index = 0 };
|
||||
struct kunit_result_stats param_stats = { 0 };
|
||||
test_case->status = KUNIT_SKIPPED;
|
||||
|
||||
kunit_init_test(&test, test_case->name, test_case->log);
|
||||
|
||||
if (!test_case->generate_params) {
|
||||
if (test_case->status == KUNIT_SKIPPED) {
|
||||
/* Test marked as skip */
|
||||
test.status = KUNIT_SKIPPED;
|
||||
kunit_update_stats(¶m_stats, test.status);
|
||||
} else if (!test_case->generate_params) {
|
||||
/* Non-parameterised test. */
|
||||
test_case->status = KUNIT_SKIPPED;
|
||||
kunit_run_case_catch_errors(suite, test_case, &test);
|
||||
kunit_update_stats(¶m_stats, test.status);
|
||||
} else {
|
||||
/* Get initial param. */
|
||||
param_desc[0] = '\0';
|
||||
test.param_value = test_case->generate_params(NULL, param_desc);
|
||||
test_case->status = KUNIT_SKIPPED;
|
||||
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
|
||||
"KTAP version 1\n");
|
||||
kunit_log(KERN_INFO, &test, KUNIT_SUBTEST_INDENT KUNIT_SUBTEST_INDENT
|
||||
@@ -651,6 +657,7 @@ int kunit_run_tests(struct kunit_suite *suite)
|
||||
}
|
||||
}
|
||||
|
||||
kunit_print_attr((void *)test_case, true, KUNIT_LEVEL_CASE);
|
||||
|
||||
kunit_print_test_stats(&test, param_stats);
|
||||
|
||||
@@ -729,12 +736,45 @@ 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);
|
||||
struct kunit_suite_set suite_set = {
|
||||
mod->kunit_suites, mod->kunit_suites + mod->num_kunit_suites,
|
||||
};
|
||||
const char *action = kunit_action();
|
||||
int err = 0;
|
||||
|
||||
suite_set = kunit_filter_suites(&suite_set,
|
||||
kunit_filter_glob() ?: "*.*",
|
||||
kunit_filter(), kunit_filter_action(),
|
||||
&err);
|
||||
if (err)
|
||||
pr_err("kunit module: error filtering suites: %d\n", err);
|
||||
|
||||
mod->kunit_suites = (struct kunit_suite **)suite_set.start;
|
||||
mod->num_kunit_suites = suite_set.end - suite_set.start;
|
||||
|
||||
if (!action)
|
||||
kunit_exec_run_tests(&suite_set, false);
|
||||
else if (!strcmp(action, "list"))
|
||||
kunit_exec_list_tests(&suite_set, false);
|
||||
else if (!strcmp(action, "list_attr"))
|
||||
kunit_exec_list_tests(&suite_set, true);
|
||||
else
|
||||
pr_err("kunit: unknown action '%s'\n", action);
|
||||
}
|
||||
|
||||
static void kunit_module_exit(struct module *mod)
|
||||
{
|
||||
__kunit_test_suites_exit(mod->kunit_suites, mod->num_kunit_suites);
|
||||
struct kunit_suite_set suite_set = {
|
||||
mod->kunit_suites, mod->kunit_suites + mod->num_kunit_suites,
|
||||
};
|
||||
const char *action = kunit_action();
|
||||
|
||||
if (!action)
|
||||
__kunit_test_suites_exit(mod->kunit_suites,
|
||||
mod->num_kunit_suites);
|
||||
|
||||
if (suite_set.start)
|
||||
kunit_free_suite_set(suite_set);
|
||||
}
|
||||
|
||||
static int kunit_module_notify(struct notifier_block *nb, unsigned long val,
|
||||
|
||||
@@ -551,10 +551,10 @@ static void strtomem_test(struct kunit *test)
|
||||
static struct kunit_case memcpy_test_cases[] = {
|
||||
KUNIT_CASE(memset_test),
|
||||
KUNIT_CASE(memcpy_test),
|
||||
KUNIT_CASE(memcpy_large_test),
|
||||
KUNIT_CASE(memmove_test),
|
||||
KUNIT_CASE(memmove_large_test),
|
||||
KUNIT_CASE(memmove_overlap_test),
|
||||
KUNIT_CASE_SLOW(memcpy_large_test),
|
||||
KUNIT_CASE_SLOW(memmove_test),
|
||||
KUNIT_CASE_SLOW(memmove_large_test),
|
||||
KUNIT_CASE_SLOW(memmove_overlap_test),
|
||||
KUNIT_CASE(strtomem_test),
|
||||
{}
|
||||
};
|
||||
|
||||
2
rust/.gitignore
vendored
2
rust/.gitignore
vendored
@@ -2,6 +2,8 @@
|
||||
|
||||
bindings_generated.rs
|
||||
bindings_helpers_generated.rs
|
||||
doctests_kernel_generated.rs
|
||||
doctests_kernel_generated_kunit.c
|
||||
uapi_generated.rs
|
||||
exports_*_generated.h
|
||||
doc/
|
||||
|
||||
@@ -27,6 +27,12 @@ endif
|
||||
|
||||
obj-$(CONFIG_RUST) += exports.o
|
||||
|
||||
always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.rs
|
||||
always-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.c
|
||||
|
||||
obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated.o
|
||||
obj-$(CONFIG_RUST_KERNEL_DOCTESTS) += doctests_kernel_generated_kunit.o
|
||||
|
||||
# Avoids running `$(RUSTC)` for the sysroot when it may not be available.
|
||||
ifdef CONFIG_RUST
|
||||
|
||||
@@ -39,9 +45,11 @@ ifeq ($(quiet),silent_)
|
||||
cargo_quiet=-q
|
||||
rust_test_quiet=-q
|
||||
rustdoc_test_quiet=--test-args -q
|
||||
rustdoc_test_kernel_quiet=>/dev/null
|
||||
else ifeq ($(quiet),quiet_)
|
||||
rust_test_quiet=-q
|
||||
rustdoc_test_quiet=--test-args -q
|
||||
rustdoc_test_kernel_quiet=>/dev/null
|
||||
else
|
||||
cargo_quiet=--verbose
|
||||
endif
|
||||
@@ -157,6 +165,27 @@ quiet_cmd_rustdoc_test = RUSTDOC T $<
|
||||
-L$(objtree)/$(obj)/test --output $(objtree)/$(obj)/doc \
|
||||
--crate-name $(subst rusttest-,,$@) $<
|
||||
|
||||
quiet_cmd_rustdoc_test_kernel = RUSTDOC TK $<
|
||||
cmd_rustdoc_test_kernel = \
|
||||
rm -rf $(objtree)/$(obj)/test/doctests/kernel; \
|
||||
mkdir -p $(objtree)/$(obj)/test/doctests/kernel; \
|
||||
OBJTREE=$(abspath $(objtree)) \
|
||||
$(RUSTDOC) --test $(rust_flags) \
|
||||
@$(objtree)/include/generated/rustc_cfg \
|
||||
-L$(objtree)/$(obj) --extern alloc --extern kernel \
|
||||
--extern build_error --extern macros \
|
||||
--extern bindings --extern uapi \
|
||||
--no-run --crate-name kernel -Zunstable-options \
|
||||
--test-builder $(objtree)/scripts/rustdoc_test_builder \
|
||||
$< $(rustdoc_test_kernel_quiet); \
|
||||
$(objtree)/scripts/rustdoc_test_gen
|
||||
|
||||
%/doctests_kernel_generated.rs %/doctests_kernel_generated_kunit.c: \
|
||||
$(src)/kernel/lib.rs $(obj)/kernel.o \
|
||||
$(objtree)/scripts/rustdoc_test_builder \
|
||||
$(objtree)/scripts/rustdoc_test_gen FORCE
|
||||
$(call if_changed,rustdoc_test_kernel)
|
||||
|
||||
# We cannot use `-Zpanic-abort-tests` because some tests are dynamic,
|
||||
# so for the moment we skip `-Cpanic=abort`.
|
||||
quiet_cmd_rustc_test = RUSTC T $<
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
* Sorted alphabetically.
|
||||
*/
|
||||
|
||||
#include <kunit/test.h>
|
||||
#include <linux/errname.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/refcount.h>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user