1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-27 19:17:43 +00:00

Merge pull request #7192 from RenjiSann/cksum-fix-error-handling

cksum: Update error and flags handling to improver GNU's match
This commit is contained in:
Sylvestre Ledru 2025-01-23 22:14:17 +01:00 committed by GitHub
commit 5129aba0f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 545 additions and 91 deletions

View file

@ -13,8 +13,9 @@ use std::iter;
use std::path::Path;
use uucore::checksum::{
calculate_blake2b_length, detect_algo, digest_reader, perform_checksum_validation,
ChecksumError, ChecksumOptions, ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD,
ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, SUPPORTED_ALGORITHMS,
ChecksumError, ChecksumOptions, ChecksumVerbose, ALGORITHM_OPTIONS_BLAKE2B,
ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV,
SUPPORTED_ALGORITHMS,
};
use uucore::{
encoding,
@ -322,13 +323,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|files| files.map(OsStr::new).collect::<Vec<_>>(),
);
let verbose = ChecksumVerbose::new(status, quiet, warn);
let opts = ChecksumOptions {
binary: binary_flag,
ignore_missing,
quiet,
status,
strict,
warn,
verbose,
};
return perform_checksum_validation(files.iter().copied(), algo_option, length, opts);
@ -462,19 +464,22 @@ pub fn uu_app() -> Command {
.short('w')
.long("warn")
.help("warn about improperly formatted checksum lines")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with_all([options::STATUS, options::QUIET]),
)
.arg(
Arg::new(options::STATUS)
.long("status")
.help("don't output anything, status code shows success")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with_all([options::WARN, options::QUIET]),
)
.arg(
Arg::new(options::QUIET)
.long(options::QUIET)
.help("don't print OK for each successfully verified file")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with_all([options::WARN, options::STATUS]),
)
.arg(
Arg::new(options::IGNORE_MISSING)

View file

@ -24,6 +24,7 @@ use uucore::checksum::escape_filename;
use uucore::checksum::perform_checksum_validation;
use uucore::checksum::ChecksumError;
use uucore::checksum::ChecksumOptions;
use uucore::checksum::ChecksumVerbose;
use uucore::checksum::HashAlgorithm;
use uucore::error::{FromIo, UResult};
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
@ -240,13 +241,14 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
|| iter::once(OsStr::new("-")).collect::<Vec<_>>(),
|files| files.map(OsStr::new).collect::<Vec<_>>(),
);
let verbose = ChecksumVerbose::new(status, quiet, warn);
let opts = ChecksumOptions {
binary,
ignore_missing,
quiet,
status,
strict,
warn,
verbose,
};
// Execute the checksum validation
@ -356,14 +358,16 @@ pub fn uu_app_common() -> Command {
.short('q')
.long(options::QUIET)
.help("don't print OK for each successfully verified file")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with_all([options::STATUS, options::WARN]),
)
.arg(
Arg::new(options::STATUS)
.short('s')
.long("status")
.help("don't output anything, status code shows success")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with_all([options::QUIET, options::WARN]),
)
.arg(
Arg::new(options::STRICT)
@ -382,7 +386,8 @@ pub fn uu_app_common() -> Command {
.short('w')
.long("warn")
.help("warn about improperly formatted checksum lines")
.action(ArgAction::SetTrue),
.action(ArgAction::SetTrue)
.overrides_with_all([options::QUIET, options::STATUS]),
)
.arg(
Arg::new("zero")

View file

@ -19,7 +19,7 @@ use std::{
};
use crate::{
error::{set_exit_code, FromIo, UError, UResult, USimpleError},
error::{FromIo, UError, UResult, USimpleError},
os_str_as_bytes, os_str_from_bytes, read_os_string_lines, show, show_error, show_warning_caps,
sum::{
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224,
@ -130,10 +130,12 @@ impl From<ChecksumError> for LineCheckError {
enum FileCheckError {
/// a generic UError was encountered in sub-functions
UError(Box<dyn UError>),
/// the checksum file is improperly formatted.
ImproperlyFormatted,
/// reading of the checksum file failed
CantOpenChecksumFile,
/// processing of the file is considered as a failure regarding the
/// provided flags. This however does not stop the processing of
/// further files.
Failed,
}
impl From<Box<dyn UError>> for FileCheckError {
@ -148,15 +150,57 @@ impl From<ChecksumError> for FileCheckError {
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Clone, Copy)]
pub enum ChecksumVerbose {
Status,
Quiet,
Normal,
Warning,
}
impl ChecksumVerbose {
pub fn new(status: bool, quiet: bool, warn: bool) -> Self {
use ChecksumVerbose::*;
// Assume only one of the three booleans will be enabled at once.
// This is ensured by clap's overriding arguments.
match (status, quiet, warn) {
(true, _, _) => Status,
(_, true, _) => Quiet,
(_, _, true) => Warning,
_ => Normal,
}
}
#[inline]
pub fn over_status(self) -> bool {
self > Self::Status
}
#[inline]
pub fn over_quiet(self) -> bool {
self > Self::Quiet
}
#[inline]
pub fn at_least_warning(self) -> bool {
self >= Self::Warning
}
}
impl Default for ChecksumVerbose {
fn default() -> Self {
Self::Normal
}
}
/// This struct regroups CLI flags.
#[derive(Debug, Default, Clone, Copy)]
pub struct ChecksumOptions {
pub binary: bool,
pub ignore_missing: bool,
pub quiet: bool,
pub status: bool,
pub strict: bool,
pub warn: bool,
pub verbose: ChecksumVerbose,
}
#[derive(Debug, Error)]
@ -235,20 +279,19 @@ pub fn create_sha3(bits: Option<usize>) -> UResult<HashAlgorithm> {
}
#[allow(clippy::comparison_chain)]
fn cksum_output(res: &ChecksumResult, status: bool) {
fn print_cksum_report(res: &ChecksumResult) {
if res.bad_format == 1 {
show_warning_caps!("{} line is improperly formatted", res.bad_format);
} else if res.bad_format > 1 {
show_warning_caps!("{} lines are improperly formatted", res.bad_format);
}
if !status {
if res.failed_cksum == 1 {
show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum);
} else if res.failed_cksum > 1 {
show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum);
}
if res.failed_cksum == 1 {
show_warning_caps!("{} computed checksum did NOT match", res.failed_cksum);
} else if res.failed_cksum > 1 {
show_warning_caps!("{} computed checksums did NOT match", res.failed_cksum);
}
if res.failed_open_file == 1 {
show_warning_caps!("{} listed file could not be read", res.failed_open_file);
} else if res.failed_open_file > 1 {
@ -284,10 +327,10 @@ impl FileChecksumResult {
/// The cli options might prevent to display on the outcome of the
/// comparison on STDOUT.
fn can_display(&self, opts: ChecksumOptions) -> bool {
fn can_display(&self, verbose: ChecksumVerbose) -> bool {
match self {
FileChecksumResult::Ok => !opts.status && !opts.quiet,
FileChecksumResult::Failed => !opts.status,
FileChecksumResult::Ok => verbose.over_quiet(),
FileChecksumResult::Failed => verbose.over_status(),
FileChecksumResult::CantOpen => true,
}
}
@ -310,9 +353,9 @@ fn print_file_report<W: Write>(
filename: &[u8],
result: FileChecksumResult,
prefix: &str,
opts: ChecksumOptions,
verbose: ChecksumVerbose,
) {
if result.can_display(opts) {
if result.can_display(verbose) {
let _ = write!(w, "{prefix}");
let _ = w.write_all(filename);
let _ = writeln!(w, ": {result}");
@ -589,7 +632,7 @@ fn get_file_to_check(
filename_bytes,
FileChecksumResult::CantOpen,
"",
opts,
opts.verbose,
);
};
match File::open(filename) {
@ -648,12 +691,11 @@ fn get_input_file(filename: &OsStr) -> UResult<Box<dyn Read>> {
fn identify_algo_name_and_length(
line_info: &LineInfo,
algo_name_input: Option<&str>,
last_algo: &mut Option<String>,
) -> Option<(String, Option<usize>)> {
let algorithm = line_info
.algo_name
.clone()
.unwrap_or_default()
.to_lowercase();
let algo_from_line = line_info.algo_name.clone().unwrap_or_default();
let algorithm = algo_from_line.to_lowercase();
*last_algo = Some(algo_from_line);
// check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file
// (for example SHA1 (f) = d...)
@ -711,7 +753,7 @@ fn compute_and_check_digest_from_file(
filename,
FileChecksumResult::from_bool(checksum_correct),
prefix,
opts,
opts.verbose,
);
if checksum_correct {
@ -726,11 +768,13 @@ fn process_algo_based_line(
line_info: &LineInfo,
cli_algo_name: Option<&str>,
opts: ChecksumOptions,
last_algo: &mut Option<String>,
) -> Result<(), LineCheckError> {
let filename_to_check = line_info.filename.as_slice();
let (algo_name, algo_byte_len) = identify_algo_name_and_length(line_info, cli_algo_name)
.ok_or(LineCheckError::ImproperlyFormatted)?;
let (algo_name, algo_byte_len) =
identify_algo_name_and_length(line_info, cli_algo_name, last_algo)
.ok_or(LineCheckError::ImproperlyFormatted)?;
// If the digest bitlen is known, we can check the format of the expected
// checksum with it.
@ -789,13 +833,13 @@ fn process_non_algo_based_line(
/// matched the expected.
/// If the comparison didn't happen, return a `LineChecksumError`.
fn process_checksum_line(
filename_input: &OsStr,
line: &OsStr,
i: usize,
cli_algo_name: Option<&str>,
cli_algo_length: Option<usize>,
opts: ChecksumOptions,
cached_regex: &mut Option<LineFormat>,
last_algo: &mut Option<String>,
) -> Result<(), LineCheckError> {
let line_bytes = os_str_as_bytes(line)?;
@ -806,34 +850,19 @@ fn process_checksum_line(
// Use `LineInfo` to extract the data of a line.
// Then, depending on its format, apply a different pre-treatment.
if let Some(line_info) = LineInfo::parse(line, cached_regex) {
if line_info.format == LineFormat::AlgoBased {
process_algo_based_line(&line_info, cli_algo_name, opts)
} else if let Some(cli_algo) = cli_algo_name {
// If we match a non-algo based regex, we expect a cli argument
// to give us the algorithm to use
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
} else {
// We have no clue of what algorithm to use
return Err(LineCheckError::ImproperlyFormatted);
}
} else {
if opts.warn {
let algo = if let Some(algo_name_input) = cli_algo_name {
algo_name_input.to_uppercase()
} else {
"Unknown algorithm".to_string()
};
eprintln!(
"{}: {}: {}: improperly formatted {} checksum line",
util_name(),
&filename_input.maybe_quote(),
i + 1,
algo
);
}
let Some(line_info) = LineInfo::parse(line, cached_regex) else {
return Err(LineCheckError::ImproperlyFormatted);
};
Err(LineCheckError::ImproperlyFormatted)
if line_info.format == LineFormat::AlgoBased {
process_algo_based_line(&line_info, cli_algo_name, opts, last_algo)
} else if let Some(cli_algo) = cli_algo_name {
// If we match a non-algo based regex, we expect a cli argument
// to give us the algorithm to use
process_non_algo_based_line(i, &line_info, cli_algo, cli_algo_length, opts)
} else {
// We have no clue of what algorithm to use
return Err(LineCheckError::ImproperlyFormatted);
}
}
@ -856,7 +885,6 @@ fn process_checksum_file(
Err(e) => {
// Could not read the file, show the error and continue to the next file
show_error!("{e}");
set_exit_code(1);
return Err(FileCheckError::CantOpenChecksumFile);
}
}
@ -868,16 +896,20 @@ fn process_checksum_file(
// cached_regex is used to ensure that several non algo-based checksum line
// will use the same regex.
let mut cached_regex = None;
// last_algo caches the algorithm used in the last line to print a warning
// message for the current line if improperly formatted.
// Behavior tested in gnu_cksum_c::test_warn
let mut last_algo = None;
for (i, line) in lines.iter().enumerate() {
let line_result = process_checksum_line(
filename_input,
line,
i,
cli_algo_name,
cli_algo_length,
opts,
&mut cached_regex,
&mut last_algo,
);
// Match a first time to elude critical UErrors, and increment the total
@ -893,7 +925,26 @@ fn process_checksum_file(
match line_result {
Ok(()) => res.correct += 1,
Err(DigestMismatch) => res.failed_cksum += 1,
Err(ImproperlyFormatted) => res.bad_format += 1,
Err(ImproperlyFormatted) => {
res.bad_format += 1;
if opts.verbose.at_least_warning() {
let algo = if let Some(algo_name_input) = cli_algo_name {
Cow::Owned(algo_name_input.to_uppercase())
} else if let Some(algo) = &last_algo {
Cow::Borrowed(algo.as_str())
} else {
Cow::Borrowed("Unknown algorithm")
};
eprintln!(
"{}: {}: {}: improperly formatted {} checksum line",
util_name(),
&filename_input.maybe_quote(),
i + 1,
algo
);
}
}
Err(CantOpenFile | FileIsDirectory) => res.failed_open_file += 1,
Err(FileNotFound) if !opts.ignore_missing => res.failed_open_file += 1,
_ => continue,
@ -903,36 +954,43 @@ fn process_checksum_file(
// not a single line correctly formatted found
// return an error
if res.total_properly_formatted() == 0 {
if !opts.status {
if opts.verbose.over_status() {
log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin));
}
set_exit_code(1);
return Err(FileCheckError::ImproperlyFormatted);
return Err(FileCheckError::Failed);
}
// if any incorrectly formatted line, show it
cksum_output(&res, opts.status);
if opts.verbose.over_status() {
print_cksum_report(&res);
}
if opts.ignore_missing && res.correct == 0 {
// we have only bad format
// and we had ignore-missing
eprintln!(
"{}: {}: no file was verified",
util_name(),
filename_input.maybe_quote(),
);
set_exit_code(1);
if opts.verbose.over_status() {
eprintln!(
"{}: {}: no file was verified",
util_name(),
filename_input.maybe_quote(),
);
}
return Err(FileCheckError::Failed);
}
// strict means that we should have an exit code.
if opts.strict && res.bad_format > 0 {
set_exit_code(1);
return Err(FileCheckError::Failed);
}
// if we have any failed checksum verification, we set an exit code
// except if we have ignore_missing
if (res.failed_cksum > 0 || res.failed_open_file > 0) && !opts.ignore_missing {
set_exit_code(1);
// If a file was missing, return an error unless we explicitly ignore it.
if res.failed_open_file > 0 && !opts.ignore_missing {
return Err(FileCheckError::Failed);
}
// Obviously, if a checksum failed at some point, report the error.
if res.failed_cksum > 0 {
return Err(FileCheckError::Failed);
}
Ok(())
@ -950,16 +1008,23 @@ pub fn perform_checksum_validation<'a, I>(
where
I: Iterator<Item = &'a OsStr>,
{
let mut failed = false;
// if cksum has several input files, it will print the result for each file
for filename_input in files {
use FileCheckError::*;
match process_checksum_file(filename_input, algo_name_input, length_input, opts) {
Err(UError(e)) => return Err(e),
Err(CantOpenChecksumFile | ImproperlyFormatted) | Ok(_) => continue,
Err(Failed | CantOpenChecksumFile) => failed = true,
Ok(_) => continue,
}
}
Ok(())
if failed {
Err(USimpleError::new(1, ""))
} else {
Ok(())
}
}
pub fn digest_reader<T: Read>(
@ -1416,7 +1481,7 @@ mod tests {
for (filename, result, prefix, expected) in cases {
let mut buffer: Vec<u8> = vec![];
print_file_report(&mut buffer, filename, *result, prefix, opts);
print_file_report(&mut buffer, filename, *result, prefix, opts.verbose);
assert_eq!(&buffer, expected)
}
}

View file

@ -2,7 +2,7 @@
//
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb
// spell-checker:ignore (words) asdf algo algos asha mgmt xffname hexa GFYEQ HYQK Yqxb dont
use crate::common::util::TestScenario;
@ -1284,6 +1284,18 @@ fn test_several_files_error_mgmt() {
.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() {
// A comment in a checksum file shall be discarded unnoticed.
@ -1811,6 +1823,373 @@ mod gnu_cksum_base64 {
}
}
/// This module reimplements the cksum-c.sh GNU test.
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
}
#[test]
#[ignore]
fn test_signed_checksums() {
todo!()
}
#[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();
}
fn make_scene_with_comment() -> TestScenario {
let scene = make_scene();
scene
.fixtures
.append("CHECKSUMS", "# Very important comment\n");
scene
}
#[test]
fn test_status_with_comment() {
let scene = make_scene_with_comment();
scene
.ucmd()
.arg("--status")
.arg("--check")
.arg("CHECKSUMS")
.succeeds()
.no_output();
}
fn make_scene_with_invalid_line() -> TestScenario {
let scene = make_scene_with_comment();
scene.fixtures.append("CHECKSUMS", "invalid_line\n");
scene
}
#[test]
fn test_check_strict() {
let scene = make_scene_with_invalid_line();
// without strict, succeeds
scene
.ucmd()
.arg("--check")
.arg("CHECKSUMS")
.succeeds()
.stderr_contains("1 line is improperly formatted");
// with strict, fails
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");
// also fails with strict
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");
// also fails with strict
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")
.run()
.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_status_and_warn() {
let scene = make_scene_with_checksum_missing();
// --status before --warn
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");
// --warn before --status (status hides the results)
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");
}
}
/// The tests in this module check the behavior of cksum when given different
/// checksum formats and algorithms in the same file, while specifying an
/// algorithm on CLI or not.