Rust:带有 lib 和二进制目标的板条箱中的属性 #[cfg(test)]
Rust: Attribute #[cfg(test)] in crates with lib and binary targets
我目前正在开发一个 Rust project,它有一个库目标和一个二进制目标。
该库定义了两个恐慌处理程序,一个用于生产,一个用于测试,它们是使用#[cfg(test)] 属性有条件地编译的。这适用于单元测试 src/lib.rs,但一旦在 tests/(或 src/main.rs 中进行集成测试),“生产”恐慌处理程序就会链接进来,即在那些在某些情况下,lib.rs 会使用 test=false 进行编译。
有什么方法可以prevent/configure吗?
使用以下目录结构,这几乎无法重现:
Cargo.toml
src/lib.rs
src/main.rs
清单如下所示:
# Cargo.toml
...
[lib]
name = "kernel"
[[bin]]
path = "src/main.rs"
name = "titanium"
二进制代码:
// main.rs
fn main() {
println!("Hello, world!");
}
#[test]
fn it_works() {
assert_eq!(kernel::hello(), "Hello!".to_string());
}
... 对于图书馆:
// lib.rs
#[cfg(test)]
pub fn hello() -> String {
"Hello!".to_string()
}
#[cfg(not(test))]
pub fn hello() -> String {
"Goodbye!".to_string()
}
#[test]
fn it_works() {
assert_eq!(hello(), "Hello!".to_string())
}
回答
上述行为实际上对于大约 99% 的用例都是可取的,但是,我的不是其中之一。
对于我的用例(玩具测试套件 OS 运行ning 在虚拟机“无头”中,即仅输出到主机的标准输出)我最终定义了一个功能来切换恐慌处理程序。适应上面的例子现在看起来像这样:
# Cargo.toml
...
[features]
test_qemu_headless = []
然后二进制文件保持不变,而在我做的库中
// lib.rs
#[cfg(feature = "test_qemu_headless")]
pub fn hello() -> String {
"Hello!".to_string()
}
#[cfg(not(feature = "test_qemu_headless"))]
pub fn hello() -> String {
"Goodbye!".to_string()
}
#[test]
fn it_works() {
assert_eq!(hello(), "Hello!".to_string())
}
然后测试 运行 和
cargo test --features test_qemu_headless
lib 目标被编译两次:一次是在 运行 进行单元测试时;此处,#[cfg(test)]
处于活动状态,因为库 当前 被编译为 rustc --test
。第二次 运行ning 集成测试时;在这里,#[cfg(test)]
是 not 活动的,因为 lib 正在正常编译,然后链接到集成测试模块,这些模块本身被编译为测试。换句话说,当与集成测试一起使用时,lib 目标不知道它正在被编译并用于测试(在某种程度上这是集成测试的重点)。
您正在寻找的是 #[cfg(debug_assertions)]
来装饰您的恐慌处理程序。无论出于何种原因,只要代码在“调试”模式下编译,此属性就处于活动状态。使用此属性,您的测试处理程序将对单元测试和集成测试都有效。
但是,如果您在“调试”模式下编译 bin 目标并且 运行 它(例如,通过 cargo run
而没有 --release
),或者如果您的库被一些依赖其他以“调试”模式编译的板条箱,库中的测试处理程序也将处于活动状态,这可能不是您想要的。如果是这样,您需要为您的 crate 定义一个 feature
,依赖于您的 panic 处理程序的测试可以用作条件编译的标志(例如 foocrate_use_internal_dummy_panic_handler
)。然后,您需要在 运行 进行测试时激活该功能 - 它成为全局旋钮。清单中还有一个 required-feature
字段。
我目前正在开发一个 Rust project,它有一个库目标和一个二进制目标。
该库定义了两个恐慌处理程序,一个用于生产,一个用于测试,它们是使用#[cfg(test)] 属性有条件地编译的。这适用于单元测试 src/lib.rs,但一旦在 tests/(或 src/main.rs 中进行集成测试),“生产”恐慌处理程序就会链接进来,即在那些在某些情况下,lib.rs 会使用 test=false 进行编译。
有什么方法可以prevent/configure吗?
使用以下目录结构,这几乎无法重现:
Cargo.toml
src/lib.rs
src/main.rs
清单如下所示:
# Cargo.toml
...
[lib]
name = "kernel"
[[bin]]
path = "src/main.rs"
name = "titanium"
二进制代码:
// main.rs
fn main() {
println!("Hello, world!");
}
#[test]
fn it_works() {
assert_eq!(kernel::hello(), "Hello!".to_string());
}
... 对于图书馆:
// lib.rs
#[cfg(test)]
pub fn hello() -> String {
"Hello!".to_string()
}
#[cfg(not(test))]
pub fn hello() -> String {
"Goodbye!".to_string()
}
#[test]
fn it_works() {
assert_eq!(hello(), "Hello!".to_string())
}
回答 上述行为实际上对于大约 99% 的用例都是可取的,但是,我的不是其中之一。
对于我的用例(玩具测试套件 OS 运行ning 在虚拟机“无头”中,即仅输出到主机的标准输出)我最终定义了一个功能来切换恐慌处理程序。适应上面的例子现在看起来像这样:
# Cargo.toml
...
[features]
test_qemu_headless = []
然后二进制文件保持不变,而在我做的库中
// lib.rs
#[cfg(feature = "test_qemu_headless")]
pub fn hello() -> String {
"Hello!".to_string()
}
#[cfg(not(feature = "test_qemu_headless"))]
pub fn hello() -> String {
"Goodbye!".to_string()
}
#[test]
fn it_works() {
assert_eq!(hello(), "Hello!".to_string())
}
然后测试 运行 和
cargo test --features test_qemu_headless
lib 目标被编译两次:一次是在 运行 进行单元测试时;此处,#[cfg(test)]
处于活动状态,因为库 当前 被编译为 rustc --test
。第二次 运行ning 集成测试时;在这里,#[cfg(test)]
是 not 活动的,因为 lib 正在正常编译,然后链接到集成测试模块,这些模块本身被编译为测试。换句话说,当与集成测试一起使用时,lib 目标不知道它正在被编译并用于测试(在某种程度上这是集成测试的重点)。
您正在寻找的是 #[cfg(debug_assertions)]
来装饰您的恐慌处理程序。无论出于何种原因,只要代码在“调试”模式下编译,此属性就处于活动状态。使用此属性,您的测试处理程序将对单元测试和集成测试都有效。
但是,如果您在“调试”模式下编译 bin 目标并且 运行 它(例如,通过 cargo run
而没有 --release
),或者如果您的库被一些依赖其他以“调试”模式编译的板条箱,库中的测试处理程序也将处于活动状态,这可能不是您想要的。如果是这样,您需要为您的 crate 定义一个 feature
,依赖于您的 panic 处理程序的测试可以用作条件编译的标志(例如 foocrate_use_internal_dummy_panic_handler
)。然后,您需要在 运行 进行测试时激活该功能 - 它成为全局旋钮。清单中还有一个 required-feature
字段。