181 lines
4.2 KiB
Rust
181 lines
4.2 KiB
Rust
use std::env;
|
|
use std::fs::File;
|
|
use std::io::prelude::*;
|
|
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),
|
|
}
|
|
};
|
|
}
|
|
|
|
#[test]
|
|
fn server_smoke() {
|
|
let c = t!(Client::new(1));
|
|
drop(c.acquire().unwrap());
|
|
drop(c.acquire().unwrap());
|
|
}
|
|
|
|
#[test]
|
|
fn server_multiple() {
|
|
let c = t!(Client::new(2));
|
|
let a = c.acquire().unwrap();
|
|
let b = c.acquire().unwrap();
|
|
drop((a, b));
|
|
}
|
|
|
|
#[test]
|
|
fn server_available() {
|
|
let c = t!(Client::new(10));
|
|
assert_eq!(c.available().unwrap(), 10);
|
|
let a = c.acquire().unwrap();
|
|
assert_eq!(c.available().unwrap(), 9);
|
|
drop(a);
|
|
assert_eq!(c.available().unwrap(), 10);
|
|
}
|
|
|
|
#[test]
|
|
fn server_none_available() {
|
|
let c = t!(Client::new(2));
|
|
assert_eq!(c.available().unwrap(), 2);
|
|
let a = c.acquire().unwrap();
|
|
assert_eq!(c.available().unwrap(), 1);
|
|
let b = c.acquire().unwrap();
|
|
assert_eq!(c.available().unwrap(), 0);
|
|
drop(a);
|
|
assert_eq!(c.available().unwrap(), 1);
|
|
drop(b);
|
|
assert_eq!(c.available().unwrap(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn server_blocks() {
|
|
let c = t!(Client::new(1));
|
|
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]
|
|
fn make_as_a_single_thread_client() {
|
|
let c = t!(Client::new(1));
|
|
let td = tempfile::tempdir().unwrap();
|
|
|
|
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
|
|
let mut cmd = Command::new(prog);
|
|
cmd.current_dir(td.path());
|
|
|
|
t!(t!(File::create(td.path().join("Makefile"))).write_all(
|
|
b"
|
|
all: foo bar
|
|
foo:
|
|
\techo foo
|
|
bar:
|
|
\techo bar
|
|
"
|
|
));
|
|
|
|
// The jobserver protocol means that the `make` process itself "runs with a
|
|
// token", so we acquire our one token to drain the jobserver, and this
|
|
// should mean that `make` itself never has a second token available to it.
|
|
let _a = c.acquire();
|
|
c.configure(&mut cmd);
|
|
let output = t!(cmd.output());
|
|
println!(
|
|
"\n\t=== stderr\n\t\t{}",
|
|
String::from_utf8_lossy(&output.stderr).replace("\n", "\n\t\t")
|
|
);
|
|
println!(
|
|
"\t=== stdout\n\t\t{}",
|
|
String::from_utf8_lossy(&output.stdout).replace("\n", "\n\t\t")
|
|
);
|
|
|
|
assert!(output.status.success());
|
|
assert!(output.stderr.is_empty());
|
|
|
|
let stdout = String::from_utf8_lossy(&output.stdout).replace("\r\n", "\n");
|
|
let a = "\
|
|
echo foo
|
|
foo
|
|
echo bar
|
|
bar
|
|
";
|
|
let b = "\
|
|
echo bar
|
|
bar
|
|
echo foo
|
|
foo
|
|
";
|
|
|
|
assert!(stdout == a || stdout == b);
|
|
}
|
|
|
|
#[test]
|
|
fn make_as_a_multi_thread_client() {
|
|
let c = t!(Client::new(1));
|
|
let td = tempfile::tempdir().unwrap();
|
|
|
|
let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string());
|
|
let mut cmd = Command::new(prog);
|
|
cmd.current_dir(td.path());
|
|
|
|
t!(t!(File::create(td.path().join("Makefile"))).write_all(
|
|
b"
|
|
all: foo bar
|
|
foo:
|
|
\techo foo
|
|
bar:
|
|
\techo bar
|
|
"
|
|
));
|
|
|
|
// We're leaking one extra token to `make` sort of violating the makefile
|
|
// jobserver protocol. It has the desired effect though.
|
|
c.configure(&mut cmd);
|
|
let output = t!(cmd.output());
|
|
println!(
|
|
"\n\t=== stderr\n\t\t{}",
|
|
String::from_utf8_lossy(&output.stderr).replace("\n", "\n\t\t")
|
|
);
|
|
println!(
|
|
"\t=== stdout\n\t\t{}",
|
|
String::from_utf8_lossy(&output.stdout).replace("\n", "\n\t\t")
|
|
);
|
|
|
|
assert!(output.status.success());
|
|
}
|
|
|
|
#[test]
|
|
fn zero_client() {
|
|
let client = t!(Client::new(0));
|
|
let (tx, rx) = mpsc::channel();
|
|
let helper = client
|
|
.into_helper_thread(move |a| drop(tx.send(a)))
|
|
.unwrap();
|
|
helper.request_token();
|
|
helper.request_token();
|
|
|
|
for _ in 0..1000 {
|
|
assert!(rx.try_recv().is_err());
|
|
}
|
|
}
|