Skip to content

Commit a598309

Browse files
Jethro Beekmanehuss
authored andcommitted
Don't rely on a thread local to uniquely create test roots
1 parent e157b6d commit a598309

File tree

6 files changed

+112
-36
lines changed

6 files changed

+112
-36
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/target
2+
/tests/testsuite/support/cargo-test-macro/target
23
Cargo.lock
34
.cargo
45
/config.stamp

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ features = [
104104
bufstream = "0.1"
105105
proptest = "0.9.1"
106106
varisat = "0.2.1"
107+
cargo-test-macro = { "path" = "tests/testsuite/support/cargo-test-macro" }
107108

108109
[[bin]]
109110
name = "cargo"

tests/testsuite/main.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#![warn(clippy::needless_borrow)]
77
#![warn(clippy::redundant_clone)]
88

9+
#[macro_use]
10+
extern crate cargo_test_macro;
11+
912
#[macro_use]
1013
mod support;
1114

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[package]
2+
name = "cargo-test-macro"
3+
version = "0.1.0"
4+
authors = ["Jethro Beekman <jethro@fortanix.com>"]
5+
edition = "2018"
6+
7+
[lib]
8+
proc-macro = true
9+
10+
[dependencies]
11+
quote = "0.6"
12+
syn = { version = "0.15", features = ["full"] }
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
extern crate proc_macro;
2+
3+
use quote::{quote, ToTokens};
4+
use syn::{*, parse::Parser};
5+
6+
#[proc_macro_attribute]
7+
pub fn cargo_test(
8+
_attr: proc_macro::TokenStream,
9+
item: proc_macro::TokenStream,
10+
) -> proc_macro::TokenStream {
11+
let mut fn_def = parse_macro_input!(item as ItemFn);
12+
13+
let attr = quote! {
14+
#[test]
15+
};
16+
fn_def.attrs.extend(Attribute::parse_outer.parse2(attr).unwrap());
17+
18+
let stmt = quote! {
19+
let _test_guard = crate::support::paths::init_root();
20+
};
21+
fn_def.block.stmts.insert(0, parse2(stmt).unwrap());
22+
23+
fn_def.into_token_stream().into()
24+
}

tests/testsuite/support/paths.rs

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,93 @@
1-
use std::cell::Cell;
1+
use std::cell::RefCell;
2+
use std::collections::HashMap;
23
use std::env;
34
use std::fs;
45
use std::io::{self, ErrorKind};
56
use std::path::{Path, PathBuf};
67
use std::sync::atomic::{AtomicUsize, Ordering};
7-
use std::sync::{Once, ONCE_INIT};
8+
use std::sync::Mutex;
89

910
use filetime::{self, FileTime};
11+
use lazy_static::lazy_static;
1012

1113
static CARGO_INTEGRATION_TEST_DIR: &'static str = "cit";
12-
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
13-
14-
thread_local!(static TASK_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst));
15-
16-
fn init() {
17-
static GLOBAL_INIT: Once = ONCE_INIT;
18-
thread_local!(static LOCAL_INIT: Cell<bool> = Cell::new(false));
19-
GLOBAL_INIT.call_once(|| {
20-
global_root().mkdir_p();
21-
});
22-
LOCAL_INIT.with(|i| {
23-
if i.get() {
24-
return;
14+
15+
lazy_static! {
16+
static ref GLOBAL_ROOT: PathBuf = {
17+
let mut path = t!(env::current_exe());
18+
path.pop(); // chop off exe name
19+
path.pop(); // chop off 'debug'
20+
21+
// If `cargo test` is run manually then our path looks like
22+
// `target/debug/foo`, in which case our `path` is already pointing at
23+
// `target`. If, however, `cargo test --target $target` is used then the
24+
// output is `target/$target/debug/foo`, so our path is pointing at
25+
// `target/$target`. Here we conditionally pop the `$target` name.
26+
if path.file_name().and_then(|s| s.to_str()) != Some("target") {
27+
path.pop();
2528
}
26-
i.set(true);
27-
root().rm_rf();
28-
home().mkdir_p();
29-
})
29+
30+
path.push(CARGO_INTEGRATION_TEST_DIR);
31+
32+
path.rm_rf();
33+
path.mkdir_p();
34+
35+
path
36+
};
37+
38+
static ref TEST_ROOTS: Mutex<HashMap<String, PathBuf>> = Default::default();
3039
}
3140

32-
fn global_root() -> PathBuf {
33-
let mut path = t!(env::current_exe());
34-
path.pop(); // chop off exe name
35-
path.pop(); // chop off 'debug'
36-
37-
// If `cargo test` is run manually then our path looks like
38-
// `target/debug/foo`, in which case our `path` is already pointing at
39-
// `target`. If, however, `cargo test --target $target` is used then the
40-
// output is `target/$target/debug/foo`, so our path is pointing at
41-
// `target/$target`. Here we conditionally pop the `$target` name.
42-
if path.file_name().and_then(|s| s.to_str()) != Some("target") {
43-
path.pop();
44-
}
41+
// We need to give each test a unique id. The test name could serve this
42+
// purpose, but the `test` crate doesn't have a way to obtain the current test
43+
// name.[*] Instead, we used the `cargo-test-macro` crate to automatically
44+
// insert an init function for each test that sets the test name in a thread
45+
// local variable.
46+
//
47+
// [*] It does set the thread name, but only when running concurrently. If not
48+
// running concurrently, all tests are run on the main thread.
49+
thread_local! {
50+
static TEST_ID: RefCell<Option<usize>> = RefCell::new(None);
51+
}
4552

46-
path.join(CARGO_INTEGRATION_TEST_DIR)
53+
pub struct TestIdGuard {
54+
_private: ()
55+
}
56+
57+
pub fn init_root() -> TestIdGuard {
58+
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
59+
60+
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
61+
TEST_ID.with(|n| { *n.borrow_mut() = Some(id) } );
62+
63+
let guard = TestIdGuard {
64+
_private: ()
65+
};
66+
67+
root().mkdir_p();
68+
69+
guard
70+
}
71+
72+
impl Drop for TestIdGuard {
73+
fn drop(&mut self) {
74+
TEST_ID.with(|n| { *n.borrow_mut() = None } );
75+
}
4776
}
4877

4978
pub fn root() -> PathBuf {
50-
init();
51-
global_root().join(&TASK_ID.with(|my_id| format!("t{}", my_id)))
79+
let id = TEST_ID.with(|n| {
80+
n.borrow().expect("Tests must use the `#[cargo_test]` attribute in \
81+
order to be able to use the crate root.")
82+
} );
83+
GLOBAL_ROOT.join(&format!("t{}", id))
5284
}
5385

5486
pub fn home() -> PathBuf {
55-
root().join("home")
87+
let mut path = root();
88+
path.push("home");
89+
path.mkdir_p();
90+
path
5691
}
5792

5893
pub trait CargoPathExt {

0 commit comments

Comments
 (0)