summaryrefslogtreecommitdiffstats
path: root/Documentation/translations/zh_CN/rust/testing.rst
blob: 6747d00129915bdfbff974ef0129dc5583e29de1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
.. SPDX-License-Identifier: GPL-2.0
.. include:: ../disclaimer-zh_CN.rst

:Original: Documentation/rust/testing.rst

:翻译:

 郭杰 Ben Guo <benx.guo@gmail.com>

测试
====

本文介绍了如何在内核中测试 Rust 代码。

有三种测试类型:

- KUnit 测试
- ``#[test]`` 测试
- Kselftests

KUnit 测试
----------

这些测试来自 Rust 文档中的示例。它们会被转换为 KUnit 测试。

使用
****

这些测试可以通过 KUnit 运行。例如,在命令行中使用 ``kunit_tool`` ( ``kunit.py`` )::

	./tools/testing/kunit/kunit.py run --make_options LLVM=1 --arch x86_64 --kconfig_add CONFIG_RUST=y

或者,KUnit 也可以在内核启动时以内置方式运行。获取更多 KUnit 信息,请参阅
Documentation/dev-tools/kunit/index.rst。
关于内核内置与命令行测试的详细信息,请参阅 Documentation/dev-tools/kunit/architecture.rst。

要使用这些 KUnit 文档测试,需要在内核配置中启用以下选项::

	CONFIG_KUNIT
	   Kernel hacking -> Kernel Testing and Coverage -> KUnit - Enable support for unit tests
	CONFIG_RUST_KERNEL_DOCTESTS
	   Kernel hacking -> Rust hacking -> Doctests for the `kernel` crate

KUnit 测试即文档测试
********************

文档测试( *doctests* )一般用于展示函数、结构体或模块等的使用方法。

它们非常方便,因为它们就写在文档旁边。例如:

.. code-block:: rust

	/// 求和两个数字。
	///
	/// ```
	/// assert_eq!(mymod::f(10, 20), 30);
	/// ```
	pub fn f(a: i32, b: i32) -> i32 {
	    a + b
	}

在用户空间中,这些测试由 ``rustdoc`` 负责收集并运行。单独使用这个工具已经很有价值,
因为它可以验证示例能否成功编译(确保和代码保持同步),
同时还可以运行那些不依赖内核 API 的示例。

然而,在内核中,这些测试会转换成 KUnit 测试套件。
这意味着文档测试会被编译成 Rust 内核对象,从而可以在构建的内核环境中运行。

通过与 KUnit 集成,Rust 的文档测试可以复用内核现有的测试设施。
例如,内核日志会显示::

	KTAP version 1
	1..1
	    KTAP version 1
	    # Subtest: rust_doctests_kernel
	    1..59
	    # rust_doctest_kernel_build_assert_rs_0.location: rust/kernel/build_assert.rs:13
	    ok 1 rust_doctest_kernel_build_assert_rs_0
	    # rust_doctest_kernel_build_assert_rs_1.location: rust/kernel/build_assert.rs:56
	    ok 2 rust_doctest_kernel_build_assert_rs_1
	    # rust_doctest_kernel_init_rs_0.location: rust/kernel/init.rs:122
	    ok 3 rust_doctest_kernel_init_rs_0
	    ...
	    # rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
	    ok 59 rust_doctest_kernel_types_rs_2
	# rust_doctests_kernel: pass:59 fail:0 skip:0 total:59
	# Totals: pass:59 fail:0 skip:0 total:59
	ok 1 rust_doctests_kernel

文档测试中,也可以正常使用 `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_ 运算符,例如:

.. code-block:: rust

	/// ```
	/// # use kernel::{spawn_work_item, workqueue};
	/// spawn_work_item!(workqueue::system(), || pr_info!("x\n"))?;
	/// # Ok::<(), Error>(())
	/// ```

这些测试和普通代码一样,也可以在 ``CLIPPY=1`` 条件下通过 Clippy 进行编译,
因此可以从额外的 lint 检查中获益。

为了便于开发者定位文档测试出错的具体行号,日志会输出一条 KTAP 诊断信息。
其中标明了原始测试的文件和行号(不是 ``rustdoc`` 生成的临时 Rust 文件位置)::

	# rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150

Rust 测试中常用的断言宏是来自 Rust 标准库( ``core`` )中的 ``assert!`` 和 ``assert_eq!`` 宏。
内核提供了一个定制版本,这些宏的调用会被转发到 KUnit。
和 KUnit 测试不同的是,这些宏不需要传递上下文参数( ``struct kunit *`` )。
这使得它们更易于使用,同时文档的读者无需关心底层用的是什么测试框架。
此外,这种方式未来也许可以让我们更容易测试第三方代码。

当前有一个限制:KUnit 不支持在其他任务中执行断言。
因此,如果断言真的失败了,我们只是简单地把错误打印到内核日志里。
另外,文档测试不适用于非公开的函数。

作为文档中的测试示例,应当像 “实际代码” 一样编写。
例如:不要使用 ``unwrap()`` 或 ``expect()``,请使用 `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_ 运算符。
更多背景信息,请参阅:

	https://rust.docs.kernel.org/kernel/error/type.Result.html#error-codes-in-c-and-rust

``#[test]`` 测试
----------------

此外,还有 ``#[test]`` 测试。与文档测试类似,这些测试与用户空间中的测试方式也非常相近,并且同样会映射到 KUnit。

这些测试通过 ``kunit_tests`` 过程宏引入,该宏将测试套件的名称作为参数。

每个测试套件都应该由 ``rust/kernel/Kconfig.test`` 中的 Kconfig 选项保护。

例如,假设想要测试前面文档测试示例中的函数 ``f``,我们可以在定义该函数的同一文件中编写:

.. code-block:: rust

	#[cfg(CONFIG_RUST_MYMOD_KUNIT_TEST)]
	#[kunit_tests(rust_kernel_mymod)]
	mod tests {
	    use super::*;

	    #[test]
	    fn test_f() {
	        assert_eq!(f(10, 20), 30);
	    }
	}

如果我们执行这段代码,内核日志会显示::

	    KTAP version 1
	    # Subtest: rust_kernel_mymod
	    # speed: normal
	    1..1
	    # test_f.speed: normal
	    ok 1 test_f
	ok 1 rust_kernel_mymod

与文档测试类似, ``assert!`` 和 ``assert_eq!`` 宏被映射回 KUnit 并且不会发生 panic。
同样,支持 `? <https://doc.rust-lang.org/reference/expressions/operator-expr.html#the-question-mark-operator>`_ 运算符,
测试函数可以什么都不返回(单元类型 ``()``)或 ``Result`` (任何 ``Result<T, E>``)。例如:

.. code-block:: rust

	#[cfg(CONFIG_RUST_MYMOD_KUNIT_TEST)]
	#[kunit_tests(rust_kernel_mymod)]
	mod tests {
	    use super::*;

	    #[test]
	    fn test_g() -> Result {
	        let x = g()?;
	        assert_eq!(x, 30);
	        Ok(())
	    }
	}

如果我们运行测试并且调用 ``g`` 失败,那么内核日志会显示::

	    KTAP version 1
	    # Subtest: rust_kernel_mymod
	    # speed: normal
	    1..1
	    # test_g: ASSERTION FAILED at rust/kernel/lib.rs:335
	    Expected is_test_result_ok(test_g()) to be true, but is false
	    # test_g.speed: normal
	    not ok 1 test_g
	not ok 1 rust_kernel_mymod

如果 ``#[test]`` 测试可以对用户起到示例作用,那就应该改用文档测试。
即使是 API 的边界情况,例如错误或边界问题,放在示例中展示也同样有价值。

``rusttest`` 宿主机测试
-----------------------

这类测试运行在用户空间,可以通过 ``rusttest`` 目标在构建内核的宿主机中编译并运行::

	make LLVM=1 rusttest

当前操作需要内核 ``.config``。

目前,它们主要用于测试 ``macros`` crate 的示例。

Kselftests
----------

Kselftests 可以在 ``tools/testing/selftests/rust`` 文件夹中找到。

测试所需的内核配置选项列在 ``tools/testing/selftests/rust/config`` 文件中,
可以借助 ``merge_config.sh`` 脚本合并到现有配置中::

	./scripts/kconfig/merge_config.sh .config tools/testing/selftests/rust/config

Kselftests 会在内核源码树中构建,以便在运行相同版本内核的系统上执行测试。

一旦安装并启动了与源码树匹配的内核,测试即可通过以下命令编译并执行::

	make TARGETS="rust" kselftest

请参阅 Documentation/dev-tools/kselftest.rst 文档以获取更多信息。