mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
cksum: implement check & strict
This commit is contained in:
parent
5f0b48e649
commit
81500ae3b8
4 changed files with 326 additions and 27 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -2494,6 +2494,7 @@ version = "0.0.26"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"hex",
|
"hex",
|
||||||
|
"regex",
|
||||||
"uucore",
|
"uucore",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ path = "src/cksum.rs"
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
uucore = { workspace = true, features = ["encoding", "sum"] }
|
uucore = { workspace = true, features = ["encoding", "sum"] }
|
||||||
hex = { workspace = true }
|
hex = { workspace = true }
|
||||||
|
regex = { workspace = true }
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "cksum"
|
name = "cksum"
|
||||||
|
|
|
@ -5,13 +5,18 @@
|
||||||
|
|
||||||
// spell-checker:ignore (ToDO) fname, algo
|
// spell-checker:ignore (ToDO) fname, algo
|
||||||
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
|
use clap::{crate_version, value_parser, Arg, ArgAction, Command};
|
||||||
|
use regex::Regex;
|
||||||
|
use std::cmp::Ordering;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
|
use std::io::BufRead;
|
||||||
use std::io::{self, stdin, stdout, BufReader, Read, Write};
|
use std::io::{self, stdin, stdout, BufReader, Read, Write};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
use uucore::error::set_exit_code;
|
||||||
|
use uucore::show_warning_caps;
|
||||||
use uucore::{
|
use uucore::{
|
||||||
encoding,
|
encoding,
|
||||||
error::{FromIo, UError, UResult, USimpleError},
|
error::{FromIo, UError, UResult, USimpleError},
|
||||||
|
@ -212,7 +217,8 @@ where
|
||||||
OutputFormat::Hexadecimal => sum_hex,
|
OutputFormat::Hexadecimal => sum_hex,
|
||||||
OutputFormat::Base64 => match options.algo_name {
|
OutputFormat::Base64 => match options.algo_name {
|
||||||
ALGORITHM_OPTIONS_CRC | ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => sum_hex,
|
ALGORITHM_OPTIONS_CRC | ALGORITHM_OPTIONS_SYSV | ALGORITHM_OPTIONS_BSD => sum_hex,
|
||||||
_ => encoding::encode(encoding::Format::Base64, &hex::decode(sum_hex).unwrap()).unwrap(),
|
_ => encoding::encode(encoding::Format::Base64, &hex::decode(sum_hex).unwrap())
|
||||||
|
.unwrap(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
// The BSD checksum output is 5 digit integer
|
// The BSD checksum output is 5 digit integer
|
||||||
|
@ -310,6 +316,7 @@ mod options {
|
||||||
pub const RAW: &str = "raw";
|
pub const RAW: &str = "raw";
|
||||||
pub const BASE64: &str = "base64";
|
pub const BASE64: &str = "base64";
|
||||||
pub const CHECK: &str = "check";
|
pub const CHECK: &str = "check";
|
||||||
|
pub const STRICT: &str = "strict";
|
||||||
pub const TEXT: &str = "text";
|
pub const TEXT: &str = "text";
|
||||||
pub const BINARY: &str = "binary";
|
pub const BINARY: &str = "binary";
|
||||||
}
|
}
|
||||||
|
@ -357,12 +364,8 @@ fn calculate_length(algo_name: &str, length: usize) -> UResult<Option<usize>> {
|
||||||
0 => Ok(None),
|
0 => Ok(None),
|
||||||
n if n % 8 != 0 => {
|
n if n % 8 != 0 => {
|
||||||
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
||||||
Err(io::Error::new(
|
Err(io::Error::new(io::ErrorKind::InvalidInput, "length is not a multiple of 8").into())
|
||||||
io::ErrorKind::InvalidInput,
|
}
|
||||||
"length is not a multiple of 8",
|
|
||||||
)
|
|
||||||
.into())
|
|
||||||
},
|
|
||||||
n if n > 512 => {
|
n if n > 512 => {
|
||||||
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
uucore::show_error!("invalid length: \u{2018}{length}\u{2019}");
|
||||||
Err(io::Error::new(
|
Err(io::Error::new(
|
||||||
|
@ -370,7 +373,7 @@ fn calculate_length(algo_name: &str, length: usize) -> UResult<Option<usize>> {
|
||||||
"maximum digest length for \u{2018}BLAKE2b\u{2019} is 512 bits",
|
"maximum digest length for \u{2018}BLAKE2b\u{2019} is 512 bits",
|
||||||
)
|
)
|
||||||
.into())
|
.into())
|
||||||
},
|
}
|
||||||
n => {
|
n => {
|
||||||
if algo_name == ALGORITHM_OPTIONS_BLAKE2B {
|
if algo_name == ALGORITHM_OPTIONS_BLAKE2B {
|
||||||
// Divide by 8, as our blake2b implementation expects bytes instead of bits.
|
// Divide by 8, as our blake2b implementation expects bytes instead of bits.
|
||||||
|
@ -391,12 +394,11 @@ fn calculate_length(algo_name: &str, length: usize) -> UResult<Option<usize>> {
|
||||||
* We handle this in this function to make sure they are self contained
|
* We handle this in this function to make sure they are self contained
|
||||||
* and "easier" to understand
|
* and "easier" to understand
|
||||||
*/
|
*/
|
||||||
fn handle_tag_text_binary_flags(matches: &clap::ArgMatches, check: bool) -> UResult<(bool, bool)> {
|
fn handle_tag_text_binary_flags(matches: &clap::ArgMatches) -> UResult<(bool, bool)> {
|
||||||
let untagged: bool = matches.get_flag(options::UNTAGGED);
|
let untagged: bool = matches.get_flag(options::UNTAGGED);
|
||||||
let tag: bool = matches.get_flag(options::TAG);
|
let tag: bool = matches.get_flag(options::TAG);
|
||||||
let tag: bool = tag || !untagged;
|
let tag: bool = tag || !untagged;
|
||||||
|
|
||||||
let text_flag: bool = matches.get_flag(options::TEXT);
|
|
||||||
let binary_flag: bool = matches.get_flag(options::BINARY);
|
let binary_flag: bool = matches.get_flag(options::BINARY);
|
||||||
|
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
@ -404,34 +406,158 @@ fn handle_tag_text_binary_flags(matches: &clap::ArgMatches, check: bool) -> URes
|
||||||
|
|
||||||
let asterisk: bool = prompt_asterisk(tag, binary_flag, had_reset);
|
let asterisk: bool = prompt_asterisk(tag, binary_flag, had_reset);
|
||||||
|
|
||||||
if (binary_flag || text_flag) && check {
|
Ok((tag, asterisk))
|
||||||
|
}
|
||||||
|
|
||||||
|
/***
|
||||||
|
* Do the checksum validation (can be strict or not)
|
||||||
|
*/
|
||||||
|
fn perform_checksum_validation<'a, I>(files: I, strict: bool) -> UResult<()>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = &'a OsStr>,
|
||||||
|
{
|
||||||
|
let re = Regex::new(r"(?P<algo>\w+)(-(?P<bits>\d+))? \((?P<filename>.*)\) = (?P<checksum>.*)")
|
||||||
|
.unwrap();
|
||||||
|
let mut properly_formatted = false;
|
||||||
|
let mut bad_format = 0;
|
||||||
|
let mut failed_cksum = 0;
|
||||||
|
let mut failed_open_file = 0;
|
||||||
|
for filename_input in files {
|
||||||
|
let input_is_stdin = filename_input == OsStr::new("-");
|
||||||
|
let file: Box<dyn Read> = if input_is_stdin {
|
||||||
|
Box::new(stdin()) // Use stdin if "-" is specified
|
||||||
|
} else {
|
||||||
|
match File::open(filename_input) {
|
||||||
|
Ok(f) => Box::new(f),
|
||||||
|
Err(err) => {
|
||||||
|
show!(err.map_err_context(|| format!(
|
||||||
|
"Failed to open file: {}",
|
||||||
|
filename_input.to_string_lossy()
|
||||||
|
)));
|
||||||
|
return Err(io::Error::new(io::ErrorKind::Other, "Failed to open file").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let reader = BufReader::new(file);
|
||||||
|
|
||||||
|
for line in reader.lines() {
|
||||||
|
let line = line?;
|
||||||
|
|
||||||
|
if let Some(caps) = re.captures(&line) {
|
||||||
|
properly_formatted = true;
|
||||||
|
let algo_name = caps.name("algo").unwrap().as_str().to_lowercase();
|
||||||
|
let filename_to_check = caps.name("filename").unwrap().as_str();
|
||||||
|
let expected_checksum = caps.name("checksum").unwrap().as_str();
|
||||||
|
|
||||||
|
let length = caps
|
||||||
|
.name("bits")
|
||||||
|
.map(|m| m.as_str().parse::<usize>().unwrap() / 8);
|
||||||
|
let (_, mut algo, bits) = detect_algo(&algo_name, length);
|
||||||
|
|
||||||
|
let file_to_check: Box<dyn Read> = if filename_to_check == "-" {
|
||||||
|
Box::new(stdin()) // Use stdin if "-" is specified in the checksum file
|
||||||
|
} else {
|
||||||
|
match File::open(filename_to_check) {
|
||||||
|
Ok(f) => Box::new(f),
|
||||||
|
Err(err) => {
|
||||||
|
show!(err.map_err_context(|| format!(
|
||||||
|
"Failed to open file: {}",
|
||||||
|
filename_to_check
|
||||||
|
)));
|
||||||
|
failed_open_file += 1;
|
||||||
|
// we could not open the file but we want to continue
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut file_reader = BufReader::new(file_to_check);
|
||||||
|
|
||||||
|
let (calculated_checksum, _) = digest_read(&mut algo, &mut file_reader, bits)
|
||||||
|
.map_err_context(|| "failed to read input".to_string())?;
|
||||||
|
if expected_checksum == calculated_checksum {
|
||||||
|
println!("{}: OK", filename_to_check);
|
||||||
|
} else {
|
||||||
|
println!("{}: FAILED", filename_to_check);
|
||||||
|
failed_cksum += 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bad_format += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not a single line correctly formatted found
|
||||||
|
// return an error
|
||||||
|
if !properly_formatted {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::Other,
|
||||||
"the --binary and --text options are meaningless when verifying checksums",
|
"no properly formatted checksum lines found",
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
Ok((tag, asterisk))
|
|
||||||
|
// strict means that we should have an exit code.
|
||||||
|
if strict && bad_format > 0 {
|
||||||
|
set_exit_code(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// if any incorrectly formatted line, show it
|
||||||
|
match bad_format.cmp(&1) {
|
||||||
|
Ordering::Equal => {
|
||||||
|
show_warning_caps!("{} line is improperly formatted", bad_format);
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
show_warning_caps!("{} lines are improperly formatted", bad_format);
|
||||||
|
}
|
||||||
|
Ordering::Less => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// if we have any failed checksum verification, we set an exit code
|
||||||
|
if failed_cksum > 0 || failed_open_file > 0 {
|
||||||
|
set_exit_code(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
match failed_open_file.cmp(&1) {
|
||||||
|
Ordering::Equal => {
|
||||||
|
show_warning_caps!("{} listed file could not be read", failed_open_file);
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
show_warning_caps!("{} listed files could not be read", failed_open_file);
|
||||||
|
}
|
||||||
|
Ordering::Less => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
match failed_cksum.cmp(&1) {
|
||||||
|
Ordering::Equal => {
|
||||||
|
show_warning_caps!("{} computed checksum did NOT match", failed_cksum);
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
show_warning_caps!("{} computed checksums did NOT match", failed_cksum);
|
||||||
|
}
|
||||||
|
Ordering::Less => {}
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[uucore::main]
|
#[uucore::main]
|
||||||
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
let matches = uu_app().try_get_matches_from(args)?;
|
let matches = uu_app().try_get_matches_from(args)?;
|
||||||
|
|
||||||
|
let check = matches.get_flag(options::CHECK);
|
||||||
|
|
||||||
let algo_name: &str = match matches.get_one::<String>(options::ALGORITHM) {
|
let algo_name: &str = match matches.get_one::<String>(options::ALGORITHM) {
|
||||||
Some(v) => v,
|
Some(v) => v,
|
||||||
None => ALGORITHM_OPTIONS_CRC,
|
None => {
|
||||||
|
if check {
|
||||||
|
// if we are doing a --check, we should not default to crc
|
||||||
|
""
|
||||||
|
} else {
|
||||||
|
ALGORITHM_OPTIONS_CRC
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let input_length = matches.get_one::<usize>(options::LENGTH);
|
|
||||||
|
|
||||||
let length = match input_length {
|
|
||||||
Some(length) => calculate_length(algo_name, *length)?,
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
let (tag, asterisk) = handle_tag_text_binary_flags(&matches, check)?;
|
|
||||||
|
|
||||||
if ["bsd", "crc", "sysv"].contains(&algo_name) && check {
|
if ["bsd", "crc", "sysv"].contains(&algo_name) && check {
|
||||||
return Err(io::Error::new(
|
return Err(io::Error::new(
|
||||||
io::ErrorKind::InvalidInput,
|
io::ErrorKind::InvalidInput,
|
||||||
|
@ -440,6 +566,34 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
|
||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if check {
|
||||||
|
let text_flag: bool = matches.get_flag(options::TEXT);
|
||||||
|
let binary_flag: bool = matches.get_flag(options::BINARY);
|
||||||
|
let strict = matches.get_flag(options::STRICT);
|
||||||
|
|
||||||
|
if (binary_flag || text_flag) && check {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidInput,
|
||||||
|
"the --binary and --text options are meaningless when verifying checksums",
|
||||||
|
)
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
return match matches.get_many::<String>(options::FILE) {
|
||||||
|
Some(files) => perform_checksum_validation(files.map(OsStr::new), strict),
|
||||||
|
None => perform_checksum_validation(iter::once(OsStr::new("-")), strict),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_length = matches.get_one::<usize>(options::LENGTH);
|
||||||
|
|
||||||
|
let length = match input_length {
|
||||||
|
Some(length) => calculate_length(algo_name, *length)?,
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (tag, asterisk) = handle_tag_text_binary_flags(&matches)?;
|
||||||
|
|
||||||
let (name, algo, bits) = detect_algo(algo_name, length);
|
let (name, algo, bits) = detect_algo(algo_name, length);
|
||||||
|
|
||||||
let output_format = if matches.get_flag(options::RAW) {
|
let output_format = if matches.get_flag(options::RAW) {
|
||||||
|
@ -532,12 +686,12 @@ pub fn uu_app() -> Command {
|
||||||
.help("emit a raw binary digest, not hexadecimal")
|
.help("emit a raw binary digest, not hexadecimal")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)
|
)
|
||||||
/*.arg(
|
.arg(
|
||||||
Arg::new(options::STRICT)
|
Arg::new(options::STRICT)
|
||||||
.long(options::STRICT)
|
.long(options::STRICT)
|
||||||
.help("exit non-zero for improperly formatted checksum lines")
|
.help("exit non-zero for improperly formatted checksum lines")
|
||||||
.action(ArgAction::SetTrue),
|
.action(ArgAction::SetTrue),
|
||||||
)*/
|
)
|
||||||
.arg(
|
.arg(
|
||||||
Arg::new(options::CHECK)
|
Arg::new(options::CHECK)
|
||||||
.short('c')
|
.short('c')
|
||||||
|
@ -577,8 +731,8 @@ pub fn uu_app() -> Command {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::had_reset;
|
use super::had_reset;
|
||||||
use crate::prompt_asterisk;
|
|
||||||
use crate::calculate_length;
|
use crate::calculate_length;
|
||||||
|
use crate::prompt_asterisk;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_had_reset() {
|
fn test_had_reset() {
|
||||||
|
|
|
@ -663,3 +663,146 @@ fn test_conflicting_options() {
|
||||||
)
|
)
|
||||||
.code_is(1);
|
.code_is(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[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()
|
||||||
|
.no_stdout()
|
||||||
|
.stderr_contains("cksum: no properly formatted checksum lines found")
|
||||||
|
.code_is(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cksum_check() {
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("CHECKSUM")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n");
|
||||||
|
scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("--check")
|
||||||
|
.arg("--strict")
|
||||||
|
.arg("CHECKSUM")
|
||||||
|
.succeeds()
|
||||||
|
.stdout_contains("f: OK\nf: OK\nf: OK\nf: OK\n");
|
||||||
|
// inject invalid content
|
||||||
|
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_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());
|
||||||
|
}
|
||||||
|
// inject invalid content
|
||||||
|
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");
|
||||||
|
|
||||||
|
// without strict, it passes
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
// inject invalid content
|
||||||
|
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("Failed to open file: input"));
|
||||||
|
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"));
|
||||||
|
|
||||||
|
// without strict
|
||||||
|
let result = scene.ucmd().arg("--check").arg("CHECKSUM").fails();
|
||||||
|
|
||||||
|
assert!(result.stderr_str().contains("Failed to open file: input"));
|
||||||
|
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"));
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue