#[test]
. All you need to do is mark the function as a test and include some checks: #[test] fn my_test() { assert!(2+2 == 4); }
rustc --test
or cargo test
commands, it will create an executable file that can run this and any other test function. This test method allows you to organically keep the tests next to the code. You can even put tests inside private modules: mod my_priv_mod { fn my_priv_func() -> bool {} #[test] fn test_priv_func() { assert!(my_priv_func()); } }
main
function call these tests if they are not visible ( note the translator : I remind you that private — declared without using the pub
keyword — modules are protected by encapsulation from being accessed from outside)? What exactly does rustc --test
?#[test]
implemented as a syntax conversion within the libsyntax compiler libsyntax
. In fact, this is a fancy macro that rewrites our crete in 3 steps:main
function without disrupting existing code. To this end, libsyntax
creates local modules, called __test_reexports
, which recursively __test_reexports
- __test_reexports
tests . This disclosure translates the example above into: mod my_priv_mod { fn my_priv_func() -> bool {} fn test_priv_func() { assert!(my_priv_func()); } pub mod __test_reexports { pub use super::test_priv_func; } }
my_priv_mod::__test_reexports::test_priv_func
. For nested modules, __test_reexports
will __test_reexports
modules containing tests, so the test a::b::my_test
becomes a::__test_reexports::b::__test_reexports::my_test
. So far this process seems to be pretty safe, but what happens if there is an existing __test_reexports
module? Answer: nothing .__test_reexports
module, it generates a new Symbol for the identifier, therefore, although the compiler-generated __test_reexports
may be the same with your samopisny module, it will not use its Symbol. This technique prevents name collisions during code generation and is the basis for the hygiene of the Rust macrosystem.libsyntax
generates this module: pub mod __test { extern crate test; const TESTS: &'static [self::test::TestDescAndFn] = &[/*...*/]; #[main] pub fn main() { self::test::test_static_main(TESTS); } }
test_static_main
, called test_static_main
. We will come back to what TestDescAndFn
, but at the moment the key conclusion is that there is a cache, called test , which is part of the Rust core and implements the entire runtime for testing. The test
interface is not stable, so the #[test]
macro is the only stable way to interact with it.#[should_panic]
if we expect the test to cause a panic. It looks like this: #[test] #[should_panic] fn foo() { panic!("intentional"); }
test
encodes this configuration data into a structure called TestDesc . For each test function in the cracks, libsyntax
will analyze its attributes and generate an instance of TestDesc
. It then combines TestDesc
and the test function into a logical structure TestDescAndFn
, with which test_static_main
works. For this test, the generated instance of TestDescAndFn
looks like this: self::test::TestDescAndFn { desc: self::test::TestDesc { name: self::test::StaticTestName("foo"), ignore: false, should_panic: self::test::ShouldPanic::Yes, allow_fail: false, }, testfn: self::test::StaticTestFn(|| self::test::assert_test_result(::crate::__test_reexports::foo())), }
unpretty
, which you can use to print the module source code after macros are opened: $ rustc my_mod.rs -Z unpretty=hir
#[test] fn my_test() { assert!(2+2 == 4); } fn main() {}
#[prelude_import] use std::prelude::v1::*; #[macro_use] extern crate std as std; #[test] pub fn my_test() { if !(2 + 2 == 4) { { ::rt::begin_panic("assertion failed: 2 + 2 == 4", &("test_test.rs", 3u32, 3u32)) } }; } #[allow(dead_code)] fn main() { } pub mod __test_reexports { pub use super::my_test; } pub mod __test { extern crate test; #[main] pub fn main() -> () { test::test_main_static(TESTS) } const TESTS: &'static [self::test::TestDescAndFn] = &[self::test::TestDescAndFn { desc: self::test::TestDesc { name: self::test::StaticTestName("my_test"), ignore: false, should_panic: self::test::ShouldPanic::No, allow_fail: false, }, testfn: self::test::StaticTestFn(::__test_reexports::my_test), }]; }
Source: https://habr.com/ru/post/418095/
All Articles