tail: Fix parsing of sleep interval. Use duration parser from fundu crate.

Activate tests for parsing sleep interval
This commit is contained in:
Joining7943 2023-02-06 10:13:31 +01:00
parent d9f05f4c52
commit 0ed6a9f882
5 changed files with 48 additions and 37 deletions

7
Cargo.lock generated
View file

@ -878,6 +878,12 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "fundu"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925250bc259498d4008ee072bf16586083ab2c491aa4b06b3c4d0a6556cebd74"
[[package]] [[package]]
name = "futures" name = "futures"
version = "0.3.25" version = "0.3.25"
@ -3094,6 +3100,7 @@ version = "0.0.17"
dependencies = [ dependencies = [
"atty", "atty",
"clap", "clap",
"fundu",
"libc", "libc",
"memchr", "memchr",
"nix", "nix",

View file

@ -1,7 +1,7 @@
# coreutils (uutils) # coreutils (uutils)
# * see the repository LICENSE, README, and CONTRIBUTING files for more information # * see the repository LICENSE, README, and CONTRIBUTING files for more information
# spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue # spell-checker:ignore (libs) libselinux gethostid procfs bigdecimal kqueue fundu
[package] [package]
name = "coreutils" name = "coreutils"
@ -282,6 +282,7 @@ filetime = "0.2"
fnv = "1.0.7" fnv = "1.0.7"
fs_extra = "1.1.0" fs_extra = "1.1.0"
fts-sys = "0.2" fts-sys = "0.2"
fundu = "0.3.0"
gcd = "2.2" gcd = "2.2"
glob = "0.3.0" glob = "0.3.0"
half = "2.1" half = "2.1"

View file

@ -1,3 +1,4 @@
# spell-checker:ignore (libs) kqueue fundu
[package] [package]
name = "uu_tail" name = "uu_tail"
version = "0.0.17" version = "0.0.17"
@ -22,6 +23,7 @@ notify = { workspace=true }
uucore = { workspace=true, features=["ringbuffer", "lines"] } uucore = { workspace=true, features=["ringbuffer", "lines"] }
same-file = { workspace=true } same-file = { workspace=true }
atty = { workspace=true } atty = { workspace=true }
fundu = { workspace=true }
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
windows-sys = { workspace=true, features = ["Win32_System_Threading", "Win32_Foundation"] } windows-sys = { workspace=true, features = ["Win32_System_Threading", "Win32_Foundation"] }

View file

@ -3,13 +3,14 @@
// * For the full copyright and license information, please view the LICENSE // * For the full copyright and license information, please view the LICENSE
// * file that was distributed with this source code. // * file that was distributed with this source code.
// spell-checker:ignore (ToDO) kqueue Signum // spell-checker:ignore (ToDO) kqueue Signum fundu
use crate::paths::Input; use crate::paths::Input;
use crate::{parse, platform, Quotable}; use crate::{parse, platform, Quotable};
use atty::Stream; use atty::Stream;
use clap::crate_version; use clap::crate_version;
use clap::{parser::ValueSource, Arg, ArgAction, ArgMatches, Command}; use clap::{parser::ValueSource, Arg, ArgAction, ArgMatches, Command};
use fundu::DurationParser;
use same_file::Handle; use same_file::Handle;
use std::collections::VecDeque; use std::collections::VecDeque;
use std::ffi::OsString; use std::ffi::OsString;
@ -148,16 +149,20 @@ impl Settings {
settings.retry = settings.retry =
matches.get_flag(options::RETRY) || matches.get_flag(options::FOLLOW_RETRY); matches.get_flag(options::RETRY) || matches.get_flag(options::FOLLOW_RETRY);
if let Some(s) = matches.get_one::<String>(options::SLEEP_INT) { if let Some(source) = matches.get_one::<String>(options::SLEEP_INT) {
settings.sleep_sec = match s.parse::<f32>() { // Advantage of `fundu` over `Duration::(try_)from_secs_f64(source.parse().unwrap())`:
Ok(s) => Duration::from_secs_f32(s), // * doesn't panic on errors like `Duration::from_secs_f64` would.
Err(_) => { // * no precision loss, rounding errors or other floating point problems.
return Err(UUsageError::new( // * evaluates to `Duration::MAX` if the parsed number would have exceeded
1, // `DURATION::MAX` or `infinity` was given
format!("invalid number of seconds: {}", s.quote()), // * not applied here but it supports customizable time units and provides better error
)) // messages
} settings.sleep_sec =
} DurationParser::without_time_units()
.parse(source)
.map_err(|_| {
UUsageError::new(1, format!("invalid number of seconds: '{source}'"))
})?;
} }
settings.use_polling = matches.get_flag(options::USE_POLLING); settings.use_polling = matches.get_flag(options::USE_POLLING);

View file

@ -13,6 +13,7 @@ use crate::common::random::*;
use crate::common::util::*; use crate::common::util::*;
use pretty_assertions::assert_eq; use pretty_assertions::assert_eq;
use rand::distributions::Alphanumeric; use rand::distributions::Alphanumeric;
use rstest::rstest;
use std::char::from_digit; use std::char::from_digit;
use std::fs::File; use std::fs::File;
use std::io::Write; use std::io::Write;
@ -4453,29 +4454,24 @@ fn test_follow_when_files_are_pointing_to_same_relative_file_and_file_stays_same
.stdout_only(expected_stdout); .stdout_only(expected_stdout);
} }
#[test] #[rstest]
#[cfg(disable_until_fixed)] #[case::exponent_exceed_float_max("1.0e2048")]
fn test_args_sleep_interval_when_illegal_argument_then_usage_error() { #[case::underscore_delimiter("1_000")]
let scene = TestScenario::new(util_name!()); #[case::only_point(".")]
for interval in [ #[case::space_in_primes("' '")]
&format!("{}0", f64::MAX), #[case::space(" ")]
&format!("{}0.0", f64::MAX), #[case::empty("")]
"1_000", #[case::comma_separator("0,0")]
".", #[case::words_nominator_fract("one.zero")]
"' '", #[case::words_fract(".zero")]
"", #[case::words_nominator("one.")]
" ", #[case::two_points("0..0")]
"0,0", #[case::seconds_unit("1.0s")]
"one.zero", #[case::circumflex_exponent("1.0e^1000")]
".zero", fn test_args_sleep_interval_when_illegal_argument_then_usage_error(#[case] sleep_interval: &str) {
"one.", new_ucmd!()
"0..0", .args(&["--sleep-interval", sleep_interval])
] { .run()
scene .usage_error(format!("invalid number of seconds: '{sleep_interval}'"))
.ucmd() .code_is(1);
.args(&["--sleep-interval", interval])
.run()
.usage_error(format!("invalid number of seconds: '{}'", interval))
.code_is(1);
}
} }