Skip to content

Commit 8f90edd

Browse files
committed
feat: Use test name for dir when running tests
1 parent 367fd9f commit 8f90edd

File tree

4 files changed

+157
-16
lines changed

4 files changed

+157
-16
lines changed

crates/cargo-test-macro/src/lib.rs

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,9 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
200200
add_attr(&mut ret, "ignore", reason);
201201
}
202202

203+
let mut test_name = None;
204+
let mut num = 0;
205+
203206
// Find where the function body starts, and add the boilerplate at the start.
204207
for token in item {
205208
let group = match token {
@@ -211,18 +214,44 @@ pub fn cargo_test(attr: TokenStream, item: TokenStream) -> TokenStream {
211214
continue;
212215
}
213216
}
217+
TokenTree::Ident(i) => {
218+
// The first time through it will be `fn` the second time is the
219+
// name of the test.
220+
if test_name.is_none() && num == 1 {
221+
test_name = Some(i.to_string())
222+
} else {
223+
num += 1;
224+
}
225+
ret.extend(Some(TokenTree::Ident(i)));
226+
continue;
227+
}
214228
other => {
215229
ret.extend(Some(other));
216230
continue;
217231
}
218232
};
219233

220-
let mut new_body = to_token_stream(
221-
r#"let _test_guard = {
222-
let tmp_dir = option_env!("CARGO_TARGET_TMPDIR");
223-
cargo_test_support::paths::init_root(tmp_dir)
224-
};"#,
225-
);
234+
let name = &test_name
235+
.clone()
236+
.map(|n| n.split("::").next().unwrap().to_string())
237+
.unwrap();
238+
239+
let mut new_body = if cfg!(windows) {
240+
to_token_stream(
241+
r#"let _test_guard = {
242+
let tmp_dir = option_env!("CARGO_TARGET_TMPDIR");
243+
cargo_test_support::paths::init_root(tmp_dir)
244+
};"#,
245+
)
246+
} else {
247+
to_token_stream(&format!(
248+
r#"let _test_guard = {{
249+
let tmp_dir = option_env!("CARGO_TARGET_TMPDIR");
250+
let test_dir = cargo_test_support::paths::test_dir(std::file!(), "{name}");
251+
cargo_test_support::paths::init_root(tmp_dir, test_dir)
252+
}};"#
253+
))
254+
};
226255

227256
new_body.extend(group.stream());
228257
ret.extend(Some(TokenTree::from(Group::new(

crates/cargo-test-support/src/paths.rs

Lines changed: 66 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use std::path::{Path, PathBuf};
1212
use std::process::Command;
1313
use std::sync::Mutex;
1414
use std::sync::OnceLock;
15+
#[cfg(windows)]
1516
use std::sync::atomic::{AtomicUsize, Ordering};
1617

1718
use crate::compare::assert_e2e;
@@ -62,16 +63,15 @@ pub fn global_root() -> PathBuf {
6263
}
6364
}
6465

65-
// We need to give each test a unique id. The test name could serve this
66-
// purpose, but the `test` crate doesn't have a way to obtain the current test
67-
// name.[*] Instead, we used the `cargo-test-macro` crate to automatically
68-
// insert an init function for each test that sets the test name in a thread
69-
// local variable.
70-
//
71-
// [*] It does set the thread name, but only when running concurrently. If not
72-
// running concurrently, all tests are run on the main thread.
66+
// We need to give each test a unique id. The test name serve this
67+
// purpose. We are able to get the test name by having the `cargo-test-macro`
68+
// crate automatically insert an init function for each test that sets the
69+
// test name in a thread local variable.
7370
thread_local! {
71+
#[cfg(windows)]
7472
static TEST_ID: RefCell<Option<usize>> = const { RefCell::new(None) };
73+
#[cfg(not(windows))]
74+
static TEST_NAME: RefCell<Option<PathBuf>> = const { RefCell::new(None) };
7575
}
7676

7777
/// See [`init_root`]
@@ -80,12 +80,26 @@ pub struct TestIdGuard {
8080
}
8181

8282
/// For test harnesses like [`crate::cargo_test`]
83+
#[cfg(windows)]
8384
pub fn init_root(tmp_dir: Option<&'static str>) -> TestIdGuard {
8485
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
8586

8687
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
8788
TEST_ID.with(|n| *n.borrow_mut() = Some(id));
89+
let guard = TestIdGuard { _private: () };
90+
91+
set_global_root(tmp_dir);
92+
let r = root();
93+
r.rm_rf();
94+
r.mkdir_p();
8895

96+
guard
97+
}
98+
99+
/// For test harnesses like [`crate::cargo_test`]
100+
#[cfg(not(windows))]
101+
pub fn init_root(tmp_dir: Option<&'static str>, test_name: PathBuf) -> TestIdGuard {
102+
TEST_NAME.with(|n| *n.borrow_mut() = Some(test_name));
89103
let guard = TestIdGuard { _private: () };
90104

91105
set_global_root(tmp_dir);
@@ -98,13 +112,17 @@ pub fn init_root(tmp_dir: Option<&'static str>) -> TestIdGuard {
98112

99113
impl Drop for TestIdGuard {
100114
fn drop(&mut self) {
115+
#[cfg(windows)]
101116
TEST_ID.with(|n| *n.borrow_mut() = None);
117+
#[cfg(not(windows))]
118+
TEST_NAME.with(|n| *n.borrow_mut() = None);
102119
}
103120
}
104121

105122
/// Path to the test's filesystem scratchpad
106123
///
107124
/// ex: `$CARGO_TARGET_TMPDIR/cit/t0`
125+
#[cfg(windows)]
108126
pub fn root() -> PathBuf {
109127
let id = TEST_ID.with(|n| {
110128
n.borrow().expect(
@@ -118,6 +136,23 @@ pub fn root() -> PathBuf {
118136
root
119137
}
120138

139+
/// Path to the test's filesystem scratchpad
140+
///
141+
/// ex: `$CARGO_TARGET_TMPDIR/cit/t0`
142+
#[cfg(not(windows))]
143+
pub fn root() -> PathBuf {
144+
let test_name = TEST_NAME.with(|n| {
145+
n.borrow().clone().expect(
146+
"Tests must use the `#[cargo_test]` attribute in \
147+
order to be able to use the crate root.",
148+
)
149+
});
150+
151+
let mut root = global_root();
152+
root.push(&test_name);
153+
root
154+
}
155+
121156
/// Path to the current test's `$HOME`
122157
///
123158
/// ex: `$CARGO_TARGET_TMPDIR/cit/t0/home`
@@ -489,3 +524,26 @@ pub fn windows_reserved_names_are_allowed() -> bool {
489524
true
490525
}
491526
}
527+
528+
/// This takes the test location (std::file!() should be passed) and the test name
529+
/// and outputs the location the test should be places in, inside of `target/tmp/cit`
530+
///
531+
/// `path: tests/testsuite/workspaces.rs`
532+
/// `name: `workspace_in_git
533+
/// `output: "testsuite/workspaces/workspace_in_git`
534+
pub fn test_dir(path: &str, name: &str) -> std::path::PathBuf {
535+
let test_dir: std::path::PathBuf = std::path::PathBuf::from(path)
536+
.components()
537+
// Trim .rs from any files
538+
.map(|c| c.as_os_str().to_str().unwrap().trim_end_matches(".rs"))
539+
// We only want to take once we have reached `tests` or `src`. This helps when in a
540+
// workspace: `workspace/more/src/...` would result in `src/...`
541+
.skip_while(|c| c != &"tests" && c != &"src")
542+
// We want to skip "tests" since it is taken in `skip_while`.
543+
// "src" is fine since you could have test in "src" named the same as one in "tests"
544+
// Skip "mod" since `snapbox` tests have a folder per test not a file and the files
545+
// are named "mod.rs"
546+
.filter(|c| c != &"tests" && c != &"mod")
547+
.collect();
548+
test_dir.join(name)
549+
}

src/doc/contrib/src/tests/writing.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,13 @@ Then populate
200200
- This is used in place of `#[test]`
201201
- This attribute injects code which does some setup before starting the
202202
test, creating a filesystem "sandbox" under the "cargo integration test"
203-
directory for each test such as
204-
`/path/to/cargo/target/cit/t123/`
203+
directory for each test similar to the following:
204+
```toml
205+
# Most platforms
206+
/path/to/cargo/target/tmp/cit/testsuite/bad_config/bad1
207+
# Windows
208+
/path/to/cargo/target/cit/t123/
209+
```
205210
- The sandbox will contain a `home` directory that will be used instead of your normal home directory
206211

207212
`Project`:

tests/testsuite/main.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,3 +210,52 @@ fn aaa_trigger_cross_compile_disabled_check() {
210210
// This triggers the cross compile disabled check to run ASAP, see #5141
211211
crate::utils::cross_compile::disabled();
212212
}
213+
214+
// This is placed here as running tests in `cargo-test-support` would rebuild it
215+
#[cargo_test]
216+
#[cfg(not(windows))]
217+
fn check_test_dir() {
218+
let tests = vec![
219+
(
220+
"tests/testsuite/workspaces.rs",
221+
"workspace_in_git",
222+
"testsuite/workspaces/workspace_in_git",
223+
),
224+
(
225+
"tests/testsuite/cargo_remove/invalid_arg/mod.rs",
226+
"case",
227+
"testsuite/cargo_remove/invalid_arg/case",
228+
),
229+
(
230+
"tests/build-std/main.rs",
231+
"cross_custom",
232+
"build-std/main/cross_custom",
233+
),
234+
(
235+
"src/tools/cargo/tests/testsuite/build.rs",
236+
"cargo_compile_simple",
237+
"src/tools/cargo/testsuite/build/cargo_compile_simple",
238+
),
239+
(
240+
"src/tools/cargo/tests/testsuite/cargo_add/add_basic/mod.rs",
241+
"case",
242+
"src/tools/cargo/testsuite/cargo_add/add_basic/case",
243+
),
244+
(
245+
"src/tools/cargo/tests/build-std/main.rs",
246+
"cross_custom",
247+
"src/tools/cargo/build-std/main/cross_custom",
248+
),
249+
(
250+
"workspace/more/src/tools/cargo/tests/testsuite/build.rs",
251+
"cargo_compile_simple",
252+
"src/tools/cargo/testsuite/build/cargo_compile_simple",
253+
),
254+
];
255+
for (path, name, expected) in tests {
256+
assert_eq!(
257+
cargo_test_support::paths::test_dir(path, name),
258+
std::path::PathBuf::from(expected)
259+
);
260+
}
261+
}

0 commit comments

Comments
 (0)