pub fn test_all() -> Result<Vec<crate::Case>, crate::Error> {
let mut results = vec![];
let config = match std::fs::read_to_string("./tests/fbt.p1") {
Ok(v) => match crate::Config::parse(v.as_str()) {
Ok(config) => {
if let Some(ref b) = config.build {
match if cfg!(target_os = "windows") {
let mut c = std::process::Command::new("cmd");
c.args(&["/C", b.as_str()]);
c
} else {
let mut c = std::process::Command::new("sh");
c.args(&["-c", b.as_str()]);
c
}
.output()
{
Ok(v) => {
if !v.status.success() {
return Err(crate::Error::BuildFailed(v));
}
}
Err(e) => return Err(crate::Error::BuildFailedToLaunch(e)),
}
}
config
}
Err(e) => return Err(crate::Error::InvalidConfig(e)),
},
Err(e) if e.kind() == std::io::ErrorKind::NotFound => crate::Config::default(),
Err(e) => return Err(crate::Error::CantReadConfig(e)),
};
for dir in {
match std::fs::read_dir("./tests") {
Ok(dir) => dir,
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return Err(crate::Error::TestsFolderMissing)
}
Err(e) => return Err(crate::Error::TestsFolderNotReadable(e)),
}
} {
let dir = match dir {
Ok(d) => d.path(),
Err(e) => {
return Err(crate::Error::TestsFolderNotReadable(e));
}
};
if !dir.is_dir() {
continue;
}
if dir
.file_name()
.map(|v| v.to_str())
.unwrap_or(None)
.unwrap_or("")
.starts_with(".")
{
continue;
}
results.push(test_one(&config, dir));
}
Ok(results)
}
fn test_one(global: &crate::Config, entry: std::path::PathBuf) -> crate::Case {
use std::borrow::BorrowMut;
use std::io::Write;
let id = entry
.file_name()
.map(|v| v.to_str())
.unwrap_or(None)
.map(ToString::to_string)
.unwrap_or_else(|| format!("{:?}", entry.file_name()));
let start = std::time::Instant::now();
let id_ = id.as_str();
let err = |e: crate::Failure| crate::Case {
id: id_.to_string(),
result: Err(e),
duration: std::time::Instant::now().duration_since(start),
};
let config = match std::fs::read_to_string(entry.join("cmd.p1")) {
Ok(c) => match crate::TestConfig::parse(c.as_str(), global) {
Ok(c) => c,
Err(e) => return err(crate::Failure::CmdFileInvalid { error: e }),
},
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
return err(crate::Failure::CmdFileMissing)
}
Err(e) => return err(crate::Failure::CantReadCmdFile { error: e }),
};
let fbt = {
let fbt = std::env::temp_dir().join(format!("fbt/{}", rand::random::<i64>()));
if fbt.exists() {
if let Err(e) = std::fs::remove_dir_all(&fbt) {
return err(crate::Failure::Other { io: e });
}
}
if let Err(e) = std::fs::create_dir_all(&fbt) {
return err(crate::Failure::Other { io: e });
}
fbt
};
let input = entry.join("input");
let dir = if input.exists() {
let dir = fbt.join("input");
if !input.is_dir() {
return err(crate::Failure::InputIsNotDir);
}
if let Err(e) = crate::copy_dir::copy_dir_all(&input, &dir) {
return err(crate::Failure::Other { io: e });
}
dir
} else {
fbt
};
let mut child = match config.cmd().current_dir(&dir).spawn() {
Ok(c) => c,
Err(e) => {
return err(crate::Failure::CommandFailed {
io: e,
reason: "cant fork process",
});
}
};
if let (Some(ref stdin), Some(cstdin)) = (config.stdin, &mut child.stdin) {
if let Err(e) = cstdin.borrow_mut().write_all(stdin.as_bytes()) {
return err(crate::Failure::CommandFailed {
io: e,
reason: "cant write to stdin",
});
}
}
let output = match child.wait_with_output() {
Ok(o) => o,
Err(e) => {
return err(crate::Failure::CommandFailed {
io: e,
reason: "cant wait",
})
}
};
match output.status.code() {
Some(code) => {
if code != config.exit_code {
return err(crate::Failure::UnexpectedStatusCode {
expected: config.exit_code,
output,
});
}
}
None => {
return err(crate::Failure::UnexpectedStatusCode {
expected: config.exit_code,
output,
})
}
}
if let Some(ref stdout) = config.stdout {
if std::str::from_utf8(&output.stdout).unwrap_or("").trim() != stdout.trim() {
return err(crate::Failure::StdoutMismatch {
output,
expected: stdout.trim().to_string(),
});
}
}
if let Some(ref stderr) = config.stderr {
if std::str::from_utf8(&output.stderr).unwrap_or("").trim() != stderr.trim() {
return err(crate::Failure::StderrMismatch {
output,
expected: stderr.trim().to_string(),
});
}
}
let reference = entry.join("output");
if !reference.exists() {
return crate::Case {
id,
result: Ok(true),
duration: std::time::Instant::now().duration_since(start),
};
}
let output = match config.output {
Some(v) => dir.join(v),
None => dir,
};
return crate::Case {
id: id.clone(),
result: match crate::dir_diff::diff(output, reference) {
Ok(Some(diff)) => {
return err(crate::Failure::OutputMismatch { diff });
}
Ok(None) => Ok(true),
Err(e) => return err(crate::Failure::DirDiffError { error: e }),
},
duration: std::time::Instant::now().duration_since(start),
};
}