use std::env; | |
use std::fs::File; | |
use std::io::Write; | |
use std::process::Command; | |
use std::sync::atomic::{AtomicBool, Ordering}; | |
use std::sync::mpsc; | |
use std::sync::Arc; | |
use std::thread; | |
use jobserver::Client; | |
macro_rules! t { | |
($e:expr) => { | |
match $e { | |
Ok(e) => e, | |
Err(e) => panic!("{} failed with {}", stringify!($e), e), | |
} | |
}; | |
} | |
struct Test { | |
name: &'static str, | |
f: &'static (dyn Fn() + Send + Sync), | |
make_args: &'static [&'static str], | |
rule: &'static (dyn Fn(&str) -> String + Send + Sync), | |
} | |
const TESTS: &[Test] = &[ | |
Test { | |
name: "no j args", | |
make_args: &[], | |
rule: &|me| me.to_string(), | |
f: &|| { | |
assert!(unsafe { Client::from_env().is_none() }); | |
}, | |
}, | |
Test { | |
name: "no j args with plus", | |
make_args: &[], | |
rule: &|me| format!("+{}", me), | |
f: &|| { | |
assert!(unsafe { Client::from_env().is_none() }); | |
}, | |
}, | |
Test { | |
name: "j args with plus", | |
make_args: &["-j2"], | |
rule: &|me| format!("+{}", me), | |
f: &|| { | |
assert!(unsafe { Client::from_env().is_some() }); | |
}, | |
}, | |
Test { | |
name: "acquire", | |
make_args: &["-j2"], | |
rule: &|me| format!("+{}", me), | |
f: &|| { | |
let c = unsafe { Client::from_env().unwrap() }; | |
drop(c.acquire().unwrap()); | |
drop(c.acquire().unwrap()); | |
}, | |
}, | |
Test { | |
name: "acquire3", | |
make_args: &["-j3"], | |
rule: &|me| format!("+{}", me), | |
f: &|| { | |
let c = unsafe { Client::from_env().unwrap() }; | |
let a = c.acquire().unwrap(); | |
let b = c.acquire().unwrap(); | |
drop((a, b)); | |
}, | |
}, | |
Test { | |
name: "acquire blocks", | |
make_args: &["-j2"], | |
rule: &|me| format!("+{}", me), | |
f: &|| { | |
let c = unsafe { Client::from_env().unwrap() }; | |
let a = c.acquire().unwrap(); | |
let hit = Arc::new(AtomicBool::new(false)); | |
let hit2 = hit.clone(); | |
let (tx, rx) = mpsc::channel(); | |
let t = thread::spawn(move || { | |
tx.send(()).unwrap(); | |
let _b = c.acquire().unwrap(); | |
hit2.store(true, Ordering::SeqCst); | |
}); | |
rx.recv().unwrap(); | |
assert!(!hit.load(Ordering::SeqCst)); | |
drop(a); | |
t.join().unwrap(); | |
assert!(hit.load(Ordering::SeqCst)); | |
}, | |
}, | |
Test { | |
name: "acquire_raw", | |
make_args: &["-j2"], | |
rule: &|me| format!("+{}", me), | |
f: &|| { | |
let c = unsafe { Client::from_env().unwrap() }; | |
c.acquire_raw().unwrap(); | |
c.release_raw().unwrap(); | |
}, | |
}, | |
]; | |
fn main() { | |
if let Ok(test) = env::var("TEST_TO_RUN") { | |
return (TESTS.iter().find(|t| t.name == test).unwrap().f)(); | |
} | |
let me = t!(env::current_exe()); | |
let me = me.to_str().unwrap(); | |
let filter = env::args().nth(1); | |
let join_handles = TESTS | |
.iter() | |
.filter(|test| match filter { | |
Some(ref s) => test.name.contains(s), | |
None => true, | |
}) | |
.map(|test| { | |
let td = t!(tempfile::tempdir()); | |
let makefile = format!( | |
"\ | |
all: export TEST_TO_RUN={} | |
all: | |
\t{} | |
", | |
test.name, | |
(test.rule)(me) | |
); | |
t!(t!(File::create(td.path().join("Makefile"))).write_all(makefile.as_bytes())); | |
thread::spawn(move || { | |
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string()); | |
let mut cmd = Command::new(prog); | |
cmd.args(test.make_args); | |
cmd.current_dir(td.path()); | |
(test, cmd.output().unwrap()) | |
}) | |
}) | |
.collect::<Vec<_>>(); | |
println!("\nrunning {} tests\n", join_handles.len()); | |
let failures = join_handles | |
.into_iter() | |
.filter_map(|join_handle| { | |
let (test, output) = join_handle.join().unwrap(); | |
if output.status.success() { | |
println!("test {} ... ok", test.name); | |
None | |
} else { | |
println!("test {} ... FAIL", test.name); | |
Some((test, output)) | |
} | |
}) | |
.collect::<Vec<_>>(); | |
if failures.is_empty() { | |
println!("\ntest result: ok\n"); | |
return; | |
} | |
println!("\n----------- failures"); | |
for (test, output) in failures { | |
println!("test {}", test.name); | |
let stdout = String::from_utf8_lossy(&output.stdout); | |
let stderr = String::from_utf8_lossy(&output.stderr); | |
println!("\texit status: {}", output.status); | |
if !stdout.is_empty() { | |
println!("\tstdout ==="); | |
for line in stdout.lines() { | |
println!("\t\t{}", line); | |
} | |
} | |
if !stderr.is_empty() { | |
println!("\tstderr ==="); | |
for line in stderr.lines() { | |
println!("\t\t{}", line); | |
} | |
} | |
} | |
std::process::exit(4); | |
} |