#![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(|_| &[]));
}