Now, our test can be accessed as
`my_priv_mod::__test_reexports::test_priv_func`. For deeper module
structures, `__test_reexports` will reexport modules that contain tests, so a
test at `a::b::my_test` becomes
`a::__test_reexports::b::__test_reexports::my_test`. While this process seems
pretty safe, what happens if there is an existing `__test_reexports` module?
The answer: nothing.
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][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 using [`rustc_ast`][ast] generates a module like so:
```rust,ignore
#[main]
pub fn main() {
extern crate test;
test::test_main_static(&[&path::to::test1, /*...*/]);
}
```
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`][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`][test]'s interface is unstable, so the only stable way
to interact with it is through the `#[test]` macro.
## Step 3: Test object generation
If you've written tests in Rust before, you may be familiar with some of the
optional attributes available on test functions. For example, a test can be
annotated with `#[should_panic]` if we expect the test to cause a panic. It
looks something like this:
```rust,ignore
#[test]
#[should_panic]
fn foo() {
panic!("intentional");
}
```
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`]. 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{
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())),
}
```
Once we've constructed an array of these test objects, they're passed to the
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:
```bash
$ rustc my_mod.rs -Z unpretty=hir
```