요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.
1. 요약·해설
원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.
2. 영어 원문 전체
번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.
원문 전체 펼치기
.. 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([email protected]): 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.
3. 한국어 전문 번역
영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.
kunit.py run을 편리하게 사용하는 방법
1-99SPDX 라이선스 식별자: GPL-2.0
KUnit 테스트 실행 팁
`kunit.py run` 사용, 즉 KUnit tool 사용
어느 디렉터리에서든 실행
다음과 같은 bash function을 만들어 두면 편리합니다.
function run_kunit() {
( cd "$(git rev-parse --show-toplevel)" && ./tools/testing/kunit/kunit.py run "$@" )
}
참고: 초기 `kunit.py` version, 즉 5.6 이전 version은 kernel root에서 실행하지 않으면 동작하지 않았습니다. 이 때문에 subshell과 `cd`를 사용합니다.
일부 테스트만 실행
`kunit.py run`은 테스트를 filtering하는 선택적 glob argument를 받습니다. 형식은 `"<suite_glob>[.test_glob]"`입니다.
예를 들어 sysctl 테스트를 실행하려면 다음과 같이 할 수 있습니다.
$ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
$ ./tools/testing/kunit/kunit.py run 'sysctl*'
그중 이름에 `write`가 들어가는 테스트만 선택할 수도 있습니다.
$ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
$ ./tools/testing/kunit/kunit.py run 'sysctl*.*write*'
이 방법은 필요한 것보다 더 많은 테스트를 build하는 비용이 들지만, `.kunitconfig` file을 일일이 조정하거나 `kunit_suite`를 주석 처리하는 것보다 간단합니다.
임시 glob 대신 더 체계적으로 테스트 집합을 정의하려면 다음 방법을 사용합니다.
테스트 집합 정의
`kunit.py run`은 `build`, `config`와 함께 `--kunitconfig` flag를 지원합니다. 정기적으로 실행할 테스트 집합이 있고 특히 다른 dependency가 필요하다면 전용 `.kunitconfig`를 만들 수 있습니다.
KUnit 자체 테스트용 config를 사용하는 예는 다음과 같습니다.
$ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit/.kunitconfig
File 이름을 `.kunitconfig`로 짓는 convention을 따른다면 file 대신 이를 포함한 directory만 전달할 수도 있습니다.
$ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit
참고: 이 기능은 비교적 새 기능인 5.12 이상에서 제공되므로 어떤 file을 repository에 넣고 어떤 file을 local에만 둘지 아직 정해진 convention은 없습니다. Config가 제출하고 유지할 만큼 유용한지는 작성자와 maintainer가 판단해야 합니다.
참고: Parent directory와 child directory 양쪽에 `.kunitconfig` fragment를 두는 것은 모호합니다. Top-level config에서 모든 child directory 테스트를 실행할 수 있도록 file에 `import` statement를 추가하자는 논의가 있지만, 그렇게 하면 `.kunitconfig`가 더 이상 단순한 `.config` fragment가 아니게 됩니다.
KUnit tool이 config를 재귀적으로 자동 결합하는 방법도 생각할 수 있지만, 서로 양립할 수 없는 option에 의존하는 테스트가 있을 수 있어 충돌 처리가 까다롭습니다.
Kernel command line parameter 설정
`--kernel_args`를 반복해서 사용하면 임의의 kernel argument를 전달할 수 있습니다.
$ ./tools/testing/kunit/kunit.py run --kernel_args=param=42 --kernel_args=param2=false
간단한 glob 실행과 재사용 가능한 configuration 집합을 목적에 따라 구분했습니다.
UML에서 code coverage report 생성
100-153UML에서 code coverage report 생성
참고: UML과 GCC 7 이상 version 조합에는 여러 문제가 있습니다. `.gcda` file이 없거나 compile error가 발생할 수 있습니다.
여기서 설명하는 방식은 `Documentation/dev-tools/gcov.rst`에 문서화된 일반적인 coverage 수집 방식과 다릅니다.
`CONFIG_GCOV_KERNEL=y`를 활성화하는 대신 다음 option을 설정합니다.
CONFIG_DEBUG_KERNEL=y
CONFIG_DEBUG_INFO=y
CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
CONFIG_GCOV=y
복사해 바로 실행할 수 있는 전체 command sequence는 다음과 같습니다.
# 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
먼저 현재 config에 `coverage_uml.config`를 추가해 테스트를 실행합니다. 이어서 `lcov`가 `.kunit/` build directory의 coverage 정보를 `coverage.info`로 수집하고, `genhtml`이 이를 `/tmp/coverage_html`의 HTML report로 변환합니다.
설치된 GCC version이 동작하지 않으면 GCC 6 compiler와 이에 대응하는 `gcov-6` tool을 명시할 수 있습니다.
$ ./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
LLVM 기반 toolchain도 사용할 수 있습니다.
# 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
LLVM 경로에서는 `llvm-profdata`가 raw profile을 merge하고 `llvm-cov export --format=lcov`가 lcov 호환 `coverage.info`를 만듭니다. 이후에는 동일하게 `genhtml`로 HTML report를 생성할 수 있습니다.
GCC와 LLVM 경로가 lcov 형식의 coverage.info에서 합쳐지는 과정을 나타냅니다.
Built-in 및 module 테스트 수동 실행
154-233테스트 수동 실행
`kunit.py run`을 사용하지 않고 테스트를 실행하는 것도 중요한 use case입니다. 현재 UML 이외 architecture에서 테스트하려면 이 방법만 사용할 수 있습니다.
UML에서는 kernel을 configure하고 compile한 뒤 `./linux` binary를 실행하면 되므로 비교적 단순합니다. 이 절은 non-UML architecture 테스트에 초점을 맞춥니다.
Built-in 테스트 실행
테스트를 `=y`로 설정하면 boot 과정에서 실행되고 결과가 TAP 형식으로 dmesg에 출력됩니다. 따라서 테스트 option을 `.config`에 추가한 뒤 평소처럼 kernel을 build하고 boot하면 됩니다.
예를 들어 다음 option으로 kernel을 compile합니다.
CONFIG_KUNIT=y
CONFIG_KUNIT_EXAMPLE_TEST=y
그러면 테스트가 실행되고 통과했음을 나타내는 다음과 같은 output이 dmesg에 나타납니다.
TAP version 14
1..1
# Subtest: example
1..1
# example_simple_test: initializing
ok 1 - example_simple_test
ok 1 - example
테스트를 module로 실행
테스트 구현에 따라 loadable module로 build할 수 있습니다. 앞의 config에서 example test를 module로 바꾸는 예는 다음과 같습니다.
CONFIG_KUNIT=y
CONFIG_KUNIT_EXAMPLE_TEST=m
해당 kernel로 boot한 뒤 다음 command로 테스트를 실행합니다.
$ modprobe kunit-example-test
그러면 TAP output이 stdout에 출력됩니다.
참고: 5.13 기준으로 테스트가 실패해도 `modprobe`는 0이 아닌 exit code를 반환하지 않습니다. 아래에서 설명하는 `kunit.py parse`는 실패 시 0이 아닌 code를 반환합니다.
`CONFIG_KUNIT=m`으로 설정할 수도 있지만 일부 기능이 동작하지 않아 테스트가 깨질 수 있습니다. 이상적으로는 이런 테스트가 자신의 `Kconfig`에서 `KUNIT=y` dependency를 선언해야 하지만, 대부분의 test author가 고려하지 못하기 쉬운 edge case입니다. 5.13 기준 유일한 차이는 `current->kunit_test`가 존재하지 않는다는 점입니다.
결과를 읽기 좋게 출력
`kunit.py parse`로 dmesg의 test output을 parsing하면 `kunit.py run`과 같은 익숙한 형식으로 결과를 출력할 수 있습니다.
$ ./tools/testing/kunit/kunit.py parse /var/log/dmesg
Suite별 결과와 수동 coverage 수집
234-277Suite별 결과 가져오기
테스트 실행 방법과 관계없이 `CONFIG_KUNIT_DEBUGFS`를 활성화하면 suite별 TAP 형식 결과를 노출할 수 있습니다.
CONFIG_KUNIT=y
CONFIG_KUNIT_EXAMPLE_TEST=m
CONFIG_KUNIT_DEBUGFS=y
각 suite의 결과는 `/sys/kernel/debug/kunit/<suite>/results` 아래에 나타납니다. 앞의 example config를 사용한 흐름은 다음과 같습니다.
$ 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
Module을 load하면 `example/results`에서 TAP output을 읽을 수 있습니다. Module을 제거하면 대응하는 debugfs file도 사라지고 이후 접근은 `No such file or directory`로 실패합니다.
Code coverage report 생성
자세한 방법은 `Documentation/dev-tools/gcov.rst`를 참조하십시오.
KUnit에 특화된 핵심 조언은 테스트를 module로 build하는 것입니다. 그러면 boot 중 다른 code가 실행해 만든 coverage와 테스트 자체 coverage를 분리할 수 있습니다.
# Reset coverage counters before running the test.
$ echo 0 > /sys/kernel/debug/gcov/reset
$ modprobe kunit-example-test
테스트 module을 load하기 직전에 `/sys/kernel/debug/gcov/reset`에 0을 써서 coverage counter를 초기화합니다.
Test attribute 지정
278-327Test attribute와 filtering
Test suite와 case에는 실행 속도 같은 test attribute를 표시할 수 있습니다. 이 attribute는 나중에 test output에 출력되며 test execution filtering에도 사용할 수 있습니다.
Test attribute 표시
Test definition에 `kunit_attributes` object를 포함해 attribute를 지정합니다.
Test case는 `KUNIT_CASE(test_name)` 대신 `KUNIT_CASE_ATTR(test_name, attributes)` macro로 정의해 attribute를 연결할 수 있습니다.
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),
};
참고: Test case를 slow로 표시할 때는 `KUNIT_CASE_SLOW(test_name)`도 사용할 수 있습니다. Slow attribute가 가장 흔히 쓰이므로 제공되는 편의 macro입니다.
Test suite는 suite definition의 `attr` field에 attribute object를 설정합니다.
static const struct kunit_attributes example_attr = {
.speed = KUNIT_VERY_SLOW,
};
static struct kunit_suite example_test_suite = {
...,
.attr = example_attr,
};
참고: `kunit_attributes` object에서 모든 attribute를 설정할 필요는 없습니다. 설정하지 않은 attribute는 초기화되지 않은 상태로 남아 값이 0 또는 NULL인 것처럼 동작합니다. 따라서 attribute를 0으로 설정하면 설정하지 않은 것으로 취급됩니다.
이런 미설정 attribute는 report되지 않으며 filtering에서는 default value 역할을 할 수 있습니다.
Attribute reporting과 filter 사용
328-403Attribute reporting
사용자가 테스트를 실행하면 attribute가 raw kernel output에 KTAP 형식으로 포함됩니다. 통과한 모든 테스트의 attribute는 기본 kunit.py output에서는 숨겨지지만, `--raw_output` flag로 raw kernel output을 볼 수 있습니다.
Test case attribute는 kernel output에서 다음 형식으로 표시됩니다.
# example_test.speed: slow
ok 1 example_test
Test suite attribute는 kernel output에서 다음 형식으로 표시됩니다.
KTAP version 2
# Subtest: example_suite
# module: kunit_example_test
1..3
...
ok 1 example_suite
또한 `--list_tests_attr` command-line flag로 모든 테스트와 그 attribute를 포함한 전체 attribute report를 출력할 수 있습니다.
kunit.py run "example" --list_tests_attr
참고: KUnit을 수동 실행할 때는 module parameter `kunit.action=list_attr`를 전달해 같은 report를 볼 수 있습니다.
Filtering
테스트 실행 시 `--filter` command-line flag를 사용해 attribute 기준으로 테스트를 filtering할 수 있습니다.
kunit.py run --filter speed=slow
Filter에는 `<`, `>`, `<=`, `>=`, `!=`, `=` operation을 사용할 수 있습니다.
kunit.py run --filter "speed>slow"
이 예는 speed가 slow보다 빠른 모든 테스트를 실행합니다. `<`와 `>` character는 shell이 해석하는 경우가 많으므로 위 예처럼 quote하거나 escape해야 할 수 있습니다.
Comma로 filter를 구분하면 여러 filter를 동시에 적용할 수 있습니다.
kunit.py run --filter "speed>slow, module=kunit_example_test"
참고: KUnit을 수동 실행할 때는 `kunit.filter="speed>slow, speed<=normal"`처럼 filter를 module parameter로 전달할 수 있습니다.
Filter된 테스트는 실행되지 않고 test output에도 나타나지 않습니다. 대신 `--filter_action=skip` flag를 사용하면 filter된 테스트를 실행하지 않되 test output에는 skipped 상태로 표시할 수 있습니다. 수동 실행에서는 module parameter `kunit.filter_action=skip`을 사용합니다.
Filtering 우선순위와 현재 attribute
404-448Filtering 절차 규칙
Suite와 test case 모두 attribute를 가질 수 있으므로 filtering 과정에서 값이 충돌할 수 있습니다. Filtering은 다음 규칙을 따릅니다.
Filtering은 항상 개별 test 단위로 수행합니다.
Test 자체에 attribute가 설정되어 있으면 그 test value로 filtering합니다.
Test에 값이 없으면 suite value를 대신 사용합니다.
Test와 suite 어느 쪽에도 값이 없으면 해당 attribute의 global default value를 사용합니다.
현재 attribute 목록
`speed`
이 attribute는 테스트 실행 속도, 즉 테스트가 얼마나 느리거나 빠른지를 나타냅니다.
값은 enum으로 저장되며 category는 `normal`, `slow`, `very_slow`입니다. Test의 기본 speed는 `normal`입니다. 실행 machine과 관계없이 비교적 사소한 시간인 1초 미만이 걸리는 테스트를 뜻합니다. 이보다 느린 테스트는 `slow` 또는 `very_slow`로 표시할 수 있습니다.
`KUNIT_CASE_SLOW(test_name)` macro를 사용하면 test case의 speed를 쉽게 `slow`로 설정할 수 있습니다.
`module`
이 attribute는 테스트와 연결된 module 이름을 나타냅니다. String으로 자동 저장되고 각 suite에 출력되며, 이 attribute를 사용해 테스트를 filtering할 수도 있습니다.
`is_init`
이 attribute는 테스트가 init data 또는 function을 사용하는지 나타냅니다. Boolean으로 자동 저장되며 filtering에도 사용할 수 있습니다.
개별 test부터 global default까지 filtering에 사용할 값을 찾는 순서를 구조화했습니다.
요약과 해설
running_tips.rst:1-448KUnit은 glob과 `.kunitconfig`로 실행 대상을 정교하게 선택하고 `--kernel_args`로 runtime parameter를 전달할 수 있습니다. UML에서는 GCC 또는 LLVM coverage toolchain을 연결해 lcov 형식 report를 생성할 수 있습니다.
Non-UML 환경에서는 built-in test를 boot 중 실행하거나 module을 load해 수동 실행할 수 있고, `kunit.py parse`와 debugfs로 결과를 확인합니다. Test와 suite attribute는 case, suite, global default 순서로 해석되며 command-line 또는 module parameter filter에 사용됩니다.