| use std::env; |
| use std::fs::File; |
| use std::io::prelude::*; |
| use std::net::{TcpListener, TcpStream}; |
| use std::process::Command; |
| |
| use jobserver::Client; |
| use tempdir::TempDir; |
| |
| macro_rules! t { |
| ($e:expr) => { |
| match $e { |
| Ok(e) => e, |
| Err(e) => panic!("{} failed with {}", stringify!($e), e), |
| } |
| }; |
| } |
| |
| fn main() { |
| if env::var("_DO_THE_TEST").is_ok() { |
| std::process::exit( |
| Command::new(env::var_os("MAKE").unwrap()) |
| .env("MAKEFLAGS", env::var_os("CARGO_MAKEFLAGS").unwrap()) |
| .env_remove("_DO_THE_TEST") |
| .args(&env::args_os().skip(1).collect::<Vec<_>>()) |
| .status() |
| .unwrap() |
| .code() |
| .unwrap_or(1), |
| ); |
| } |
| |
| if let Ok(s) = env::var("TEST_ADDR") { |
| let mut contents = Vec::new(); |
| t!(t!(TcpStream::connect(&s)).read_to_end(&mut contents)); |
| return; |
| } |
| |
| let c = t!(Client::new(1)); |
| let td = TempDir::new("foo").unwrap(); |
| |
| let prog = env::var("MAKE").unwrap_or_else(|_| "make".to_string()); |
| |
| let me = t!(env::current_exe()); |
| let me = me.to_str().unwrap(); |
| |
| let mut cmd = Command::new(&me); |
| cmd.current_dir(td.path()); |
| cmd.env("MAKE", prog); |
| cmd.env("_DO_THE_TEST", "1"); |
| |
| t!(t!(File::create(td.path().join("Makefile"))).write_all( |
| format!( |
| "\ |
| all: foo bar |
| foo: |
| \t{0} |
| bar: |
| \t{0} |
| ", |
| me |
| ) |
| .as_bytes() |
| )); |
| |
| // 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 listener = t!(TcpListener::bind("127.0.0.1:0")); |
| let addr = t!(listener.local_addr()); |
| cmd.env("TEST_ADDR", addr.to_string()); |
| let mut child = t!(cmd.spawn()); |
| |
| // We should get both connections as the two programs should be run |
| // concurrently. |
| let a = t!(listener.accept()); |
| let b = t!(listener.accept()); |
| drop((a, b)); |
| |
| assert!(t!(child.wait()).success()); |
| } |