From 91abafaaf1a3ee319c971afa032a973bf35ca451 Mon Sep 17 00:00:00 2001 From: Tbkhi Date: Tue, 12 Mar 2024 16:08:38 -0300 Subject: [PATCH 1/2] Update test-implementation.md --- src/test-implementation.md | 63 ++++++++++++++++++++------------------ 1 file changed, 34 insertions(+), 29 deletions(-) diff --git a/src/test-implementation.md b/src/test-implementation.md index 1b8247005..1c67b552e 100644 --- a/src/test-implementation.md +++ b/src/test-implementation.md @@ -2,8 +2,8 @@ -Today, Rust programmers rely on a built in attribute called `#[test]`. All -you have to do is mark a function as a test and include some asserts like so: +Many Rust programmers rely on a built-in attribute called `#[test]`. All +you have to do is mark a function and include some asserts like so: ```rust,ignore #[test] @@ -35,14 +35,14 @@ How does any sort of `main` function invoke these tests if they're not visible? What exactly is `rustc --test` doing? `#[test]` is implemented as a syntactic transformation inside the compiler's -[`rustc_ast` crate][rustc_ast]. Essentially, it's a fancy macro, that +[`rustc_ast`][rustc_ast]. Essentially, it's a fancy [`macro`] that rewrites the crate in 3 steps: ## Step 1: Re-Exporting As mentioned earlier, tests can exist inside private modules, so we need a way of exposing them to the main function, without breaking any existing -code. To that end, `rustc_ast` will create local modules called +code. To that end, [`rustc_ast`][rustc_ast] will create local modules called `__test_reexports` that recursively reexport tests. This expansion translates the above example into: @@ -68,22 +68,22 @@ test at `a::b::my_test` becomes pretty safe, what happens if there is an existing `__test_reexports` module? The answer: nothing. -To explain, we need to understand [how the AST represents -identifiers][Ident]. The name of every function, variable, module, etc. is -not stored as a string, but rather as an opaque [Symbol][Symbol] which is -essentially an ID number for each identifier. The compiler keeps a separate +To explain, we need to understand how Rust's [Abstract Syntax Tree][ast] +represents [identifiers][Ident]. The name of every function, variable, module, +etc. is not stored as a string, but rather as an opaque [Symbol][Symbol] which +is essentially an ID number for each identifier. The compiler keeps a separate hashtable that allows us to recover the human-readable name of a Symbol when necessary (such as when printing a syntax error). When the compiler generates -the `__test_reexports` module, it generates a new Symbol for the identifier, -so while the compiler-generated `__test_reexports` may share a name with your -hand-written one, it will not share a Symbol. This technique prevents name -collision during code generation and is the foundation of Rust's macro -hygiene. +the `__test_reexports` module, it generates a new [Symbol][Symbol] for the +identifier, so while the compiler-generated `__test_reexports` may share a name +with your hand-written one, it will not share a [Symbol][Symbol]. This +technique prevents name collision during code generation and is the foundation +of Rust's [`macro`] hygiene. ## Step 2: Harness Generation Now that our tests are accessible from the root of our crate, we need to do -something with them. `rustc_ast` generates a module like so: +something with them using [`rustc_ast`][ast] generates a module like so: ```rust,ignore #[main] @@ -93,14 +93,14 @@ pub fn main() { } ``` -where `path::to::test1` is a constant of type `test::TestDescAndFn`. +Here `path::to::test1` is a constant of type [`test::TestDescAndFn`][tdaf]. While this transformation is simple, it gives us a lot of insight into how tests are actually run. The tests are aggregated into an array and passed to a test runner called `test_main_static`. We'll come back to exactly what -`TestDescAndFn` is, but for now, the key takeaway is that there is a crate +[`TestDescAndFn`][tdaf] is, but for now, the key takeaway is that there is a crate called [`test`][test] that is part of Rust core, that implements all of the -runtime for testing. `test`'s interface is unstable, so the only stable way +runtime for testing. [`test`][test]'s interface is unstable, so the only stable way to interact with it is through the `#[test]` macro. ## Step 3: Test Object Generation @@ -119,12 +119,13 @@ fn foo() { ``` This means our tests are more than just simple functions, they have -configuration information as well. `test` encodes this configuration data -into a struct called [`TestDesc`][TestDesc]. For each test function in a -crate, `rustc_ast` will parse its attributes and generate a `TestDesc` -instance. It then combines the `TestDesc` and test function into the -predictably named `TestDescAndFn` struct, that `test_main_static` operates -on. For a given test, the generated `TestDescAndFn` instance looks like so: +configuration information as well. `test` encodes this configuration data into +a `struct` called [`TestDesc`]. For each test function in a crate, +[`rustc_ast`][rustc_ast] will parse its attributes and generate a [`TestDesc`] +instance. It then combines the [`TestDesc`] and test function into the +predictably named [`TestDescAndFn`][tdaf] `struct`, that [`test_main_static`] +operates on. +For a given test, the generated [`TestDescAndFn`][tdaf] instance looks like so: ```rust,ignore self::test::TestDescAndFn{ @@ -140,19 +141,23 @@ self::test::TestDescAndFn{ ``` Once we've constructed an array of these test objects, they're passed to the -test runner via the harness generated in step 2. +test runner via the harness generated in Step 2. ## Inspecting the generated code -On nightly rust, there's an unstable flag called `unpretty` that you can use -to print out the module source after macro expansion: +On `nightly` `rustc`, there's an unstable flag called `unpretty` that you can use +to print out the module source after `macro` expansion: ```bash $ rustc my_mod.rs -Z unpretty=hir ``` -[test]: https://doc.rust-lang.org/test/index.html -[TestDesc]: https://doc.rust-lang.org/test/struct.TestDesc.html -[Symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html +[`macro`]: ./macro-expansion.md +[`TestDesc`]: https://doc.rust-lang.org/test/struct.TestDesc.html +[ast]: ./ast-validation.md [Ident]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Ident.html [rustc_ast]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_ast +[Symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html +[test]: https://doc.rust-lang.org/test/index.html +[tdaf]: https://doc.rust-lang.org/test/struct.TestDescAndFn.html +[`test_main_static`]: https://doc.rust-lang.org/test/fn.test_main_static.html \ No newline at end of file From 7bc00a1ade4f52344571f0a2d7ef3974ca6e4fd0 Mon Sep 17 00:00:00 2001 From: Tbkhi Date: Tue, 12 Mar 2024 16:11:23 -0300 Subject: [PATCH 2/2] Update test-implementation.md --- src/test-implementation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test-implementation.md b/src/test-implementation.md index 1c67b552e..e39038896 100644 --- a/src/test-implementation.md +++ b/src/test-implementation.md @@ -146,7 +146,7 @@ test runner via the harness generated in Step 2. ## Inspecting the generated code On `nightly` `rustc`, there's an unstable flag called `unpretty` that you can use -to print out the module source after `macro` expansion: +to print out the module source after [`macro`] expansion: ```bash $ rustc my_mod.rs -Z unpretty=hir @@ -160,4 +160,4 @@ $ rustc my_mod.rs -Z unpretty=hir [Symbol]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_span/symbol/struct.Symbol.html [test]: https://doc.rust-lang.org/test/index.html [tdaf]: https://doc.rust-lang.org/test/struct.TestDescAndFn.html -[`test_main_static`]: https://doc.rust-lang.org/test/fn.test_main_static.html \ No newline at end of file +[`test_main_static`]: https://doc.rust-lang.org/test/fn.test_main_static.html