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 24, 2023
1 parent d1d16f2 commit 719a4cd
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 30 deletions.
30 changes: 25 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,35 @@ 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)
};"#,
);
let test_dir = cargo_test_support::paths::test_dir(std::file!(), "{name}");
cargo_test_support::paths::init_root(tmp_dir, test_dir)
}};"#
));

new_body.extend(group.stream());
ret.extend(Some(TokenTree::from(Group::new(
Expand Down
53 changes: 33 additions & 20 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 @@ -51,28 +50,20 @@ pub fn global_root() -> PathBuf {
}
}

// We need to give each test a unique id. The test name could serve this
// purpose, but the `test` crate doesn't have a way to obtain the current test
// name.[*] Instead, we used the `cargo-test-macro` crate to automatically
// insert an init function for each test that sets the test name in a thread
// local variable.
//
// [*] It does set the thread name, but only when running concurrently. If not
// running concurrently, all tests are run on the main thread.
// We need to give each test a unique id. The test name serve this
// purpose. We are able to get the test name by having the `cargo-test-macro`
// crate automatically insert an init function for each test that sets the
// test name in a thread local variable.
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 +76,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 Expand Up @@ -345,3 +336,25 @@ pub fn windows_reserved_names_are_allowed() -> bool {
true
}
}

/// This takes the test location (std::file!() should be passed) and the test name
/// and outputs the location the test should be places in, inside of `target/tmp/cit`
///
/// `path: tests/testsuite/workspaces.rs`
/// `name: `workspace_in_git
/// `output: "testsuite/workspaces/workspace_in_git`
pub fn test_dir(path: &str, name: &str) -> std::path::PathBuf {
let test_dir: std::path::PathBuf = std::path::PathBuf::from(path)
.components()
// Trim .rs from any files
.map(|c| c.as_os_str().to_str().unwrap().trim_end_matches(".rs"))
// We only want to take once we have reached `tests` or `src`. This helps when in a
// workspace: `workspace/more/src/...` would result in `src/...`
.skip_while(|c| c != &"tests" && c != &"src")
// We want to skip "tests" and "src" since they are taken in `skip_while`.
// Skip "mod" since `snapbox` tests have a folder per test not a file and the files
// are named "mod.rs"
.filter(|c| c != &"tests" && c != &"src" && c != &"mod")
.collect();
test_dir.join(name)
}
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
48 changes: 48 additions & 0 deletions tests/testsuite/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,51 @@ fn aaa_trigger_cross_compile_disabled_check() {
// This triggers the cross compile disabled check to run ASAP, see #5141
cargo_test_support::cross_compile::disabled();
}

// This is placed here as running tests in `cargo-test-support` would rebuild it
#[cargo_test]
fn check_test_dir() {
let tests = vec![
(
"tests/testsuite/workspaces.rs",
"workspace_in_git",
"testsuite/workspaces/workspace_in_git",
),
(
"tests/testsuite/cargo_remove/invalid_arg/mod.rs",
"case",
"testsuite/cargo_remove/invalid_arg/case",
),
(
"tests/build-std/main.rs",
"cross_custom",
"build-std/main/cross_custom",
),
(
"src/tools/cargo/tests/testsuite/build.rs",
"cargo_compile_simple",
"tools/cargo/testsuite/build/cargo_compile_simple",
),
(
"src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs",
"case",
"tools/cargo/testsuite/cargo_add/add_basic/case",
),
(
"src/tools/cargo/tests/build-std/main.rs",
"cross_custom",
"tools/cargo/build-std/main/cross_custom",
),
(
"workspace/more/src/tools/cargo/tests/testsuite/build.rs",
"cargo_compile_simple",
"tools/cargo/testsuite/build/cargo_compile_simple",
),
];
for (path, name, expected) in tests {
assert_eq!(
cargo_test_support::paths::test_dir(path, name),
std::path::PathBuf::from(expected)
);
}
}

0 comments on commit 719a4cd

Please sign in to comment.