1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-07-28 19:47:45 +00:00

truncate: add error handling for SIZE argument

* add tests for SIZE argument
* fix clap argument handling for --size and --reference
This commit is contained in:
Jan Scheer 2021-06-02 22:08:42 +02:00
parent 2f5f7c6fa1
commit 5898b99627
2 changed files with 55 additions and 15 deletions

View file

@ -61,10 +61,9 @@ pub mod options {
pub static NO_CREATE: &str = "no-create"; pub static NO_CREATE: &str = "no-create";
pub static REFERENCE: &str = "reference"; pub static REFERENCE: &str = "reference";
pub static SIZE: &str = "size"; pub static SIZE: &str = "size";
pub static ARG_FILES: &str = "files";
} }
static ARG_FILES: &str = "files";
fn get_usage() -> String { fn get_usage() -> String {
format!("{0} [OPTION]... [FILE]...", executable!()) format!("{0} [OPTION]... [FILE]...", executable!())
} }
@ -116,21 +115,23 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
Arg::with_name(options::REFERENCE) Arg::with_name(options::REFERENCE)
.short("r") .short("r")
.long(options::REFERENCE) .long(options::REFERENCE)
.required_unless(options::SIZE)
.help("base the size of each file on the size of RFILE") .help("base the size of each file on the size of RFILE")
.value_name("RFILE") .value_name("RFILE")
) )
.arg( .arg(
Arg::with_name(options::SIZE) Arg::with_name(options::SIZE)
.short("s") .short("s")
.long("size") .long(options::SIZE)
.required_unless(options::REFERENCE)
.help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified") .help("set or adjust the size of each file according to SIZE, which is in bytes unless --io-blocks is specified")
.value_name("SIZE") .value_name("SIZE")
) )
.arg(Arg::with_name(ARG_FILES).multiple(true).takes_value(true).min_values(1)) .arg(Arg::with_name(options::ARG_FILES).value_name("FILE").multiple(true).takes_value(true).required(true).min_values(1))
.get_matches_from(args); .get_matches_from(args);
let files: Vec<String> = matches let files: Vec<String> = matches
.values_of(ARG_FILES) .values_of(options::ARG_FILES)
.map(|v| v.map(ToString::to_string).collect()) .map(|v| v.map(ToString::to_string).collect())
.unwrap_or_default(); .unwrap_or_default();
@ -152,8 +153,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
crash!( crash!(
1, 1,
"cannot stat '{}': No such file or directory", "cannot stat '{}': No such file or directory",
reference.unwrap() reference.unwrap_or("".to_string())
); ); // TODO: fix '--no-create' see test_reference and test_truncate_bytes_size
} }
_ => crash!(1, "{}", e.to_string()), _ => crash!(1, "{}", e.to_string()),
} }
@ -209,8 +210,7 @@ fn truncate_reference_and_size(
} }
_ => m, _ => m,
}, },
// TODO: handle errors ParseFailure(String)/SizeTooBig(String) Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
Err(_) => crash!(1, "Invalid number: {}", size_string),
}; };
let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap(); let fsize = usize::try_from(metadata(rfilename)?.len()).unwrap();
let tsize = mode.to_size(fsize); let tsize = mode.to_size(fsize);
@ -265,7 +265,7 @@ fn truncate_size_only(
) -> std::io::Result<()> { ) -> std::io::Result<()> {
let mode = match parse_mode_and_size(size_string) { let mode = match parse_mode_and_size(size_string) {
Ok(m) => m, Ok(m) => m,
Err(_) => crash!(1, "Invalid number: {}", size_string), Err(e) => crash!(1, "Invalid number: {}", e.to_string()),
}; };
for filename in &filenames { for filename in &filenames {
let fsize = usize::try_from(metadata(filename)?.len()).unwrap(); let fsize = usize::try_from(metadata(filename)?.len()).unwrap();
@ -294,7 +294,7 @@ fn truncate(
} }
(Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create), (Some(rfilename), None) => truncate_reference_file_only(&rfilename, filenames, create),
(None, Some(size_string)) => truncate_size_only(&size_string, filenames, create), (None, Some(size_string)) => truncate_size_only(&size_string, filenames, create),
(None, None) => crash!(1, "you must specify either --reference or --size"), (None, None) => crash!(1, "you must specify either --reference or --size"), // this case cannot happen anymore because it's handled by clap
} }
} }

View file

@ -48,7 +48,7 @@ fn test_reference() {
// manpage: "A FILE argument that does not exist is created." // manpage: "A FILE argument that does not exist is created."
// TODO: 'truncate' does not create the file in this case, // TODO: 'truncate' does not create the file in this case,
// but should because '--no-create' wasn't specified. // but should because '--no-create' wasn't specified.
at.touch(FILE1); // TODO: remove this when fixed at.touch(FILE1); // TODO: remove this when 'no-create' is fixed
scene.ucmd().arg("-s").arg("+5KB").arg(FILE1).succeeds(); scene.ucmd().arg("-s").arg("+5KB").arg(FILE1).succeeds();
scene scene
@ -240,11 +240,18 @@ fn test_size_and_reference() {
); );
} }
#[test]
fn test_error_filename_only() {
// truncate: you must specify either --size or --reference
new_ucmd!().args(&["file"]).fails().stderr_contains(
"error: The following required arguments were not provided:
--reference <RFILE>
--size <SIZE>",
);
}
#[test] #[test]
fn test_invalid_numbers() { fn test_invalid_numbers() {
// TODO For compatibility with GNU, `truncate -s 0X` should cause
// the same error as `truncate -s 0X file`, but currently it returns
// a different error.
new_ucmd!() new_ucmd!()
.args(&["-s", "0X", "file"]) .args(&["-s", "0X", "file"])
.fails() .fails()
@ -274,3 +281,36 @@ fn test_reference_with_size_file_not_found() {
.fails() .fails()
.stderr_contains("cannot stat 'a': No such file or directory"); .stderr_contains("cannot stat 'a': No such file or directory");
} }
#[test]
fn test_truncate_bytes_size() {
// TODO: this should succeed without error, uncomment when '--no-create' is fixed
// new_ucmd!()
// .args(&["--no-create", "--size", "K", "file"])
// .succeeds();
new_ucmd!()
.args(&["--size", "1024R", "file"])
.fails()
.code_is(1)
.stderr_only("truncate: Invalid number: 1024R");
#[cfg(not(target_pointer_width = "128"))]
new_ucmd!()
.args(&["--size", "1Y", "file"])
.fails()
.code_is(1)
.stderr_only("truncate: Invalid number: 1Y: Value too large for defined data type");
#[cfg(target_pointer_width = "32")]
{
let sizes = ["1000G", "10T"];
for size in &sizes {
new_ucmd!()
.args(&["--size", size, "file"])
.fails()
.code_is(1)
.stderr_only(format!(
"truncate: Invalid number: {}: Value too large for defined data type",
size
));
}
}
}