← Documents Documentation/dev-tools/kunit/running_tips.rst GitHub 원문 ↗

Linux 6.18.37 · Dev Tools

Tips For Running KUnit Tests

KUnit test subset과 config 선택, UML coverage, 수동 실행, debugfs 결과, attribute reporting과 filtering 절차를 설명합니다.

Source pathDocumentation/dev-tools/kunit/running_tips.rst
Source versionLinux v6.18.37
TranslationDUJINLABS 전문 번역 + 해설

요약·해설과 원문, 전문 번역을 서로 분리했습니다. API 이름, symbol, source path는 원문 표기를 사용합니다.

1. 요약·해설

원문의 핵심 논리와 kernel programming 관점의 보충 설명입니다. 아래의 전문 번역과는 별도로 작성했습니다.

요약과 해설

running_tips.rst:1-448

KUnit은 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에 사용됩니다.

2. 영어 원문 전체

번역 기준이 된 Linux v6.18.37 원문입니다. 줄 번호는 이 버전의 파일 좌표입니다.

원문 전체 펼치기
1 .. SPDX-License-Identifier: GPL-2.0
2
3 ============================
4 Tips For Running KUnit Tests
5 ============================
6
7 Using ``kunit.py run`` ("kunit tool")
8 =====================================
9
10 Running from any directory
11 --------------------------
12
13 It can be handy to create a bash function like:
14
15 .. code-block:: bash
16
17 function run_kunit() {
18 ( cd "$(git rev-parse --show-toplevel)" && ./tools/testing/kunit/kunit.py run "$@" )
19 }
20
21 .. note::
22 Early versions of ``kunit.py`` (before 5.6) didn't work unless run from
23 the kernel root, hence the use of a subshell and ``cd``.
24
25 Running a subset of tests
26 -------------------------
27
28 ``kunit.py run`` accepts an optional glob argument to filter tests. The format
29 is ``"<suite_glob>[.test_glob]"``.
30
31 Say that we wanted to run the sysctl tests, we could do so via:
32
33 .. code-block:: bash
34
35 $ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
36 $ ./tools/testing/kunit/kunit.py run 'sysctl*'
37
38 We can filter down to just the "write" tests via:
39
40 .. code-block:: bash
41
42 $ echo -e 'CONFIG_KUNIT=y\nCONFIG_KUNIT_ALL_TESTS=y' > .kunit/.kunitconfig
43 $ ./tools/testing/kunit/kunit.py run 'sysctl*.*write*'
44
45 We're paying the cost of building more tests than we need this way, but it's
46 easier than fiddling with ``.kunitconfig`` files or commenting out
47 ``kunit_suite``'s.
48
49 However, if we wanted to define a set of tests in a less ad hoc way, the next
50 tip is useful.
51
52 Defining a set of tests
53 -----------------------
54
55 ``kunit.py run`` (along with ``build``, and ``config``) supports a
56 ``--kunitconfig`` flag. So if you have a set of tests that you want to run on a
57 regular basis (especially if they have other dependencies), you can create a
58 specific ``.kunitconfig`` for them.
59
60 E.g. kunit has one for its tests:
61
62 .. code-block:: bash
63
64 $ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit/.kunitconfig
65
66 Alternatively, if you're following the convention of naming your
67 file ``.kunitconfig``, you can just pass in the dir, e.g.
68
69 .. code-block:: bash
70
71 $ ./tools/testing/kunit/kunit.py run --kunitconfig=lib/kunit
72
73 .. note::
74 This is a relatively new feature (5.12+) so we don't have any
75 conventions yet about on what files should be checked in versus just
76 kept around locally. It's up to you and your maintainer to decide if a
77 config is useful enough to submit (and therefore have to maintain).
78
79 .. note::
80 Having ``.kunitconfig`` fragments in a parent and child directory is
81 iffy. There's discussion about adding an "import" statement in these
82 files to make it possible to have a top-level config run tests from all
83 child directories. But that would mean ``.kunitconfig`` files are no
84 longer just simple .config fragments.
85
86 One alternative would be to have kunit tool recursively combine configs
87 automagically, but tests could theoretically depend on incompatible
88 options, so handling that would be tricky.
89
90 Setting kernel commandline parameters
91 -------------------------------------
92
93 You can use ``--kernel_args`` to pass arbitrary kernel arguments, e.g.
94
95 .. code-block:: bash
96
97 $ ./tools/testing/kunit/kunit.py run --kernel_args=param=42 --kernel_args=param2=false
98
99
100 Generating code coverage reports under UML
101 ------------------------------------------
102
103 .. note::
104 TODO(brendanhiggins@google.com): There are various issues with UML and
105 versions of gcc 7 and up. You're likely to run into missing ``.gcda``
106 files or compile errors.
107
108 This is different from the "normal" way of getting coverage information that is
109 documented in Documentation/dev-tools/gcov.rst.
110
111 Instead of enabling ``CONFIG_GCOV_KERNEL=y``, we can set these options:
112
113 .. code-block:: none
114
115 CONFIG_DEBUG_KERNEL=y
116 CONFIG_DEBUG_INFO=y
117 CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT=y
118 CONFIG_GCOV=y
119
120
121 Putting it together into a copy-pastable sequence of commands:
122
123 .. code-block:: bash
124
125 # Append coverage options to the current config
126 $ ./tools/testing/kunit/kunit.py run --kunitconfig=.kunit/ --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
127 # Extract the coverage information from the build dir (.kunit/)
128 $ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/
129
130 # From here on, it's the same process as with CONFIG_GCOV_KERNEL=y
131 # E.g. can generate an HTML report in a tmp dir like so:
132 $ genhtml -o /tmp/coverage_html coverage.info
133
134
135 If your installed version of gcc doesn't work, you can tweak the steps:
136
137 .. code-block:: bash
138
139 $ ./tools/testing/kunit/kunit.py run --make_options=CC=/usr/bin/gcc-6
140 $ lcov -t "my_kunit_tests" -o coverage.info -c -d .kunit/ --gcov-tool=/usr/bin/gcov-6
141
142 Alternatively, LLVM-based toolchains can also be used:
143
144 .. code-block:: bash
145
146 # Build with LLVM and append coverage options to the current config
147 $ ./tools/testing/kunit/kunit.py run --make_options LLVM=1 --kunitconfig=.kunit/ --kunitconfig=tools/testing/kunit/configs/coverage_uml.config
148 $ llvm-profdata merge -sparse default.profraw -o default.profdata
149 $ llvm-cov export --format=lcov .kunit/vmlinux -instr-profile default.profdata > coverage.info
150 # The coverage.info file is in lcov-compatible format and it can be used to e.g. generate HTML report
151 $ genhtml -o /tmp/coverage_html coverage.info
152
153
154 Running tests manually
155 ======================
156
157 Running tests without using ``kunit.py run`` is also an important use case.
158 Currently it's your only option if you want to test on architectures other than
159 UML.
160
161 As running the tests under UML is fairly straightforward (configure and compile
162 the kernel, run the ``./linux`` binary), this section will focus on testing
163 non-UML architectures.
164
165
166 Running built-in tests
167 ----------------------
168
169 When setting tests to ``=y``, the tests will run as part of boot and print
170 results to dmesg in TAP format. So you just need to add your tests to your
171 ``.config``, build and boot your kernel as normal.
172
173 So if we compiled our kernel with:
174
175 .. code-block:: none
176
177 CONFIG_KUNIT=y
178 CONFIG_KUNIT_EXAMPLE_TEST=y
179
180 Then we'd see output like this in dmesg signaling the test ran and passed:
181
182 .. code-block:: none
183
184 TAP version 14
185 1..1
186 # Subtest: example
187 1..1
188 # example_simple_test: initializing
189 ok 1 - example_simple_test
190 ok 1 - example
191
192 Running tests as modules
193 ------------------------
194
195 Depending on the tests, you can build them as loadable modules.
196
197 For example, we'd change the config options from before to
198
199 .. code-block:: none
200
201 CONFIG_KUNIT=y
202 CONFIG_KUNIT_EXAMPLE_TEST=m
203
204 Then after booting into our kernel, we can run the test via
205
206 .. code-block:: none
207
208 $ modprobe kunit-example-test
209
210 This will then cause it to print TAP output to stdout.
211
212 .. note::
213 The ``modprobe`` will *not* have a non-zero exit code if any test
214 failed (as of 5.13). But ``kunit.py parse`` would, see below.
215
216 .. note::
217 You can set ``CONFIG_KUNIT=m`` as well, however, some features will not
218 work and thus some tests might break. Ideally tests would specify they
219 depend on ``KUNIT=y`` in their ``Kconfig``'s, but this is an edge case
220 most test authors won't think about.
221 As of 5.13, the only difference is that ``current->kunit_test`` will
222 not exist.
223
224 Pretty-printing results
225 -----------------------
226
227 You can use ``kunit.py parse`` to parse dmesg for test output and print out
228 results in the same familiar format that ``kunit.py run`` does.
229
230 .. code-block:: bash
231
232 $ ./tools/testing/kunit/kunit.py parse /var/log/dmesg
233
234
235 Retrieving per suite results
236 ----------------------------
237
238 Regardless of how you're running your tests, you can enable
239 ``CONFIG_KUNIT_DEBUGFS`` to expose per-suite TAP-formatted results:
240
241 .. code-block:: none
242
243 CONFIG_KUNIT=y
244 CONFIG_KUNIT_EXAMPLE_TEST=m
245 CONFIG_KUNIT_DEBUGFS=y
246
247 The results for each suite will be exposed under
248 ``/sys/kernel/debug/kunit/<suite>/results``.
249 So using our example config:
250
251 .. code-block:: bash
252
253 $ modprobe kunit-example-test > /dev/null
254 $ cat /sys/kernel/debug/kunit/example/results
255 ... <TAP output> ...
256
257 # After removing the module, the corresponding files will go away
258 $ modprobe -r kunit-example-test
259 $ cat /sys/kernel/debug/kunit/example/results
260 /sys/kernel/debug/kunit/example/results: No such file or directory
261
262 Generating code coverage reports
263 --------------------------------
264
265 See Documentation/dev-tools/gcov.rst for details on how to do this.
266
267 The only vaguely KUnit-specific advice here is that you probably want to build
268 your tests as modules. That way you can isolate the coverage from tests from
269 other code executed during boot, e.g.
270
271 .. code-block:: bash
272
273 # Reset coverage counters before running the test.
274 $ echo 0 > /sys/kernel/debug/gcov/reset
275 $ modprobe kunit-example-test
276
277
278 Test Attributes and Filtering
279 =============================
280
281 Test suites and cases can be marked with test attributes, such as speed of
282 test. These attributes will later be printed in test output and can be used to
283 filter test execution.
284
285 Marking Test Attributes
286 -----------------------
287
288 Tests are marked with an attribute by including a ``kunit_attributes`` object
289 in the test definition.
290
291 Test cases can be marked using the ``KUNIT_CASE_ATTR(test_name, attributes)``
292 macro to define the test case instead of ``KUNIT_CASE(test_name)``.
293
294 .. code-block:: c
295
296 static const struct kunit_attributes example_attr = {
297 .speed = KUNIT_VERY_SLOW,
298 };
299
300 static struct kunit_case example_test_cases[] = {
301 KUNIT_CASE_ATTR(example_test, example_attr),
302 };
303
304 .. note::
305 To mark a test case as slow, you can also use ``KUNIT_CASE_SLOW(test_name)``.
306 This is a helpful macro as the slow attribute is the most commonly used.
307
308 Test suites can be marked with an attribute by setting the "attr" field in the
309 suite definition.
310
311 .. code-block:: c
312
313 static const struct kunit_attributes example_attr = {
314 .speed = KUNIT_VERY_SLOW,
315 };
316
317 static struct kunit_suite example_test_suite = {
318 ...,
319 .attr = example_attr,
320 };
321
322 .. note::
323 Not all attributes need to be set in a ``kunit_attributes`` object. Unset
324 attributes will remain uninitialized and act as though the attribute is set
325 to 0 or NULL. Thus, if an attribute is set to 0, it is treated as unset.
326 These unset attributes will not be reported and may act as a default value
327 for filtering purposes.
328
329 Reporting Attributes
330 --------------------
331
332 When a user runs tests, attributes will be present in the raw kernel output (in
333 KTAP format). Note that attributes will be hidden by default in kunit.py output
334 for all passing tests but the raw kernel output can be accessed using the
335 ``--raw_output`` flag. This is an example of how test attributes for test cases
336 will be formatted in kernel output:
337
338 .. code-block:: none
339
340 # example_test.speed: slow
341 ok 1 example_test
342
343 This is an example of how test attributes for test suites will be formatted in
344 kernel output:
345
346 .. code-block:: none
347
348 KTAP version 2
349 # Subtest: example_suite
350 # module: kunit_example_test
351 1..3
352 ...
353 ok 1 example_suite
354
355 Additionally, users can output a full attribute report of tests with their
356 attributes, using the command line flag ``--list_tests_attr``:
357
358 .. code-block:: bash
359
360 kunit.py run "example" --list_tests_attr
361
362 .. note::
363 This report can be accessed when running KUnit manually by passing in the
364 module_param ``kunit.action=list_attr``.
365
366 Filtering
367 ---------
368
369 Users can filter tests using the ``--filter`` command line flag when running
370 tests. As an example:
371
372 .. code-block:: bash
373
374 kunit.py run --filter speed=slow
375
376
377 You can also use the following operations on filters: "<", ">", "<=", ">=",
378 "!=", and "=". Example:
379
380 .. code-block:: bash
381
382 kunit.py run --filter "speed>slow"
383
384 This example will run all tests with speeds faster than slow. Note that the
385 characters < and > are often interpreted by the shell, so they may need to be
386 quoted or escaped, as above.
387
388 Additionally, you can use multiple filters at once. Simply separate filters
389 using commas. Example:
390
391 .. code-block:: bash
392
393 kunit.py run --filter "speed>slow, module=kunit_example_test"
394
395 .. note::
396 You can use this filtering feature when running KUnit manually by passing
397 the filter as a module param: ``kunit.filter="speed>slow, speed<=normal"``.
398
399 Filtered tests will not run or show up in the test output. You can use the
400 ``--filter_action=skip`` flag to skip filtered tests instead. These tests will be
401 shown in the test output in the test but will not run. To use this feature when
402 running KUnit manually, use the module param ``kunit.filter_action=skip``.
403
404 Rules of Filtering Procedure
405 ----------------------------
406
407 Since both suites and test cases can have attributes, there may be conflicts
408 between attributes during filtering. The process of filtering follows these
409 rules:
410
411 - Filtering always operates at a per-test level.
412
413 - If a test has an attribute set, then the test's value is filtered on.
414
415 - Otherwise, the value falls back to the suite's value.
416
417 - If neither are set, the attribute has a global "default" value, which is used.
418
419 List of Current Attributes
420 --------------------------
421
422 ``speed``
423
424 This attribute indicates the speed of a test's execution (how slow or fast the
425 test is).
426
427 This attribute is saved as an enum with the following categories: "normal",
428 "slow", or "very_slow". The assumed default speed for tests is "normal". This
429 indicates that the test takes a relatively trivial amount of time (less than
430 1 second), regardless of the machine it is running on. Any test slower than
431 this could be marked as "slow" or "very_slow".
432
433 The macro ``KUNIT_CASE_SLOW(test_name)`` can be easily used to set the speed
434 of a test case to "slow".
435
436 ``module``
437
438 This attribute indicates the name of the module associated with the test.
439
440 This attribute is automatically saved as a string and is printed for each suite.
441 Tests can also be filtered using this attribute.
442
443 ``is_init``
444
445 This attribute indicates whether the test uses init data or functions.
446
447 This attribute is automatically saved as a boolean and tests can also be
448 filtered using this attribute.
449

3. 한국어 전문 번역

영어 원문의 문단 순서와 의미를 유지한 전체 번역입니다. 코드, 함수명, symbol과 URL은 원문 표기를 유지합니다.

kunit.py run을 편리하게 사용하는 방법

1-99

SPDX 라이선스 식별자: 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
KUnit 실행 대상 선택 흐름
전체 또는 임시 일부 실행`<suite_glob>[.test_glob]` 전달
Suite만 선택`sysctl*`처럼 suite glob 사용
Case까지 선택`sysctl*.*write*`처럼 test glob 추가
재사용할 test set전용 `.kunitconfig`를 `--kunitconfig`로 전달
Runtime parameter`--kernel_args`를 필요한 만큼 반복

간단한 glob 실행과 재사용 가능한 configuration 집합을 목적에 따라 구분했습니다.

UML에서 code coverage report 생성

100-153

UML에서 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를 생성할 수 있습니다.

UML coverage report pipeline
KUnit UML build`coverage_uml.config` option 추가
GCC 경로`lcov -c -d .kunit/`로 수집
LLVM 경로`llvm-profdata` merge 후 `llvm-cov export --format=lcov`
공통 결과`coverage.info` 생성
HTML 변환`genhtml -o /tmp/coverage_html coverage.info`

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-277

Suite별 결과 가져오기

테스트 실행 방법과 관계없이 `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-327

Test 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-403

Attribute 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-448

Filtering 절차 규칙

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에도 사용할 수 있습니다.

Attribute value 선택 우선순위
순서값의 출처적용 조건
1Test case attributeCase에 값이 명시됨
2Suite attributeCase 값은 없고 suite 값이 있음
3Global defaultCase와 suite 모두 값이 없음

개별 test부터 global default까지 filtering에 사용할 값을 찾는 순서를 구조화했습니다.