use rstest::rstest;
use rstest_reuse::{apply, template};
use uutests::at_and_ucmd;
use uutests::new_ucmd;
use uutests::util::TestScenario;
use uutests::util::UCommand;
use uutests::util::log_info;
use uutests::util_name;
#[template]
#[rstest]
#[case::sysv("sysv")]
#[case::bsd("bsd")]
#[case::crc("crc")]
#[case::md5("md5")]
#[case::sha1("sha1")]
#[case::sha224("sha224")]
#[case::sha256("sha256")]
#[case::sha384("sha384")]
#[case::sha512("sha512")]
#[case::blake2b("blake2b")]
#[case::blake3("blake3")]
#[case::sm3("sm3")]
fn test_all_algos(#[case] algo: &str) {}
#[template]
#[rstest]
fn test_sha(#[values("sha2", "sha3")] algo: &str, #[values(224, 256, 384, 512)] len: u32) {}
fn sha_fixture_name(algo: &str, len: u32, prefix: &str, suffix: &str) -> String {
match algo {
"sha2" => format!("{prefix}sha{len}{suffix}"),
_ => format!("{prefix}sha3_{len}{suffix}"),
}
}
#[test]
fn test_invalid_arg() {
new_ucmd!().arg("--definitely-invalid").fails_with_code(1);
}
#[test]
fn test_single_file() {
new_ucmd!()
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("crc_single_file.expected");
}
#[test]
fn test_multiple_files() {
new_ucmd!()
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture("crc_multiple_files.expected");
}
#[test]
fn test_stdin() {
new_ucmd!()
.pipe_in_fixture("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("crc_stdin.expected");
}
#[test]
fn test_empty_file() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("a");
ucmd.arg("a")
.succeeds()
.no_stderr()
.normalized_newlines_stdout_is("4294967295 0 a\n");
}
#[test]
fn test_arg_overrides_stdin() {
let (at, mut ucmd) = at_and_ucmd!();
let input = "foobarfoobar";
at.touch("a");
ucmd.arg("a")
.pipe_in(input.as_bytes())
.ignore_stdin_write_error()
.succeeds()
.no_stderr()
.normalized_newlines_stdout_is("4294967295 0 a\n");
}
#[test]
fn test_nonexisting_file() {
let file_name = "asdf";
new_ucmd!()
.arg(file_name)
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("cksum: {file_name}: No such file or directory"));
}
#[test]
fn test_nonexisting_file_out() {
let (at, mut ucmd) = at_and_ucmd!();
at.write(
"f",
"MD5 (nonexistent) = e5773576fc75ff0f8eba14f61587ae28\n",
);
ucmd.arg("-c")
.arg("f")
.fails()
.stdout_contains("nonexistent: FAILED open or read")
.stderr_contains("cksum: nonexistent: No such file or directory");
}
#[test]
fn test_one_nonexisting_file() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("abc.txt");
at.touch("xyz.txt");
ucmd.arg("abc.txt")
.arg("asdf.txt")
.arg("xyz.txt")
.fails()
.stdout_contains_line("4294967295 0 xyz.txt")
.stderr_contains("asdf.txt: No such file or directory")
.stdout_contains_line("4294967295 0 abc.txt");
}
#[test]
fn test_crc_for_bigger_than_32_bytes() {
let result = new_ucmd!().arg("chars.txt").succeeds();
let mut stdout_split = result.stdout_str().split(' ');
let cksum: i64 = stdout_split.next().unwrap().parse().unwrap();
let bytes_cnt: i64 = stdout_split.next().unwrap().parse().unwrap();
assert_eq!(cksum, 586_047_089);
assert_eq!(bytes_cnt, 16);
}
#[test]
fn test_stdin_larger_than_128_bytes() {
let result = new_ucmd!().arg("larger_than_2056_bytes.txt").succeeds();
let mut stdout_split = result.stdout_str().split(' ');
let cksum: i64 = stdout_split.next().unwrap().parse().unwrap();
let bytes_cnt: i64 = stdout_split.next().unwrap().parse().unwrap();
assert_eq!(cksum, 945_881_979);
assert_eq!(bytes_cnt, 2058);
}
#[test]
fn test_repeated_flags() {
new_ucmd!()
.arg("-a")
.arg("sha1")
.arg("--algo=sha256")
.arg("-a=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("md5_single_file.expected");
}
#[test]
fn test_tag_after_untagged() {
new_ucmd!()
.arg("--untagged")
.arg("--tag")
.arg("-a=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("md5_single_file.expected");
}
#[apply(test_all_algos)]
fn test_algorithm_single_file(#[case] algo: &str) {
for option in ["-a", "--algorithm"] {
new_ucmd!()
.arg(format!("{option}={algo}"))
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(format!("{algo}_single_file.expected"));
}
}
#[apply(test_all_algos)]
fn test_algorithm_multiple_files(#[case] algo: &str) {
for option in ["-a", "--algorithm"] {
new_ucmd!()
.arg(format!("{option}={algo}"))
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture(format!("{algo}_multiple_files.expected"));
}
}
#[apply(test_all_algos)]
fn test_algorithm_stdin(#[case] algo: &str) {
for option in ["-a", "--algorithm"] {
new_ucmd!()
.arg(format!("{option}={algo}"))
.pipe_in_fixture("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(format!("{algo}_stdin.expected"));
}
}
#[test]
fn test_untagged_single_file() {
new_ucmd!()
.arg("--untagged")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("untagged/crc_single_file.expected");
}
#[test]
fn test_untagged_multiple_files() {
new_ucmd!()
.arg("--untagged")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture("untagged/crc_multiple_files.expected");
}
#[test]
fn test_untagged_stdin() {
new_ucmd!()
.arg("--untagged")
.pipe_in_fixture("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("untagged/crc_stdin.expected");
}
#[apply(test_all_algos)]
fn test_untagged_algorithm_single_file(#[case] algo: &str) {
new_ucmd!()
.arg("--untagged")
.arg(format!("--algorithm={algo}"))
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(format!("untagged/{algo}_single_file.expected"));
}
#[test]
fn test_tag_short() {
new_ucmd!()
.arg("-t")
.arg("--untagged")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is("cd724690f7dc61775dfac400a71f2caa lorem_ipsum.txt\n");
}
#[test]
fn test_untagged_algorithm_after_tag() {
new_ucmd!()
.arg("--tag")
.arg("--untagged")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("untagged/md5_single_file.expected");
}
#[apply(test_all_algos)]
fn test_untagged_algorithm_multiple_files(#[case] algo: &str) {
new_ucmd!()
.arg("--untagged")
.arg(format!("--algorithm={algo}"))
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture(format!("untagged/{algo}_multiple_files.expected"));
}
#[apply(test_all_algos)]
fn test_untagged_algorithm_stdin(#[case] algo: &str) {
new_ucmd!()
.arg("--untagged")
.arg(format!("--algorithm={algo}"))
.pipe_in_fixture("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(format!("untagged/{algo}_stdin.expected"));
}
#[test]
fn test_sha_length_invalid() {
for algo in ["sha2", "sha3"] {
for l in ["0", "00", "13", "56", "99999999999999999999999999"] {
new_ucmd!()
.arg("--algorithm")
.arg(algo)
.arg("--length")
.arg(l)
.arg("/dev/null")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("invalid length: '{l}'"))
.stderr_contains(format!(
"digest length for '{}' must be 224, 256, 384, or 512",
algo.to_ascii_uppercase()
));
new_ucmd!()
.arg("--algorithm")
.arg(algo)
.arg("--length")
.arg(l)
.arg("/dev/null")
.arg("--check")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("invalid length: '{l}'"))
.stderr_contains(format!(
"digest length for '{}' must be 224, 256, 384, or 512",
algo.to_ascii_uppercase()
));
}
for l in ["512x", "x512", "512x512"] {
new_ucmd!()
.arg("--algorithm")
.arg(algo)
.arg("--length")
.arg(l)
.arg("/dev/null")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("invalid length: '{l}'"));
new_ucmd!()
.arg("--algorithm")
.arg(algo)
.arg("--length")
.arg(l)
.arg("/dev/null")
.arg("--check")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("invalid length: '{l}'"));
}
}
}
#[test]
fn test_sha_missing_length() {
for algo in ["sha2", "sha3"] {
new_ucmd!()
.arg("--algorithm")
.arg(algo)
.arg("lorem_ipsum.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!(
"--algorithm={algo} requires specifying --length 224, 256, 384, or 512"
));
}
}
fn sha_cmd(algo: &str, len: u32) -> UCommand {
let mut ucmd = new_ucmd!();
ucmd.arg(format!("--algorithm={algo}"))
.arg(format!("--length={len}"));
ucmd
}
#[apply(test_sha)]
fn test_sha_single_file(algo: &str, len: u32) {
sha_cmd(algo, len)
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(sha_fixture_name(algo, len, "", "_single_file.expected"));
}
#[apply(test_sha)]
fn test_sha_multiple_files(algo: &str, len: u32) {
sha_cmd(algo, len)
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture(sha_fixture_name(algo, len, "", "_multiple_files.expected"));
}
#[apply(test_sha)]
fn test_sha_stdin(algo: &str, len: u32) {
sha_cmd(algo, len)
.pipe_in_fixture("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(sha_fixture_name(algo, len, "", "_stdin.expected"));
}
#[apply(test_sha)]
fn test_untagged_sha_single_file(algo: &str, len: u32) {
sha_cmd(algo, len)
.arg("--untagged")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(sha_fixture_name(
algo,
len,
"untagged/",
"_single_file.expected",
));
}
#[apply(test_sha)]
fn test_untagged_sha_multiple_files(algo: &str, len: u32) {
sha_cmd(algo, len)
.arg("--untagged")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture(sha_fixture_name(
algo,
len,
"untagged/",
"_multiple_files.expected",
));
}
#[apply(test_sha)]
fn test_untagged_sha_stdin(algo: &str, len: u32) {
sha_cmd(algo, len)
.arg("--untagged")
.pipe_in_fixture("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture(sha_fixture_name(algo, len, "untagged/", "_stdin.expected"));
}
#[apply(test_sha)]
fn test_check_tagged_sha_single_file(algo: &str, len: u32) {
new_ucmd!()
.arg("--check")
.arg(sha_fixture_name(algo, len, "", "_single_file.expected"))
.succeeds()
.stdout_is("lorem_ipsum.txt: OK\n");
}
#[apply(test_sha)]
fn test_check_tagged_sha_multiple_files(algo: &str, len: u32) {
new_ucmd!()
.arg("--check")
.arg(sha_fixture_name(algo, len, "", "_multiple_files.expected"))
.succeeds()
.stdout_contains("lorem_ipsum.txt: OK\n")
.stdout_contains("alice_in_wonderland.txt: OK\n");
}
#[apply(test_sha)]
fn test_check_untagged_sha_single_file(algo: &str, len: u32) {
new_ucmd!()
.arg("--check")
.arg(format!("--algorithm={algo}"))
.arg(sha_fixture_name(
algo,
len,
"untagged/",
"_single_file.expected",
))
.succeeds()
.stdout_is("lorem_ipsum.txt: OK\n");
}
#[apply(test_sha)]
fn test_check_untagged_sha_multiple_files(algo: &str, len: u32) {
new_ucmd!()
.arg("--check")
.arg(format!("--algorithm={algo}"))
.arg(sha_fixture_name(
algo,
len,
"untagged/",
"_multiple_files.expected",
))
.succeeds()
.stdout_contains("lorem_ipsum.txt: OK\n")
.stdout_contains("alice_in_wonderland.txt: OK\n");
}
#[rstest]
#[case::sha2("sha2", &[
"d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
])]
#[case::sha3("sha3", &[
"6b4e03423667dbb73b6e15454f0eb1abd4597f9a1b078e3f5b5a6bc7",
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
"0c63a75b845e4f7d01107d852e4c2485c51a50aaaa94fc61995e71bbee983a2ac3713831264adb47fb6bd1e058d5f004",
"a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26"
])]
fn test_check_untagged_sha_invalid_length(#[case] algo: &str, #[case] digests: &[&str; 4]) {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("a");
at.touch("b");
at.touch("c");
at.touch("d");
at.touch("e");
let invalid = "xxxx e";
ucmd.arg("-a")
.arg(algo)
.arg("-c")
.pipe_in(format!(
"{} a\n{} b\n{} c\n{} d\n{invalid}",
digests[0], digests[1], digests[2], digests[3]
))
.succeeds()
.stdout_contains("a: OK")
.stdout_contains("b: OK")
.stdout_contains("c: OK")
.stdout_contains("d: OK")
.stdout_does_not_contain("e: FAILED")
.stderr_contains("improperly formatted");
}
#[test]
fn test_check_sha2_tagged_variant() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
let checksum_lines = [
(
"SHA224",
"SHA2-224",
"(f) = d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f",
),
(
"SHA256",
"SHA2-256",
"(f) = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
),
(
"SHA384",
"SHA2-384",
"(f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
),
(
"SHA512",
"SHA2-512",
"(f) = cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
),
];
for (basic, variant, digest) in checksum_lines {
let stdin = format!("{basic} {digest}");
log_info("stdin is: ", &stdin);
scene
.ucmd()
.arg("--check")
.arg("--algorithm=sha2")
.pipe_in(stdin)
.succeeds()
.stdout_is("f: OK\n");
let stdin = format!("{variant} {digest}");
log_info("stdin is: ", &stdin);
scene
.ucmd()
.arg("--check")
.arg("--algorithm=sha2")
.pipe_in(stdin)
.succeeds()
.stdout_is("f: OK\n");
}
}
#[test]
fn test_check_sha2_tagged_missing_hint() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("a");
at.touch("b");
let invalid_sha224 = "SHA2 (a) = d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f";
let invalid = "SHA2 (b) = xxxx";
ucmd.arg("-c")
.pipe_in(format!("{invalid_sha224}\n{invalid}"))
.fails()
.stderr_contains("no properly formatted checksum lines found");
}
#[rstest]
#[case::md5("md5", "d41d8cd98f00b204e9800998ecf8427e")]
#[case::sha1("sha1", "da39a3ee5e6b4b0d3255bfef95601890afd80709")]
#[case::sha224("sha224", "d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f")]
#[case::sha256(
"sha256",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)]
#[case::sha384(
"sha384",
"38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b"
)]
#[case::sha512(
"sha512",
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
)]
#[case::sm3(
"sm3",
"1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b"
)]
fn test_check_untagged_with_invalid_length(#[case] algo: &str, #[case] digest: &str) {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("a");
at.touch("b");
let good_checksum = format!("{digest} a");
let invalid_checksum = "e3b0 b";
ucmd.arg("-a")
.arg(algo)
.arg("-c")
.pipe_in(format!("{invalid_checksum}\n{good_checksum}"))
.succeeds()
.stdout_contains("a: OK")
.stdout_does_not_contain("b: FAILED")
.stderr_contains("WARNING: 1 line is improperly formatted");
}
#[test]
fn test_check_algo() {
for algo in ["bsd", "sysv", "crc", "crc32b"] {
new_ucmd!()
.arg("-a")
.arg(algo)
.arg("--check")
.arg("lorem_ipsum.txt")
.fails()
.no_stdout()
.stderr_contains(
"cksum: --check is not supported with --algorithm={bsd,sysv,crc,crc32b}",
);
}
}
#[test]
fn test_length_with_wrong_algorithm() {
new_ucmd!()
.arg("--length=16")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains(
"cksum: --length is only supported with --algorithm blake2b, sha2, or sha3",
);
new_ucmd!()
.arg("--length=16")
.arg("--algorithm=md5")
.arg("-c")
.arg("foo.sums")
.fails_with_code(1)
.no_stdout()
.stderr_contains(
"cksum: --length is only supported with --algorithm blake2b, sha2, or sha3",
);
}
#[test]
fn test_length_is_zero_with_wrong_algorithm() {
for algo in ["md5", "crc", "sha1", "sha224", "sha256", "sha384", "sha512"] {
new_ucmd!()
.arg("--length=0")
.args(&["-a", algo])
.arg("lorem_ipsum.txt")
.succeeds()
.no_stderr()
.stdout_is_fixture(format!("{algo}_single_file.expected"));
}
}
#[test]
fn test_length_not_supported() {
new_ucmd!()
.arg("--length=15")
.arg("lorem_ipsum.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains(
"cksum: --length is only supported with --algorithm blake2b, sha2, or sha3",
);
new_ucmd!()
.arg("-l")
.arg("158")
.arg("-c")
.arg("-a")
.arg("crc")
.arg("/tmp/xxx")
.fails_with_code(1)
.no_stdout()
.stderr_contains(
"cksum: --length is only supported with --algorithm blake2b, sha2, or sha3",
);
}
#[test]
fn test_blake2b_length() {
new_ucmd!()
.arg("--length=16")
.arg("--algorithm=blake2b")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_contains(
"BLAKE2b-16 (lorem_ipsum.txt) = 7e2f\nBLAKE2b-16 (alice_in_wonderland.txt) = a546",
);
}
#[test]
fn test_blake2b_length_greater_than_512() {
for l in ["513", "1024", "73786976294838206464"] {
new_ucmd!()
.arg("--algorithm=blake2b")
.arg("--length")
.arg(l)
.arg("lorem_ipsum.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("invalid length: '{l}'"))
.stderr_contains("maximum digest length for 'BLAKE2b' is 512 bits");
}
}
#[test]
fn test_blake2b_length_nan() {
for l in ["foo", "512x", "x512", "0xff"] {
new_ucmd!()
.arg("--algorithm=blake2b")
.arg("--length")
.arg(l)
.arg("lorem_ipsum.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains(format!("invalid length: '{l}'"));
}
}
#[test]
fn test_blake2b_length_is_zero() {
new_ucmd!()
.arg("--length=0")
.arg("--algorithm=blake2b")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.no_stderr()
.stdout_is_fixture("length_is_zero.expected");
}
#[test]
fn test_blake2b_length_repeated() {
new_ucmd!()
.arg("--length=10")
.arg("--length=123456")
.arg("--length=0")
.arg("--algorithm=blake2b")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.no_stderr()
.stdout_is_fixture("length_is_zero.expected");
}
#[test]
fn test_blake2b_length_invalid() {
for len in [
"1", "01", "",
] {
new_ucmd!()
.arg("--length")
.arg(len)
.arg("--algorithm=blake2b")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.fails_with_code(1)
.stderr_contains(format!("invalid length: '{len}'"));
}
}
#[apply(test_all_algos)]
fn test_raw_single_file(#[case] algo: &str) {
new_ucmd!()
.arg("--raw")
.arg("lorem_ipsum.txt")
.arg(format!("--algorithm={algo}"))
.succeeds()
.no_stderr()
.stdout_is_fixture_bytes(format!("raw/{algo}_single_file.expected"));
}
#[test]
fn test_raw_multiple_files() {
new_ucmd!()
.arg("--raw")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains("cksum: the --raw option is not supported with multiple files");
}
#[test]
fn test_base64_raw_conflicts() {
new_ucmd!()
.arg("--base64")
.arg("--raw")
.arg("lorem_ipsum.txt")
.fails_with_code(1)
.no_stdout()
.stderr_contains("--base64")
.stderr_contains("cannot be used with")
.stderr_contains("--raw");
}
#[apply(test_all_algos)]
fn test_base64_single_file(#[case] algo: &str) {
new_ucmd!()
.arg("--base64")
.arg("lorem_ipsum.txt")
.arg(format!("--algorithm={algo}"))
.succeeds()
.no_stderr()
.stdout_is_fixture_bytes(format!("base64/{algo}_single_file.expected"));
}
#[test]
fn test_base64_multiple_files() {
new_ucmd!()
.arg("--base64")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.no_stderr()
.stdout_is_fixture_bytes("base64/md5_multiple_files.expected");
}
#[test]
fn test_fail_on_folder() {
let (at, mut ucmd) = at_and_ucmd!();
let folder_name = "a_folder";
at.mkdir(folder_name);
ucmd.arg(folder_name)
.fails()
.no_stdout()
.stderr_contains(format!("cksum: {folder_name}: Is a directory"));
}
#[apply(test_all_algos)]
fn test_all_algorithms_fail_on_folder(#[case] algo: &str) {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let folder_name = "a_folder";
at.mkdir(folder_name);
scene
.ucmd()
.arg(format!("--algorithm={algo}"))
.arg(folder_name)
.fails()
.no_stdout()
.stderr_contains(format!("cksum: {folder_name}: Is a directory"));
}
#[cfg(unix)]
#[test]
fn test_check_error_incorrect_format() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("checksum", "e5773576fc75ff0f8eba14f61587ae28 README.md");
scene
.ucmd()
.arg("-c")
.arg("checksum")
.fails()
.stderr_contains("no properly formatted checksum lines found");
}
#[cfg(unix)]
#[test]
fn test_dev_null() {
let scene = TestScenario::new(util_name!());
scene
.ucmd()
.arg("--tag")
.arg("--untagged")
.arg("--algorithm=md5")
.arg("/dev/null")
.succeeds()
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e ");
}
#[cfg(unix)]
#[test]
fn test_blake2b_default_length() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("-a")
.arg("blake2b")
.arg("-l512")
.arg("f")
.succeeds()
.stdout_contains("BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
at.write("checksum", "BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce");
scene
.ucmd()
.arg("--check")
.arg("checksum")
.succeeds()
.stdout_contains("f: OK");
scene
.ucmd()
.arg("--status")
.arg("--check")
.arg("checksum")
.succeeds()
.no_output();
}
#[test]
fn test_reset_binary() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("--binary")
.arg("--tag")
.arg("--untagged")
.arg("--algorithm=md5")
.arg(at.subdir.join("f"))
.succeeds()
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e *");
}
#[test]
fn test_reset_binary_but_set() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("--binary")
.arg("--tag")
.arg("--untagged")
.arg("--binary")
.arg("--algorithm=md5")
.arg(at.subdir.join("f"))
.succeeds()
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e *");
}
mod output_format {
use super::*;
#[test]
fn test_text_tag() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("f");
ucmd.arg("--text")
.arg("--tag")
.args(&["-a", "md5"])
.arg(at.subdir.join("f"))
.fails();
}
#[test]
fn test_text_no_untagged() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("f");
ucmd.arg("--text")
.args(&["-a", "md5"])
.arg(at.subdir.join("f"))
.fails_with_code(1)
.stderr_contains("--text mode is only supported with --untagged");
}
#[test]
fn test_text_binary() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("f");
ucmd.arg("--text")
.arg("--binary")
.args(&["-a", "md5"])
.arg(at.subdir.join("f"))
.succeeds()
.stdout_contains("f) = d41d8cd98f00b204e9800998ecf8427e");
}
#[test]
fn test_text_binary_untagged() {
let (at, mut ucmd) = at_and_ucmd!();
at.touch("f");
ucmd.arg("--text")
.arg("--binary")
.arg("--untagged")
.args(&["-a", "md5"])
.arg(at.subdir.join("f"))
.succeeds()
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e *");
}
}
#[test]
fn test_binary_file() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("--untagged")
.arg("-b")
.arg("--algorithm=md5")
.arg(at.subdir.join("f"))
.succeeds()
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e *");
scene
.ucmd()
.arg("--tag")
.arg("--untagged")
.arg("--binary")
.arg("--algorithm=md5")
.arg(at.subdir.join("f"))
.succeeds()
.stdout_contains("d41d8cd98f00b204e9800998ecf8427e *");
scene
.ucmd()
.arg("--tag")
.arg("--untagged")
.arg("--binary")
.arg("--algorithm=md5")
.arg("raw/blake2b_single_file.expected")
.succeeds()
.stdout_contains("7e297c07ed8e053600092f91bdd1dad7 *");
new_ucmd!()
.arg("--tag")
.arg("--untagged")
.arg("--binary")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is("cd724690f7dc61775dfac400a71f2caa *lorem_ipsum.txt\n");
new_ucmd!()
.arg("--untagged")
.arg("--binary")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is("cd724690f7dc61775dfac400a71f2caa *lorem_ipsum.txt\n");
new_ucmd!()
.arg("--binary")
.arg("--untagged")
.arg("--algorithm=md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is("cd724690f7dc61775dfac400a71f2caa *lorem_ipsum.txt\n");
new_ucmd!()
.arg("-a")
.arg("md5")
.arg("--binary")
.arg("--untagged")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is("cd724690f7dc61775dfac400a71f2caa *lorem_ipsum.txt\n");
new_ucmd!()
.arg("-a")
.arg("md5")
.arg("--binary")
.arg("--tag")
.arg("--untagged")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is("cd724690f7dc61775dfac400a71f2caa *lorem_ipsum.txt\n");
}
#[test]
fn test_folder_and_file() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let folder_name = "a_folder";
at.mkdir(folder_name);
scene
.ucmd()
.arg(folder_name)
.arg("lorem_ipsum.txt")
.fails()
.stderr_contains(format!("cksum: {folder_name}: Is a directory"))
.stdout_is_fixture("crc_single_file.expected");
}
#[test]
fn test_conflicting_options() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("--binary")
.arg("--check")
.arg("f")
.fails_with_code(1)
.no_stdout()
.stderr_contains(
"cksum: the --binary and --text options are meaningless when verifying checksums",
);
scene
.ucmd()
.arg("--tag")
.arg("-c")
.arg("-a")
.arg("md5")
.fails_with_code(1)
.no_stdout()
.stderr_contains("cksum: the --tag option is meaningless when verifying checksums");
}
#[test]
fn test_check_algo_err() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("-a")
.arg("sm3")
.arg("--check")
.arg("f")
.fails_with_code(1)
.no_stdout()
.stderr_contains("cksum: f: no properly formatted checksum lines found");
}
#[test]
fn test_check_pipe() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
.ucmd()
.arg("--check")
.arg("-")
.pipe_in("f")
.fails_with_code(1)
.no_stdout()
.stderr_contains("cksum: 'standard input': no properly formatted checksum lines found");
}
#[test]
fn test_cksum_check_empty_line() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
at.write("CHECKSUM", "\
SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n\n");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
.stderr_does_not_contain("line is improperly formatted");
}
#[test]
fn test_cksum_check_space() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
at.write("CHECKSUM", "\
SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n \n");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
.stderr_contains("line is improperly formatted");
}
#[test]
fn test_cksum_check_leading_info() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
at.write("CHECKSUM", "\
\\SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
\\BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
\\BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
\\SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n");
}
#[test]
fn test_cksum_check() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
at.write("CHECKSUM", "\
SHA384 (f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b\n\
BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
BLAKE2b-384 (f) = b32811423377f52d7862286ee1a72ee540524380fda1724a6f25d7978c6fd3244a6caf0498812673c5e05ef583825100\n\
SM3 (f) = 1ab21d8355cfa17f8e61194831e81a8f22bec8c728fefb747ed035eb5082aa2b\n");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
.stderr_does_not_contain("line is improperly formatted");
scene
.ucmd()
.arg("--check")
.arg("--strict")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
.stderr_does_not_contain("line is improperly formatted");
at.append("CHECKSUM", "incorrect data");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
.stderr_contains("line is improperly formatted");
scene
.ucmd()
.arg("--check")
.arg("--strict")
.arg("CHECKSUM")
.fails()
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n")
.stderr_contains("line is improperly formatted");
}
#[test]
fn test_cksum_check_case() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
at.write(
"CHECKSUM",
"Sha1 (f) = da39a3ee5e6b4b0d3255bfef95601890afd80709\n",
);
scene.ucmd().arg("--check").arg("CHECKSUM").fails();
}
#[test]
fn test_cksum_check_invalid() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let commands = [vec!["-a", "sha384"]];
at.touch("f");
at.touch("CHECKSUM");
for command in &commands {
let result = scene.ucmd().args(command).arg("f").succeeds();
at.append("CHECKSUM", result.stdout_str());
}
at.append("CHECKSUM", "again incorrect data\naze\n");
scene
.ucmd()
.arg("--check")
.arg("--strict")
.arg("CHECKSUM")
.fails()
.stdout_contains("f: OK\n")
.stderr_contains("2 lines");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.succeeds()
.stdout_contains("f: OK\n")
.stderr_contains("2 lines");
}
#[test]
fn test_cksum_check_failed() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let commands = [vec!["-a", "sha384"]];
at.touch("f");
at.touch("CHECKSUM");
for command in &commands {
let result = scene.ucmd().args(command).arg("f").succeeds();
at.append("CHECKSUM", result.stdout_str());
}
at.append("CHECKSUM", "again incorrect data\naze\nSM3 (input) = 7cfb120d4fabea2a904948538a438fdb57c725157cb40b5aee8d937b8351477e\n");
let result = scene
.ucmd()
.arg("--check")
.arg("--strict")
.arg("CHECKSUM")
.fails();
assert!(
result
.stderr_str()
.contains("input: No such file or directory")
);
assert!(
result
.stderr_str()
.contains("2 lines are improperly formatted\n")
);
assert!(
result
.stderr_str()
.contains("1 listed file could not be read\n")
);
assert!(result.stdout_str().contains("f: OK\n"));
let result = scene.ucmd().arg("--check").arg("CHECKSUM").fails();
assert!(
result
.stderr_str()
.contains("input: No such file or directory")
);
assert!(
result
.stderr_str()
.contains("2 lines are improperly formatted\n")
);
assert!(
result
.stderr_str()
.contains("1 listed file could not be read\n")
);
assert!(result.stdout_str().contains("f: OK\n"));
at.touch("CHECKSUM2");
at.write("f2", "42");
for command in &commands {
let result = scene.ucmd().args(command).arg("f2").succeeds();
at.append("CHECKSUM2", result.stdout_str());
}
at.append("CHECKSUM2", "again incorrect data\naze\nSM3 (input2) = 7cfb120d4fabea2a904948538a438fdb57c725157cb40b5aee8d937b8351477e\n");
at.append("CHECKSUM2", "again incorrect data\naze\nSM3 (input2) = 7cfb120d4fabea2a904948538a438fdb57c725157cb40b5aee8d937b8351477e\n");
let result = scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.arg("CHECKSUM2")
.fails();
println!("result.stderr_str() {}", result.stderr_str());
println!("result.stdout_str() {}", result.stdout_str());
assert!(
result
.stderr_str()
.contains("input2: No such file or directory")
);
assert!(
result
.stderr_str()
.contains("4 lines are improperly formatted\n")
);
assert!(
result
.stderr_str()
.contains("2 listed files could not be read\n")
);
assert!(result.stdout_str().contains("f: OK\n"));
assert!(result.stdout_str().contains("2: OK\n"));
}
#[test]
fn test_check_md5_format() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("empty");
at.write("f", "d41d8cd98f00b204e9800998ecf8427e *empty\n");
scene
.ucmd()
.arg("-a")
.arg("md5")
.arg("--check")
.arg("f")
.succeeds()
.stdout_contains("empty: OK");
at.write("not-empty", "42");
at.write("f2", "a1d0c6e83f027327d8461063f4ac58a6 *not-empty\n");
scene
.ucmd()
.arg("-a")
.arg("md5")
.arg("--check")
.arg("f")
.arg("f2")
.succeeds()
.stdout_contains("empty: OK")
.stdout_contains("not-empty: OK");
}
#[test]
fn test_cksum_mixed() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let commands = [
vec!["-a", "sha384"],
vec!["-a", "blake2b"],
vec!["-a", "blake2b", "-l", "384"],
vec!["-a", "sm3"],
];
at.touch("f");
at.touch("CHECKSUM");
for command in &commands {
let result = scene.ucmd().args(command).arg("f").succeeds();
at.append("CHECKSUM", result.stdout_str());
}
println!("Content of CHECKSUM:\n{}", at.read("CHECKSUM"));
let result = scene
.ucmd()
.arg("--check")
.arg("-a")
.arg("sm3")
.arg("CHECKSUM")
.succeeds();
println!("result.stderr_str() {}", result.stderr_str());
println!("result.stdout_str() {}", result.stdout_str());
assert!(result.stdout_str().contains("f: OK"));
assert!(
result
.stderr_str()
.contains("3 lines are improperly formatted")
);
}
#[test]
fn test_cksum_garbage() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write(
"check-file",
"garbage MD5 (README.md) = e5773576fc75ff0f8eba14f61587ae28",
);
scene
.ucmd()
.arg("--check")
.arg("check-file")
.fails()
.stderr_contains("check-file: no properly formatted checksum lines found");
at.write(
"check-file",
"MD5 (README.md) = e5773576fc75ff0f8eba14f61587ae28 garbage",
);
scene
.ucmd()
.arg("--check")
.arg("check-file")
.fails()
.stderr_contains("check-file: no properly formatted checksum lines found");
}
#[test]
fn test_md5_bits() {
let (at, mut ucmd) = at_and_ucmd!();
at.write(
"f",
"MD5-65536 (README.md) = e5773576fc75ff0f8eba14f61587ae28\n",
);
ucmd.arg("-c")
.arg("f")
.fails()
.stderr_contains("f: no properly formatted checksum lines found");
}
#[test]
fn test_blake2b_bits() {
let (at, mut ucmd) = at_and_ucmd!();
at.write(
"f",
"BLAKE2b-257 (README.md) = f9a984b70cf9a7549920864860fd1131c9fb6c0552def0b6dcce1d87b4ec4c5d\n"
);
ucmd.arg("-c")
.arg("f")
.fails()
.stderr_contains("f: no properly formatted checksum lines found");
}
#[test]
fn test_bsd_case() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("f", "bSD (README.md) = 0000\n");
scene
.ucmd()
.arg("-c")
.arg("f")
.fails()
.stderr_contains("f: no properly formatted checksum lines found");
at.write("f", "BsD (README.md) = 0000\n");
scene
.ucmd()
.arg("-c")
.arg("f")
.fails()
.stderr_contains("f: no properly formatted checksum lines found");
}
#[test]
fn test_blake2d_tested_with_sha1() {
let (at, mut ucmd) = at_and_ucmd!();
at.write(
"f",
"BLAKE2b (f) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n"
);
ucmd.arg("-a")
.arg("sha1")
.arg("-c")
.arg("f")
.fails()
.stderr_contains("f: no properly formatted checksum lines found");
}
#[test]
fn test_unknown_sha() {
let (at, mut ucmd) = at_and_ucmd!();
at.write("f", "SHA4 (README.md) = 00000000\n");
ucmd.arg("-c")
.arg("f")
.fails()
.stderr_contains("f: no properly formatted checksum lines found");
}
#[test]
fn test_check_directory_error() {
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("d");
at.write(
"f",
"BLAKE2b (d) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n"
);
#[cfg(not(windows))]
let err_msg = "cksum: d: Is a directory\n";
#[cfg(windows)]
let err_msg = "cksum: d: Permission denied\n";
ucmd.arg("--check")
.arg(at.subdir.join("f"))
.fails()
.stderr_contains(err_msg);
}
#[test]
fn test_check_base64_hashes() {
let hashes = "MD5 (empty) = 1B2M2Y8AsgTpgAmY7PhCfg==\nSHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=\nBLAKE2b (empty) = eGoC90IBWQPGxv2FJVLScpEvR0DhWEdhiobiF/cfVBnSXhAxr+5YUxOJZESTTrBLkDpoWxRIt1XVb3Aa/pvizg==\n";
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("empty");
at.write("check", hashes);
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("check"))
.succeeds()
.stdout_is("empty: OK\nempty: OK\nempty: OK\n");
}
#[test]
fn test_several_files_error_mgmt() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
scene
.ucmd()
.arg("--check")
.arg("empty")
.arg("incorrect")
.fails()
.stderr_contains("empty: No such file ")
.stderr_contains("incorrect: No such file ");
at.touch("empty");
at.touch("incorrect");
scene
.ucmd()
.arg("--check")
.arg("empty")
.arg("incorrect")
.fails()
.stderr_contains("empty: no properly ")
.stderr_contains("incorrect: no properly ");
}
#[test]
fn test_check_unknown_checksum_file() {
let scene = TestScenario::new(util_name!());
scene
.ucmd()
.arg("--check")
.arg("missing")
.fails()
.stderr_only("cksum: missing: No such file or directory\n");
}
#[test]
fn test_check_comment_line() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("foo", "foo-content\n");
at.write(
"CHECKSUM-sha1",
"\
# This is a comment\n\
SHA1 (foo) = 058ab38dd3603703b3a7063cf95dc51a4286b6fe\n\
# next comment is empty\n#",
);
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM-sha1")
.succeeds()
.stdout_contains("foo: OK")
.no_stderr();
}
#[test]
fn test_check_comment_only() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("CHECKSUM", "# This is a comment\n");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.fails()
.stderr_contains("no properly formatted checksum lines found");
}
#[test]
fn test_check_comment_leading_space() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("foo", "foo-content\n");
at.write(
"CHECKSUM-sha1",
" # This is a comment\n\
SHA1 (foo) = 058ab38dd3603703b3a7063cf95dc51a4286b6fe\n",
);
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM-sha1")
.succeeds()
.stdout_contains("foo: OK")
.stderr_contains("WARNING: 1 line is improperly formatted");
}
#[cfg(not(windows))]
#[test]
fn test_check_failed_to_read() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write(
"CHECKSUM",
"SHA1 (dir) = ffffffffffffffffffffffffffffffffffffffff\n\
SHA1 (not-file) = ffffffffffffffffffffffffffffffffffffffff\n",
);
at.mkdir("dir");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.fails()
.stdout_is(
"dir: FAILED open or read\n\
not-file: FAILED open or read\n",
)
.stderr_contains("cksum: WARNING: 2 listed files could not be read");
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.arg("--ignore-missing")
.fails()
.stdout_is("dir: FAILED open or read\n")
.stderr_contains("cksum: WARNING: 1 listed file could not be read");
}
#[test]
fn test_zero_multiple_file() {
new_ucmd!()
.arg("-z")
.arg("alice_in_wonderland.txt")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("zero_multiple_file.expected");
}
#[test]
fn test_zero_single_file() {
new_ucmd!()
.arg("--zero")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture("zero_single_file.expected");
}
#[test]
fn test_check_trailing_space_fails() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("foo", "foo-content\n");
at.write(
"CHECKSUM",
"SHA1 (foo) = 058ab38dd3603703b3a7063cf95dc51a4286b6fe \n",
);
scene
.ucmd()
.arg("--check")
.arg("CHECKSUM")
.fails()
.no_stdout()
.stderr_contains("CHECKSUM: no properly formatted checksum lines found");
}
mod check_encoding {
#[cfg(not(windows))]
#[test]
fn test_check_non_utf8_comment() {
use super::*;
let hashes =
b"MD5 (empty) = 1B2M2Y8AsgTpgAmY7PhCfg==\n\
# Comment with a non utf8 char: >>\xff<<\n\
SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=\n\
BLAKE2b (empty) = eGoC90IBWQPGxv2FJVLScpEvR0DhWEdhiobiF/cfVBnSXhAxr+5YUxOJZESTTrBLkDpoWxRIt1XVb3Aa/pvizg==\n"
;
let (at, mut cmd) = at_and_ucmd!();
at.touch("empty");
at.write_bytes("check", hashes);
cmd.arg("--check")
.arg(at.subdir.join("check"))
.succeeds()
.stdout_is("empty: OK\nempty: OK\nempty: OK\n")
.no_stderr();
}
#[cfg(target_os = "linux")]
#[test]
fn test_check_non_utf8_filename() {
use super::*;
use std::{ffi::OsString, os::unix::ffi::OsStringExt};
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let filename: OsString = OsStringExt::from_vec(b"funky\xffname".to_vec());
at.touch(filename);
at.write_bytes("check",
b"SHA256 (funky\xffname) = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n");
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("check"))
.succeeds()
.stdout_is_bytes(b"funky\xffname: OK\n")
.no_stderr();
at.write_bytes("check",
b"SHA256 (funky\xffname) = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n");
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("check"))
.fails()
.stdout_is_bytes(b"funky\xffname: FAILED\n")
.stderr_contains("1 computed checksum did NOT match");
at.write_bytes("check",
b"SHA256 (flakey\xffname) = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff\n");
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("check"))
.fails()
.stdout_is_bytes(b"flakey\xffname: FAILED open or read\n")
.stderr_contains("1 listed file could not be read");
}
#[cfg(target_os = "linux")]
#[test]
fn test_quoting_in_stderr() {
use super::*;
use std::{ffi::OsStr, os::unix::ffi::OsStrExt};
let (at, mut cmd) = at_and_ucmd!();
at.mkdir(<OsStr as OsStrExt>::from_bytes(b"FFF\xffDIR"));
at.write_bytes(
"check",
b"SHA256 (FFF\xffFFF) = 29953405eaa3dcc41c37d1621d55b6a47eee93e05613e439e73295029740b10c\nSHA256 (FFF\xffDIR) = 29953405eaa3dcc41c37d1621d55b6a47eee93e05613e439e73295029740b10c\n",
);
cmd.arg("-c")
.arg("check")
.fails_with_code(1)
.stdout_contains_bytes(b"FFF\xffFFF: FAILED open or read")
.stdout_contains_bytes(b"FFF\xffDIR: FAILED open or read")
.stderr_contains("'FFF'$'\\377''FFF': No such file or directory")
.stderr_contains("'FFF'$'\\377''DIR': Is a directory");
}
}
#[test]
fn test_check_blake_length_guess() {
let correct_lines = [
"BLAKE2b (foo.dat) = ca002330e69d3e6b84a46a56a6533fd79d51d97a3bb7cad6c2ff43b354185d6dc1e723fb3db4ae0737e120378424c714bb982d9dc5bbd7a0ab318240ddd18f8d",
"BLAKE2b-512 (foo.dat) = ca002330e69d3e6b84a46a56a6533fd79d51d97a3bb7cad6c2ff43b354185d6dc1e723fb3db4ae0737e120378424c714bb982d9dc5bbd7a0ab318240ddd18f8d",
"BLAKE2b-48 (foo.dat) = 171cdfdf84ed",
];
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("foo.dat", "foo");
for line in correct_lines {
at.write("foo.sums", line);
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("foo.sums"))
.succeeds()
.stdout_is("foo.dat: OK\n");
}
let incorrect = "BLAKE2b (foo.dat) = 171cdfdf84ed";
at.write("foo.sums", incorrect);
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("foo.sums"))
.fails()
.stderr_contains("foo.sums: no properly formatted checksum lines found");
let length_mismatch = "BLAKE2b-8 (foo.dat) = 171cdfdf84ed";
at.write("foo.sums", length_mismatch);
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("foo.sums"))
.fails()
.stderr_contains("foo.sums: no properly formatted checksum lines found");
let too_long = "BLAKE2b-520 (/dev/null) = 0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000";
at.write("foo.sums", too_long);
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("foo.sums"))
.fails()
.stderr_contains("foo.sums: no properly formatted checksum lines found");
}
#[test]
fn test_check_confusing_base64() {
let cksum = "BLAKE2b-48 (foo.dat) = fc1f97C4";
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("foo.dat", "esq");
at.write("foo.sums", cksum);
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("foo.sums"))
.succeeds()
.stdout_is("foo.dat: OK\n");
}
#[test]
fn test_check_mix_hex_base64() {
let b64 = "BLAKE2b-128 (foo1.dat) = BBNuJPhdRwRlw9tm5Y7VbA==";
let hex = "BLAKE2b-128 (foo2.dat) = 04136e24f85d470465c3db66e58ed56c";
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("foo1.dat", "foo");
at.write("foo2.dat", "foo");
at.write("hex_b64", &format!("{hex}\n{b64}"));
at.write("b64_hex", &format!("{b64}\n{hex}"));
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("hex_b64"))
.succeeds()
.stdout_only("foo2.dat: OK\nfoo1.dat: OK\n");
scene
.ucmd()
.arg("--check")
.arg(at.subdir.join("b64_hex"))
.succeeds()
.stdout_only("foo1.dat: OK\nfoo2.dat: OK\n");
}
#[test]
fn test_check_incorrectly_formatted_checksum_keeps_processing_b64() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
let good_ck = "MD5 (f) = 1B2M2Y8AsgTpgAmY7PhCfg=="; let bad_ck = "MD5 (f) = 1B2M2Y8AsgTpgAmY7PhCfg=";
scene
.ucmd()
.arg("--check")
.pipe_in([good_ck, bad_ck].join("\n").as_bytes().to_vec())
.succeeds()
.stdout_contains("f: OK")
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
scene
.ucmd()
.arg("--check")
.pipe_in([bad_ck, good_ck].join("\n").as_bytes().to_vec())
.succeeds()
.stdout_contains("f: OK")
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
}
#[test]
fn test_check_incorrectly_formatted_checksum_keeps_processing_hex() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
let good_ck = "MD5 (f) = d41d8cd98f00b204e9800998ecf8427e"; let bad_ck = "MD5 (f) = d41d8cd98f00b204e9800998ecf8427";
scene
.ucmd()
.arg("--check")
.pipe_in([good_ck, bad_ck].join("\n").as_bytes().to_vec())
.succeeds()
.stdout_contains("f: OK")
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
scene
.ucmd()
.arg("--check")
.pipe_in([bad_ck, good_ck].join("\n").as_bytes().to_vec())
.succeeds()
.stdout_contains("f: OK")
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
}
mod gnu_cksum_base64 {
use super::*;
use uutests::util::log_info;
const PAIRS: [(&str, &str); 12] = [
("sysv", "0 0 f"),
("bsd", "00000 0 f"),
("crc", "4294967295 0 f"),
("crc32b", "0 0 f"),
("md5", "1B2M2Y8AsgTpgAmY7PhCfg=="),
("sha1", "2jmj7l5rSw0yVb/vlWAYkK/YBwk="),
("sha224", "0UoCjCo6K8lHYQK7KII0xBWisB+CjqYqxbPkLw=="),
("sha256", "47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU="),
(
"sha384",
"OLBgp1GsljhM2TJ+sbHjaiH9txEUvgdDTAzHv2P24donTt6/529l+9Ua0vFImLlb",
),
(
"sha512",
"z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==",
),
(
"blake2b",
"eGoC90IBWQPGxv2FJVLScpEvR0DhWEdhiobiF/cfVBnSXhAxr+5YUxOJZESTTrBLkDpoWxRIt1XVb3Aa/pvizg==",
),
("sm3", "GrIdg1XPoX+OYRlIMegajyK+yMco/vt0ftA161CCqis="),
];
fn make_scene() -> TestScenario {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("f");
scene
}
fn output_format(algo: &str, digest: &str) -> String {
if ["sysv", "bsd", "crc", "crc32b"].contains(&algo) {
digest.to_string()
} else {
format!("{} (f) = {digest}", algo.to_uppercase()).replace("BLAKE2B", "BLAKE2b")
}
}
#[test]
fn test_generating() {
let scene = make_scene();
for (algo, digest) in PAIRS {
log_info("ALGORITHM", algo);
scene
.ucmd()
.arg("--base64")
.arg("-a")
.arg(algo)
.arg("f")
.succeeds()
.stdout_only(format!("{}\n", output_format(algo, digest)));
}
}
#[test]
fn test_chk() {
let scene = make_scene();
for (algo, digest) in PAIRS {
if ["sysv", "bsd", "crc", "crc32b"].contains(&algo) {
scene
.ucmd()
.arg("--check")
.arg("-a")
.arg(algo)
.fails()
.stderr_only(
"cksum: --check is not supported with --algorithm={bsd,sysv,crc,crc32b}\n",
);
continue;
}
let line = output_format(algo, digest);
scene
.ucmd()
.arg("--check")
.arg("--strict")
.pipe_in(line)
.succeeds()
.stdout_only("f: OK\n");
}
}
#[test]
fn test_chk_eq1() {
let scene = make_scene();
for (algo, digest) in PAIRS {
if !digest.ends_with('=') {
continue;
}
let mut line = output_format(algo, digest);
if line.ends_with('=') {
line.pop();
}
log_info(format!("ALGORITHM: {algo}, STDIN: '{line}'"), "");
scene
.ucmd()
.arg("--check")
.pipe_in(line)
.fails()
.no_stdout()
.stderr_contains("no properly formatted checksum lines found");
}
}
#[test]
fn test_chk_eq2() {
let scene = make_scene();
for (algo, digest) in PAIRS {
if !digest.ends_with("==") {
continue;
}
let line = output_format(algo, digest);
let line = line.trim_end_matches("==");
log_info(format!("ALGORITHM: {algo}, STDIN: '{line}'"), "");
scene
.ucmd()
.arg("--check")
.pipe_in(line)
.fails()
.no_stdout()
.stderr_contains("no properly formatted checksum lines found");
}
}
}
mod gnu_cksum_base64_untagged {
use super::*;
macro_rules! decl_sha_test {
($id:ident, $algo:literal, $len:expr) => {
mod $id {
use super::*;
#[test]
fn check_length_guess() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("inp", "test input\n");
let compute = ts
.ucmd()
.arg("-a")
.arg($algo)
.arg("-l")
.arg(stringify!($len))
.arg("--base64")
.arg("--untagged")
.arg("inp")
.succeeds();
at.write_bytes("check", compute.stdout());
ts.ucmd()
.arg("-a")
.arg($algo)
.arg("--check")
.arg("check")
.succeeds()
.stdout_only("inp: OK\n");
at.write("check", " inp");
ts.ucmd()
.arg("-a")
.arg($algo)
.arg("check")
.fails()
.stderr_contains(concat!(
"--algorithm=",
$algo,
" requires specifying --length"
));
}
}
};
}
decl_sha_test!(sha2_224, "sha2", 224);
decl_sha_test!(sha2_256, "sha2", 256);
decl_sha_test!(sha2_384, "sha2", 384);
decl_sha_test!(sha2_512, "sha2", 512);
decl_sha_test!(sha3_224, "sha3", 224);
decl_sha_test!(sha3_256, "sha3", 256);
decl_sha_test!(sha3_384, "sha3", 384);
decl_sha_test!(sha3_512, "sha3", 512);
macro_rules! decl_blake_test {
($id:ident, $len:expr) => {
mod $id {
use super::*;
#[test]
fn check_length_guess() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
at.write("inp", "test input\n");
let compute = ts
.ucmd()
.arg("-a")
.arg("blake2b")
.arg("-l")
.arg(stringify!($len))
.arg("--base64")
.arg("--untagged")
.arg("inp")
.succeeds();
at.write_bytes("check", compute.stdout());
ts.ucmd()
.arg("-a")
.arg("blake2b")
.arg("--check")
.arg("check")
.succeeds()
.stdout_only("inp: OK\n");
}
}
};
}
decl_blake_test!(blake2b_8, 8);
decl_blake_test!(blake2b_216, 216);
decl_blake_test!(blake2b_224, 224);
decl_blake_test!(blake2b_232, 232);
decl_blake_test!(blake2b_248, 248);
decl_blake_test!(blake2b_256, 256);
decl_blake_test!(blake2b_264, 264);
decl_blake_test!(blake2b_376, 376);
decl_blake_test!(blake2b_384, 384);
decl_blake_test!(blake2b_392, 392);
decl_blake_test!(blake2b_504, 504);
decl_blake_test!(blake2b_512, 512);
}
mod gnu_cksum_c {
use super::*;
const INVALID_SUM: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaafdb57c725157cb40b5aee8d937b8351477e";
fn make_scene() -> TestScenario {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("input", "9\n7\n1\n4\n2\n6\n3\n5\n8\n10\n");
let algos: &[&[&str]] = &[
&["-a", "sha384"],
&["-a", "blake2b"],
&["-a", "blake2b", "-l", "384"],
&["-a", "sm3"],
];
for args in algos {
let result = scene.ucmd().args(args).succeeds();
let stdout = result.stdout();
at.append_bytes("CHECKSUMS", stdout);
}
scene
}
fn make_scene_with_comment() -> TestScenario {
let scene = make_scene();
scene
.fixtures
.append("CHECKSUMS", "# Very important comment\n");
scene
}
fn make_scene_with_invalid_line() -> TestScenario {
let scene = make_scene_with_comment();
scene.fixtures.append("CHECKSUMS", "invalid_line\n");
scene
}
#[test]
fn test_tagged_invalid_length() {
let (at, mut ucmd) = at_and_ucmd!();
at.write(
"sha2-bad-length.sum",
"SHA2-128 (/dev/null) = 38b060a751ac96384cd9327eb1b1e36a",
);
ucmd.arg("--check")
.arg("sha2-bad-length.sum")
.fails()
.stderr_contains("sha2-bad-length.sum: no properly formatted checksum lines found");
}
#[test]
#[cfg_attr(not(unix), ignore = "/dev/null is only available on UNIX")]
fn test_untagged_base64_matching_tag() {
let (at, mut ucmd) = at_and_ucmd!();
at.write("tag-prefix.sum", "SHA1+++++++++++++++++++++++= /dev/null");
ucmd.arg("--check")
.arg("-a")
.arg("sha1")
.arg("tag-prefix.sum")
.fails()
.stderr_contains("WARNING: 1 computed checksum did NOT match");
}
#[test]
#[cfg_attr(windows, ignore = "Awkward filename is not supported on windows")]
fn test_awkward_filename() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let awkward_file = "abc (f) = abc";
at.touch(awkward_file);
let result = ts.ucmd().arg("-a").arg("sha1").arg(awkward_file).succeeds();
at.write_bytes("tag-awkward.sum", result.stdout());
ts.ucmd().arg("-c").arg("tag-awkward.sum").succeeds();
}
#[test]
fn test_signed_checksums() {
let ts = TestScenario::new(util_name!());
let at = &ts.fixtures;
let filename = "test_file";
at.touch(filename);
let checksum_result = ts.ucmd().arg("-a").arg("sha256").arg(filename).succeeds();
let valid_checksum_line = checksum_result.stdout_str().trim();
let signed_content = format!(
"-----BEGIN PGP SIGNED MESSAGE-----\n\
Hash: SHA256\n\
\n\
# This is a comment that should be ignored\n\
{valid_checksum_line}\n\
-----BEGIN PGP SIGNATURE-----\n\
\n\
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\n\
=PlaceHolder\n\
-----END PGP SIGNATURE-----"
);
at.write("signed_CHECKSUMS", &signed_content);
ts.ucmd().arg("--check").arg("signed_CHECKSUMS").succeeds();
}
#[test]
fn test_check_individual_digests_in_mixed_file() {
let scene = make_scene();
scene
.ucmd()
.arg("--check")
.arg("-a")
.arg("sm3")
.arg("CHECKSUMS")
.succeeds();
}
#[test]
fn test_check_against_older_non_hex_formats() {
let scene = make_scene();
scene
.ucmd()
.arg("-c")
.arg("-a")
.arg("crc")
.arg("CHECKSUMS")
.fails();
let crc_cmd = scene.ucmd().arg("-a").arg("crc").arg("input").succeeds();
let crc_cmd_out = crc_cmd.stdout();
scene.fixtures.write_bytes("CHECKSUMS.crc", crc_cmd_out);
scene.ucmd().arg("-c").arg("CHECKSUMS.crc").fails();
}
#[test]
fn test_status() {
let scene = make_scene();
scene
.ucmd()
.arg("--status")
.arg("--check")
.arg("CHECKSUMS")
.succeeds()
.no_output();
}
#[test]
fn test_status_with_comment() {
let scene = make_scene_with_comment();
scene
.ucmd()
.arg("--status")
.arg("--check")
.arg("CHECKSUMS")
.succeeds()
.no_output();
}
#[test]
fn test_check_strict() {
let scene = make_scene_with_invalid_line();
scene
.ucmd()
.arg("--check")
.arg("CHECKSUMS")
.succeeds()
.stderr_contains("1 line is improperly formatted");
scene
.ucmd()
.arg("--strict")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stderr_contains("1 line is improperly formatted");
}
fn make_scene_with_two_invalid_lines() -> TestScenario {
let scene = make_scene_with_comment();
scene
.fixtures
.append("CHECKSUMS", "invalid_line\ninvalid_line\n");
scene
}
#[test]
fn test_check_strict_plural_checks() {
let scene = make_scene_with_two_invalid_lines();
scene
.ucmd()
.arg("--strict")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stderr_contains("2 lines are improperly formatted");
}
fn make_scene_with_incorrect_checksum() -> TestScenario {
let scene = make_scene_with_two_invalid_lines();
scene
.fixtures
.append("CHECKSUMS", &format!("SM3 (input) = {INVALID_SUM}\n"));
scene
}
#[test]
fn test_check_with_incorrect_checksum() {
let scene = make_scene_with_incorrect_checksum();
scene
.ucmd()
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stdout_contains("input: FAILED")
.stderr_contains("1 computed checksum did NOT match");
scene
.ucmd()
.arg("--strict")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stdout_contains("input: FAILED")
.stderr_contains("1 computed checksum did NOT match");
}
#[test]
fn test_status_with_errors() {
let scene = make_scene_with_incorrect_checksum();
scene
.ucmd()
.arg("--status")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.no_output();
}
#[test]
fn test_check_with_non_existing_file() {
let scene = make_scene();
scene
.fixtures
.write("CHECKSUMS2", &format!("SM3 (input2) = {INVALID_SUM}\n"));
scene
.ucmd()
.arg("--check")
.arg("CHECKSUMS2")
.fails()
.stdout_contains("input2: FAILED open or read")
.stderr_contains("1 listed file could not be read");
scene
.ucmd()
.arg("--strict")
.arg("--check")
.arg("CHECKSUMS2")
.fails()
.stdout_contains("input2: FAILED open or read")
.stderr_contains("1 listed file could not be read");
}
fn make_scene_with_another_improperly_formatted() -> TestScenario {
let scene = make_scene_with_incorrect_checksum();
scene.fixtures.append(
"CHECKSUMS",
&format!("BLAKE2b (missing-file) = {INVALID_SUM}\n"),
);
scene
}
#[test]
fn test_warn() {
let scene = make_scene_with_another_improperly_formatted();
scene
.ucmd()
.arg("--warn")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stderr_contains("CHECKSUMS: 6: improperly formatted SM3 checksum line")
.stderr_contains("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line");
}
fn make_scene_with_checksum_missing() -> TestScenario {
let scene = make_scene_with_another_improperly_formatted();
scene.fixtures.write(
"CHECKSUMS-missing",
&format!("SM3 (nonexistent) = {INVALID_SUM}\n"),
);
scene
}
#[test]
fn test_ignore_missing() {
let scene = make_scene_with_checksum_missing();
scene
.ucmd()
.arg("--ignore-missing")
.arg("--check")
.arg("CHECKSUMS-missing")
.fails()
.stdout_does_not_contain("nonexistent: No such file or directory")
.stdout_does_not_contain("nonexistent: FAILED open or read")
.stderr_contains("CHECKSUMS-missing: no file was verified");
}
#[test]
fn test_ignore_missing_stdin() {
let scene = make_scene_with_checksum_missing();
scene
.ucmd()
.arg("--ignore-missing")
.arg("--check")
.pipe_in_fixture("CHECKSUMS-missing")
.fails()
.no_stdout()
.stderr_contains("'standard input': no file was verified");
}
#[test]
fn test_status_and_warn() {
let scene = make_scene_with_checksum_missing();
scene
.ucmd()
.arg("--status")
.arg("--warn")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stderr_contains("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line")
.stderr_contains("WARNING: 3 lines are improperly formatted")
.stderr_contains("WARNING: 1 computed checksum did NOT match");
scene
.ucmd()
.arg("--warn")
.arg("--status")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.stderr_does_not_contain("CHECKSUMS: 9: improperly formatted BLAKE2b checksum line")
.stderr_does_not_contain("WARNING: 3 lines are improperly formatted")
.stderr_does_not_contain("WARNING: 1 computed checksum did NOT match");
}
#[test]
fn test_status_and_ignore_missing() {
let scene = make_scene_with_checksum_missing();
scene
.ucmd()
.arg("--status")
.arg("--ignore-missing")
.arg("--check")
.arg("CHECKSUMS")
.fails()
.no_output();
}
#[test]
fn test_status_warn_and_ignore_missing() {
let scene = make_scene_with_checksum_missing();
scene
.ucmd()
.arg("--status")
.arg("--warn")
.arg("--ignore-missing")
.arg("--check")
.arg("CHECKSUMS-missing")
.fails()
.stderr_contains("CHECKSUMS-missing: no file was verified")
.stdout_does_not_contain("nonexistent: No such file or directory");
}
#[test]
fn test_check_several_files_dont_exist() {
let scene = make_scene();
scene
.ucmd()
.arg("--check")
.arg("non-existing-1")
.arg("non-existing-2")
.fails()
.stderr_contains("non-existing-1: No such file or directory")
.stderr_contains("non-existing-2: No such file or directory");
}
#[test]
fn test_check_several_files_empty() {
let scene = make_scene();
scene.fixtures.touch("empty-1");
scene.fixtures.touch("empty-2");
scene
.ucmd()
.arg("--check")
.arg("empty-1")
.arg("empty-2")
.fails()
.stderr_contains("empty-1: no properly formatted checksum lines found")
.stderr_contains("empty-2: no properly formatted checksum lines found");
}
}
mod format_mix {
use super::*;
const INPUT_ALGO_NON_ALGO: &str = "\
BLAKE2b (bar) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce\n\
786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce foo";
const INPUT_NON_ALGO_ALGO: &str = "\
786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce foo\n\
BLAKE2b (bar) = 786a02f742015903c6c6fd852552d272912f4740e15847618a86e217f71f5419d25e1031afee585313896444934eb04b903a685b1448b755d56f701afe9be2ce";
fn make_scene() -> TestScenario {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("foo");
at.touch("bar");
scene
}
#[test]
fn test_check_cli_algo_non_algo() {
let scene = make_scene();
scene
.ucmd()
.arg("--check")
.arg("--algo=blake2b")
.pipe_in(INPUT_ALGO_NON_ALGO)
.succeeds()
.stdout_contains("bar: OK\nfoo: OK")
.no_stderr();
}
#[test]
fn test_check_cli_non_algo_algo() {
let scene = make_scene();
scene
.ucmd()
.arg("--check")
.arg("--algo=blake2b")
.pipe_in(INPUT_NON_ALGO_ALGO)
.succeeds()
.stdout_contains("foo: OK\nbar: OK")
.no_stderr();
}
#[test]
fn test_check_algo_non_algo() {
let scene = make_scene();
scene
.ucmd()
.arg("--check")
.pipe_in(INPUT_ALGO_NON_ALGO)
.succeeds()
.stdout_contains("bar: OK")
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
}
#[test]
fn test_check_non_algo_algo() {
let scene = make_scene();
scene
.ucmd()
.arg("--check")
.pipe_in(INPUT_NON_ALGO_ALGO)
.succeeds()
.stdout_contains("bar: OK")
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
}
}
#[cfg(not(target_os = "android"))]
mod debug_flag {
use super::*;
#[test]
fn test_debug_flag() {
new_ucmd!()
.arg("--debug")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("crc_single_file.expected")
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
new_ucmd!()
.arg("--debug")
.arg("-a")
.arg("md5")
.arg("lorem_ipsum.txt")
.succeeds()
.stdout_is_fixture("md5_single_file.expected")
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
new_ucmd!()
.arg("--debug")
.pipe_in("test")
.succeeds()
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
new_ucmd!()
.arg("--debug")
.arg("lorem_ipsum.txt")
.arg("alice_in_wonderland.txt")
.succeeds()
.stdout_is_fixture("crc_multiple_files.expected")
.stderr_str_check(|stderr| {
let avx512_count = stderr
.lines()
.filter(|line| line.contains("avx512"))
.count();
let avx2_count = stderr.lines().filter(|line| line.contains("avx2")).count();
let pclmul_count = stderr
.lines()
.filter(|line| line.contains("pclmul"))
.count();
avx512_count == 1 && avx2_count == 1 && pclmul_count == 1
});
}
#[test]
fn test_debug_with_algorithms() {
new_ucmd!()
.arg("--debug")
.arg("-a")
.arg("sha256")
.arg("lorem_ipsum.txt")
.succeeds()
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
new_ucmd!()
.arg("--debug")
.arg("-a")
.arg("blake2b")
.arg("lorem_ipsum.txt")
.succeeds()
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
new_ucmd!()
.arg("--debug")
.arg("-a")
.arg("blake2b")
.arg("--length")
.arg("256")
.arg("lorem_ipsum.txt")
.succeeds()
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
new_ucmd!()
.arg("--debug")
.arg("-a")
.arg("sha1")
.arg("lorem_ipsum.txt")
.succeeds()
.stderr_contains("avx512")
.stderr_contains("avx2")
.stderr_contains("pclmul");
}
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn test_check_file_with_io_error() {
new_ucmd!()
.arg("-a")
.arg("md5")
.arg("--check")
.pipe_in("d8e8fca2dc0f896fd7cb4cb0031ba249 /proc/self/mem\n")
.fails()
.stderr_contains("Input/output error")
.stdout_contains("FAILED open or read");
}
#[test]
#[cfg(all(target_os = "linux", not(target_env = "musl")))]
fn test_check_checkfile_with_io_error() {
new_ucmd!()
.arg("-a")
.arg("md5")
.arg("--check")
.arg("/proc/self/mem")
.fails()
.stderr_contains("/proc/self/mem: read error")
.no_stdout();
}
#[rstest]
#[case::default_length(
&[],
"ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf"
)]
#[case::pass_default_length(
&["-l", "256"],
"ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf"
)]
#[case::smaller_length(
&["-l", "128"],
"ac8549b2861a151896ab721bd29d7a20"
)]
#[case::bigger_length(
&["-l", "264"],
"ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdfc2"
)]
#[case::length_0(
&["-l", "0"],
"ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf"
)]
#[case::length_1(
&["-l", "1"],
"00"
)]
#[case::length_2(
&["-l", "2"],
"00"
)]
#[case::length_3(
&["-l", "3"],
"04"
)]
#[case::length_4(
&["-l", "4"],
"0c"
)]
#[case::length_5(
&["-l", "5"],
"0c"
)]
#[case::length_6(
&["-l", "6"],
"2c"
)]
#[case::length_7(
&["-l", "7"],
"2c"
)]
#[case::length_8(
&["-l", "8"],
"ac"
)]
fn test_shake128(#[case] args: &[&str], #[case] expected: &str) {
let bit_len = if args.is_empty() || args[1] == "0" {
"256"
} else {
args[1]
};
new_ucmd!()
.arg("-a")
.arg("shake128")
.args(args)
.pipe_in("xxx")
.succeeds()
.stdout_only(format!("SHAKE128-{bit_len} (-) = {expected}\n"));
}
#[rstest]
#[case::default_length(
&[],
"2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba"
)]
#[case::pass_default_length(
&["-l", "512"],
"2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba"
)]
#[case::smaller_length(
&["-l", "128"],
"2fa631503c3ea5fe85131dbfa2480518"
)]
#[case::bigger_length(
&["-l", "1024"],
"2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba18783d25f8b36d92b8607f016352b5c405945a7859a8339201728f680647324d1b8ea93a01d2ef965dadf4a1bee3ff044ed2b4bd95e4311f5e3f2cd5bae0b7c6"
)]
#[case::length_0(
&["-l", "0"],
"2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba"
)]
#[case::length_1(
&["-l", "1"],
"01"
)]
#[case::length_2(
&["-l", "2"],
"03"
)]
#[case::length_3(
&["-l", "3"],
"07"
)]
#[case::length_4(
&["-l", "4"],
"0f"
)]
#[case::length_5(
&["-l", "5"],
"0f"
)]
#[case::length_6(
&["-l", "6"],
"2f"
)]
#[case::length_7(
&["-l", "7"],
"2f"
)]
#[case::length_8(
&["-l", "8"],
"2f"
)]
fn test_shake256(#[case] args: &[&str], #[case] expected: &str) {
let bit_len = if args.is_empty() || args[1] == "0" {
"512"
} else {
args[1]
};
new_ucmd!()
.arg("-a")
.arg("shake256")
.args(args)
.pipe_in("xxx")
.succeeds()
.stdout_only(format!("SHAKE256-{bit_len} (-) = {expected}\n"));
}
#[test]
fn test_check_shake128_no_length() {
const INPUT_SHAKE128_CORRECT_LEN: &str =
"SHAKE128 (bar) = ac8549b2861a151896ab721bd29d7a20c1a3d1f75b31266f786f20d963fb0fdf";
const INPUT_SHAKE128_WRONG_LEN: &str = "SHAKE128 (bar) = ac8549b2861a151896ab721bd29d7a20";
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("bar", "xxx");
scene
.ucmd()
.arg("-a")
.arg("shake128")
.arg("-c")
.pipe_in(INPUT_SHAKE128_CORRECT_LEN)
.succeeds();
scene
.ucmd()
.arg("-a")
.arg("shake128")
.arg("-c")
.pipe_in(INPUT_SHAKE128_WRONG_LEN)
.fails()
.stderr_only("cksum: 'standard input': no properly formatted checksum lines found\n");
}
#[test]
fn test_check_shake256_no_length() {
const INPUT_SHAKE256_CORRECT_LEN: &str = "SHAKE256 (bar) = 2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7b24958f3e3c4cc0e71f1fe6f054cd3fb28b9efb62b4f8f3fbe6d50d90f5c6eba";
const INPUT_SHAKE256_WRONG_LEN: &str =
"SHAKE256 (bar) = 2fa631503c3ea5fe85131dbfa24805185474740e6dcb5f2a64f69d932bcb55f7";
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.write("bar", "xxx");
scene
.ucmd()
.arg("-c")
.pipe_in(INPUT_SHAKE256_CORRECT_LEN)
.succeeds();
scene
.ucmd()
.arg("-c")
.pipe_in(INPUT_SHAKE256_WRONG_LEN)
.fails()
.stderr_only("cksum: 'standard input': no properly formatted checksum lines found\n");
}
#[template]
#[rstest]
#[case::no_length(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9",
None
)]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9",
Some(0)
)]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9",
Some(256)
)]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9791074b7511b59d31c71c62f5a745689fa6c",
Some(400)
)]
#[case(b"foo", "04e0bb39f3", Some(40))]
#[case(b"foo", "04e0", Some(16))]
#[case(b"foo", "04", Some(8))]
fn test_blake3(#[case] input: &[u8], #[case] expected: &str, #[case] length: Option<usize>) {}
#[apply(test_blake3)]
fn test_compute_blake3(
#[case] input: &[u8],
#[case] expected: &str,
#[case] length: Option<usize>,
) {
let length_args: &[String] = if let Some(len) = length {
&["-l".into(), len.to_string()]
} else {
&[]
};
new_ucmd!()
.arg("-a")
.arg("blake3")
.args(length_args)
.pipe_in(input)
.succeeds()
.stdout_only(format!(
"BLAKE3{} (-) = {expected}\n",
match length {
Some(0) | None => "-256".into(),
Some(i) => format!("-{i}"),
}
));
new_ucmd!()
.arg("-a")
.arg("blake3")
.arg("--untagged")
.args(length_args)
.pipe_in(input)
.succeeds()
.stdout_only(format!("{expected} -\n"));
}
#[apply(test_blake3)]
fn test_check_blake3_tagged(
#[case] input: &[u8],
#[case] digest: &str,
#[case] opt_len: Option<usize>,
) {
let (at, mut ucmd) = at_and_ucmd!();
at.write_bytes("FILE", input);
let len = match opt_len {
Some(0) => "-256".into(),
Some(i) => format!("-{i}"),
None => String::new(),
};
let tagged = format!("BLAKE3{len} (FILE) = {digest}",);
ucmd.arg("-c")
.arg("-a")
.arg("blake3")
.pipe_in(tagged)
.succeeds()
.stdout_only("FILE: OK\n");
}
#[apply(test_blake3)]
#[allow(clippy::used_underscore_binding)]
fn test_check_blake3_untagged(
#[case] input: &[u8],
#[case] digest: &str,
#[case] _opt_len: Option<usize>,
) {
let (at, mut ucmd) = at_and_ucmd!();
at.write_bytes("FILE", input);
let untagged = format!("{digest} FILE");
ucmd.arg("-c")
.arg("-a")
.arg("blake3")
.pipe_in(untagged)
.succeeds()
.stdout_only("FILE: OK\n");
}