use serial_test::serial;
use std::collections::HashMap;
use std::env::set_current_dir;
use std::mem::drop;
use std::path::{Path, PathBuf};
use std::process::Command;
use std::{fs, io};
use tempfile::TempDir;
const EXAMPLE_REPO: &str = "https://github.com/MrHedmad/kerblam-examples.git";
fn setup_test() -> TempDir {
let target = TempDir::new().expect("Could not create temp dir");
let mut clone_options = git2::FetchOptions::new();
clone_options.depth(1);
clone_options.download_tags(git2::AutotagOption::None);
let mut fetcher = git2::build::RepoBuilder::new();
fetcher.fetch_options(clone_options);
fetcher
.clone(EXAMPLE_REPO, target.path())
.expect("Could not clone remote repo.");
target
}
type Content = HashMap<PathBuf, Vec<u8>>;
fn snapshot(dir: &PathBuf) -> io::Result<Content> {
let mut content: Content = HashMap::new();
for entry in fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
let metadata = fs::metadata(&path)?;
if metadata.is_dir() {
continue;
}
content.insert(entry.path().to_path_buf(), fs::read(entry.path())?);
}
Ok(content)
}
fn two_way_check<T>(a: &Vec<T>, b: &Vec<T>) -> bool
where
T: PartialEq,
{
a.iter().all(|item| b.contains(item)) & b.iter().all(|item| a.contains(item))
}
fn check_identical(old_snap: Content, target: impl AsRef<Path>) -> io::Result<()> {
let new_snap = snapshot(&target.as_ref().to_owned().to_path_buf()).unwrap();
let old_files: Vec<PathBuf> = old_snap.keys().cloned().collect();
let new_files: Vec<PathBuf> = new_snap.keys().cloned().collect();
if &old_files.len() != &new_files.len() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"Number of files differ: old: {:?}, new: {:?}",
old_files, new_files
),
));
}
if !two_way_check(&old_files, &new_files) {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("Files differ: old: {:?}, new: {:?}", old_files, new_files),
));
}
for path in old_files {
let current = fs::read(&path)?;
if current != *old_snap.get(&path).unwrap() {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!("File {:?} has modified data!", path),
));
}
}
Ok(())
}
macro_rules! run_example_named {
($name:literal, $check:literal) => {
paste::item! {
#[test]
#[serial]
fn [< example_ $name >] () {
let repo = setup_test();
let target = repo.path().join(format!("examples/{}", $name));
println!("{:?}", target);
set_current_dir(&target).unwrap();
let snap = snapshot(&target).expect("Could not take snapshot of repo");
let test_bin = PathBuf::from(env!("CARGO_BIN_EXE_kerblam"));
let original_path = env!("PATH");
let code = Command::new("bash")
.args([target.join("run")])
.env("PATH", format!("{}:{}", test_bin.parent().unwrap().to_string_lossy(), original_path))
.output()
.expect("Could not collect child output");
eprintln!(
"Command Output\nSTDOUT:\n{}\n\nSTDERR:\n{}",
String::from_utf8_lossy(&code.stdout),
String::from_utf8_lossy(&code.stderr),
);
if $check == "success" {
assert!(code.status.success());
} else {
assert!(! code.status.success());
}
assert!(check_identical(snap, &target).is_ok());
drop(repo);
}
}
};
}
macro_rules! run_ignored_example_named {
($name:literal, $check:literal) => {
paste::item! {
#[test]
#[serial]
#[ignore] fn [< example_ $name >] () {
let repo = setup_test();
let target = repo.path().join(format!("examples/{}", $name));
println!("{:?}", target);
set_current_dir(&target).unwrap();
let snap = snapshot(&target).expect("Could not take snapshot of repo");
let test_bin = PathBuf::from(env!("CARGO_BIN_EXE_kerblam"));
let original_path = env!("PATH");
let code = Command::new("bash")
.args([target.join("run")])
.env("PATH", format!("{}:{}", test_bin.parent().unwrap().to_string_lossy(), original_path))
.output()
.expect("Could not collect child output");
eprintln!(
"Command Output\nSTDOUT:\n{}\n\nSTDERR:\n{}",
String::from_utf8_lossy(&code.stdout),
String::from_utf8_lossy(&code.stderr),
);
if $check == "success" {
assert!(code.status.success());
} else {
assert!(! code.status.success());
}
assert!(check_identical(snap, &target).is_ok());
drop(repo);
}
}
};
}
run_example_named!("basic_pipes", "success");
run_ignored_example_named!("basic_containers", "success");
run_example_named!("safe_failure", "failure");
run_ignored_example_named!("overview", "success");
run_ignored_example_named!("package", "success");