This sounds like an opportunity to introduce something which could be used to build #[test]; specifically, the “collecting items” behaviour.
There are various cases where you want some kind of index or registry of things defined elsewhere. Sub commands, tests, error types… possibly even on-startup functions.
Just off the top of my head thinking:
macro_rules! test {
(() $(pub)* fn $test_fn_name:ident $($_tail:tt)*) => {
// I'm assuming eager expansion here ($*):
add_to_global_list!(tests,
($*module_path_as_path!(), $test_fn_name, false,
test::ShouldPanic::No));
};
}
macro_rules! inject_test_harness {
() => {
mod __test {
extern crate test;
fn main() {
test::test_main_static(&::os::args()[], tests)
}
inject_test_harness! { @tests [$*read_global_list!(tests)] }
}
};
(@tests [$(($test_mod:path, $test_fn_name:ident, $test_ignore:expr, $test_panic:expr))*]) => {
static tests: &'static [test::TestDescAndFn] = &[
$(
test::TestDescAndFn {
desc: test::TestDesc {
name: test::TestName::StaticTestString(
concat!(stringify!($test_mod), "::",
stringify!($test_fn_name))
),
ignore: $test_ignore,
should_panic: $test_panic,
},
testfn: test::TestFn::StaticTestFn(
as_expr!($test_mod :: $test_fn_name)
),
},
)*
];
};
}
Ok, ignore for a moment that we don’t have eager expansion, or the ability to use macros-as-attributes: those can be worked around. We’d need module_path_as_path! or something similar to be added as a built-in (currently, module_path! returns a string literal).
add_to_global_list! and read_global_list! would be used to append to and dump a global, named list of tokens. For sanity, it should be an error to add to a list that’s already been dumped (so that things don’t get lost); so long as the read is triggered at the end of the root module, it should be fine. It’s up to the user to sort out ordering issues with the actual contents. This allows us to construct the list of tests from the attributes.
Also, it’d be nice to have a way to inject a macro invocation at the end of the crate module. --test could just be a shorthand for --extern test=.. --macro-use-extern test --inject-macro inject_test_harness. This would also allow Cargo to specify one or more custom test harnesses.
Aside from the “there’s no stable way to capture output” issue, that should be enough to divorce testing from the compiler, and buy us a hugely convenient way to collect information from across a crate at the same time.