1 use std::{ 2 env, fs, 3 io::{self, Write}, 4 path::PathBuf, 5 process::{self, Command, ExitStatus}, 6 }; 7 8 fn expected(bin: &str, kind: &str, generated: &[u8], status: ExitStatus) -> Result<(), String> { 9 let mut expected_file = PathBuf::from(format!("expected/{}.{}", bin, kind)); 10 if !expected_file.exists() { 11 expected_file = PathBuf::from(format!( 12 "expected/{}.{}", 13 bin.replace("bins_static", "").replace("bins_dynamic", ""), 14 kind 15 )); 16 } 17 18 let expected = match fs::read(&expected_file) { 19 Ok(ok) => ok, 20 Err(err) => { 21 return Err(format!( 22 "{} failed to read {}: {}", 23 bin, 24 expected_file.display(), 25 err 26 )); 27 } 28 }; 29 30 if expected != generated { 31 println!("# {}: {}: expected #", bin, kind); 32 io::stdout().write(&expected).unwrap(); 33 34 println!("# {}: {}: generated #", bin, kind); 35 io::stdout().write(generated).unwrap(); 36 37 return Err(format!( 38 "{} failed - retcode {}, {} mismatch", 39 bin, status, kind 40 )); 41 } 42 43 Ok(()) 44 } 45 46 fn main() { 47 let mut failures = Vec::new(); 48 49 for bin in env::args().skip(1) { 50 println!("# {} #", bin); 51 52 match Command::new(&bin).arg("test").arg("args").output() { 53 Ok(output) => { 54 if let Err(failure) = expected(&bin, "stdout", &output.stdout, output.status) { 55 println!("{}", failure); 56 failures.push(failure); 57 } 58 59 if let Err(failure) = expected(&bin, "stderr", &output.stderr, output.status) { 60 println!("{}", failure); 61 failures.push(failure); 62 } 63 } 64 Err(err) => { 65 let failure = format!("{}: failed to execute: {}", bin, err); 66 println!("{}", failure); 67 failures.push(failure); 68 } 69 } 70 } 71 72 if !failures.is_empty() { 73 println!("# FAILURES #"); 74 for failure in failures { 75 println!("{}", failure); 76 } 77 process::exit(1); 78 } 79 } 80