diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 047d6889c86..19e8ad9db04 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -7,7 +7,6 @@ use std::ffi::{OsStr, OsString}; use std::iter; -use std::num::ParseIntError; use std::path::Path; use clap::builder::ValueParser; @@ -19,7 +18,10 @@ use uucore::checksum::compute::{ use uucore::checksum::validate::{ ChecksumValidateOptions, ChecksumVerbose, perform_checksum_validation, }; -use uucore::checksum::{AlgoKind, ChecksumError, SizedAlgoKind, calculate_blake2b_length_str}; +use uucore::checksum::{ + AlgoKind, ChecksumError, SizedAlgoKind, calculate_blake2b_length_str, + sanitize_sha2_sha3_length_str, +}; use uucore::error::UResult; use uucore::line_ending::LineEnding; use uucore::{format_usage, translate}; @@ -74,9 +76,11 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Optio set_or_err((AlgoKind::Blake3, None))?; } if matches.get_flag("sha3") { - match matches.get_one::("bits") { - Some(bits @ (224 | 256 | 384 | 512)) => set_or_err((AlgoKind::Sha3, Some(*bits)))?, - Some(bits) => return Err(ChecksumError::InvalidLengthForSha(bits.to_string()).into()), + match matches.get_one::(options::LENGTH) { + Some(len) => set_or_err(( + AlgoKind::Sha3, + Some(sanitize_sha2_sha3_length_str(AlgoKind::Sha3, len)?), + ))?, None => return Err(ChecksumError::LengthRequired("SHA3".into()).into()), } } @@ -93,16 +97,10 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Optio set_or_err((AlgoKind::Sha3, Some(512)))?; } if matches.get_flag("shake128") { - match matches.get_one::("bits") { - Some(bits) => set_or_err((AlgoKind::Shake128, Some(*bits)))?, - None => return Err(ChecksumError::LengthRequired("SHAKE128".into()).into()), - } + set_or_err((AlgoKind::Shake128, Some(128)))?; } if matches.get_flag("shake256") { - match matches.get_one::("bits") { - Some(bits) => set_or_err((AlgoKind::Shake256, Some(*bits)))?, - None => return Err(ChecksumError::LengthRequired("SHAKE256".into()).into()), - } + set_or_err((AlgoKind::Shake256, Some(256)))?; } if alg.is_none() { @@ -112,11 +110,6 @@ fn create_algorithm_from_flags(matches: &ArgMatches) -> UResult<(AlgoKind, Optio Ok(alg.unwrap()) } -// TODO: return custom error type -fn parse_bit_num(arg: &str) -> Result { - arg.parse() -} - #[uucore::main] pub fn uumain(mut args: impl uucore::Args) -> UResult<()> { // if there is no program name for some reason, default to "hashsum" @@ -139,17 +132,16 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> { // least somewhat better from a user's perspective. let matches = uucore::clap_localization::handle_clap_result(command, args)?; - let input_length: Option<&String> = if binary_name == "b2sum" { - matches.get_one::(options::LENGTH) + let length: Option = if binary_name == "b2sum" { + if let Some(len) = matches.get_one::(options::LENGTH) { + calculate_blake2b_length_str(len)? + } else { + None + } } else { None }; - let length = match input_length { - Some(length) => calculate_blake2b_length_str(length)?, - None => None, - }; - let (algo_kind, length) = if is_hashsum_bin { create_algorithm_from_flags(&matches)? } else { @@ -371,24 +363,8 @@ fn uu_app_opt_length(command: Command) -> Command { ) } -pub fn uu_app_bits() -> Command { - uu_app_opt_bits(uu_app_common()) -} - -fn uu_app_opt_bits(command: Command) -> Command { - // Needed for variable-length output sums (e.g. SHAKE) - command.arg( - Arg::new("bits") - .long("bits") - .help(translate!("hashsum-help-bits")) - .value_name("BITS") - // XXX: should we actually use validators? they're not particularly efficient - .value_parser(parse_bit_num), - ) -} - pub fn uu_app_custom() -> Command { - let mut command = uu_app_opt_bits(uu_app_common()); + let mut command = uu_app_opt_length(uu_app_common()); let algorithms = &[ ("md5", translate!("hashsum-help-md5")), ("sha1", translate!("hashsum-help-sha1")), diff --git a/tests/by-util/test_hashsum.rs b/tests/by-util/test_hashsum.rs index e39fe429efb..2f1719b0eca 100644 --- a/tests/by-util/test_hashsum.rs +++ b/tests/by-util/test_hashsum.rs @@ -16,86 +16,206 @@ macro_rules! get_hash( ); macro_rules! test_digest { - ($($id:ident $t:ident $size:expr)*) => ($( - - mod $id { - use uutests::util::*; - use uutests::util_name; - static DIGEST_ARG: &'static str = concat!("--", stringify!($t)); - static BITS_ARG: &'static str = concat!("--bits=", stringify!($size)); - static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected"); - static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile"); - static INPUT_FILE: &'static str = "input.txt"; - - #[test] - fn test_single_file() { - let ts = TestScenario::new(util_name!()); - assert_eq!(ts.fixtures.read(EXPECTED_FILE), - get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg(INPUT_FILE).succeeds().no_stderr().stdout_str())); + ($id:ident, $t:ident) => { + mod $id { + use uutests::util::*; + use uutests::util_name; + static DIGEST_ARG: &'static str = concat!("--", stringify!($t)); + static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected"); + static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile"); + static INPUT_FILE: &'static str = "input.txt"; + + #[test] + fn test_single_file() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read(EXPECTED_FILE), + get_hash!( + ts.ucmd() + .arg(DIGEST_ARG) + .arg(INPUT_FILE) + .succeeds() + .no_stderr() + .stdout_str() + ) + ); + } + + #[test] + fn test_stdin() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read(EXPECTED_FILE), + get_hash!( + ts.ucmd() + .arg(DIGEST_ARG) + .pipe_in_fixture(INPUT_FILE) + .succeeds() + .no_stderr() + .stdout_str() + ) + ); + } + + #[test] + fn test_check() { + let ts = TestScenario::new(util_name!()); + println!("File content='{}'", ts.fixtures.read(INPUT_FILE)); + println!("Check file='{}'", ts.fixtures.read(CHECK_FILE)); + + ts.ucmd() + .args(&[DIGEST_ARG, "--check", CHECK_FILE]) + .succeeds() + .no_stderr() + .stdout_is("input.txt: OK\n"); + } + + #[test] + fn test_zero() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read(EXPECTED_FILE), + get_hash!( + ts.ucmd() + .arg(DIGEST_ARG) + .arg("--zero") + .arg(INPUT_FILE) + .succeeds() + .no_stderr() + .stdout_str() + ) + ); + } + + #[test] + fn test_missing_file() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.write("a", "file1\n"); + at.write("c", "file3\n"); + + ts.ucmd() + .args(&[DIGEST_ARG, "a", "b", "c"]) + .fails() + .stdout_contains("a\n") + .stdout_contains("c\n") + .stderr_contains("b: No such file or directory"); + } } + }; +} - #[test] - fn test_stdin() { - let ts = TestScenario::new(util_name!()); - assert_eq!(ts.fixtures.read(EXPECTED_FILE), - get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).pipe_in_fixture(INPUT_FILE).succeeds().no_stderr().stdout_str())); - } - - #[test] - fn test_check() { - let ts = TestScenario::new(util_name!()); - println!("File content='{}'", ts.fixtures.read(INPUT_FILE)); - println!("Check file='{}'", ts.fixtures.read(CHECK_FILE)); - - ts.ucmd() - .args(&[DIGEST_ARG, BITS_ARG, "--check", CHECK_FILE]) - .succeeds() - .no_stderr() - .stdout_is("input.txt: OK\n"); - } - - #[test] - fn test_zero() { - let ts = TestScenario::new(util_name!()); - assert_eq!(ts.fixtures.read(EXPECTED_FILE), - get_hash!(ts.ucmd().arg(DIGEST_ARG).arg(BITS_ARG).arg("--zero").arg(INPUT_FILE).succeeds().no_stderr().stdout_str())); - } - - #[test] - fn test_missing_file() { - let ts = TestScenario::new(util_name!()); - let at = &ts.fixtures; - - at.write("a", "file1\n"); - at.write("c", "file3\n"); - - ts.ucmd() - .args(&[DIGEST_ARG, BITS_ARG, "a", "b", "c"]) - .fails() - .stdout_contains("a\n") - .stdout_contains("c\n") - .stderr_contains("b: No such file or directory"); +macro_rules! test_digest_with_len { + ($id:ident, $t:ident, $size:expr) => { + mod $id { + use uutests::util::*; + use uutests::util_name; + static DIGEST_ARG: &'static str = concat!("--", stringify!($t)); + static LENGTH_ARG: &'static str = concat!("--length=", stringify!($size)); + static EXPECTED_FILE: &'static str = concat!(stringify!($id), ".expected"); + static CHECK_FILE: &'static str = concat!(stringify!($id), ".checkfile"); + static INPUT_FILE: &'static str = "input.txt"; + + #[test] + fn test_single_file() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read(EXPECTED_FILE), + get_hash!( + ts.ucmd() + .arg(DIGEST_ARG) + .arg(LENGTH_ARG) + .arg(INPUT_FILE) + .succeeds() + .no_stderr() + .stdout_str() + ) + ); + } + + #[test] + fn test_stdin() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read(EXPECTED_FILE), + get_hash!( + ts.ucmd() + .arg(DIGEST_ARG) + .arg(LENGTH_ARG) + .pipe_in_fixture(INPUT_FILE) + .succeeds() + .no_stderr() + .stdout_str() + ) + ); + } + + #[test] + fn test_check() { + let ts = TestScenario::new(util_name!()); + println!("File content='{}'", ts.fixtures.read(INPUT_FILE)); + println!("Check file='{}'", ts.fixtures.read(CHECK_FILE)); + + ts.ucmd() + .args(&[DIGEST_ARG, LENGTH_ARG, "--check", CHECK_FILE]) + .succeeds() + .no_stderr() + .stdout_is("input.txt: OK\n"); + } + + #[test] + fn test_zero() { + let ts = TestScenario::new(util_name!()); + assert_eq!( + ts.fixtures.read(EXPECTED_FILE), + get_hash!( + ts.ucmd() + .arg(DIGEST_ARG) + .arg(LENGTH_ARG) + .arg("--zero") + .arg(INPUT_FILE) + .succeeds() + .no_stderr() + .stdout_str() + ) + ); + } + + #[test] + fn test_missing_file() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + + at.write("a", "file1\n"); + at.write("c", "file3\n"); + + ts.ucmd() + .args(&[DIGEST_ARG, LENGTH_ARG, "a", "b", "c"]) + .fails() + .stdout_contains("a\n") + .stdout_contains("c\n") + .stderr_contains("b: No such file or directory"); + } } - } - )*) + }; } -test_digest! { - md5 md5 128 - sha1 sha1 160 - sha224 sha224 224 - sha256 sha256 256 - sha384 sha384 384 - sha512 sha512 512 - sha3_224 sha3 224 - sha3_256 sha3 256 - sha3_384 sha3 384 - sha3_512 sha3 512 - shake128_256 shake128 256 - shake256_512 shake256 512 - b2sum b2sum 512 - b3sum b3sum 256 -} +test_digest! {md5, md5} +test_digest! {sha1, sha1} +test_digest! {b3sum, b3sum} +test_digest! {shake128, shake128} +test_digest! {shake256, shake256} + +test_digest_with_len! {sha224, sha224, 224} +test_digest_with_len! {sha256, sha256, 256} +test_digest_with_len! {sha384, sha384, 384} +test_digest_with_len! {sha512, sha512, 512} +test_digest_with_len! {sha3_224, sha3, 224} +test_digest_with_len! {sha3_256, sha3, 256} +test_digest_with_len! {sha3_384, sha3, 384} +test_digest_with_len! {sha3_512, sha3, 512} +test_digest_with_len! {b2sum, b2sum, 512} #[test] fn test_check_sha1() { @@ -1037,7 +1157,6 @@ fn test_sha256_binary() { get_hash!( ts.ucmd() .arg("--sha256") - .arg("--bits=256") .arg("binary.png") .succeeds() .no_stderr() @@ -1054,7 +1173,6 @@ fn test_sha256_stdin_binary() { get_hash!( ts.ucmd() .arg("--sha256") - .arg("--bits=256") .pipe_in_fixture("binary.png") .succeeds() .no_stderr() @@ -1068,12 +1186,7 @@ fn test_sha256_stdin_binary() { #[cfg_attr(windows, ignore = "Discussion is in #9168")] fn test_check_sha256_binary() { new_ucmd!() - .args(&[ - "--sha256", - "--bits=256", - "--check", - "binary.sha256.checkfile", - ]) + .args(&["--sha256", "--check", "binary.sha256.checkfile"]) .succeeds() .no_stderr() .stdout_is("binary.png: OK\n"); diff --git a/tests/fixtures/hashsum/shake128_256.checkfile b/tests/fixtures/hashsum/shake128.checkfile similarity index 100% rename from tests/fixtures/hashsum/shake128_256.checkfile rename to tests/fixtures/hashsum/shake128.checkfile diff --git a/tests/fixtures/hashsum/shake128_256.expected b/tests/fixtures/hashsum/shake128.expected similarity index 100% rename from tests/fixtures/hashsum/shake128_256.expected rename to tests/fixtures/hashsum/shake128.expected diff --git a/tests/fixtures/hashsum/shake256_512.checkfile b/tests/fixtures/hashsum/shake256.checkfile similarity index 100% rename from tests/fixtures/hashsum/shake256_512.checkfile rename to tests/fixtures/hashsum/shake256.checkfile diff --git a/tests/fixtures/hashsum/shake256_512.expected b/tests/fixtures/hashsum/shake256.expected similarity index 100% rename from tests/fixtures/hashsum/shake256_512.expected rename to tests/fixtures/hashsum/shake256.expected