mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Don't rely on a thread local to uniquely create test roots
This commit is contained in:
parent
e157b6d84c
commit
a598309cb6
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
|||||||
/target
|
/target
|
||||||
|
/tests/testsuite/support/cargo-test-macro/target
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
.cargo
|
.cargo
|
||||||
/config.stamp
|
/config.stamp
|
||||||
|
@ -104,6 +104,7 @@ features = [
|
|||||||
bufstream = "0.1"
|
bufstream = "0.1"
|
||||||
proptest = "0.9.1"
|
proptest = "0.9.1"
|
||||||
varisat = "0.2.1"
|
varisat = "0.2.1"
|
||||||
|
cargo-test-macro = { "path" = "tests/testsuite/support/cargo-test-macro" }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "cargo"
|
name = "cargo"
|
||||||
|
@ -6,6 +6,9 @@
|
|||||||
#![warn(clippy::needless_borrow)]
|
#![warn(clippy::needless_borrow)]
|
||||||
#![warn(clippy::redundant_clone)]
|
#![warn(clippy::redundant_clone)]
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate cargo_test_macro;
|
||||||
|
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod support;
|
mod support;
|
||||||
|
|
||||||
|
12
tests/testsuite/support/cargo-test-macro/Cargo.toml
Normal file
12
tests/testsuite/support/cargo-test-macro/Cargo.toml
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "cargo-test-macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Jethro Beekman <jethro@fortanix.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc-macro = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
quote = "0.6"
|
||||||
|
syn = { version = "0.15", features = ["full"] }
|
24
tests/testsuite/support/cargo-test-macro/src/lib.rs
Normal file
24
tests/testsuite/support/cargo-test-macro/src/lib.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use quote::{quote, ToTokens};
|
||||||
|
use syn::{*, parse::Parser};
|
||||||
|
|
||||||
|
#[proc_macro_attribute]
|
||||||
|
pub fn cargo_test(
|
||||||
|
_attr: proc_macro::TokenStream,
|
||||||
|
item: proc_macro::TokenStream,
|
||||||
|
) -> proc_macro::TokenStream {
|
||||||
|
let mut fn_def = parse_macro_input!(item as ItemFn);
|
||||||
|
|
||||||
|
let attr = quote! {
|
||||||
|
#[test]
|
||||||
|
};
|
||||||
|
fn_def.attrs.extend(Attribute::parse_outer.parse2(attr).unwrap());
|
||||||
|
|
||||||
|
let stmt = quote! {
|
||||||
|
let _test_guard = crate::support::paths::init_root();
|
||||||
|
};
|
||||||
|
fn_def.block.stmts.insert(0, parse2(stmt).unwrap());
|
||||||
|
|
||||||
|
fn_def.into_token_stream().into()
|
||||||
|
}
|
@ -1,58 +1,93 @@
|
|||||||
use std::cell::Cell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::{self, ErrorKind};
|
use std::io::{self, ErrorKind};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{Once, ONCE_INIT};
|
use std::sync::Mutex;
|
||||||
|
|
||||||
use filetime::{self, FileTime};
|
use filetime::{self, FileTime};
|
||||||
|
use lazy_static::lazy_static;
|
||||||
|
|
||||||
static CARGO_INTEGRATION_TEST_DIR: &'static str = "cit";
|
static CARGO_INTEGRATION_TEST_DIR: &'static str = "cit";
|
||||||
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
thread_local!(static TASK_ID: usize = NEXT_ID.fetch_add(1, Ordering::SeqCst));
|
lazy_static! {
|
||||||
|
static ref GLOBAL_ROOT: PathBuf = {
|
||||||
|
let mut path = t!(env::current_exe());
|
||||||
|
path.pop(); // chop off exe name
|
||||||
|
path.pop(); // chop off 'debug'
|
||||||
|
|
||||||
fn init() {
|
// If `cargo test` is run manually then our path looks like
|
||||||
static GLOBAL_INIT: Once = ONCE_INIT;
|
// `target/debug/foo`, in which case our `path` is already pointing at
|
||||||
thread_local!(static LOCAL_INIT: Cell<bool> = Cell::new(false));
|
// `target`. If, however, `cargo test --target $target` is used then the
|
||||||
GLOBAL_INIT.call_once(|| {
|
// output is `target/$target/debug/foo`, so our path is pointing at
|
||||||
global_root().mkdir_p();
|
// `target/$target`. Here we conditionally pop the `$target` name.
|
||||||
});
|
if path.file_name().and_then(|s| s.to_str()) != Some("target") {
|
||||||
LOCAL_INIT.with(|i| {
|
path.pop();
|
||||||
if i.get() {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
i.set(true);
|
|
||||||
root().rm_rf();
|
path.push(CARGO_INTEGRATION_TEST_DIR);
|
||||||
home().mkdir_p();
|
|
||||||
})
|
path.rm_rf();
|
||||||
|
path.mkdir_p();
|
||||||
|
|
||||||
|
path
|
||||||
|
};
|
||||||
|
|
||||||
|
static ref TEST_ROOTS: Mutex<HashMap<String, PathBuf>> = Default::default();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn global_root() -> PathBuf {
|
// We need to give each test a unique id. The test name could serve this
|
||||||
let mut path = t!(env::current_exe());
|
// purpose, but the `test` crate doesn't have a way to obtain the current test
|
||||||
path.pop(); // chop off exe name
|
// name.[*] Instead, we used the `cargo-test-macro` crate to automatically
|
||||||
path.pop(); // chop off 'debug'
|
// 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.
|
||||||
|
thread_local! {
|
||||||
|
static TEST_ID: RefCell<Option<usize>> = RefCell::new(None);
|
||||||
|
}
|
||||||
|
|
||||||
// If `cargo test` is run manually then our path looks like
|
pub struct TestIdGuard {
|
||||||
// `target/debug/foo`, in which case our `path` is already pointing at
|
_private: ()
|
||||||
// `target`. If, however, `cargo test --target $target` is used then the
|
}
|
||||||
// output is `target/$target/debug/foo`, so our path is pointing at
|
|
||||||
// `target/$target`. Here we conditionally pop the `$target` name.
|
pub fn init_root() -> TestIdGuard {
|
||||||
if path.file_name().and_then(|s| s.to_str()) != Some("target") {
|
static NEXT_ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
path.pop();
|
|
||||||
|
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
||||||
|
TEST_ID.with(|n| { *n.borrow_mut() = Some(id) } );
|
||||||
|
|
||||||
|
let guard = TestIdGuard {
|
||||||
|
_private: ()
|
||||||
|
};
|
||||||
|
|
||||||
|
root().mkdir_p();
|
||||||
|
|
||||||
|
guard
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestIdGuard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
TEST_ID.with(|n| { *n.borrow_mut() = None } );
|
||||||
}
|
}
|
||||||
|
|
||||||
path.join(CARGO_INTEGRATION_TEST_DIR)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn root() -> PathBuf {
|
pub fn root() -> PathBuf {
|
||||||
init();
|
let id = TEST_ID.with(|n| {
|
||||||
global_root().join(&TASK_ID.with(|my_id| format!("t{}", my_id)))
|
n.borrow().expect("Tests must use the `#[cargo_test]` attribute in \
|
||||||
|
order to be able to use the crate root.")
|
||||||
|
} );
|
||||||
|
GLOBAL_ROOT.join(&format!("t{}", id))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn home() -> PathBuf {
|
pub fn home() -> PathBuf {
|
||||||
root().join("home")
|
let mut path = root();
|
||||||
|
path.push("home");
|
||||||
|
path.mkdir_p();
|
||||||
|
path
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait CargoPathExt {
|
pub trait CargoPathExt {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user