Skip to content

Commit

Permalink
feat: Use test name for dir when running tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Muscraft committed Feb 21, 2023
1 parent d1d16f2 commit fbf7a1b
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 22 deletions.
56 changes: 51 additions & 5 deletions crates/cargo-test-macro/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,9 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
add_attr(&mut ret, "ignore", reason);
}

let mut test_name = None;
let mut num = 0;

// Find where the function body starts, and add the boilerplate at the start.
for token in item {
let group = match token {
Expand All @@ -144,18 +147,61 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
continue;
}
}
TokenTree::Ident(i) => {
// The first time through it will be `fn` the second time is the
// name of the test.
if test_name.is_none() && num == 1 {
test_name = Some(i.to_string())
} else {
num += 1;
}
ret.extend(Some(TokenTree::Ident(i)));
continue;
}
other => {
ret.extend(Some(other));
continue;
}
};

let mut new_body = to_token_stream(
r#"let _test_guard = {
let name = &test_name
.clone()
.map(|n| n.split("::").next().unwrap().to_string())
.unwrap();
let mut new_body = to_token_stream(&format!(
r#"let _test_guard = {{
let tmp_dir = option_env!("CARGO_TARGET_TMPDIR");
cargo_test_support::paths::init_root(tmp_dir)
};"#,
);
// looks at `file!()` to figure out where the test's directory should be.
// test fn `workspaces::workspace_in_git`
// -> `file!` expands to `tests/testsuite/workspaces.rs`
// -> `testsuite/workspaces`
// test fn `cargo_remove::cargo_remove::invalid_arg::case`
// -> `file!` expands to `tests/testsuite/cargo_remove/invalid_arg.rs`
// -> `testsuite/cargo_remove/invalid_arg`
// test fn `build-std::cross_custom`
// -> `file!` expands to `tests/build-std/main.rs`
// -> `build-std/main`
let test_dir: std::path::PathBuf = std::path::PathBuf::from(std::file!())
.components()
// skips `tests`
.skip(1)
// gets 0-3 of the nest componests. 3 is needed since we use
// the file name for `snapbox` tests as the test name.
.take(3)
// remove `.rs` when using file name as test name
.map(|c| c.as_os_str().to_str().unwrap().trim_end_matches(".rs"))
.collect();
// some `snapbox` tests (i.e. `cargo_remove` ) use `case` as the test name.
// In this case we should use the file name as the test name. If it doesn't
// `case` add `name` to the `Pathbuf`
let test_dir = if "{name}" == "case" {{
test_dir
}} else {{
test_dir.join("{name}").to_path_buf()
}};
cargo_test_support::paths::init_root(tmp_dir, test_dir)
}};"#
));

new_body.extend(group.stream());
ret.extend(Some(TokenTree::from(Group::new(
Expand Down
19 changes: 7 additions & 12 deletions crates/cargo-test-support/src/paths.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use std::fs;
use std::io::{self, ErrorKind};
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;

static CARGO_INTEGRATION_TEST_DIR: &str = "cit";
Expand Down Expand Up @@ -60,19 +59,15 @@ pub fn global_root() -> PathBuf {
// [*] It does set the thread name, but only when running concurrently. If not
// running concurrently, all tests are run on the main thread.
thread_local! {
static TEST_ID: RefCell<Option<usize>> = RefCell::new(None);
static TEST_NAME: RefCell<Option<PathBuf>> = RefCell::new(None);
}

pub struct TestIdGuard {
_private: (),
}

pub fn init_root(tmp_dir: Option<&'static str>) -> TestIdGuard {
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);

let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
TEST_ID.with(|n| *n.borrow_mut() = Some(id));

pub fn init_root(tmp_dir: Option<&'static str>, test_name: PathBuf) -> TestIdGuard {
TEST_NAME.with(|n| *n.borrow_mut() = Some(test_name));
let guard = TestIdGuard { _private: () };

set_global_root(tmp_dir);
Expand All @@ -85,20 +80,20 @@ pub fn init_root(tmp_dir: Option<&'static str>) -> TestIdGuard {

impl Drop for TestIdGuard {
fn drop(&mut self) {
TEST_ID.with(|n| *n.borrow_mut() = None);
TEST_NAME.with(|n| *n.borrow_mut() = None);
}
}

pub fn root() -> PathBuf {
let id = TEST_ID.with(|n| {
n.borrow().expect(
let test_name = TEST_NAME.with(|n| {
n.borrow().clone().expect(
"Tests must use the `#[cargo_test]` attribute in \
order to be able to use the crate root.",
)
});

let mut root = global_root();
root.push(&format!("t{}", id));
root.push(&test_name);
root
}

Expand Down
11 changes: 6 additions & 5 deletions src/doc/contrib/src/tests/writing.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,9 @@ The [`#[cargo_test]` attribute](#cargo_test-attribute) is used in place of `#[te
#### `#[cargo_test]` attribute

The `#[cargo_test]` attribute injects code which does some setup before starting the test.
It will create a filesystem "sandbox" under the "cargo integration test" directory for each test, such as `/path/to/cargo/target/tmp/cit/t123/`.
The sandbox will contain a `home` directory that will be used instead of your normal home directory.
It will create a filesystem "sandbox" under the "cargo integration test" directory for each test, such as
`/path/to/cargo/target/tmp/cit/testsuite/bad_config/bad1`. The sandbox will contain a `home` directory that
will be used instead of your normal home directory.

The `#[cargo_test]` attribute takes several options that will affect how the test is generated.
They are listed in parentheses separated with commas, such as:
Expand Down Expand Up @@ -200,7 +201,7 @@ Then populate
- This attribute injects code which does some setup before starting the
test, creating a filesystem "sandbox" under the "cargo integration test"
directory for each test such as
`/path/to/cargo/target/cit/t123/`
`/path/to/cargo/target/tmp/cit/testsuite/cargo_add/add_basic`
- The sandbox will contain a `home` directory that will be used instead of your normal home directory

`Project`:
Expand Down Expand Up @@ -267,9 +268,9 @@ environment. The general process is:

`cargo test --test testsuite -- features2::inactivate_targets`.
2. In another terminal, head into the sandbox directory to inspect the files and run `cargo` directly.
1. The sandbox directories start with `t0` for the first test.
1. The sandbox directories match the format of `tests/testsuite`, just replace `tests` with `target/tmp/cit`

`cd target/tmp/cit/t0`
`cd target/tmp/cit/testsuite/features2/inactivate_target`
2. Set up the environment so that the sandbox configuration takes effect:

`export CARGO_HOME=$(pwd)/home/.cargo`
Expand Down

0 comments on commit fbf7a1b

Please sign in to comment.