測試¶
本文件包含有關如何在核心中測試 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 文件,請參閱 KUnit - Linux 核心單元測試;有關核心內建與命令列測試的詳細資訊,請參閱 KUnit 架構。
要使用這些 KUnit doctest,必須啟用以下內容
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 測試是文件測試¶
這些文件測試通常是任何專案(例如函式、結構體、模組...)的用法示例。
它們非常方便,因為它們只是與文件一起編寫。例如
/// Sums two numbers.
///
/// ```
/// assert_eq!(mymod::f(10, 20), 30);
/// ```
pub fn f(a: i32, b: i32) -> i32 {
a + b
}
在使用者空間中,測試透過 rustdoc 收集和執行。按原樣使用該工具已經很有用,因為它允許驗證示例是否編譯(從而強制它們與它們記錄的程式碼保持同步),以及執行那些不依賴於核心 API 的示例。
但是,對於核心,這些測試會轉換為 KUnit 測試套件。這意味著 doctest 被編譯為 Rust 核心物件,允許它們針對已構建的核心執行。
這種 KUnit 整合的一個好處是 Rust doctest 可以重用現有的測試工具。例如,核心日誌將如下所示
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
也像往常一樣支援使用 ? 運算子的測試,例如
/// ```
/// # use kernel::{spawn_work_item, workqueue};
/// spawn_work_item!(workqueue::system(), || pr_info!("x\n"))?;
/// # Ok::<(), Error>(())
/// ```
測試也使用 CLIPPY=1 在 Clippy 下編譯,就像普通程式碼一樣,因此也可以從額外的 linting 中受益。
為了讓開發人員能夠輕鬆檢視 doctest 程式碼的哪一行導致了失敗,KTAP 診斷行會列印到日誌中。這包含原始測試的位置(檔案和行)(即,而不是生成的 Rust 檔案中的位置)
# rust_doctest_kernel_types_rs_2.location: rust/kernel/types.rs:150
Rust 測試似乎使用來自 Rust 標準庫 (core) 的常用 assert! 和 assert_eq! 宏進行斷言。我們提供了一個自定義版本,該版本將呼叫轉發到 KUnit。重要的是,這些宏不需要傳遞上下文,不像 KUnit 測試的那些宏(即 struct kunit *)。這使得它們更容易使用,並且文件的讀者不需要關心使用哪個測試框架。此外,它可能允許我們在將來更輕鬆地測試第三方程式碼。
當前的限制是 KUnit 不支援其他任務中的斷言。因此,如果斷言實際上失敗了,我們目前只是將錯誤列印到核心日誌中。此外,doctest 不會為非公共函式執行。
由於這些測試是示例,即它們是文件的一部分,因此它們通常應該像“真實程式碼”一樣編寫。因此,例如,不要使用 unwrap() 或 expect(),而是使用 ? 運算子。有關更多背景資訊,請參閱
#[test] 測試¶
此外,還有 #[test] 測試。與文件測試一樣,這些測試也與您期望從使用者空間獲得的測試非常相似,並且它們也對映到 KUnit。
這些測試由 kunit_tests 過程宏引入,該宏將測試套件的名稱作為引數。
例如,假設我們要測試文件測試部分中的函式 f。我們可以在與我們的函式相同的檔案中編寫
#[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。類似地,支援 ? 運算子,即測試函式可以返回 nothing(即 unit 型別 ())或 Result(即任何 Result<T, E>)。例如
#[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 目標在主機(即執行核心構建的主機)中構建和執行
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
有關常規 Kselftest 文件,請參閱 Linux 核心自測試。