#![deny(warnings, clippy::pedantic, clippy::dbg_macro)] #![forbid(unsafe_code)] #![allow(clippy::too_many_lines)] #![deny(clippy::semicolon_if_nothing_returned)] use std::{ borrow::Cow, env::{self, consts}, fs::{self, File}, io::{prelude::*, ErrorKind}, str, }; use tryrun::prelude::*; #[path = "ui/output.rs"] mod output; #[test] fn ui() { const STDOUT: &[u8] = br#"args: ArgsOs { inner: ["./output"] } stdin: [stdin end] stdout "#; const STDERR: &[u8] = b"stderr\n"; Command::rustc() .args(&["tests/ui/output.rs", concat!("--out-dir=", env!("OUT_DIR"))]) .run_pass(OutputStr::empty(), normalize::noop()); for will_exist in &[ concat!(env!("OUT_DIR"), "/will_exist.stdout"), concat!(env!("OUT_DIR"), "/will_exist.stderr"), ] { fs::remove_file(will_exist) .or_else(|e| { if e.kind() == ErrorKind::NotFound { Ok(()) } else { Err(e) } }) .unwrap(); } fs::write(concat!(env!("OUT_DIR"), "/output.stdout"), STDOUT).unwrap(); fs::write(concat!(env!("OUT_DIR"), "/output-ok.stdout"), STDOUT).unwrap(); fs::write(concat!(env!("OUT_DIR"), "/output.stderr"), STDERR).unwrap(); fs::write(concat!(env!("OUT_DIR"), "/output-ok.stderr"), STDERR).unwrap(); for empty in &[ concat!(env!("OUT_DIR"), "/empty.stdout"), concat!(env!("OUT_DIR"), "/empty.stderr"), ] { File::create(empty).unwrap(); } let mut ui = Command::new(env::current_exe().unwrap()); ui.args(&["--ignored", "--test-threads=1"]) .env_clear() .env("PATH", env::var_os("PATH").unwrap_or_default()) .env("DEBUG_ASSERTIONS", cfg!(debug_assertions).to_string()) .current_dir(env!("OUT_DIR")); let normalizer = normalize::stdout(|output| { let s = str::from_utf8(output).unwrap(); let mut result = String::new(); for s in s.lines() { const DIFF_B_START: &str = "b/"; const DIFF_QUOTE_B_START: &str = r#""b/"#; const EXIT_STATUS_START: &str = "[exit"; fn normalize_path_separators(s: &str) -> Cow<'_, str> { if cfg!(unix) { s.into() } else { s.replace(r"tests\ui.rs", "tests/ui.rs").into() } } if s.starts_with("test result: FAILED.") { result.pop().unwrap(); break; } if s.starts_with(r#"failed to open stdout file ""#) || s.starts_with(r#"failed to open stderr file ""#) { result.push_str("failed to open $STDIO file $NOT_FOUND_ERROR"); result.push('\n'); } else if let Some(prefix) = s.find(DIFF_QUOTE_B_START).or_else(|| s.find(DIFF_B_START)) { result.push_str(&s[..prefix]); result.push_str(DIFF_B_START); result.push('\n'); } else if let Some(prefix) = s.find(EXIT_STATUS_START) { const EXIT_CODE: &str = "$EXIT_CODE"; let status = &s[prefix + EXIT_STATUS_START.len()..]; assert!(status.ends_with(": 123]") || status.ends_with(": 0]")); let suffix = status.rfind(']').unwrap() + 1; result.push_str(&s[..prefix]); result.push_str(EXIT_CODE); result.push_str(&status[suffix..]); result.push('\n'); } else { result.push_str(&normalize_path_separators(s)); result.push('\n'); } } *output = result.into_bytes(); output }); ui.exit_with_code(OutputStr::stdout("tests/ui/fail.stdout"), normalizer, 101); let assert_file_content = |path, content: &[_]| { assert!(File::open(path) .unwrap() .bytes() .map(Result::unwrap) .eq(content.iter().copied())); }; let assert_inexisted = || { for entry in fs::read_dir(env!("OUT_DIR")).unwrap() { let path = entry.unwrap().path(); assert!( (path.file_stem() == Some("output".as_ref()) || path.file_stem() == Some("output-ok".as_ref()) || path.file_stem() == Some("empty".as_ref()) || path.file_stem() == Some("will_exist".as_ref())) && (path.extension() == Some("stdout".as_ref()) || path.extension() == Some("stderr".as_ref()) || path.extension() == (!consts::EXE_EXTENSION.is_empty()) .then(|| consts::EXE_EXTENSION.as_ref()) || path.extension() == Some("pdb".as_ref())), "stray file: {:?}", path ); } }; let not_blessed = || { assert_file_content(concat!(env!("OUT_DIR"), "/output.stdout"), STDOUT); assert_file_content(concat!(env!("OUT_DIR"), "/output.stderr"), STDERR); assert_file_content(concat!(env!("OUT_DIR"), "/output-ok.stdout"), STDOUT); assert_file_content(concat!(env!("OUT_DIR"), "/output-ok.stderr"), STDERR); assert_eq!( fs::metadata(concat!(env!("OUT_DIR"), "/empty.stdout")) .unwrap() .len(), 0 ); assert_eq!( fs::metadata(concat!(env!("OUT_DIR"), "/empty.stderr")) .unwrap() .len(), 0 ); assert_inexisted(); }; not_blessed(); #[cfg(unix)] ui.env(output::NON_UNICODE, "").exit_with_code( OutputStr::stdout("tests/ui/fail_non_unicode.stdout"), normalizer, 101, ); ui.env_remove(output::NON_UNICODE) .env("GIT", "./output") .exit_with_code( OutputStr::stdout("tests/ui/diff_fail_zero.stdout"), normalizer, 101, ); not_blessed(); ui.env(output::FAIL, "").exit_with_code( OutputStr::stdout("tests/ui/diff_fail.stdout"), normalizer, 101, ); not_blessed(); ui.env(output::NO_STDERR, "").exit_with_code( OutputStr::stdout("tests/ui/fake_diff.stdout"), normalizer, 101, ); not_blessed(); ui.env(output::NO_STDOUT, "").exit_with_code( OutputStr::stdout("tests/ui/fake_diff_no_output.stdout"), normalizer, 101, ); not_blessed(); ui.env_remove(output::NO_STDERR).exit_with_code( OutputStr::stdout("tests/ui/diff_fail_no_stdout.stdout"), normalizer, 101, ); not_blessed(); ui.env_remove("GIT") .env_remove(output::FAIL) .env_remove(output::NO_STDOUT) .env("TRYRUN", "garbage") .exit_with_code(OutputStr::stdout("tests/ui/fail.stdout"), normalizer, 101); not_blessed(); ui.env("TRYRUN", "bless").exit_with_code( OutputStr::stdout("tests/ui/bless.stdout"), normalizer, 101, ); let blessed = || { assert_file_content( concat!(env!("OUT_DIR"), "/output.stdout"), br#"args: ArgsOs { inner: ["./output"] } stdin: [stdin end] stdout updated stdout "#, ); assert_file_content( concat!(env!("OUT_DIR"), "/output.stderr"), b"stderr\nupdated stderr\n", ); assert_file_content(concat!(env!("OUT_DIR"), "/output-ok.stdout"), STDOUT); assert_file_content(concat!(env!("OUT_DIR"), "/output-ok.stderr"), STDERR); assert_file_content(concat!(env!("OUT_DIR"), "/empty.stdout"), STDOUT); assert_file_content(concat!(env!("OUT_DIR"), "/empty.stderr"), STDERR); assert_inexisted(); }; blessed(); ui.env_remove("TRYRUN").exit_with_code( OutputStr::stdout("tests/ui/bless.stdout"), normalizer, 101, ); blessed(); } #[test] #[ignore] fn probe() { assert!(Command::new("./output") .env_remove(output::FAIL) .probe() .success()); assert_eq!( Command::new("./output") .env(output::FAIL, "") .probe() .code() .unwrap(), output::EXIT_CODE ); } #[test] #[ignore] fn fail_to_open_output() { Command::new("./output").run_pass(tryrun::output!("will_exist"), normalize::noop()); } #[test] #[ignore] fn output_ok() { Command::new("./output") .env_clear() .env( output::DEBUG_ASSERTIONS, env::var_os(output::DEBUG_ASSERTIONS).unwrap(), ) .run_pass(tryrun::output!("output-ok"), normalize::noop()); } #[test] #[ignore] fn output_stdout_stderr() { Command::new("./output") .env(output::UPDATE_STDOUT, "") .env(output::UPDATE_STDERR, "") .run_pass(tryrun::output!("output"), normalize::noop()); } #[test] #[ignore] fn output_stdout() { Command::new("./output") .env(output::UPDATE_STDOUT, "") .run_pass(OutputStr::stdout("output.stdout"), normalize::noop()); } #[test] #[ignore] fn output_stderr() { let mut hit = Some(()); Command::new("./output") .env(output::UPDATE_STDERR, "") .run_pass( OutputStr::stderr("output.stderr"), normalize::stdout(|output| { hit.take().unwrap(); for byte in output.iter_mut() { if *byte == 0xff { *byte = b'?'; } } output }), ); assert!(hit.is_none()); } #[test] #[ignore] fn output_stdout_stderr_fail() { Command::new("./output") .env(output::FAIL, "") .env(output::UPDATE_STDOUT, "") .env(output::UPDATE_STDERR, "") .exit_with_code( tryrun::output!("output"), normalize::noop(), output::EXIT_CODE, ); } #[test] #[ignore] fn output_stdout_stderr_status() { Command::new("./output") .env(output::FAIL, "") .env(output::UPDATE_STDOUT, "") .env(output::UPDATE_STDERR, "") .try_run(tryrun::output!("output"), normalize::noop(), |s| { s.code() == Some(output::EXIT_CODE) }); } #[test] #[ignore] fn output_fail() { Command::new("./output") .env(output::FAIL, "") .run_pass(tryrun::output!("inexisted"), normalize::noop()); } #[test] #[ignore] fn output_status() { Command::new("./output").env(output::FAIL, "").try_run( tryrun::output!("inexisted"), normalize::noop(), |_| false, ); } #[test] #[ignore] fn output_fail_update() { Command::new("./output") .env(output::FAIL, "") .env(output::UPDATE_STDOUT, "") .env(output::UPDATE_STDERR, "") .run_pass(tryrun::output!("inexisted"), normalize::noop()); } #[test] #[ignore] fn output_fail_empty_output() { Command::new("./output") .env(output::FAIL, "") .env(output::NO_STDOUT, "") .env(output::NO_STDERR, "") .run_pass(OutputStr::empty(), normalize::noop()); } #[test] #[ignore] fn empty_stdout_stderr() { Command::new("./output") .env(output::NO_STDOUT, "") .env(output::NO_STDERR, "") .run_pass(tryrun::output!("empty"), normalize::noop()); } #[test] #[ignore] fn normalize_to_empty_stdout_stderr() { Command::new("./output").run_pass(tryrun::output!("empty"), normalize::closure(|_| &[])); } #[test] #[ignore] fn empty_stdout() { Command::new("./output") .env(output::NO_STDOUT, "") .run_pass(tryrun::output!("empty"), normalize::noop()); } #[test] #[ignore] fn normalize_to_empty_stdout() { Command::new("./output").run_pass(tryrun::output!("empty"), normalize::stdout(|_| &[])); } #[test] #[ignore] fn empty_stderr() { Command::new("./output") .env(output::NO_STDERR, "") .run_pass(tryrun::output!("empty"), normalize::noop()); } #[test] #[ignore] fn normalize_to_empty_stderr() { Command::new("./output").run_pass(tryrun::output!("empty"), normalize::stderr(|_| &[])); }