From a3b740355057027bc556b02f9791a49317870927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 3 Nov 2024 12:29:30 +0100 Subject: [PATCH 1/8] feat(checksum): improve FileCheckError variants to be meaningful --- src/uucore/src/lib/features/checksum.rs | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index e7a0a2653..160644046 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -107,14 +107,16 @@ impl From for LineCheckError { } /// Represents an error that was encountered when processing a checksum file. -#[allow(clippy::enum_variant_names)] enum FileCheckError { /// a generic UError was encountered in sub-functions UError(Box), - /// the error does not stop the processing of next files - NonCriticalError, - /// the error must stop the run of the program - CriticalError, + /// the checksum file is improperly formatted. + ImproperlyFormatted, + /// reading of the checksum file failed + CantOpenChecksumFile, + /// Algorithm detection was unsuccessful. + /// Either none is provided, or there is a conflict. + AlgoDetectionError, } impl From> for FileCheckError { @@ -735,7 +737,7 @@ fn process_checksum_file( // Could not read the file, show the error and continue to the next file show_error!("{e}"); set_exit_code(1); - return Err(FileCheckError::NonCriticalError); + return Err(FileCheckError::CantOpenChecksumFile); } } }; @@ -749,7 +751,7 @@ fn process_checksum_file( }; show_error!("{e}"); set_exit_code(1); - return Err(FileCheckError::NonCriticalError); + return Err(FileCheckError::AlgoDetectionError); }; for (i, line) in lines.iter().enumerate() { @@ -791,7 +793,7 @@ fn process_checksum_file( .into()); } set_exit_code(1); - return Err(FileCheckError::CriticalError); + return Err(FileCheckError::ImproperlyFormatted); } // if any incorrectly formatted line, show it @@ -839,8 +841,8 @@ where use FileCheckError::*; match process_checksum_file(filename_input, algo_name_input, length_input, opts) { Err(UError(e)) => return Err(e), - Err(CriticalError) => break, - Err(NonCriticalError) | Ok(_) => continue, + Err(ImproperlyFormatted) => break, + Err(CantOpenChecksumFile | AlgoDetectionError) | Ok(_) => continue, } } From 20dfe2dc10d80e2a7b3f4f6edb036b1c22c2b005 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 17 Nov 2024 12:06:37 +0100 Subject: [PATCH 2/8] test(cksum): remove duplicate testcase --- tests/by-util/test_cksum.rs | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index ee1e05292..86de7ea95 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -1251,33 +1251,6 @@ fn test_several_files_error_mgmt() { .stderr_contains("incorrect: no properly "); } -#[cfg(target_os = "linux")] -#[test] -fn test_non_utf8_filename() { - use std::ffi::OsString; - use std::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); - - scene - .ucmd() - .arg(&filename) - .succeeds() - .stdout_is_bytes(b"4294967295 0 funky\xffname\n") - .no_stderr(); - scene - .ucmd() - .arg("-asha256") - .arg(filename) - .succeeds() - .stdout_is_bytes(b"SHA256 (funky\xffname) = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\n") - .no_stderr(); -} - #[test] fn test_check_comment_line() { // A comment in a checksum file shall be discarded unnoticed. @@ -1458,7 +1431,6 @@ mod check_utf8 { .no_stderr(); } - #[cfg(target_os = "linux")] #[test] fn test_check_non_utf8_filename() { use std::{ffi::OsString, os::unix::ffi::OsStringExt}; From f3763ef190eb5497e10a1fa89bc16028ff35fa4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 17 Nov 2024 14:09:25 +0100 Subject: [PATCH 3/8] feat(checksum): simplify get_expected_checksum - Rename the function to emphasize its goal - Do not pass the filename anymore, as it is only used to create an error, that may be done in the scope calling the function - Change the return type to Option, as the error is made in the outer scope - Don't try to decode the base64 string as UTF8 string. This most oftenly fails and is wrong. - Get rid of the `bytes_to_hex` function, as it provides the same functionality as `hex::encode` --- src/uucore/src/lib/features/checksum.rs | 56 ++++++++----------------- 1 file changed, 17 insertions(+), 39 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 160644046..8dce955fc 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -441,43 +441,15 @@ fn determine_regex(lines: &[OsString]) -> Option<(Regex, bool)> { None } -// Converts bytes to a hexadecimal string -fn bytes_to_hex(bytes: &[u8]) -> String { - use std::fmt::Write; - bytes - .iter() - .fold(String::with_capacity(bytes.len() * 2), |mut hex, byte| { - write!(hex, "{byte:02x}").unwrap(); - hex - }) -} +/// Extract the expected digest from the checksum string +fn get_expected_digest_as_hexa_string(caps: &Captures, chosen_regex: &Regex) -> Option { + // Unwraps are safe, ensured by regex. + let ck = caps.name("checksum").unwrap().as_bytes(); -fn get_expected_checksum( - filename: &[u8], - caps: &Captures, - chosen_regex: &Regex, -) -> UResult { if chosen_regex.as_str() == ALGO_BASED_REGEX_BASE64 { - // Unwrap is safe, ensured by regex - let ck = caps.name("checksum").unwrap().as_bytes(); - match BASE64.decode(ck) { - Ok(decoded_bytes) => { - match std::str::from_utf8(&decoded_bytes) { - Ok(decoded_str) => Ok(decoded_str.to_string()), - Err(_) => Ok(bytes_to_hex(&decoded_bytes)), // Handle as raw bytes if not valid UTF-8 - } - } - Err(_) => Err(Box::new( - ChecksumError::NoProperlyFormattedChecksumLinesFound { - filename: String::from_utf8_lossy(filename).to_string(), - }, - )), - } + BASE64.decode(ck).map(hex::encode).ok() } else { - // Unwraps are safe, ensured by regex. - Ok(str::from_utf8(caps.name("checksum").unwrap().as_bytes()) - .unwrap() - .to_string()) + Some(str::from_utf8(ck).unwrap().to_string()) } } @@ -631,7 +603,13 @@ fn process_checksum_line( filename_to_check = &filename_to_check[1..]; } - let expected_checksum = get_expected_checksum(filename_to_check, &caps, chosen_regex)?; + let expected_checksum = get_expected_digest_as_hexa_string(&caps, chosen_regex).ok_or( + LineCheckError::UError(Box::new( + ChecksumError::NoProperlyFormattedChecksumLinesFound { + filename: String::from_utf8_lossy(filename_to_check).to_string(), + }, + )), + )?; // If the algo_name is provided, we use it, otherwise we try to detect it let (algo_name, length) = if is_algo_based_format { @@ -1250,13 +1228,13 @@ mod tests { } #[test] - fn test_get_expected_checksum() { + fn test_get_expected_digest() { let re = Regex::new(ALGO_BASED_REGEX_BASE64).unwrap(); let caps = re .captures(b"SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") .unwrap(); - let result = get_expected_checksum(b"filename", &caps, &re); + let result = get_expected_digest_as_hexa_string(&caps, &re); assert_eq!( result.unwrap(), @@ -1271,9 +1249,9 @@ mod tests { .captures(b"SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU") .unwrap(); - let result = get_expected_checksum(b"filename", &caps, &re); + let result = get_expected_digest_as_hexa_string(&caps, &re); - assert!(result.is_err()); + assert!(result.is_none()); } #[test] From a0af49f2d8576e8e17ce9653abb5bd492c70d6db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 17 Nov 2024 15:52:11 +0100 Subject: [PATCH 4/8] feat(checksum): get rid of the properly_formatted variable --- src/uucore/src/lib/features/checksum.rs | 78 ++++++++++--------------- 1 file changed, 30 insertions(+), 48 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 8dce955fc..386da1f71 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -174,8 +174,6 @@ pub enum ChecksumError { CombineMultipleAlgorithms, #[error("Needs an algorithm to hash with.\nUse --help for more information.")] NeedAlgorithmToHash, - #[error("{filename}: no properly formatted checksum lines found")] - NoProperlyFormattedChecksumLinesFound { filename: String }, } impl UError for ChecksumError { @@ -241,6 +239,12 @@ fn cksum_output(res: &ChecksumResult, status: bool) { } } +/// Print a "no properly formatted lines" message in stderr +#[inline] +fn log_no_properly_formatted(filename: String) { + show_error!("{filename}: no properly formatted checksum lines found"); +} + /// Represents the different outcomes that can happen to a file /// that is being checked. #[derive(Debug, Clone, Copy)] @@ -442,7 +446,7 @@ fn determine_regex(lines: &[OsString]) -> Option<(Regex, bool)> { } /// Extract the expected digest from the checksum string -fn get_expected_digest_as_hexa_string(caps: &Captures, chosen_regex: &Regex) -> Option { +fn get_expected_digest_as_hex_string(caps: &Captures, chosen_regex: &Regex) -> Option { // Unwraps are safe, ensured by regex. let ck = caps.name("checksum").unwrap().as_bytes(); @@ -528,8 +532,6 @@ fn get_input_file(filename: &OsStr) -> UResult> { fn identify_algo_name_and_length( caps: &Captures, algo_name_input: Option<&str>, - res: &mut ChecksumResult, - properly_formatted: &mut bool, ) -> Option<(String, Option)> { // When the algo-based format is matched, extract details from regex captures let algorithm = caps @@ -543,14 +545,11 @@ fn identify_algo_name_and_length( // (for example SHA1 (f) = d...) // Also handle the case cksum -s sm3 but the file contains other formats if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { - res.bad_format += 1; - *properly_formatted = false; return None; } if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { // Not supported algo, leave early - *properly_formatted = false; return None; } @@ -562,7 +561,6 @@ fn identify_algo_name_and_length( if bits_value % 8 == 0 { Some(Some(bits_value / 8)) } else { - *properly_formatted = false; None // Return None to signal a divisibility issue } })?; @@ -583,16 +581,12 @@ fn process_checksum_line( i: usize, chosen_regex: &Regex, is_algo_based_format: bool, - res: &mut ChecksumResult, cli_algo_name: Option<&str>, cli_algo_length: Option, - properly_formatted: &mut bool, opts: ChecksumOptions, ) -> Result<(), LineCheckError> { let line_bytes = os_str_as_bytes(line)?; if let Some(caps) = chosen_regex.captures(line_bytes) { - *properly_formatted = true; - let mut filename_to_check = caps.name("filename").unwrap().as_bytes(); if filename_to_check.starts_with(b"*") @@ -603,18 +597,13 @@ fn process_checksum_line( filename_to_check = &filename_to_check[1..]; } - let expected_checksum = get_expected_digest_as_hexa_string(&caps, chosen_regex).ok_or( - LineCheckError::UError(Box::new( - ChecksumError::NoProperlyFormattedChecksumLinesFound { - filename: String::from_utf8_lossy(filename_to_check).to_string(), - }, - )), - )?; + let expected_checksum = get_expected_digest_as_hex_string(&caps, chosen_regex) + .ok_or(LineCheckError::ImproperlyFormatted)?; // If the algo_name is provided, we use it, otherwise we try to detect it let (algo_name, length) = if is_algo_based_format { - identify_algo_name_and_length(&caps, cli_algo_name, res, properly_formatted) - .unwrap_or((String::new(), None)) + identify_algo_name_and_length(&caps, cli_algo_name) + .ok_or(LineCheckError::ImproperlyFormatted)? } else if let Some(a) = cli_algo_name { // When a specific algorithm name is input, use it and use the provided bits // except when dealing with blake2b, where we will detect the length @@ -628,16 +617,9 @@ fn process_checksum_line( } } else { // Default case if no algorithm is specified and non-algo based format is matched - (String::new(), None) + return Err(LineCheckError::ImproperlyFormatted); }; - if algo_name.is_empty() { - // we haven't been able to detect the algo name. No point to continue - *properly_formatted = false; - - // TODO: return error? - return Err(LineCheckError::ImproperlyFormatted); - } let mut algo = detect_algo(&algo_name, length)?; let (filename_to_check_unescaped, prefix) = unescape_filename(filename_to_check); @@ -689,7 +671,6 @@ fn process_checksum_line( ); } - res.bad_format += 1; Err(LineCheckError::ImproperlyFormatted) } } @@ -701,8 +682,9 @@ fn process_checksum_file( opts: ChecksumOptions, ) -> Result<(), FileCheckError> { let mut correct_format = 0; - let mut properly_formatted = false; + let mut properly_formatted_lines = 0; let mut res = ChecksumResult::default(); + let input_is_stdin = filename_input == OsStr::new("-"); let file: Box = if input_is_stdin { @@ -724,10 +706,7 @@ fn process_checksum_file( let lines = read_os_string_lines(reader).collect::>(); let Some((chosen_regex, is_algo_based_format)) = determine_regex(&lines) else { - let e = ChecksumError::NoProperlyFormattedChecksumLinesFound { - filename: get_filename_for_output(filename_input, input_is_stdin), - }; - show_error!("{e}"); + log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin)); set_exit_code(1); return Err(FileCheckError::AlgoDetectionError); }; @@ -739,21 +718,27 @@ fn process_checksum_file( i, &chosen_regex, is_algo_based_format, - &mut res, cli_algo_name, cli_algo_length, - &mut properly_formatted, opts, ) { - Ok(()) => correct_format += 1, - Err(LineCheckError::DigestMismatch) => res.failed_cksum += 1, + Ok(()) => { + correct_format += 1; + properly_formatted_lines += 1 + } + Err(LineCheckError::DigestMismatch) => { + res.failed_cksum += 1; + properly_formatted_lines += 1 + } Err(LineCheckError::UError(e)) => return Err(e.into()), Err(LineCheckError::Skipped) => continue, - Err(LineCheckError::ImproperlyFormatted) => (), + Err(LineCheckError::ImproperlyFormatted) => res.bad_format += 1, Err(LineCheckError::CantOpenFile | LineCheckError::FileIsDirectory) => { + properly_formatted_lines += 1; res.failed_open_file += 1 } Err(LineCheckError::FileNotFound) => { + properly_formatted_lines += 1; if !opts.ignore_missing { res.failed_open_file += 1 } @@ -763,12 +748,9 @@ fn process_checksum_file( // not a single line correctly formatted found // return an error - if !properly_formatted { + if properly_formatted_lines == 0 { if !opts.status { - return Err(ChecksumError::NoProperlyFormattedChecksumLinesFound { - filename: get_filename_for_output(filename_input, input_is_stdin), - } - .into()); + log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin)); } set_exit_code(1); return Err(FileCheckError::ImproperlyFormatted); @@ -1234,7 +1216,7 @@ mod tests { .captures(b"SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=") .unwrap(); - let result = get_expected_digest_as_hexa_string(&caps, &re); + let result = get_expected_digest_as_hex_string(&caps, &re); assert_eq!( result.unwrap(), @@ -1249,7 +1231,7 @@ mod tests { .captures(b"SHA256 (empty) = 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU") .unwrap(); - let result = get_expected_digest_as_hexa_string(&caps, &re); + let result = get_expected_digest_as_hex_string(&caps, &re); assert!(result.is_none()); } From 7c4724edc32dd4ecfd7d16051eb074fb3bcc0ea4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 17 Nov 2024 16:28:53 +0100 Subject: [PATCH 5/8] feat(checksum): refactor ChecksumResult to include more counters in it - Add comments to explain what each field is counting --- src/uucore/src/lib/features/checksum.rs | 71 ++++++++++++++----------- 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 386da1f71..8c435afed 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -68,11 +68,27 @@ pub struct HashAlgorithm { pub bits: usize, } +/// This structure holds the count of checksum test lines' outcomes. #[derive(Default)] struct ChecksumResult { - pub bad_format: i32, - pub failed_cksum: i32, - pub failed_open_file: i32, + /// Number of lines in the file where the computed checksum MATCHES + /// the expectation. + pub correct: u32, + /// Number of lines in the file where the computed checksum DIFFERS + /// from the expectation. + pub failed_cksum: u32, + pub failed_open_file: u32, + /// Number of improperly formatted lines. + pub bad_format: u32, + /// Total number of non-empty, non-comment lines. + pub total: u32, +} + +impl ChecksumResult { + #[inline] + fn total_properly_formatted(&self) -> u32 { + self.total - self.bad_format + } } /// Represents a reason for which the processing of a checksum line @@ -681,8 +697,6 @@ fn process_checksum_file( cli_algo_length: Option, opts: ChecksumOptions, ) -> Result<(), FileCheckError> { - let mut correct_format = 0; - let mut properly_formatted_lines = 0; let mut res = ChecksumResult::default(); let input_is_stdin = filename_input == OsStr::new("-"); @@ -712,7 +726,7 @@ fn process_checksum_file( }; for (i, line) in lines.iter().enumerate() { - match process_checksum_line( + let line_result = process_checksum_line( filename_input, line, i, @@ -721,34 +735,31 @@ fn process_checksum_file( cli_algo_name, cli_algo_length, opts, - ) { - Ok(()) => { - correct_format += 1; - properly_formatted_lines += 1 - } - Err(LineCheckError::DigestMismatch) => { - res.failed_cksum += 1; - properly_formatted_lines += 1 - } - Err(LineCheckError::UError(e)) => return Err(e.into()), - Err(LineCheckError::Skipped) => continue, - Err(LineCheckError::ImproperlyFormatted) => res.bad_format += 1, - Err(LineCheckError::CantOpenFile | LineCheckError::FileIsDirectory) => { - properly_formatted_lines += 1; - res.failed_open_file += 1 - } - Err(LineCheckError::FileNotFound) => { - properly_formatted_lines += 1; - if !opts.ignore_missing { - res.failed_open_file += 1 - } - } + ); + + // Match a first time to elude critical UErrors, and increment the total + // in all cases except on skipped. + use LineCheckError::*; + match line_result { + Err(UError(e)) => return Err(e.into()), + Err(Skipped) => (), + _ => res.total += 1, + } + + // Match a second time to update the right field of `res`. + match line_result { + Ok(()) => res.correct += 1, + Err(DigestMismatch) => res.failed_cksum += 1, + Err(ImproperlyFormatted) => res.bad_format += 1, + Err(CantOpenFile | FileIsDirectory) => res.failed_open_file += 1, + Err(FileNotFound) if !opts.ignore_missing => res.failed_open_file += 1, + _ => continue, }; } // not a single line correctly formatted found // return an error - if properly_formatted_lines == 0 { + if res.total_properly_formatted() == 0 { if !opts.status { log_no_properly_formatted(get_filename_for_output(filename_input, input_is_stdin)); } @@ -759,7 +770,7 @@ fn process_checksum_file( // if any incorrectly formatted line, show it cksum_output(&res, opts.status); - if opts.ignore_missing && correct_format == 0 { + if opts.ignore_missing && res.correct == 0 { // we have only bad format // and we had ignore-missing eprintln!( From ba7c02860e30120d0a64aaf17d97bb0b3d2a8ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 17 Nov 2024 23:16:16 +0100 Subject: [PATCH 6/8] feat(checksum): odd number of hexa characters is wrong formatting --- src/uucore/src/lib/features/checksum.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 8c435afed..1fbf201e6 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -468,8 +468,12 @@ fn get_expected_digest_as_hex_string(caps: &Captures, chosen_regex: &Regex) -> O if chosen_regex.as_str() == ALGO_BASED_REGEX_BASE64 { BASE64.decode(ck).map(hex::encode).ok() - } else { + } else if ck.len() % 2 == 0 { Some(str::from_utf8(ck).unwrap().to_string()) + } else { + // If the length of the digest is not a multiple of 2, then it + // must be improperly formatted (1 hex digit is 2 characters) + None } } From 8c4f595f2414d9dc0aa97e75386bba6f2b76ff6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Sun, 17 Nov 2024 16:58:17 +0100 Subject: [PATCH 7/8] test(cksum): rework test for improperly formatted keeps processing --- tests/by-util/test_cksum.rs | 76 +++++++++++++++++++++++++++---------- 1 file changed, 57 insertions(+), 19 deletions(-) diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 86de7ea95..bf74de9cc 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -1403,12 +1403,13 @@ fn test_check_trailing_space_fails() { /// in checksum files. /// These tests are excluded from Windows because it does not provide any safe /// conversion between `OsString` and byte sequences for non-utf-8 strings. -#[cfg(not(windows))] mod check_utf8 { - use super::*; + // This test should pass on linux and macos. + #[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\ @@ -1431,8 +1432,12 @@ mod check_utf8 { .no_stderr(); } + // This test should pass on linux. Windows and macos will fail to + // create a file which name contains '\xff'. + #[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!()); @@ -1569,35 +1574,68 @@ fn test_check_mix_hex_base64() { .stdout_only("foo1.dat: OK\nfoo2.dat: OK\n"); } -#[ignore = "not yet implemented"] +/// This test ensures that an improperly formatted base64 checksum in a file +/// does not interrupt the processing of next lines. #[test] -fn test_check_incorrectly_formatted_checksum_does_not_stop_processing() { - // The first line contains an incorrectly formatted checksum that can't be - // correctly decoded. This must not prevent the program from looking at the - // rest of the file. - let lines = [ - "BLAKE2b-56 (foo1) = GFYEQ7HhAw=", // Should be 2 '=' at the end - "BLAKE2b-56 (foo2) = 18560443b1e103", // OK - ]; - +fn test_check_incorrectly_formatted_checksum_keeps_processing_b64() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; + at.touch("f"); - at.write("foo1", "foo"); - at.write("foo2", "foo"); - at.write("sum", &lines.join("\n")); + let good_ck = "MD5 (f) = 1B2M2Y8AsgTpgAmY7PhCfg=="; // OK + let bad_ck = "MD5 (f) = 1B2M2Y8AsgTpgAmY7PhCfg="; // Missing last '=' + // Good then Bad scene .ucmd() .arg("--check") - .arg(at.subdir.join("sum")) + .pipe_in([good_ck, bad_ck].join("\n").as_bytes().to_vec()) .succeeds() - .stderr_contains("1 line is improperly formatted") - .stdout_contains("foo2: OK"); + .stdout_contains("f: OK") + .stderr_contains("cksum: WARNING: 1 line is improperly formatted"); + + // Bad then Good + 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"); +} + +/// This test ensures that an improperly formatted hexadecimal checksum in a +/// file does not interrupt the processing of next lines. +#[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"; // OK + let bad_ck = "MD5 (f) = d41d8cd98f00b204e9800998ecf8427"; // Missing last + + // Good then Bad + 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"); + + // Bad then Good + 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"); } /// This module reimplements the cksum-base64.pl GNU test. -mod cksum_base64 { +mod gnu_cksum_base64 { use super::*; use crate::common::util::log_info; From cfc66f9f6fe8df12b3378c6d6416fe28689ff217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dorian=20P=C3=A9ron?= Date: Tue, 26 Nov 2024 01:46:35 +0100 Subject: [PATCH 8/8] chore(checksum): fix clippy warnings in tests --- src/uucore/src/lib/features/checksum.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 1fbf201e6..f7228830b 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -1056,7 +1056,7 @@ mod tests { ]; for (input, expected) in test_cases { - let captures = algo_based_regex.captures(*input); + let captures = algo_based_regex.captures(input); match expected { Some((algo, bits, filename, checksum)) => { assert!(captures.is_some()); @@ -1206,7 +1206,7 @@ mod tests { // Test leading space before checksum line let lines_algo_based_leading_space = - vec![" MD5 (example.txt) = d41d8cd98f00b204e9800998ecf8427e"] + [" MD5 (example.txt) = d41d8cd98f00b204e9800998ecf8427e"] .iter() .map(|s| OsString::from(s.to_string())) .collect::>(); @@ -1216,7 +1216,7 @@ mod tests { // Test trailing space after checksum line (should fail) let lines_algo_based_leading_space = - vec!["MD5 (example.txt) = d41d8cd98f00b204e9800998ecf8427e "] + ["MD5 (example.txt) = d41d8cd98f00b204e9800998ecf8427e "] .iter() .map(|s| OsString::from(s.to_string())) .collect::>();