extern crate fac;
use std::io::{Write, Read};
struct TempDir(std::path::PathBuf);
impl TempDir {
fn new<P: AsRef<std::path::Path>> (p: P) -> TempDir {
println!("remove test repository");
std::fs::remove_dir_all(&p.as_ref()).ok();
println!("create {:?}", p.as_ref());
assert!(std::fs::create_dir_all(p.as_ref()).is_ok());
TempDir(std::path::PathBuf::from(p.as_ref()))
}
fn fac(&self, args: &[&str]) -> std::process::Output {
let newpath =
match std::env::var_os("PATH") {
Some(paths) => {
let mut new_paths = vec![location_of_executables()];
for path in std::env::split_paths(&paths) {
new_paths.push(path);
}
std::env::join_paths(new_paths).unwrap()
}
None => {
println!("PATH is not defined in the environment.");
std::env::join_paths(&[std::env::current_dir().unwrap()
.join("target/debug")]).unwrap()
},
};
println!("PATH is {:?}", &newpath);
let s = std::process::Command::new("fac").args(args).env("PATH", newpath)
.current_dir(&self.0).output();
println!("I am in {:?} with args {:?}", std::env::current_dir(), args);
if !s.is_ok() {
println!("Bad news: {:?}", s);
println!(" exists:: {:?}", std::path::Path::new("target/debug/fac").exists());
for x in std::path::Path::new("target/debug").read_dir().unwrap() {
println!(" target/debug has {:?}", x);
}
} else {
let s = s.unwrap();
println!("output is:\n{}", String::from_utf8_lossy(&s.stdout));
println!("error is:\n{}", String::from_utf8_lossy(&s.stderr));
return s;
}
s.unwrap()
}
fn git_init(&self) {
let s = std::process::Command::new("git").arg("init")
.current_dir(&self.0).output().unwrap();
assert!(s.status.success());
}
fn add_file(&self, p: &str, contents: &[u8]) {
let absp = self.0.join(p);
let mut f = std::fs::File::create(absp).unwrap();
f.write(contents).unwrap();
let s = std::process::Command::new("git").arg("add").arg(p)
.current_dir(&self.0).output().unwrap();
assert!(s.status.success());
}
fn expect_file(&self, p: &str, contents: &[u8]) {
let absp = self.0.join(p);
let mut f = std::fs::File::open(absp).unwrap();
let mut actual_contents = Vec::new();
f.read_to_end(&mut actual_contents).unwrap();
while b" \n\r".contains(&actual_contents[actual_contents.len()-1]) {
actual_contents.pop();
}
let mut contents = Vec::from(contents);
while b" \n\r".contains(&contents[contents.len()-1]) {
contents.pop();
}
assert_eq!(std::str::from_utf8(actual_contents.as_slice()),
std::str::from_utf8(&contents));
}
fn no_such_file(&self, p: &str) {
let absp = self.0.join(p);
assert!(!absp.exists());
}
}
impl Drop for TempDir {
fn drop(&mut self) {
std::fs::remove_dir_all(&self.0).ok(); }
}
fn location_of_executables() -> std::path::PathBuf {
let mut path = std::env::current_exe().unwrap();
path.pop(); path.pop(); path
}
#[test]
fn fac_version() {
let tempdir = TempDir::new(&format!("tests/test-repositories/test-{}", line!()));
tempdir.git_init();
tempdir.add_file("top.fac", b"
| fac --version > version
");
assert!(tempdir.fac(&[]).status.success());
tempdir.expect_file("version", format!("fac {}", fac::version::VERSION).as_bytes());
}
#[test]
fn echo_to_file() {
let tempdir = TempDir::new(&format!("tests/test-repositories/test-{}", line!()));
tempdir.git_init();
tempdir.add_file("top.fac", b"# comment
| echo hello world > foo
");
tempdir.expect_file("top.fac", b"# comment
| echo hello world > foo");
assert!(tempdir.fac(&[]).status.success());
tempdir.expect_file("foo", b"hello world");
}
#[test]
fn dry_run() {
let tempdir = TempDir::new(&format!("tests/test-repositories/test-{}", line!()));
tempdir.git_init();
tempdir.add_file("top.fac", b"# comment
| echo hello world > foo
");
tempdir.expect_file("top.fac", b"# comment
| echo hello world > foo
");
let output = tempdir.fac(&["--dry"]);
println!("output:\n{}\n", String::from_utf8_lossy(&output.stdout));
assert!(output.status.success());
assert!(has_match(&output.stdout, b"dry run"));
assert!(has_match(&output.stdout, b"echo hello world > foo"));
assert!(has_match(&output.stdout, b"Build failed"));
tempdir.no_such_file("foo");
}
fn has_match(bigstr: &[u8], substr: &[u8]) -> bool {
bigstr.windows(substr.len()).any(|x| x == substr)
}
#[cfg(windows)]
const AM_WINDOWS: bool = true;
#[cfg(not(windows))]
const AM_WINDOWS: bool = false;
#[test]
fn dependency_makefile() {
if let Some(cc) = pick_executable(&["clang", "gcc", "cc", "cl.exe"]) {
println!("We found cc = {}", cc);
let tempdir = TempDir::new(&format!("tests/test-repositories/test-{}", line!()));
tempdir.git_init();
if AM_WINDOWS {
tempdir.add_file("top.fac",format!("# comment
| {} -MD -MF .foo.o.dep -c foo.c
M .foo.o.dep
| {} -o foo.exe foo.o
< foo.o
> foo.exe
| foo.exe > message
< foo.exe
> message
", cc, cc).as_bytes());
} else {
tempdir.add_file("top.fac",format!("# comment
| {} -MD -MF .foo.o.dep -c foo.c
M .foo.o.dep
| {} -o foo foo.o
< foo.o
> foo
| ./foo > message
< foo
> message
", cc, cc).as_bytes());
}
tempdir.add_file("foo.c", b"
#include <stdio.h>
#include \"foo.h\"
int main() {
printf(\"%s\", message);
return 0;
}
");
tempdir.add_file("foo.h", b"
const char *message = \"hello\\n\";
");
assert!(tempdir.fac(&["--blind","-v", "-j1"]).status.success());
tempdir.expect_file("message", b"hello");
assert!(tempdir.fac(&["--clean"]).status.success());
tempdir.no_such_file("foo.o");
tempdir.no_such_file(".foo.o.dep");
tempdir.no_such_file("foo");
tempdir.no_such_file("message");
}
}
#[test]
fn failing_rule() {
let tempdir = TempDir::new(&format!("tests/test-repositories/test-{}", line!()));
tempdir.git_init();
tempdir.add_file("top.fac", b"# comment
| ec ho hello world > foo
");
assert!(! tempdir.fac(&[]).status.success());
}
#[test]
fn failure_stdout_captured() {
let tempdir = TempDir::new(&format!("tests/test-repositories/test-{}", line!()));
tempdir.git_init();
tempdir.add_file("top.fac", b"# comment
| echo this fails && false
");
let output = tempdir.fac(&[]);
assert!(! output.status.success());
assert!(has_match(&output.stdout, b"this fails"));
}
fn executable_exists(cmd: &str) -> bool {
std::process::Command::new(cmd).args(&["--missing-flag-hopefully"]).output().is_ok()
}
fn pick_executable(cmds: &[&'static str]) -> Option<&'static str> {
cmds.iter().map(|&x| x).filter(|&x| executable_exists(x)).next()
}