Skip to content

Commit ee03c3c

Browse files
committed
feat: Use test name for dir when running tests
1 parent e6b0a65 commit ee03c3c

File tree

8 files changed

+142
-31
lines changed

8 files changed

+142
-31
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ cargo-credential-macos-keychain = { version = "0.4.20", path = "credential/cargo
3232
cargo-credential-wincred = { version = "0.4.20", path = "credential/cargo-credential-wincred" }
3333
cargo-platform = { path = "crates/cargo-platform", version = "0.3.0" }
3434
cargo-test-macro = { version = "0.4.9", path = "crates/cargo-test-macro" }
35-
cargo-test-support = { version = "0.9.2", path = "crates/cargo-test-support" }
35+
cargo-test-support = { version = "0.10.0", path = "crates/cargo-test-support" }
3636
cargo-util = { version = "0.2.27", path = "crates/cargo-util" }
3737
cargo-util-schemas = { version = "0.12.0", path = "crates/cargo-util-schemas" }
3838
cargo_metadata = "0.23.1"

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

Lines changed: 25 additions & 5 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,35 @@ 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 = {
234+
let name = &test_name
235+
.clone()
236+
.map(|n| n.split("::").next().unwrap().to_string())
237+
.unwrap();
238+
239+
let mut new_body = to_token_stream(&format!(
240+
r#"let _test_guard = {{
222241
let tmp_dir = env!("CARGO_TARGET_TMPDIR");
223-
cargo_test_support::paths::init_root(tmp_dir)
224-
};"#,
225-
);
242+
let test_dir = cargo_test_support::paths::test_dir(std::file!(), "{name}");
243+
cargo_test_support::paths::init_root(tmp_dir, test_dir)
244+
}};"#
245+
));
226246

227247
new_body.extend(group.stream());
228248
ret.extend(Some(TokenTree::from(Group::new(

crates/cargo-test-support/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cargo-test-support"
3-
version = "0.9.2"
3+
version = "0.10.0"
44
edition.workspace = true
55
rust-version = "1.92" # MSRV:1
66
license.workspace = true

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

Lines changed: 51 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,13 @@ pub fn global_root() -> PathBuf {
4646
}
4747
}
4848

49-
// We need to give each test a unique id. The test name could serve this
50-
// purpose, but the `test` crate doesn't have a way to obtain the current test
51-
// name.[*] Instead, we used the `cargo-test-macro` crate to automatically
52-
// insert an init function for each test that sets the test name in a thread
53-
// local variable.
54-
//
55-
// [*] It does set the thread name, but only when running concurrently. If not
56-
// running concurrently, all tests are run on the main thread.
49+
// We need to give each test a unique id. The test name serve this
50+
// purpose. We are able to get the test name by having the `cargo-test-macro`
51+
// crate automatically insert an init function for each test that sets the
52+
// test name in a thread local variable.
5753
thread_local! {
5854
static TEST_ID: RefCell<Option<usize>> = const { RefCell::new(None) };
55+
static TEST_DIR: RefCell<Option<PathBuf>> = const { RefCell::new(None) };
5956
}
6057

6158
/// See [`init_root`]
@@ -64,44 +61,56 @@ pub struct TestIdGuard {
6461
}
6562

6663
/// For test harnesses like [`crate::cargo_test`]
67-
pub fn init_root(tmp_dir: &'static str) -> TestIdGuard {
64+
pub fn init_root(tmp_dir: &'static str, test_dir: PathBuf) -> TestIdGuard {
6865
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
69-
7066
let id = NEXT_ID.fetch_add(1, Ordering::SeqCst);
7167
TEST_ID.with(|n| *n.borrow_mut() = Some(id));
72-
68+
if cfg!(windows) {
69+
// Due to path-length limits, Windows doesn't use the full test name.
70+
TEST_DIR.with(|n| *n.borrow_mut() = Some(PathBuf::from(format!("t{id}"))));
71+
} else {
72+
TEST_DIR.with(|n| *n.borrow_mut() = Some(test_dir));
73+
}
7374
let guard = TestIdGuard { _private: () };
74-
7575
set_global_root(tmp_dir);
7676
let r = root();
7777
r.rm_rf();
7878
r.mkdir_p();
79-
79+
#[cfg(not(windows))]
80+
if id == 0 {
81+
// Create a symlink from `t0` to the first test to make it easier to
82+
// find and reuse when running a single test.
83+
use crate::SymlinkBuilder;
84+
let mut alias = global_root();
85+
alias.push("t0");
86+
alias.rm_rf();
87+
SymlinkBuilder::new_dir(r, alias).mk();
88+
}
8089
guard
8190
}
8291

8392
impl Drop for TestIdGuard {
8493
fn drop(&mut self) {
8594
TEST_ID.with(|n| *n.borrow_mut() = None);
95+
TEST_DIR.with(|n| *n.borrow_mut() = None);
8696
}
8797
}
8898

8999
/// Path to the test's filesystem scratchpad
90100
///
91-
/// ex: `$CARGO_TARGET_TMPDIR/cit/t0`
101+
/// ex: `$CARGO_TARGET_TMPDIR/cit/<integration test>/<module>/<fn name>/`
102+
/// or `$CARGO_TARGET_TMPDIR/cit/t0` on Windows
92103
pub fn root() -> PathBuf {
93-
let id = TEST_ID.with(|n| {
94-
n.borrow().expect(
104+
let test_dir = TEST_DIR.with(|n| {
105+
n.borrow().clone().expect(
95106
"Tests must use the `#[cargo_test]` attribute in \
96107
order to be able to use the crate root.",
97108
)
98109
});
99-
100110
let mut root = global_root();
101-
root.push(&format!("t{}", id));
111+
root.push(&test_dir);
102112
root
103113
}
104-
105114
/// Path to the current test's `$HOME`
106115
///
107116
/// ex: `$CARGO_TARGET_TMPDIR/cit/t0/home`
@@ -473,3 +482,26 @@ pub fn windows_reserved_names_are_allowed() -> bool {
473482
true
474483
}
475484
}
485+
486+
/// This takes the test location (std::file!() should be passed) and the test name
487+
/// and outputs the location the test should be places in, inside of `target/tmp/cit`
488+
///
489+
/// `path: tests/testsuite/workspaces.rs`
490+
/// `name: `workspace_in_git
491+
/// `output: "testsuite/workspaces/workspace_in_git`
492+
pub fn test_dir(path: &str, name: &str) -> std::path::PathBuf {
493+
let test_dir: std::path::PathBuf = std::path::PathBuf::from(path)
494+
.components()
495+
// Trim .rs from any files
496+
.map(|c| c.as_os_str().to_str().unwrap().trim_end_matches(".rs"))
497+
// We only want to take once we have reached `tests` or `src`. This helps when in a
498+
// workspace: `workspace/more/src/...` would result in `src/...`
499+
.skip_while(|c| c != &"tests" && c != &"src")
500+
// We want to skip "tests" since it is taken in `skip_while`.
501+
// "src" is fine since you could have test in "src" named the same as one in "tests"
502+
// Skip "mod" since `snapbox` tests have a folder per test not a file and the files
503+
// are named "mod.rs"
504+
.filter(|c| c != &"tests" && c != &"mod")
505+
.collect();
506+
test_dir.join(name)
507+
}

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -200,8 +200,10 @@ 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. The directory for each test is based on the
204+
integration test name, module (if there is one), and function name[^1]:
205+
206+
`/path/to/cargo/target/tmp/cit/<integration test>/<module>/<fn name>/`
205207
- The sandbox will contain a `home` directory that will be used instead of your normal home directory
206208

207209
`Project`:
@@ -258,6 +260,11 @@ or overwrite a binary immediately after running it. Under some conditions
258260
Windows will fail with errors like "directory not empty" or "failed to remove"
259261
or "access is denied".
260262

263+
On Windows, to avoid path length limitations, the tests use the following
264+
directory structure instead:
265+
266+
`/path/to/cargo/target/tmp/cit/t123/`
267+
261268
## Debugging tests
262269

263270
In some cases, you may need to dig into a test that is not working as you
@@ -268,7 +275,7 @@ environment. The general process is:
268275

269276
`cargo test --test testsuite -- features2::inactivate_targets`.
270277
2. In another terminal, head into the sandbox directory to inspect the files and run `cargo` directly.
271-
1. The sandbox directories start with `t0` for the first test.
278+
1. The first test's sandbox directory is called `t0`.
272279

273280
`cd target/tmp/cit/t0`
274281
2. Set up the environment so that the sandbox configuration takes effect:
@@ -299,3 +306,6 @@ environment. The general process is:
299306
[`Command`]: https://docs.rs/snapbox/latest/snapbox/cmd/struct.Command.html
300307
[`OutputAssert`]: https://docs.rs/snapbox/latest/snapbox/cmd/struct.OutputAssert.html
301308
[`Assert`]: https://docs.rs/snapbox/latest/snapbox/struct.Assert.html
309+
310+
[^1]: Windows uses a separate directory layout, see [Platform-Specific Notes](#platform-specific-notes)
311+
for more details.

tests/testsuite/lints/implicit_minimum_version_req.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -717,7 +717,7 @@ implicit_minimum_version_req = "warn"
717717
[WARNING] dependency version requirement without an explicit minimum version
718718
--> Cargo.toml:7:[..]
719719
|
720-
7 | bar = { git = '[ROOTURL]/bar', version = "0.1" }
720+
7 | ...git_dep_with_registry_version/bar', version = "0.1" }
721721
| [..]^^^^^ missing full version components
722722
|
723723
[HELP] consider specifying full `major.minor.patch` version components

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)