mirror of
https://github.com/uutils/coreutils
synced 2024-12-13 23:02:38 +00:00
tail: Fix parsing of sleep interval. Use duration parser from fundu crate.
Activate tests for parsing sleep interval
This commit is contained in:
parent
d9f05f4c52
commit
0ed6a9f882
5 changed files with 48 additions and 37 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"] }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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])
|
||||||
] {
|
|
||||||
scene
|
|
||||||
.ucmd()
|
|
||||||
.args(&["--sleep-interval", interval])
|
|
||||||
.run()
|
.run()
|
||||||
.usage_error(format!("invalid number of seconds: '{}'", interval))
|
.usage_error(format!("invalid number of seconds: '{sleep_interval}'"))
|
||||||
.code_is(1);
|
.code_is(1);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue