#![warn(anonymous_parameters, bad_style, missing_docs)]
#![warn(unused, unused_extern_crates, unused_import_braces, unused_qualifications)]
#![warn(unsafe_code)]
use std::env;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::process;
const DATE_RE: &str = "[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]:[0-5][0-9]";
const VERSION_RE: &str = "[0-9]+\\.[0-9]+\\.[0-9]+";
fn self_dir() -> PathBuf {
let self_exe = env::current_exe().expect("Cannot get self's executable path");
let dir = self_exe.parent().expect("Cannot get self's directory");
assert!(dir.ends_with("target/debug/deps") || dir.ends_with("target/release/deps"));
dir.to_owned()
}
fn bin_path<P: AsRef<Path>>(name: P) -> PathBuf {
let test_dir = self_dir();
let debug_or_release_dir = test_dir.parent().expect("Failed to get parent directory");
debug_or_release_dir.join(name).with_extension(env::consts::EXE_EXTENSION)
}
fn src_path(name: &str) -> PathBuf {
let test_dir = self_dir();
let debug_or_release_dir = test_dir.parent().expect("Failed to get parent directory");
let target_dir = debug_or_release_dir.parent().expect("Failed to get parent directory");
let dir = target_dir.parent().expect("Failed to get parent directory");
assert!(dir.join("Cargo.toml").exists());
dir.join(name)
}
fn src_str(p: &str) -> String {
src_path(p).to_str().expect("Need paths to be valid strings").to_owned()
}
enum Behavior {
Null,
File(PathBuf),
}
fn read_golden(path: &Path) -> String {
let mut f = File::open(path).expect("Failed to open golden data file");
let mut golden = vec![];
f.read_to_end(&mut golden).expect("Failed to read golden data file");
let raw = String::from_utf8(golden).expect("Golden data file is not valid UTF-8");
let golden = if cfg!(target_os = "windows") { raw.replace("\r\n", "\n") } else { raw };
let version_re = regex::Regex::new(VERSION_RE).unwrap();
assert!(
!version_re.is_match(&golden),
"Golden file {} contains a version number",
path.display()
);
let date_re = regex::Regex::new(DATE_RE).unwrap();
assert!(!date_re.is_match(&golden), "Golden file {} contains a date", path.display());
golden
}
fn apply_mocks(input: String) -> String {
let version_re = regex::Regex::new(VERSION_RE).unwrap();
let input = version_re.replace_all(&input, "X.Y.Z").to_owned();
let date_re = regex::Regex::new(DATE_RE).unwrap();
date_re.replace_all(&input, "YYYY-MM-DD HH:MM").into()
}
fn check<P: AsRef<Path>>(
bin: P,
args: &[&str],
exp_code: i32,
stdin_behavior: Behavior,
stdout_behavior: Behavior,
stderr_behavior: Behavior,
) {
let golden_stdin = match stdin_behavior {
Behavior::Null => process::Stdio::null(),
Behavior::File(path) => File::open(path).unwrap().into(),
};
let exp_stdout = match stdout_behavior {
Behavior::Null => "".to_owned(),
Behavior::File(path) => read_golden(&path),
};
let exp_stderr = match stderr_behavior {
Behavior::Null => "".to_owned(),
Behavior::File(path) => read_golden(&path),
};
let result = process::Command::new(bin.as_ref())
.args(args)
.stdin(golden_stdin)
.output()
.expect("Failed to execute subprocess");
let code = result.status.code().expect("Subprocess didn't exit cleanly");
let stdout =
apply_mocks(String::from_utf8(result.stdout).expect("Stdout not is not valid UTF-8"));
let stderr =
apply_mocks(String::from_utf8(result.stderr).expect("Stderr not is not valid UTF-8"));
if exp_code != code || exp_stdout != stdout || exp_stderr != stderr {
eprintln!("Exit code: {}", code);
eprintln!("stdout:\n{}", stdout);
eprintln!("stderr:\n{}", stderr);
assert_eq!(exp_code, code);
assert_eq!(exp_stdout, stdout);
assert_eq!(exp_stderr, stderr);
}
}
#[test]
fn test_example_complete() {
}
#[test]
fn test_example_custom_commands() {
check(
bin_path("examples/custom-commands"),
&[],
0,
Behavior::Null,
Behavior::File(src_path("core/examples/custom-commands.out")),
Behavior::Null,
);
}
#[test]
fn test_example_guess() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/demo-guess.bas")],
0,
Behavior::File(src_path("core/tests/demo-guess.in")),
Behavior::File(src_path("core/tests/demo-guess.out")),
Behavior::Null,
);
}
#[test]
fn test_example_hello() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/demo-hello.bas")],
0,
Behavior::File(src_path("core/tests/demo-hello.in")),
Behavior::File(src_path("core/tests/demo-hello.out")),
Behavior::Null,
);
}
#[test]
fn test_example_tour() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/demo-tour.bas")],
0,
Behavior::File(src_path("core/tests/demo-tour.in")),
Behavior::File(src_path("core/tests/demo-tour.out")),
Behavior::Null,
);
}
#[test]
fn test_example_minimal() {
check(
bin_path("examples/minimal"),
&[],
0,
Behavior::Null,
Behavior::File(src_path("core/examples/minimal.out")),
Behavior::Null,
);
}
#[test]
fn test_lang_control_flow() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/control-flow.bas")],
0,
Behavior::Null,
Behavior::File(src_path("core/tests/control-flow.out")),
Behavior::Null,
);
}
#[test]
fn test_lang_exec_error() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/exec-error.bas")],
1,
Behavior::Null,
Behavior::File(src_path("core/tests/exec-error.out")),
Behavior::File(src_path("core/tests/exec-error.err")),
);
}
#[test]
fn test_lang_hello() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/hello.bas")],
0,
Behavior::Null,
Behavior::File(src_path("core/tests/hello.out")),
Behavior::Null,
);
}
#[test]
fn test_lang_lexer_error() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/lexer-error.bas")],
1,
Behavior::Null,
Behavior::File(src_path("core/tests/lexer-error.out")),
Behavior::File(src_path("core/tests/lexer-error.err")),
);
}
#[test]
fn test_lang_no_repl_commands() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/no-repl-commands.bas")],
1,
Behavior::Null,
Behavior::File(src_path("core/tests/no-repl-commands.out")),
Behavior::File(src_path("core/tests/no-repl-commands.err")),
);
}
#[test]
fn test_lang_parser_error() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/parser-error.bas")],
1,
Behavior::Null,
Behavior::File(src_path("core/tests/parser-error.out")),
Behavior::File(src_path("core/tests/parser-error.err")),
);
}
#[test]
fn test_lang_types() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/types.bas")],
0,
Behavior::Null,
Behavior::File(src_path("core/tests/types.out")),
Behavior::Null,
);
}
#[test]
fn test_lang_utf8() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/utf8.bas")],
0,
Behavior::File(src_path("core/tests/utf8.in")),
Behavior::File(src_path("core/tests/utf8.out")),
Behavior::Null,
);
}
#[test]
fn test_lang_yes_no() {
check(
bin_path("examples/complete"),
&[&src_str("core/tests/yes-no.bas")],
0,
Behavior::File(src_path("core/tests/yes-no.in")),
Behavior::File(src_path("core/tests/yes-no.out")),
Behavior::Null,
);
}