| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448 |
- .. SPDX-License-Identifier: GPL-2.0
- ============================
- Tips For Running KUnit Tests
- ============================
- Using ``kunit.py run`` ("kunit tool")
- =====================================
- Running from any directory
- --------------------------
- It can be handy to create a bash function like:
- .. code-block:: bash
- function run_kunit() {
- ( cd "$(git rev-parse --show-toplevel)" && ./tools/testing/kunit/kunit.py run "$@" )
- }
- .. note::
- Early versions of ``kunit.py`` (before 5.6) didn't work unless run from
- the kernel root, hence the use of a subshell and ``cd``.
- Running a subset of tests
- -------------------------
- ``kunit.py run`` accepts an optional glob argument to filter tests. The format
- is ``"<suite_glob>[.test_glob]"``.
- Say that we wanted to run the sysctl tests, we could do so via:
- .. code-block:: bash
- $ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
- $ ./tools/testing/kunit/kunit.py run 'sysctl*'
- We can filter down to just the "write" tests via:
- .. code-block:: bash
- $ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
- $ ./tools/testing/kunit/kunit.py run 'sysctl*.*write*'
- We're paying the cost of building more tests than we need this way, but it's
- easier than fiddling with ``.kunitconfig`` files or commenting out
- ``kunit_suite``'s.
- However, if we wanted to define a set of tests in a less ad hoc way, the next
- tip is useful.
- Defining a set of tests
- -----------------------
- ``kunit.py run`` (along with ``build``, and ``config``) supports a
- ``--kunitconfig`` flag. So if you have a set of tests that you want to run on a
- regular basis (especially if they have other dependencies), you can create a
- specific ``.kunitconfig`` for them.
- E.g. kunit has one for its tests:
- .. code-block:: bash
- $ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit/.kunitconfig
- Alternatively, if you're following the convention of naming your
- file ``.kunitconfig``, you can just pass in the dir, e.g.
- .. code-block:: bash
- $ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit
- .. note::
- This is a relatively new feature (5.12+) so we don't have any
- conventions yet about on what files should be checked in versus just
- kept around locally. It's up to you and your maintainer to decide if a
- config is useful enough to submit (and therefore have to maintain).
- .. note::
- Having ``.kunitconfig`` fragments in a parent and child directory is
- iffy. There's discussion about adding an "import" statement in these
- files to make it possible to have a top-level config run tests from all
- child directories. But that would mean ``.kunitconfig`` files are no
- longer just simple .config fragments.
- One alternative would be to have kunit tool recursively combine configs
- automagically, but tests could theoretically depend on incompatible
- options, so handling that would be tricky.
- Setting kernel commandline parameters
- -------------------------------------
- You can use ``--kernel_args`` to pass arbitrary kernel arguments, e.g.
- .. code-block:: bash
- $ ./tools/testing/kunit/kunit.py run --kernel_args=param=42 --kernel_args=param2=false
- Generating code coverage reports under UML
- ------------------------------------------
- .. note::
- TODO(brendanhiggins@google.com): There are various issues with UML and
- versions of gcc 7 and up. You're likely to run into missing ``.gcda``
- files or compile errors.
- This is different from the "normal" way of getting coverage information that is
- documented in Documentation/dev-tools/gcov.rst.
- Instead of enabling ``CONFIG_GCOV_KERNEL=y``, we can set these options:
- .. code-block:: none
- CONFIG_DEBUG_KERNEL=y
- CONFIG_DEBUG_INFO=y
- CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
- CONFIG_GCOV=y
- Putting it together into a copy-pastable sequence of commands:
- .. code-block:: bash
- # Append coverage options to the current config
- $ ./tools/testing/kunit/kunit.py run --kunitconfig=.kunit/ --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
- # Extract the coverage information from the build dir (.kunit/)
- $ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/
- # From here on, it's the same process as with CONFIG_GCOV_KERNEL=y
- # E.g. can generate an HTML report in a tmp dir like so:
- $ genhtml -o /tmp/coverage_html coverage.info
- If your installed version of gcc doesn't work, you can tweak the steps:
- .. code-block:: bash
- $ ./tools/testing/kunit/kunit.py run --make_options=CC=/usr/bin/gcc-6
- $ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/ --gcov-tool=/usr/bin/gcov-6
- Alternatively, LLVM-based toolchains can also be used:
- .. code-block:: bash
- # Build with LLVM and append coverage options to the current config
- $ ./tools/testing/kunit/kunit.py run --make_options LLVM=1 --kunitconfig=.kunit/ --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
- $ llvm-profdata merge -sparse default.profraw -o default.profdata
- $ llvm-cov export --format=lcov .kunit/vmlinux -instr-profile default.profdata > coverage.info
- # The coverage.info file is in lcov-compatible format and it can be used to e.g. generate HTML report
- $ genhtml -o /tmp/coverage_html coverage.info
- Running tests manually
- ======================
- Running tests without using ``kunit.py run`` is also an important use case.
- Currently it's your only option if you want to test on architectures other than
- UML.
- As running the tests under UML is fairly straightforward (configure and compile
- the kernel, run the ``./linux`` binary), this section will focus on testing
- non-UML architectures.
- Running built-in tests
- ----------------------
- When setting tests to ``=y``, the tests will run as part of boot and print
- results to dmesg in TAP format. So you just need to add your tests to your
- ``.config``, build and boot your kernel as normal.
- So if we compiled our kernel with:
- .. code-block:: none
- CONFIG_KUNIT=y
- CONFIG_KUNIT_EXAMPLE_TEST=y
- Then we'd see output like this in dmesg signaling the test ran and passed:
- .. code-block:: none
- TAP version 14
- 1..1
- # Subtest: example
- 1..1
- # example_simple_test: initializing
- ok 1 - example_simple_test
- ok 1 - example
- Running tests as modules
- ------------------------
- Depending on the tests, you can build them as loadable modules.
- For example, we'd change the config options from before to
- .. code-block:: none
- CONFIG_KUNIT=y
- CONFIG_KUNIT_EXAMPLE_TEST=m
- Then after booting into our kernel, we can run the test via
- .. code-block:: none
- $ modprobe kunit-example-test
- This will then cause it to print TAP output to stdout.
- .. note::
- The ``modprobe`` will *not* have a non-zero exit code if any test
- failed (as of 5.13). But ``kunit.py parse`` would, see below.
- .. note::
- You can set ``CONFIG_KUNIT=m`` as well, however, some features will not
- work and thus some tests might break. Ideally tests would specify they
- depend on ``KUNIT=y`` in their ``Kconfig``'s, but this is an edge case
- most test authors won't think about.
- As of 5.13, the only difference is that ``current->kunit_test`` will
- not exist.
- Pretty-printing results
- -----------------------
- You can use ``kunit.py parse`` to parse dmesg for test output and print out
- results in the same familiar format that ``kunit.py run`` does.
- .. code-block:: bash
- $ ./tools/testing/kunit/kunit.py parse /var/log/dmesg
- Retrieving per suite results
- ----------------------------
- Regardless of how you're running your tests, you can enable
- ``CONFIG_KUNIT_DEBUGFS`` to expose per-suite TAP-formatted results:
- .. code-block:: none
- CONFIG_KUNIT=y
- CONFIG_KUNIT_EXAMPLE_TEST=m
- CONFIG_KUNIT_DEBUGFS=y
- The results for each suite will be exposed under
- ``/sys/kernel/debug/kunit/<suite>/results``.
- So using our example config:
- .. code-block:: bash
- $ modprobe kunit-example-test > /dev/null
- $ cat /sys/kernel/debug/kunit/example/results
- ... <TAP output> ...
- # After removing the module, the corresponding files will go away
- $ modprobe -r kunit-example-test
- $ cat /sys/kernel/debug/kunit/example/results
- /sys/kernel/debug/kunit/example/results: No such file or directory
- Generating code coverage reports
- --------------------------------
- See Documentation/dev-tools/gcov.rst for details on how to do this.
- The only vaguely KUnit-specific advice here is that you probably want to build
- your tests as modules. That way you can isolate the coverage from tests from
- other code executed during boot, e.g.
- .. code-block:: bash
- # 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.
- ``is_init``
- This attribute indicates whether the test uses init data or functions.
- This attribute is automatically saved as a boolean and tests can also be
- filtered using this attribute.
|