headr/tests/cli.rs
2023-09-20 01:55:43 -04:00

422 lines
9.5 KiB
Rust

use assert_cmd::Command;
use predicates::prelude::*;
use rand::{distributions::Alphanumeric, Rng};
use std::io::prelude::*;
use std::{
error::Error,
fs::{self, File},
};
type TestResult = Result<(), Box<dyn Error>>;
const PRG: &str = "headr";
const EMPTY: &str = "./tests/inputs/empty.txt";
const ONE: &str = "./tests/inputs/one.txt";
const TWO: &str = "./tests/inputs/two.txt";
const THREE: &str = "./tests/inputs/three.txt";
const TEN: &str = "./tests/inputs/ten.txt";
// --------------------------------------------------
fn random_string() -> String {
rand::thread_rng()
.sample_iter(&Alphanumeric)
.take(7)
.map(char::from)
.collect()
}
// --------------------------------------------------
fn gen_bad_file() -> String {
loop {
let filename = random_string();
if fs::metadata(&filename).is_err() {
return filename;
}
}
}
// --------------------------------------------------
#[test]
fn dies_bad_bytes() -> TestResult {
let bad = random_string();
let expected = format!("illegal byte count -- {}", &bad);
Command::cargo_bin(PRG)?
.args(&["-c", &bad, EMPTY])
.assert()
.failure()
.stderr(predicate::str::contains(expected));
Ok(())
}
// --------------------------------------------------
#[test]
fn dies_bad_lines() -> TestResult {
let bad = random_string();
let expected = format!("illegal line count -- {}", &bad);
Command::cargo_bin(PRG)?
.args(&["-n", &bad, EMPTY])
.assert()
.failure()
.stderr(predicate::str::contains(expected));
Ok(())
}
// --------------------------------------------------
#[test]
fn dies_bytes_and_lines() -> TestResult {
let msg = "The argument '--lines <LINES>' cannot be \
used with '--bytes <BYTES>'";
Command::cargo_bin(PRG)?
.args(&["-n", "1", "-c", "2"])
.assert()
.failure()
.stderr(predicate::str::contains(msg));
Ok(())
}
// --------------------------------------------------
#[test]
fn skips_bad_file() -> TestResult {
let bad = gen_bad_file();
let expected = format!("{}: .* [(]os error 2[)]", bad);
Command::cargo_bin(PRG)?
.args([EMPTY, &bad, ONE])
.assert()
.stderr(predicate::str::is_match(expected)?);
Ok(())
}
// --------------------------------------------------
fn run(args: &[&str], expected_file: &str) -> TestResult {
// Extra work here due to lossy UTF
let mut file = File::open(expected_file)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
let expected = String::from_utf8_lossy(&buffer);
Command::cargo_bin(PRG)?
.args(args)
.assert()
.success()
.stdout(predicate::eq(&expected.as_bytes() as &[u8]));
Ok(())
}
// --------------------------------------------------
fn run_stdin(
args: &[&str],
input_file: &str,
expected_file: &str,
) -> TestResult {
// Extra work here due to lossy UTF
let mut file = File::open(expected_file)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
let expected = String::from_utf8_lossy(&buffer);
let input = fs::read_to_string(input_file)?;
Command::cargo_bin(PRG)?
.write_stdin(input)
.args(args)
.assert()
.stdout(predicate::eq(&expected.as_bytes() as &[u8]));
Ok(())
}
// --------------------------------------------------
#[test]
fn empty() -> TestResult {
run(&[EMPTY], "tests/expected/empty.txt.out")
}
// --------------------------------------------------
#[test]
fn empty_n2() -> TestResult {
run(&[EMPTY, "-n", "2"], "tests/expected/empty.txt.n2.out")
}
// --------------------------------------------------
#[test]
fn empty_n4() -> TestResult {
run(&[EMPTY, "-n", "4"], "tests/expected/empty.txt.n4.out")
}
// --------------------------------------------------
#[test]
fn empty_c2() -> TestResult {
run(&[EMPTY, "-c", "2"], "tests/expected/empty.txt.c2.out")
}
// --------------------------------------------------
#[test]
fn empty_c4() -> TestResult {
run(&[EMPTY, "-c", "4"], "tests/expected/empty.txt.c4.out")
}
// --------------------------------------------------
#[test]
fn one() -> TestResult {
run(&[ONE], "tests/expected/one.txt.out")
}
#[test]
fn one_n2() -> TestResult {
run(&[ONE, "-n", "2"], "tests/expected/one.txt.n2.out")
}
#[test]
fn one_n4() -> TestResult {
run(&[ONE, "-n", "4"], "tests/expected/one.txt.n4.out")
}
#[test]
fn one_c1() -> TestResult {
run(&[ONE, "-c", "1"], "tests/expected/one.txt.c1.out")
}
#[test]
fn one_c2() -> TestResult {
run(&[ONE, "-c", "2"], "tests/expected/one.txt.c2.out")
}
#[test]
fn one_c4() -> TestResult {
run(&[ONE, "-c", "4"], "tests/expected/one.txt.c4.out")
}
#[test]
fn one_stdin() -> TestResult {
run_stdin(&[], ONE, "tests/expected/one.txt.out")
}
#[test]
fn one_n2_stdin() -> TestResult {
run_stdin(&["-n", "2"], ONE, "tests/expected/one.txt.n2.out")
}
#[test]
fn one_n4_stdin() -> TestResult {
run_stdin(&["-n", "4"], ONE, "tests/expected/one.txt.n4.out")
}
#[test]
fn one_c1_stdin() -> TestResult {
run_stdin(&["-c", "1"], ONE, "tests/expected/one.txt.c1.out")
}
#[test]
fn one_c2_stdin() -> TestResult {
run_stdin(&["-c", "2"], ONE, "tests/expected/one.txt.c2.out")
}
#[test]
fn one_c4_stdin() -> TestResult {
run_stdin(&["-c", "4"], ONE, "tests/expected/one.txt.c4.out")
}
// --------------------------------------------------
#[test]
fn two() -> TestResult {
run(&[TWO], "tests/expected/two.txt.out")
}
#[test]
fn two_n2() -> TestResult {
run(&[TWO, "-n", "2"], "tests/expected/two.txt.n2.out")
}
#[test]
fn two_n4() -> TestResult {
run(&[TWO, "-n", "4"], "tests/expected/two.txt.n4.out")
}
#[test]
fn two_c2() -> TestResult {
run(&[TWO, "-c", "2"], "tests/expected/two.txt.c2.out")
}
#[test]
fn two_c4() -> TestResult {
run(&[TWO, "-c", "4"], "tests/expected/two.txt.c4.out")
}
#[test]
fn two_stdin() -> TestResult {
run_stdin(&[], TWO, "tests/expected/two.txt.out")
}
#[test]
fn two_n2_stdin() -> TestResult {
run_stdin(&["-n", "2"], TWO, "tests/expected/two.txt.n2.out")
}
#[test]
fn two_n4_stdin() -> TestResult {
run_stdin(&["-n", "4"], TWO, "tests/expected/two.txt.n4.out")
}
#[test]
fn two_c2_stdin() -> TestResult {
run_stdin(&["-c", "2"], TWO, "tests/expected/two.txt.c2.out")
}
#[test]
fn two_c4_stdin() -> TestResult {
run_stdin(&["-c", "4"], TWO, "tests/expected/two.txt.c4.out")
}
// --------------------------------------------------
#[test]
fn three() -> TestResult {
run(&[THREE], "tests/expected/three.txt.out")
}
#[test]
fn three_n2() -> TestResult {
run(&[THREE, "-n", "2"], "tests/expected/three.txt.n2.out")
}
#[test]
fn three_n4() -> TestResult {
run(&[THREE, "-n", "4"], "tests/expected/three.txt.n4.out")
}
#[test]
fn three_c2() -> TestResult {
run(&[THREE, "-c", "2"], "tests/expected/three.txt.c2.out")
}
#[test]
fn three_c4() -> TestResult {
run(&[THREE, "-c", "4"], "tests/expected/three.txt.c4.out")
}
#[test]
fn three_stdin() -> TestResult {
run_stdin(&[], THREE, "tests/expected/three.txt.out")
}
#[test]
fn three_n2_stdin() -> TestResult {
run_stdin(&["-n", "2"], THREE, "tests/expected/three.txt.n2.out")
}
#[test]
fn three_n4_stdin() -> TestResult {
run_stdin(&["-n", "4"], THREE, "tests/expected/three.txt.n4.out")
}
#[test]
fn three_c2_stdin() -> TestResult {
run_stdin(&["-c", "2"], THREE, "tests/expected/three.txt.c2.out")
}
#[test]
fn three_c4_stdin() -> TestResult {
run_stdin(&["-c", "4"], THREE, "tests/expected/three.txt.c4.out")
}
// --------------------------------------------------
#[test]
fn ten() -> TestResult {
run(&[TEN], "tests/expected/ten.txt.out")
}
#[test]
fn ten_n2() -> TestResult {
run(&[TEN, "-n", "2"], "tests/expected/ten.txt.n2.out")
}
#[test]
fn ten_n4() -> TestResult {
run(&[TEN, "-n", "4"], "tests/expected/ten.txt.n4.out")
}
#[test]
fn ten_c2() -> TestResult {
run(&[TEN, "-c", "2"], "tests/expected/ten.txt.c2.out")
}
#[test]
fn ten_c4() -> TestResult {
run(&[TEN, "-c", "4"], "tests/expected/ten.txt.c4.out")
}
#[test]
fn ten_stdin() -> TestResult {
run_stdin(&[], TEN, "tests/expected/ten.txt.out")
}
#[test]
fn ten_n2_stdin() -> TestResult {
run_stdin(&["-n", "2"], TEN, "tests/expected/ten.txt.n2.out")
}
#[test]
fn ten_n4_stdin() -> TestResult {
run_stdin(&["-n", "4"], TEN, "tests/expected/ten.txt.n4.out")
}
#[test]
fn ten_c2_stdin() -> TestResult {
run_stdin(&["-c", "2"], TEN, "tests/expected/ten.txt.c2.out")
}
#[test]
fn ten_c4_stdin() -> TestResult {
run_stdin(&["-c", "4"], TEN, "tests/expected/ten.txt.c4.out")
}
// --------------------------------------------------
#[test]
fn multiple_files() -> TestResult {
run(&[EMPTY, ONE, TWO, THREE, TEN], "tests/expected/all.out")
}
#[test]
fn multiple_files_n2() -> TestResult {
run(
&[EMPTY, ONE, TWO, THREE, TEN, "-n", "2"],
"tests/expected/all.n2.out",
)
}
#[test]
fn multiple_files_n4() -> TestResult {
run(
&["-n", "4", EMPTY, ONE, TWO, THREE, TEN],
"tests/expected/all.n4.out",
)
}
#[test]
fn multiple_files_c1() -> TestResult {
run(
&[EMPTY, ONE, TWO, THREE, TEN, "-c", "1"],
"tests/expected/all.c1.out",
)
}
#[test]
fn multiple_files_c2() -> TestResult {
run(
&[EMPTY, ONE, TWO, THREE, TEN, "-c", "2"],
"tests/expected/all.c2.out",
)
}
#[test]
fn multiple_files_c4() -> TestResult {
run(
&["-c", "4", EMPTY, ONE, TWO, THREE, TEN],
"tests/expected/all.c4.out",
)
}