From fd5ec099d0529ad3cad12723fa573d34ec2eb994 Mon Sep 17 00:00:00 2001 From: Terts Diepraam Date: Sat, 13 Mar 2021 17:20:39 +0100 Subject: [PATCH 1/3] touch: use an ArgGroup for sources and turn macros into functions (#1813) * touch: use arggroup for sources * tests/touch: add tests for multiple sources * touch: turn macros into functions * test/touch: fmt * touch: constant for the sources ArgGroup --- src/uu/touch/src/touch.rs | 57 +++++++++++++------------------------ tests/by-util/test_touch.rs | 47 ++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 37 deletions(-) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index 1cd3b2a70..39405900e 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -13,7 +13,7 @@ pub extern crate filetime; #[macro_use] extern crate uucore; -use clap::{App, Arg}; +use clap::{App, Arg, ArgGroup}; use filetime::*; use std::fs::{self, File}; use std::io::Error; @@ -22,6 +22,8 @@ use std::path::Path; static VERSION: &str = env!("CARGO_PKG_VERSION"); static ABOUT: &str = "Update the access and modification times of each FILE to the current time."; pub mod options { + // Both SOURCES and sources are needed as we need to be able to refer to the ArgGroup. + pub static SOURCES: &str = "sources"; pub mod sources { pub static DATE: &str = "date"; pub static REFERENCE: &str = "reference"; @@ -36,23 +38,15 @@ pub mod options { static ARG_FILES: &str = "files"; -// Since touch's date/timestamp parsing doesn't account for timezone, the -// returned value from time::strptime() is UTC. We get system's timezone to -// localize the time. -macro_rules! to_local( - ($exp:expr) => ({ - let mut tm = $exp; - tm.tm_utcoff = time::now().tm_utcoff; - tm - }) -); +fn to_local(mut tm: time::Tm) -> time::Tm { + tm.tm_utcoff = time::now().tm_utcoff; + tm +} -macro_rules! local_tm_to_filetime( - ($exp:expr) => ({ - let ts = $exp.to_timespec(); - FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32) - }) -); +fn local_tm_to_filetime(tm: time::Tm) -> FileTime { + let ts = tm.to_timespec(); + FileTime::from_unix_time(ts.sec as i64, ts.nsec as u32) +} fn get_usage() -> String { format!("{0} [OPTION]... [USER]", executable!()) @@ -129,6 +123,11 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .takes_value(true) .min_values(1), ) + .group(ArgGroup::with_name(options::SOURCES).args(&[ + options::sources::CURRENT, + options::sources::DATE, + options::sources::REFERENCE, + ])) .get_matches_from(args); let files: Vec = matches @@ -136,19 +135,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 { .map(|v| v.map(ToString::to_string).collect()) .unwrap_or_default(); - if matches.is_present(options::sources::DATE) - && (matches.is_present(options::sources::REFERENCE) - || matches.is_present(options::sources::CURRENT)) - || matches.is_present(options::sources::REFERENCE) - && (matches.is_present(options::sources::DATE) - || matches.is_present(options::sources::CURRENT)) - || matches.is_present(options::sources::CURRENT) - && (matches.is_present(options::sources::DATE) - || matches.is_present(options::sources::REFERENCE)) - { - panic!("Invalid options: cannot specify reference time from more than one source"); - } - let (mut atime, mut mtime) = if matches.is_present(options::sources::REFERENCE) { stat( &matches.value_of(options::sources::REFERENCE).unwrap()[..], @@ -169,7 +155,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }; (timestamp, timestamp) } else { - let now = local_tm_to_filetime!(time::now()); + let now = local_tm_to_filetime(time::now()); (now, now) }; @@ -188,10 +174,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { }; // Minor optimization: if no reference time was specified, we're done. - if !(matches.is_present(options::sources::DATE) - || matches.is_present(options::sources::REFERENCE) - || matches.is_present(options::sources::CURRENT)) - { + if !matches.is_present(options::SOURCES) { continue; } } @@ -260,7 +243,7 @@ fn parse_date(str: &str) -> FileTime { // not about to implement GNU parse_datetime. // http://git.savannah.gnu.org/gitweb/?p=gnulib.git;a=blob_plain;f=lib/parse-datetime.y match time::strptime(str, "%c") { - Ok(tm) => local_tm_to_filetime!(to_local!(tm)), + Ok(tm) => local_tm_to_filetime(to_local(tm)), Err(e) => panic!("Unable to parse date\n{}", e), } } @@ -278,7 +261,7 @@ fn parse_timestamp(s: &str) -> FileTime { }; match time::strptime(&ts, format) { - Ok(tm) => local_tm_to_filetime!(to_local!(tm)), + Ok(tm) => local_tm_to_filetime(to_local(tm)), Err(e) => panic!("Unable to parse timestamp\n{}", e), } } diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index 7e04beced..9921c16b5 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -203,6 +203,53 @@ fn test_touch_set_only_mtime_failed() { ucmd.args(&["-t", "2015010112342", "-m", file]).fails(); } +#[test] +fn test_touch_set_both_time_and_reference() { + let (at, mut ucmd) = at_and_ucmd!(); + let ref_file = "test_touch_reference"; + let file = "test_touch_set_both_time_and_reference"; + + let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000"); + + at.touch(ref_file); + set_file_times(&at, ref_file, start_of_year, start_of_year); + assert!(at.file_exists(ref_file)); + + ucmd.args(&["-t", "2015010112342", "-r", ref_file, file]) + .fails(); +} + +#[test] +fn test_touch_set_both_date_and_reference() { + let (at, mut ucmd) = at_and_ucmd!(); + let ref_file = "test_touch_reference"; + let file = "test_touch_set_both_date_and_reference"; + + let start_of_year = str_to_filetime("%Y%m%d%H%M", "201501010000"); + + at.touch(ref_file); + set_file_times(&at, ref_file, start_of_year, start_of_year); + assert!(at.file_exists(ref_file)); + + ucmd.args(&["-d", "Thu Jan 01 12:34:00 2015", "-r", ref_file, file]) + .fails(); +} + +#[test] +fn test_touch_set_both_time_and_date() { + let (_at, mut ucmd) = at_and_ucmd!(); + let file = "test_touch_set_both_time_and_date"; + + ucmd.args(&[ + "-t", + "2015010112342", + "-d", + "Thu Jan 01 12:34:00 2015", + file, + ]) + .fails(); +} + #[test] fn test_touch_set_only_mtime() { let (at, mut ucmd) = at_and_ucmd!(); From 2158b2c5b43c501b66e1db0fc8715878c78c0f73 Mon Sep 17 00:00:00 2001 From: Andre Julius Date: Sat, 13 Mar 2021 23:11:11 +0100 Subject: [PATCH 2/3] sleep: move from getopts to clap #1735 (#1777) and Add some sleep test cases #1735 --- src/uu/sleep/Cargo.toml | 2 +- src/uu/sleep/src/sleep.rs | 81 +++++++++++++++++++------------------ tests/by-util/test_sleep.rs | 48 +++++++++++++++++++++- 3 files changed, 89 insertions(+), 42 deletions(-) diff --git a/src/uu/sleep/Cargo.toml b/src/uu/sleep/Cargo.toml index 7bcbd1f4e..0ac5d6519 100644 --- a/src/uu/sleep/Cargo.toml +++ b/src/uu/sleep/Cargo.toml @@ -15,7 +15,7 @@ edition = "2018" path = "src/sleep.rs" [dependencies] -getopts = "0.2.18" +clap = "2.33" uucore = { version=">=0.0.7", package="uucore", path="../../uucore", features=["parse_time"] } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/sleep/src/sleep.rs b/src/uu/sleep/src/sleep.rs index f08a6d3a4..5c1f06e5e 100644 --- a/src/uu/sleep/src/sleep.rs +++ b/src/uu/sleep/src/sleep.rs @@ -11,55 +11,56 @@ extern crate uucore; use std::thread; use std::time::Duration; -static NAME: &str = "sleep"; +use clap::{App, Arg}; + static VERSION: &str = env!("CARGO_PKG_VERSION"); - -pub fn uumain(args: impl uucore::Args) -> i32 { - let args = args.collect_str(); - - let mut opts = getopts::Options::new(); - opts.optflag("h", "help", "display this help and exit"); - opts.optflag("V", "version", "output version information and exit"); - - let matches = match opts.parse(&args[1..]) { - Ok(m) => m, - Err(f) => { - show_error!("{}", f); - return 1; - } - }; - - if matches.opt_present("help") { - let msg = format!( - "{0} {1} - -Usage: - {0} NUMBER[SUFFIX] -or - {0} OPTION - -Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default), +static ABOUT: &str = "Pause for NUMBER seconds."; +static LONG_HELP: &str = "Pause for NUMBER seconds. SUFFIX may be 's' for seconds (the default), 'm' for minutes, 'h' for hours or 'd' for days. Unlike most implementations that require NUMBER be an integer, here NUMBER may be an arbitrary floating point number. Given two or more arguments, pause for the amount of time -specified by the sum of their values.", - NAME, VERSION - ); - print!("{}", opts.usage(&msg)); - } else if matches.opt_present("version") { - println!("{} {}", NAME, VERSION); - } else if matches.free.is_empty() { - show_error!("missing an argument"); - show_error!("for help, try '{0} --help'", NAME); - return 1; - } else { - sleep(matches.free); +specified by the sum of their values."; + +mod options { + pub const NUMBER: &str = "NUMBER"; +} + +fn get_usage() -> String { + format!( + "{0} {1}[SUFFIX]... \n {0} OPTION", + executable!(), + options::NUMBER + ) +} + +pub fn uumain(args: impl uucore::Args) -> i32 { + let usage = get_usage(); + + let matches = App::new(executable!()) + .version(VERSION) + .about(ABOUT) + .usage(&usage[..]) + .after_help(LONG_HELP) + .arg( + Arg::with_name(options::NUMBER) + .long(options::NUMBER) + .help("pause for NUMBER seconds") + .value_name(options::NUMBER) + .index(1) + .multiple(true) + .required(true), + ) + .get_matches_from(args); + + if let Some(values) = matches.values_of(options::NUMBER) { + let numbers = values.collect(); + sleep(numbers); } 0 } -fn sleep(args: Vec) { +fn sleep(args: Vec<&str>) { let sleep_dur = args.iter().fold( Duration::new(0, 0), diff --git a/tests/by-util/test_sleep.rs b/tests/by-util/test_sleep.rs index 651491045..389befd0a 100644 --- a/tests/by-util/test_sleep.rs +++ b/tests/by-util/test_sleep.rs @@ -1 +1,47 @@ -// ToDO: add tests +use crate::common::util::*; + +use std::time::{Duration, Instant}; + +#[test] +fn test_sleep_no_suffix() { + let millis_100 = Duration::from_millis(100); + let before_test = Instant::now(); + + new_ucmd!().args(&["0.1"]).succeeds().stdout_only(""); + + let duration = before_test.elapsed(); + assert!(duration >= millis_100); +} + +#[test] +fn test_sleep_s_suffix() { + let millis_100 = Duration::from_millis(100); + let before_test = Instant::now(); + + new_ucmd!().args(&["0.1s"]).succeeds().stdout_only(""); + + let duration = before_test.elapsed(); + assert!(duration >= millis_100); +} + +#[test] +fn test_sleep_m_suffix() { + let millis_600 = Duration::from_millis(600); + let before_test = Instant::now(); + + new_ucmd!().args(&["0.01m"]).succeeds().stdout_only(""); + + let duration = before_test.elapsed(); + assert!(duration >= millis_600); +} + +#[test] +fn test_sleep_h_suffix() { + let millis_360 = Duration::from_millis(360); + let before_test = Instant::now(); + + new_ucmd!().args(&["0.0001h"]).succeeds().stdout_only(""); + + let duration = before_test.elapsed(); + assert!(duration >= millis_360); +} From 2c09556964de74f685de382d92d41e1e4072c1bb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 13 Mar 2021 23:30:32 +0100 Subject: [PATCH 3/3] rustfmt some tests --- tests/by-util/test_seq.rs | 1 - tests/by-util/test_timeout.rs | 11 ++--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index d4cff9aaa..a74938377 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -45,4 +45,3 @@ fn test_seq_wrong_arg() { fn test_zero_step() { new_ucmd!().args(&["10", "0", "32"]).fails(); } - diff --git a/tests/by-util/test_timeout.rs b/tests/by-util/test_timeout.rs index edea760c2..592516cca 100644 --- a/tests/by-util/test_timeout.rs +++ b/tests/by-util/test_timeout.rs @@ -5,14 +5,7 @@ use crate::common::util::*; // utility that requires executing another program (kill, for instance) #[test] fn test_subcommand_retcode() { - new_ucmd!() - .arg("1") - .arg("true") - .succeeds(); + new_ucmd!().arg("1").arg("true").succeeds(); - new_ucmd!() - .arg("1") - .arg("false") - .run() - .status_code(1); + new_ucmd!().arg("1").arg("false").run().status_code(1); }