diff --git a/docs/src/extensions.md b/docs/src/extensions.md index 79746498f2..2319b2a6f1 100644 --- a/docs/src/extensions.md +++ b/docs/src/extensions.md @@ -75,6 +75,9 @@ number of spaces representing a tab when determining the line length. GNU `ls` provides two ways to use a long listing format: `-l` and `--format=long`. We support a third way: `--long`. +GNU `ls --sort=VALUE` only supports special non-default sort orders. +We support `--sort=name`, which makes it possible to override an earlier value. + ## `du` `du` allows `birth` and `creation` as values for the `--time` argument to show the creation time. It diff --git a/src/uu/cp/src/cp.rs b/src/uu/cp/src/cp.rs index d45f994180..fba2db7ab3 100644 --- a/src/uu/cp/src/cp.rs +++ b/src/uu/cp/src/cp.rs @@ -37,8 +37,8 @@ use uucore::{backup_control, update_control}; // requires these enum. pub use uucore::{backup_control::BackupMode, update_control::UpdateMode}; use uucore::{ - format_usage, help_about, help_section, help_usage, prompt_yes, show_error, show_warning, - util_name, + format_usage, help_about, help_section, help_usage, prompt_yes, + shortcut_value_parser::ShortcutValueParser, show_error, show_warning, util_name, }; use crate::copydir::copy_directory; @@ -396,22 +396,14 @@ static PRESERVABLE_ATTRIBUTES: &[&str] = &[ "ownership", "timestamps", "context", - "link", "links", "xattr", "all", ]; #[cfg(not(unix))] -static PRESERVABLE_ATTRIBUTES: &[&str] = &[ - "mode", - "timestamps", - "context", - "link", - "links", - "xattr", - "all", -]; +static PRESERVABLE_ATTRIBUTES: &[&str] = + &["mode", "timestamps", "context", "links", "xattr", "all"]; pub fn uu_app() -> Command { const MODE_ARGS: &[&str] = &[ @@ -543,7 +535,7 @@ pub fn uu_app() -> Command { .overrides_with_all(MODE_ARGS) .require_equals(true) .default_missing_value("always") - .value_parser(["auto", "always", "never"]) + .value_parser(ShortcutValueParser::new(["auto", "always", "never"])) .num_args(0..=1) .help("control clone/CoW copies. See below"), ) @@ -559,9 +551,7 @@ pub fn uu_app() -> Command { .long(options::PRESERVE) .action(ArgAction::Append) .use_value_delimiter(true) - .value_parser(clap::builder::PossibleValuesParser::new( - PRESERVABLE_ATTRIBUTES, - )) + .value_parser(ShortcutValueParser::new(PRESERVABLE_ATTRIBUTES)) .num_args(0..) .require_equals(true) .value_name("ATTR_LIST") @@ -655,7 +645,7 @@ pub fn uu_app() -> Command { Arg::new(options::SPARSE) .long(options::SPARSE) .value_name("WHEN") - .value_parser(["never", "auto", "always"]) + .value_parser(ShortcutValueParser::new(["never", "auto", "always"])) .help("control creation of sparse files. See below"), ) // TODO: implement the following args diff --git a/src/uu/du/src/du.rs b/src/uu/du/src/du.rs index 0de16121aa..1935248daf 100644 --- a/src/uu/du/src/du.rs +++ b/src/uu/du/src/du.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. use chrono::{DateTime, Local}; -use clap::{crate_version, Arg, ArgAction, ArgMatches, Command}; +use clap::{builder::PossibleValue, crate_version, Arg, ArgAction, ArgMatches, Command}; use glob::Pattern; use std::collections::HashSet; use std::env; @@ -30,6 +30,7 @@ use uucore::error::{set_exit_code, FromIo, UError, UResult, USimpleError}; use uucore::line_ending::LineEnding; use uucore::parse_glob; use uucore::parse_size::{parse_size_u64, ParseSizeError}; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage, show, show_error, show_warning}; #[cfg(windows)] use windows_sys::Win32::Foundation::HANDLE; @@ -1040,7 +1041,11 @@ pub fn uu_app() -> Command { .value_name("WORD") .require_equals(true) .num_args(0..) - .value_parser(["atime", "access", "use", "ctime", "status", "birth", "creation"]) + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("atime").alias("access").alias("use"), + PossibleValue::new("ctime").alias("status"), + PossibleValue::new("creation").alias("birth"), + ])) .help( "show time of the last modification of any file in the \ directory, or any of its subdirectories. If WORD is given, show time as WORD instead \ diff --git a/src/uu/ls/src/ls.rs b/src/uu/ls/src/ls.rs index 65e0969cbd..0221ae0968 100644 --- a/src/uu/ls/src/ls.rs +++ b/src/uu/ls/src/ls.rs @@ -6,7 +6,7 @@ // spell-checker:ignore (ToDO) somegroup nlink tabsize dired subdired dtype colorterm stringly use clap::{ - builder::{NonEmptyStringValueParser, ValueParser}, + builder::{NonEmptyStringValueParser, PossibleValue, ValueParser}, crate_version, Arg, ArgAction, Command, }; use glob::{MatchOptions, Pattern}; @@ -62,6 +62,7 @@ use uucore::{ format_usage, fs::display_permissions, parse_size::parse_size_u64, + shortcut_value_parser::ShortcutValueParser, version_cmp::version_cmp, }; use uucore::{help_about, help_section, help_usage, parse_glob, show, show_error, show_warning}; @@ -1203,7 +1204,7 @@ pub fn uu_app() -> Command { Arg::new(options::FORMAT) .long(options::FORMAT) .help("Set the display format.") - .value_parser([ + .value_parser(ShortcutValueParser::new([ "long", "verbose", "single-column", @@ -1212,7 +1213,7 @@ pub fn uu_app() -> Command { "across", "horizontal", "commas", - ]) + ])) .hide_possible_values(true) .require_equals(true) .overrides_with_all([ @@ -1303,9 +1304,11 @@ pub fn uu_app() -> Command { Arg::new(options::HYPERLINK) .long(options::HYPERLINK) .help("hyperlink file names WHEN") - .value_parser([ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("always").alias("yes").alias("force"), + PossibleValue::new("auto").alias("tty").alias("if-tty"), + PossibleValue::new("never").alias("no").alias("none"), + ])) .require_equals(true) .num_args(0..=1) .default_missing_value("always") @@ -1351,15 +1354,15 @@ pub fn uu_app() -> Command { Arg::new(options::QUOTING_STYLE) .long(options::QUOTING_STYLE) .help("Set quoting style.") - .value_parser([ - "literal", - "shell", - "shell-always", - "shell-escape", - "shell-escape-always", - "c", - "escape", - ]) + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("literal"), + PossibleValue::new("shell"), + PossibleValue::new("shell-escape"), + PossibleValue::new("shell-always"), + PossibleValue::new("shell-escape-always"), + PossibleValue::new("c").alias("c-maybe"), + PossibleValue::new("escape"), + ])) .overrides_with_all([ options::QUOTING_STYLE, options::quoting::LITERAL, @@ -1434,9 +1437,11 @@ pub fn uu_app() -> Command { \tbirth time: birth, creation;", ) .value_name("field") - .value_parser([ - "atime", "access", "use", "ctime", "status", "birth", "creation", - ]) + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("atime").alias("access").alias("use"), + PossibleValue::new("ctime").alias("status"), + PossibleValue::new("birth").alias("creation"), + ])) .hide_possible_values(true) .require_equals(true) .overrides_with_all([options::TIME, options::time::ACCESS, options::time::CHANGE]), @@ -1496,7 +1501,7 @@ pub fn uu_app() -> Command { .long(options::SORT) .help("Sort by : name, none (-U), time (-t), size (-S), extension (-X) or width") .value_name("field") - .value_parser(["name", "none", "time", "size", "version", "extension", "width"]) + .value_parser(ShortcutValueParser::new(["name", "none", "time", "size", "version", "extension", "width"])) .require_equals(true) .overrides_with_all([ options::SORT, @@ -1744,9 +1749,11 @@ pub fn uu_app() -> Command { Arg::new(options::COLOR) .long(options::COLOR) .help("Color output based on file type.") - .value_parser([ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("always").alias("yes").alias("force"), + PossibleValue::new("auto").alias("tty").alias("if-tty"), + PossibleValue::new("never").alias("no").alias("none"), + ])) .require_equals(true) .num_args(0..=1), ) @@ -1757,7 +1764,7 @@ pub fn uu_app() -> Command { "Append indicator with style WORD to entry names: \ none (default), slash (-p), file-type (--file-type), classify (-F)", ) - .value_parser(["none", "slash", "file-type", "classify"]) + .value_parser(ShortcutValueParser::new(["none", "slash", "file-type", "classify"])) .overrides_with_all([ options::indicator_style::FILE_TYPE, options::indicator_style::SLASH, @@ -1788,9 +1795,11 @@ pub fn uu_app() -> Command { --dereference-command-line-symlink-to-dir options are specified.", ) .value_name("when") - .value_parser([ - "always", "yes", "force", "auto", "tty", "if-tty", "never", "no", "none", - ]) + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("always").alias("yes").alias("force"), + PossibleValue::new("auto").alias("tty").alias("if-tty"), + PossibleValue::new("never").alias("no").alias("none"), + ])) .default_missing_value("always") .require_equals(true) .num_args(0..=1) diff --git a/src/uu/numfmt/src/numfmt.rs b/src/uu/numfmt/src/numfmt.rs index 80a2051bd4..2b9932ee57 100644 --- a/src/uu/numfmt/src/numfmt.rs +++ b/src/uu/numfmt/src/numfmt.rs @@ -15,6 +15,7 @@ use units::{IEC_BASES, SI_BASES}; use uucore::display::Quotable; use uucore::error::UResult; use uucore::ranges::Range; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage, show, show_error}; pub mod errors; @@ -340,7 +341,13 @@ pub fn uu_app() -> Command { .help("use METHOD for rounding when scaling") .value_name("METHOD") .default_value("from-zero") - .value_parser(["up", "down", "from-zero", "towards-zero", "nearest"]), + .value_parser(ShortcutValueParser::new([ + "up", + "down", + "from-zero", + "towards-zero", + "nearest", + ])), ) .arg( Arg::new(options::SUFFIX) diff --git a/src/uu/od/src/od.rs b/src/uu/od/src/od.rs index 769dae98e2..1c9548f50f 100644 --- a/src/uu/od/src/od.rs +++ b/src/uu/od/src/od.rs @@ -43,6 +43,7 @@ use clap::{crate_version, parser::ValueSource, Arg, ArgMatches, Command}; use uucore::display::Quotable; use uucore::error::{UResult, USimpleError}; use uucore::parse_size::ParseSizeError; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_warning}; const PEEK_BUFFER_SIZE: usize = 4; // utf-8 can be 4 bytes @@ -287,7 +288,7 @@ pub fn uu_app() -> Command { Arg::new(options::ENDIAN) .long(options::ENDIAN) .help("byte order to use for multi-byte formats") - .value_parser(["big", "little"]) + .value_parser(ShortcutValueParser::new(["big", "little"])) .value_name("big|little"), ) .arg( diff --git a/src/uu/shred/src/shred.rs b/src/uu/shred/src/shred.rs index d023b62107..763d6cfd4b 100644 --- a/src/uu/shred/src/shred.rs +++ b/src/uu/shred/src/shred.rs @@ -17,6 +17,7 @@ use std::path::{Path, PathBuf}; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError, UUsageError}; use uucore::parse_size::parse_size_u64; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage, show_error, show_if_err}; const ABOUT: &str = help_about!("shred.md"); @@ -315,11 +316,11 @@ pub fn uu_app() -> Command { Arg::new(options::REMOVE) .long(options::REMOVE) .value_name("HOW") - .value_parser([ + .value_parser(ShortcutValueParser::new([ options::remove::UNLINK, options::remove::WIPE, options::remove::WIPESYNC, - ]) + ])) .num_args(0..=1) .require_equals(true) .default_missing_value(options::remove::WIPESYNC) diff --git a/src/uu/sort/src/sort.rs b/src/uu/sort/src/sort.rs index 07420d9c1d..1bc413aaac 100644 --- a/src/uu/sort/src/sort.rs +++ b/src/uu/sort/src/sort.rs @@ -43,6 +43,7 @@ use uucore::display::Quotable; use uucore::error::{set_exit_code, strip_errno, UError, UResult, USimpleError, UUsageError}; use uucore::line_ending::LineEnding; use uucore::parse_size::{ParseSizeError, Parser}; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::version_cmp::version_cmp; use uucore::{format_usage, help_about, help_section, help_usage}; @@ -1297,14 +1298,14 @@ pub fn uu_app() -> Command { .arg( Arg::new(options::modes::SORT) .long(options::modes::SORT) - .value_parser([ + .value_parser(ShortcutValueParser::new([ "general-numeric", "human-numeric", "month", "numeric", "version", "random", - ]) + ])) .conflicts_with_all(options::modes::ALL_SORT_MODES), ) .arg(make_sort_mode_arg( @@ -1363,11 +1364,11 @@ pub fn uu_app() -> Command { .long(options::check::CHECK) .require_equals(true) .num_args(0..) - .value_parser([ + .value_parser(ShortcutValueParser::new([ options::check::SILENT, options::check::QUIET, options::check::DIAGNOSE_FIRST, - ]) + ])) .conflicts_with(options::OUTPUT) .help("check for sorted input; do not sort"), ) diff --git a/src/uu/tail/src/args.rs b/src/uu/tail/src/args.rs index 6ae5e68bdd..5cadac6086 100644 --- a/src/uu/tail/src/args.rs +++ b/src/uu/tail/src/args.rs @@ -16,6 +16,7 @@ use std::io::IsTerminal; use std::time::Duration; use uucore::error::{UResult, USimpleError, UUsageError}; use uucore::parse_size::{parse_size_u64, ParseSizeError}; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_usage, show_warning}; const ABOUT: &str = help_about!("tail.md"); @@ -494,7 +495,7 @@ pub fn uu_app() -> Command { .default_missing_value("descriptor") .num_args(0..=1) .require_equals(true) - .value_parser(["descriptor", "name"]) + .value_parser(ShortcutValueParser::new(["descriptor", "name"])) .overrides_with(options::FOLLOW) .help("Print the file as it grows"), ) diff --git a/src/uu/tee/src/tee.rs b/src/uu/tee/src/tee.rs index b1443dbb95..2cf693af67 100644 --- a/src/uu/tee/src/tee.rs +++ b/src/uu/tee/src/tee.rs @@ -9,6 +9,7 @@ use std::io::{copy, stdin, stdout, Error, ErrorKind, Read, Result, Write}; use std::path::PathBuf; use uucore::display::Quotable; use uucore::error::UResult; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage, show_error}; // spell-checker:ignore nopipe @@ -119,7 +120,7 @@ pub fn uu_app() -> Command { .long(options::OUTPUT_ERROR) .require_equals(true) .num_args(0..=1) - .value_parser([ + .value_parser(ShortcutValueParser::new([ PossibleValue::new("warn") .help("produce warnings for errors writing to any output"), PossibleValue::new("warn-nopipe") @@ -127,7 +128,7 @@ pub fn uu_app() -> Command { PossibleValue::new("exit").help("exit on write errors to any output"), PossibleValue::new("exit-nopipe") .help("exit on write errors to any output that are not pipe errors (equivalent to exit on non-unix platforms)"), - ]) + ])) .help("set write error behavior") .conflicts_with(options::IGNORE_PIPE_ERRORS), ) diff --git a/src/uu/touch/src/touch.rs b/src/uu/touch/src/touch.rs index fe1783b214..4192a7a838 100644 --- a/src/uu/touch/src/touch.rs +++ b/src/uu/touch/src/touch.rs @@ -10,7 +10,7 @@ use chrono::{ DateTime, Datelike, Duration, Local, LocalResult, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Timelike, }; -use clap::builder::ValueParser; +use clap::builder::{PossibleValue, ValueParser}; use clap::{crate_version, Arg, ArgAction, ArgGroup, ArgMatches, Command}; use filetime::{set_file_times, set_symlink_file_times, FileTime}; use std::ffi::OsString; @@ -18,6 +18,7 @@ use std::fs::{self, File}; use std::path::{Path, PathBuf}; use uucore::display::Quotable; use uucore::error::{FromIo, UResult, USimpleError}; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_usage, show}; const ABOUT: &str = help_about!("touch.md"); @@ -216,7 +217,10 @@ pub fn uu_app() -> Command { equivalent to -m", ) .value_name("WORD") - .value_parser(["access", "atime", "use", "modify", "mtime"]), + .value_parser(ShortcutValueParser::new([ + PossibleValue::new("atime").alias("access").alias("use"), + PossibleValue::new("mtime").alias("modify"), + ])), ) .arg( Arg::new(ARG_FILES) diff --git a/src/uu/uniq/src/uniq.rs b/src/uu/uniq/src/uniq.rs index 2c4f9b7811..4084a7b3f2 100644 --- a/src/uu/uniq/src/uniq.rs +++ b/src/uu/uniq/src/uniq.rs @@ -14,6 +14,7 @@ use std::num::IntErrorKind; use uucore::display::Quotable; use uucore::error::{FromIo, UError, UResult, USimpleError}; use uucore::posix::{posix_version, OBSOLETE}; +use uucore::shortcut_value_parser::ShortcutValueParser; use uucore::{format_usage, help_about, help_section, help_usage}; const ABOUT: &str = help_about!("uniq.md"); @@ -609,11 +610,11 @@ pub fn uu_app() -> Command { Arg::new(options::ALL_REPEATED) .short('D') .long(options::ALL_REPEATED) - .value_parser([ + .value_parser(ShortcutValueParser::new([ "none", "prepend", "separate" - ]) + ])) .help("print all duplicate lines. Delimiting is done with blank lines. [default: none]") .value_name("delimit-method") .num_args(0..=1) @@ -623,12 +624,12 @@ pub fn uu_app() -> Command { .arg( Arg::new(options::GROUP) .long(options::GROUP) - .value_parser([ + .value_parser(ShortcutValueParser::new([ "separate", "prepend", "append", "both", - ]) + ])) .help("show all items, separating groups with an empty line. [default: separate]") .value_name("group-method") .num_args(0..=1) diff --git a/src/uu/wc/src/wc.rs b/src/uu/wc/src/wc.rs index 06f9be9168..33b70ee62f 100644 --- a/src/uu/wc/src/wc.rs +++ b/src/uu/wc/src/wc.rs @@ -29,6 +29,7 @@ use uucore::{ error::{FromIo, UError, UResult}, format_usage, help_about, help_usage, quoting_style::{escape_name, QuotingStyle}, + shortcut_value_parser::ShortcutValueParser, show, }; @@ -439,7 +440,9 @@ pub fn uu_app() -> Command { .arg( Arg::new(options::TOTAL) .long(options::TOTAL) - .value_parser(["auto", "always", "only", "never"]) + .value_parser(ShortcutValueParser::new([ + "auto", "always", "only", "never", + ])) .value_name("WHEN") .hide_possible_values(true) .help(concat!( diff --git a/src/uucore/src/lib/features/update_control.rs b/src/uucore/src/lib/features/update_control.rs index bd42925145..83700e5512 100644 --- a/src/uucore/src/lib/features/update_control.rs +++ b/src/uucore/src/lib/features/update_control.rs @@ -61,6 +61,7 @@ pub enum UpdateMode { } pub mod arguments { + use crate::shortcut_value_parser::ShortcutValueParser; use clap::ArgAction; pub static OPT_UPDATE: &str = "update"; @@ -71,7 +72,7 @@ pub mod arguments { clap::Arg::new(OPT_UPDATE) .long("update") .help("move only when the SOURCE file is newer than the destination file or when the destination file is missing") - .value_parser(["none", "all", "older"]) + .value_parser(ShortcutValueParser::new(["none", "all", "older"])) .num_args(0..=1) .default_missing_value("older") .require_equals(true) diff --git a/src/uucore/src/lib/parser/shortcut_value_parser.rs b/src/uucore/src/lib/parser/shortcut_value_parser.rs index bdc96b8ef5..125d53ee2c 100644 --- a/src/uucore/src/lib/parser/shortcut_value_parser.rs +++ b/src/uucore/src/lib/parser/shortcut_value_parser.rs @@ -66,7 +66,7 @@ impl TypedValueParser for ShortcutValueParser { let matched_values: Vec<_> = self .0 .iter() - .filter(|x| x.get_name().starts_with(value)) + .filter(|x| x.get_name_and_aliases().any(|name| name.starts_with(value))) .collect(); match matched_values.len() { @@ -101,7 +101,7 @@ where mod tests { use std::ffi::OsStr; - use clap::{builder::TypedValueParser, error::ErrorKind, Command}; + use clap::{builder::PossibleValue, builder::TypedValueParser, error::ErrorKind, Command}; use super::ShortcutValueParser; @@ -166,4 +166,30 @@ mod tests { let result = parser.parse_ref(&cmd, None, OsStr::from_bytes(&[0xc3, 0x28])); assert_eq!(ErrorKind::InvalidUtf8, result.unwrap_err().kind()); } + + #[test] + fn test_ambiguous_word_same_meaning() { + let cmd = Command::new("cmd"); + let parser = ShortcutValueParser::new([ + PossibleValue::new("atime").alias("access"), + "status".into(), + ]); + // Even though "a" is ambiguous (it might mean "atime" or "access"), + // the meaning is uniquely defined, therefore accept it. + let atime_values = [ + // spell-checker:disable-next-line + "atime", "atim", "at", "a", "access", "acces", "acce", "acc", "ac", + ]; + // spell-checker:disable-next-line + let status_values = ["status", "statu", "stat", "sta", "st", "st"]; + + for value in atime_values { + let result = parser.parse_ref(&cmd, None, OsStr::new(value)); + assert_eq!("atime", result.unwrap()); + } + for value in status_values { + let result = parser.parse_ref(&cmd, None, OsStr::new(value)); + assert_eq!("status", result.unwrap()); + } + } } diff --git a/tests/by-util/test_cp.rs b/tests/by-util/test_cp.rs index bdfc9c8e8a..1f81011dbb 100644 --- a/tests/by-util/test_cp.rs +++ b/tests/by-util/test_cp.rs @@ -2,7 +2,7 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs ROOTDIR USERDIR procfs outfile uufs xattrs +// spell-checker:ignore (flags) reflink (fs) tmpfs (linux) rlimit Rlim NOFILE clob btrfs neve ROOTDIR USERDIR procfs outfile uufs xattrs use crate::common::util::TestScenario; #[cfg(not(windows))] @@ -286,16 +286,18 @@ fn test_cp_arg_update_interactive_error() { #[test] fn test_cp_arg_update_none() { - let (at, mut ucmd) = at_and_ucmd!(); + for argument in ["--update=none", "--update=non", "--update=n"] { + let (at, mut ucmd) = at_and_ucmd!(); - ucmd.arg(TEST_HELLO_WORLD_SOURCE) - .arg(TEST_HOW_ARE_YOU_SOURCE) - .arg("--update=none") - .succeeds() - .no_stderr() - .no_stdout(); + ucmd.arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_HOW_ARE_YOU_SOURCE) + .arg(argument) + .succeeds() + .no_stderr() + .no_stdout(); - assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); + assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "How are you?\n"); + } } #[test] @@ -1402,29 +1404,28 @@ fn test_cp_preserve_no_args_before_opts() { #[test] fn test_cp_preserve_all() { - let (at, mut ucmd) = at_and_ucmd!(); - let src_file = "a"; - let dst_file = "b"; + for argument in ["--preserve=all", "--preserve=al"] { + let (at, mut ucmd) = at_and_ucmd!(); + let src_file = "a"; + let dst_file = "b"; - // Prepare the source file - at.touch(src_file); - #[cfg(unix)] - at.set_mode(src_file, 0o0500); + // Prepare the source file + at.touch(src_file); + #[cfg(unix)] + at.set_mode(src_file, 0o0500); - // TODO: create a destination that does not allow copying of xattr and context - // Copy - ucmd.arg(src_file) - .arg(dst_file) - .arg("--preserve=all") - .succeeds(); + // TODO: create a destination that does not allow copying of xattr and context + // Copy + ucmd.arg(src_file).arg(dst_file).arg(argument).succeeds(); - #[cfg(all(unix, not(target_os = "freebsd")))] - { - // Assert that the mode, ownership, and timestamps are preserved - // NOTICE: the ownership is not modified on the src file, because that requires root permissions - let metadata_src = at.metadata(src_file); - let metadata_dst = at.metadata(dst_file); - assert_metadata_eq!(metadata_src, metadata_dst); + #[cfg(all(unix, not(target_os = "freebsd")))] + { + // Assert that the mode, ownership, and timestamps are preserved + // NOTICE: the ownership is not modified on the src file, because that requires root permissions + let metadata_src = at.metadata(src_file); + let metadata_dst = at.metadata(dst_file); + assert_metadata_eq!(metadata_src, metadata_dst); + } } } @@ -1472,6 +1473,35 @@ fn test_cp_preserve_all_context_fails_on_non_selinux() { .fails(); } +#[test] +fn test_cp_preserve_link_parses() { + // TODO: Also check whether --preserve=link did the right thing! + for argument in [ + "--preserve=links", + "--preserve=link", + "--preserve=li", + "--preserve=l", + ] { + new_ucmd!() + .arg(argument) + .arg(TEST_COPY_FROM_FOLDER_FILE) + .arg(TEST_HELLO_WORLD_DEST) + .succeeds() + .no_output(); + } +} + +#[test] +fn test_cp_preserve_invalid_rejected() { + new_ucmd!() + .arg("--preserve=invalid-value") + .arg(TEST_COPY_FROM_FOLDER_FILE) + .arg(TEST_HELLO_WORLD_DEST) + .fails() + .code_is(1) + .no_stdout(); +} + #[test] #[cfg(target_os = "android")] #[cfg(disabled_until_fixed)] // FIXME: the test looks to .succeed on android @@ -2196,14 +2226,16 @@ fn test_cp_reflink_none() { #[test] #[cfg(any(target_os = "linux", target_os = "android", target_os = "macos"))] fn test_cp_reflink_never() { - let (at, mut ucmd) = at_and_ucmd!(); - ucmd.arg("--reflink=never") - .arg(TEST_HELLO_WORLD_SOURCE) - .arg(TEST_EXISTING_FILE) - .succeeds(); + for argument in ["--reflink=never", "--reflink=neve", "--reflink=n"] { + let (at, mut ucmd) = at_and_ucmd!(); + ucmd.arg(argument) + .arg(TEST_HELLO_WORLD_SOURCE) + .arg(TEST_EXISTING_FILE) + .succeeds(); - // Check the content of the destination file - assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n"); + // Check the content of the destination file + assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n"); + } } #[test] @@ -2286,19 +2318,21 @@ fn test_cp_sparse_never_empty() { #[cfg(any(target_os = "linux", target_os = "android"))] #[test] fn test_cp_sparse_always_empty() { - let (at, mut ucmd) = at_and_ucmd!(); + for argument in ["--sparse=always", "--sparse=alway", "--sparse=al"] { + let (at, mut ucmd) = at_and_ucmd!(); - const BUFFER_SIZE: usize = 4096 * 4; - let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; + const BUFFER_SIZE: usize = 4096 * 4; + let buf: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; - at.make_file("src_file1"); - at.write_bytes("src_file1", &buf); + at.make_file("src_file1"); + at.write_bytes("src_file1", &buf); - ucmd.args(&["--sparse=always", "src_file1", "dst_file_sparse"]) - .succeeds(); + ucmd.args(&[argument, "src_file1", "dst_file_sparse"]) + .succeeds(); - assert_eq!(at.read_bytes("dst_file_sparse"), buf); - assert_eq!(at.metadata("dst_file_sparse").blocks(), 0); + assert_eq!(at.read_bytes("dst_file_sparse"), buf); + assert_eq!(at.metadata("dst_file_sparse").blocks(), 0); + } } #[cfg(any(target_os = "linux", target_os = "android"))] diff --git a/tests/by-util/test_du.rs b/tests/by-util/test_du.rs index dbd2c9c946..2bc694acbc 100644 --- a/tests/by-util/test_du.rs +++ b/tests/by-util/test_du.rs @@ -3,7 +3,7 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. -// spell-checker:ignore (paths) sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink testfile1 testfile2 filelist testdir testfile +// spell-checker:ignore (paths) atim sublink subwords azerty azeaze xcwww azeaz amaz azea qzerty tazerty tsublink testfile1 testfile2 filelist testdir testfile #[cfg(not(windows))] use regex::Regex; @@ -576,13 +576,15 @@ fn test_du_time() { .succeeds(); result.stdout_only("0\t2016-06-16 00:00\tdate_test\n"); - let result = ts - .ucmd() - .env("TZ", "UTC") - .arg("--time=atime") - .arg("date_test") - .succeeds(); - result.stdout_only("0\t2015-05-15 00:00\tdate_test\n"); + for argument in ["--time=atime", "--time=atim", "--time=a"] { + let result = ts + .ucmd() + .env("TZ", "UTC") + .arg(argument) + .arg("date_test") + .succeeds(); + result.stdout_only("0\t2015-05-15 00:00\tdate_test\n"); + } let result = ts .ucmd() diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 2f48e4460b..099f18fb80 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -27,6 +27,7 @@ const LONG_ARGS: &[&str] = &[ "-l", "--long", "--format=long", + "--format=lon", "--for=long", "--format=verbose", "--for=verbose", @@ -35,6 +36,7 @@ const LONG_ARGS: &[&str] = &[ const ACROSS_ARGS: &[&str] = &[ "-x", "--format=across", + "--format=acr", "--format=horizontal", "--for=across", "--for=horizontal", @@ -999,6 +1001,8 @@ fn test_ls_zero() { let ignored_opts = [ "--quoting-style=c", "--color=always", + "--color=alway", + "--color=al", "-m", "--hide-control-chars", ]; @@ -1601,6 +1605,24 @@ fn test_ls_deref() { .succeeds(); assert!(re.is_match(result.stdout_str().trim())); + let result = scene + .ucmd() + .arg("-l") + .arg("--color=neve") // spell-checker:disable-line + .arg("test-long") + .arg("test-long.link") + .succeeds(); + assert!(re.is_match(result.stdout_str().trim())); + + let result = scene + .ucmd() + .arg("-l") + .arg("--color=n") + .arg("test-long") + .arg("test-long.link") + .succeeds(); + assert!(re.is_match(result.stdout_str().trim())); + let result = scene .ucmd() .arg("-L") @@ -1676,6 +1698,10 @@ fn test_ls_sort_none() { // Order is not specified so we just check that it doesn't // give any errors. scene.ucmd().arg("--sort=none").succeeds(); + scene.ucmd().arg("--sort=non").succeeds(); + scene.ucmd().arg("--sort=no").succeeds(); + // scene.ucmd().arg("--sort=n").succeeds(); + // We refuse to accept "--sort=n", since this is too confusable with "--sort=name", which is our own extension. scene.ucmd().arg("-U").succeeds(); } @@ -1693,6 +1719,16 @@ fn test_ls_sort_name() { .arg("--sort=name") .succeeds() .stdout_is("test-1\ntest-2\ntest-3\n"); + scene + .ucmd() + .arg("--sort=nam") + .succeeds() + .stdout_is("test-1\ntest-2\ntest-3\n"); + scene + .ucmd() + .arg("--sort=na") + .succeeds() + .stdout_is("test-1\ntest-2\ntest-3\n"); let scene_dot = TestScenario::new(util_name!()); let at = &scene_dot.fixtures; @@ -1729,6 +1765,16 @@ fn test_ls_sort_width() { .arg("--sort=width") .succeeds() .stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n"); + scene + .ucmd() + .arg("--sort=widt") // spell-checker:disable-line + .succeeds() + .stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n"); + scene + .ucmd() + .arg("--sort=w") + .succeeds() + .stdout_is("d\nzz\nabc\nbbb\neee\ncccc\naaaaa\nbcdef\nfffff\n"); } #[test] @@ -1757,6 +1803,12 @@ fn test_ls_order_size() { let result = scene.ucmd().arg("--sort=size").succeeds(); result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); + let result = scene.ucmd().arg("--sort=siz").succeeds(); + result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); + + let result = scene.ucmd().arg("--sort=s").succeeds(); + result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); + let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds(); result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n"); } @@ -1961,7 +2013,14 @@ fn test_ls_order_time() { // 3 was accessed last in the read // So the order should be 2 3 4 1 - for arg in ["-u", "--time=atime", "--time=access", "--time=use"] { + for arg in [ + "-u", + "--time=atime", + "--time=atim", // spell-checker:disable-line + "--time=a", + "--time=access", + "--time=use", + ] { let result = scene.ucmd().arg("-t").arg(arg).succeeds(); at.open("test-3").metadata().unwrap().accessed().unwrap(); at.open("test-4").metadata().unwrap().accessed().unwrap(); @@ -2216,12 +2275,16 @@ fn test_ls_indicator_style() { for opt in [ "--indicator-style=classify", "--ind=classify", + "--indicator-style=clas", // spell-checker:disable-line + "--indicator-style=c", "--indicator-style=file-type", "--ind=file-type", "--indicator-style=slash", "--ind=slash", "--classify", "--classify=always", + "--classify=alway", // spell-checker:disable-line + "--classify=al", "--classify=yes", "--classify=force", "--class", @@ -2236,10 +2299,13 @@ fn test_ls_indicator_style() { // Classify, Indicator options should not contain any indicators when value is none. for opt in [ "--indicator-style=none", + "--indicator-style=n", "--ind=none", "--classify=none", "--classify=never", + "--classify=non", "--classify=no", + "--classify=n", ] { // Verify that there are no indicators for any of the file types. scene @@ -2553,6 +2619,12 @@ fn test_ls_version_sort() { expected ); + let result = scene.ucmd().arg("-1").arg("--sort=v").succeeds(); + assert_eq!( + result.stdout_str().split('\n').collect::>(), + expected + ); + let result = scene.ucmd().arg("-a1v").succeeds(); expected.insert(expected.len() - 1, ".."); expected.insert(0, "."); @@ -2589,19 +2661,27 @@ fn test_ls_quoting_style() { for (arg, correct) in [ ("--quoting-style=literal", "one?two"), + ("--quoting-style=litera", "one?two"), // spell-checker:disable-line + ("--quoting-style=li", "one?two"), ("-N", "one?two"), ("--literal", "one?two"), ("--l", "one?two"), ("--quoting-style=c", "\"one\\ntwo\""), + ("--quoting-style=c-", "\"one\\ntwo\""), + ("--quoting-style=c-maybe", "\"one\\ntwo\""), ("-Q", "\"one\\ntwo\""), ("--quote-name", "\"one\\ntwo\""), ("--quoting-style=escape", "one\\ntwo"), + ("--quoting-style=escap", "one\\ntwo"), // spell-checker:disable-line ("-b", "one\\ntwo"), ("--escape", "one\\ntwo"), ("--quoting-style=shell-escape", "'one'$'\\n''two'"), ("--quoting-style=shell-escape-always", "'one'$'\\n''two'"), + ("--quoting-style=shell-escape-alway", "'one'$'\\n''two'"), + ("--quoting-style=shell-escape-a", "'one'$'\\n''two'"), ("--quoting-style=shell", "one?two"), ("--quoting-style=shell-always", "'one?two'"), + ("--quoting-style=shell-a", "'one?two'"), ] { scene .ucmd() @@ -4244,11 +4324,18 @@ fn test_ls_hyperlink() { .stdout_str() .contains(&format!("{path}{separator}{file}\x07{file}\x1b]8;;\x07"))); - scene - .ucmd() - .arg("--hyperlink=never") - .succeeds() - .stdout_is(format!("{file}\n")); + for argument in [ + "--hyperlink=never", + "--hyperlink=neve", // spell-checker:disable-line + "--hyperlink=ne", // spell-checker:disable-line + "--hyperlink=n", + ] { + scene + .ucmd() + .arg(argument) + .succeeds() + .stdout_is(format!("{file}\n")); + } } // spell-checker: disable diff --git a/tests/by-util/test_numfmt.rs b/tests/by-util/test_numfmt.rs index bb80502d58..1dddedfdde 100644 --- a/tests/by-util/test_numfmt.rs +++ b/tests/by-util/test_numfmt.rs @@ -550,10 +550,14 @@ fn test_delimiter_with_padding_and_fields() { fn test_round() { for (method, exp) in [ ("from-zero", ["9.1K", "-9.1K", "9.1K", "-9.1K"]), + ("from-zer", ["9.1K", "-9.1K", "9.1K", "-9.1K"]), // spell-checker:disable-line + ("f", ["9.1K", "-9.1K", "9.1K", "-9.1K"]), ("towards-zero", ["9.0K", "-9.0K", "9.0K", "-9.0K"]), ("up", ["9.1K", "-9.0K", "9.1K", "-9.0K"]), ("down", ["9.0K", "-9.1K", "9.0K", "-9.1K"]), ("nearest", ["9.0K", "-9.0K", "9.1K", "-9.1K"]), + ("near", ["9.0K", "-9.0K", "9.1K", "-9.1K"]), + ("n", ["9.0K", "-9.0K", "9.1K", "-9.1K"]), ] { new_ucmd!() .args(&[ diff --git a/tests/by-util/test_od.rs b/tests/by-util/test_od.rs index 78c4e1b043..569d096c17 100644 --- a/tests/by-util/test_od.rs +++ b/tests/by-util/test_od.rs @@ -53,6 +53,20 @@ fn test_file() { .no_stderr() .stdout_is(unindent(ALPHA_OUT)); + new_ucmd!() + .arg("--endian=littl") // spell-checker:disable-line + .arg(file.as_os_str()) + .succeeds() + .no_stderr() + .stdout_is(unindent(ALPHA_OUT)); + + new_ucmd!() + .arg("--endian=l") + .arg(file.as_os_str()) + .succeeds() + .no_stderr() + .stdout_is(unindent(ALPHA_OUT)); + // Ensure that default format matches `-t o2`, and that `-t` does not absorb file argument new_ucmd!() .arg("--endian=little") @@ -463,6 +477,16 @@ fn test_big_endian() { .run_piped_stdin(&input[..]) .no_stderr() .success() + .stdout_is(&expected_output); + new_ucmd!() + .arg("--endian=b") + .arg("-F") + .arg("-f") + .arg("-X") + .arg("-x") + .run_piped_stdin(&input[..]) + .no_stderr() + .success() .stdout_is(expected_output); } diff --git a/tests/by-util/test_shred.rs b/tests/by-util/test_shred.rs index 1ff847afcd..82e421839a 100644 --- a/tests/by-util/test_shred.rs +++ b/tests/by-util/test_shred.rs @@ -17,6 +17,11 @@ fn test_invalid_remove_arg() { new_ucmd!().arg("--remove=unknown").fails().code_is(1); } +#[test] +fn test_ambiguous_remove_arg() { + new_ucmd!().arg("--remove=wip").fails().code_is(1); +} + #[test] fn test_shred() { let (at, mut ucmd) = at_and_ucmd!(); @@ -49,15 +54,15 @@ fn test_shred_remove() { #[test] fn test_shred_remove_unlink() { - let (at, mut ucmd) = at_and_ucmd!(); - - let file = "test_shred_remove_unlink"; - at.touch(file); - - ucmd.arg("--remove=unlink").arg(file).succeeds(); - - // File was deleted - assert!(!at.file_exists(file)); + // spell-checker:disable-next-line + for argument in ["--remove=unlink", "--remove=unlin", "--remove=u"] { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_shred_remove_unlink"; + at.touch(file); + ucmd.arg(argument).arg(file).succeeds(); + // File was deleted + assert!(!at.file_exists(file)); + } } #[test] @@ -75,15 +80,15 @@ fn test_shred_remove_wipe() { #[test] fn test_shred_remove_wipesync() { - let (at, mut ucmd) = at_and_ucmd!(); - - let file = "test_shred_remove_wipesync"; - at.touch(file); - - ucmd.arg("--remove=wipesync").arg(file).succeeds(); - - // File was deleted - assert!(!at.file_exists(file)); + // spell-checker:disable-next-line + for argument in ["--remove=wipesync", "--remove=wipesyn", "--remove=wipes"] { + let (at, mut ucmd) = at_and_ucmd!(); + let file = "test_shred_remove_wipesync"; + at.touch(file); + ucmd.arg(argument).arg(file).succeeds(); + // File was deleted + assert!(!at.file_exists(file)); + } } #[test] diff --git a/tests/by-util/test_sort.rs b/tests/by-util/test_sort.rs index 2e114348b4..83c925af91 100644 --- a/tests/by-util/test_sort.rs +++ b/tests/by-util/test_sort.rs @@ -126,7 +126,16 @@ fn test_ext_sort_zero_terminated() { #[test] fn test_months_whitespace() { - test_helper("months-whitespace", &["-M", "--month-sort", "--sort=month"]); + test_helper( + "months-whitespace", + &[ + "-M", + "--month-sort", + "--sort=month", + "--sort=mont", // spell-checker:disable-line + "--sort=m", + ], + ); } #[test] @@ -141,6 +150,16 @@ fn test_version_sort_unstable() { .pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n") .succeeds() .stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n"); + new_ucmd!() + .arg("--sort=versio") // spell-checker:disable-line + .pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n") + .succeeds() + .stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n"); + new_ucmd!() + .arg("--sort=v") + .pipe_in("0.1\n0.02\n0.2\n0.002\n0.3\n") + .succeeds() + .stdout_is("0.1\n0.002\n0.02\n0.2\n0.3\n"); } #[test] @@ -157,7 +176,14 @@ fn test_version_sort_stable() { fn test_human_numeric_whitespace() { test_helper( "human-numeric-whitespace", - &["-h", "--human-numeric-sort", "--sort=human-numeric"], + &[ + "-h", + "--human-numeric-sort", + "--sort=human-numeric", + "--sort=human-numeri", // spell-checker:disable-line + "--sort=human", + "--sort=h", + ], ); } @@ -177,7 +203,14 @@ fn test_ext_sort_as64_bailout() { fn test_multiple_decimals_general() { test_helper( "multiple_decimals_general", - &["-g", "--general-numeric-sort", "--sort=general-numeric"], + &[ + "-g", + "--general-numeric-sort", + "--sort=general-numeric", + "--sort=general-numeri", // spell-checker:disable-line + "--sort=general", + "--sort=g", + ], ); } @@ -185,7 +218,7 @@ fn test_multiple_decimals_general() { fn test_multiple_decimals_numeric() { test_helper( "multiple_decimals_numeric", - &["-n", "--numeric-sort", "--sort=numeric"], + &["-n", "--numeric-sort", "--sort=numeric", "--sort=n"], ); } @@ -784,7 +817,13 @@ fn test_pipe() { #[test] fn test_check() { - for diagnose_arg in ["-c", "--check", "--check=diagnose-first"] { + for diagnose_arg in [ + "-c", + "--check", + "--check=diagnose-first", + "--check=diagnose", + "--check=d", + ] { new_ucmd!() .arg(diagnose_arg) .arg("check_fail.txt") @@ -802,12 +841,25 @@ fn test_check() { #[test] fn test_check_silent() { - for silent_arg in ["-C", "--check=silent", "--check=quiet"] { + for silent_arg in [ + "-C", + "--check=silent", + "--check=quiet", + "--check=silen", // spell-checker:disable-line + "--check=quie", // spell-checker:disable-line + "--check=s", + "--check=q", + ] { new_ucmd!() .arg(silent_arg) .arg("check_fail.txt") .fails() .stdout_is(""); + new_ucmd!() + .arg(silent_arg) + .arg("empty.txt") + .succeeds() + .no_output(); } } diff --git a/tests/by-util/test_tail.rs b/tests/by-util/test_tail.rs index cc5a76c31a..f11eac190d 100644 --- a/tests/by-util/test_tail.rs +++ b/tests/by-util/test_tail.rs @@ -533,37 +533,40 @@ fn test_follow_multiple() { #[test] #[cfg(not(target_os = "windows"))] // FIXME: test times out fn test_follow_name_multiple() { - let (at, mut ucmd) = at_and_ucmd!(); - let mut child = ucmd - .arg("--follow=name") - .arg(FOOBAR_TXT) - .arg(FOOBAR_2_TXT) - .run_no_wait(); + // spell-checker:disable-next-line + for argument in ["--follow=name", "--follo=nam", "--f=n"] { + let (at, mut ucmd) = at_and_ucmd!(); + let mut child = ucmd + .arg(argument) + .arg(FOOBAR_TXT) + .arg(FOOBAR_2_TXT) + .run_no_wait(); - child - .make_assertion_with_delay(500) - .is_alive() - .with_current_output() - .stdout_only_fixture("foobar_follow_multiple.expected"); + child + .make_assertion_with_delay(500) + .is_alive() + .with_current_output() + .stdout_only_fixture("foobar_follow_multiple.expected"); - let first_append = "trois\n"; - at.append(FOOBAR_2_TXT, first_append); + let first_append = "trois\n"; + at.append(FOOBAR_2_TXT, first_append); - child - .make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS) - .with_current_output() - .stdout_only(first_append); + child + .make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS) + .with_current_output() + .stdout_only(first_append); - let second_append = "twenty\nthirty\n"; - at.append(FOOBAR_TXT, second_append); + let second_append = "twenty\nthirty\n"; + at.append(FOOBAR_TXT, second_append); - child - .make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS) - .with_current_output() - .stdout_only_fixture("foobar_follow_multiple_appended.expected"); + child + .make_assertion_with_delay(DEFAULT_SLEEP_INTERVAL_MILLIS) + .with_current_output() + .stdout_only_fixture("foobar_follow_multiple_appended.expected"); - child.make_assertion().is_alive(); - child.kill(); + child.make_assertion().is_alive(); + child.kill(); + } } #[test] @@ -844,7 +847,7 @@ fn test_follow_missing() { // Ensure that --follow=name does not imply --retry. // Ensure that --follow={descriptor,name} (without --retry) does *not wait* for the // file to appear. - for follow_mode in &["--follow=descriptor", "--follow=name"] { + for follow_mode in &["--follow=descriptor", "--follow=name", "--fo=d", "--fo=n"] { new_ucmd!() .arg(follow_mode) .arg("missing") diff --git a/tests/by-util/test_tee.rs b/tests/by-util/test_tee.rs index 9ff4ea7dcf..8b94fe77f0 100644 --- a/tests/by-util/test_tee.rs +++ b/tests/by-util/test_tee.rs @@ -311,6 +311,23 @@ mod linux_only { expect_correct(file_out_a, &at, content.as_str()); } + #[test] + fn test_pipe_error_warn_nopipe_3_shortcut() { + let (at, mut ucmd) = at_and_ucmd!(); + + let file_out_a = "tee_file_out_a"; + + let proc = ucmd + .arg("--output-error=warn-") + .arg(file_out_a) + .set_stdout(make_broken_pipe()); + + let (content, output) = run_tee(proc); + + expect_success(&output); + expect_correct(file_out_a, &at, content.as_str()); + } + #[test] fn test_pipe_error_warn() { let (at, mut ucmd) = at_and_ucmd!(); @@ -362,6 +379,23 @@ mod linux_only { expect_correct(file_out_a, &at, content.as_str()); } + #[test] + fn test_pipe_error_exit_nopipe_shortcut() { + let (at, mut ucmd) = at_and_ucmd!(); + + let file_out_a = "tee_file_out_a"; + + let proc = ucmd + .arg("--output-error=exit-nop") + .arg(file_out_a) + .set_stdout(make_broken_pipe()); + + let (content, output) = run_tee(proc); + + expect_success(&output); + expect_correct(file_out_a, &at, content.as_str()); + } + #[test] fn test_space_error_default() { let (at, mut ucmd) = at_and_ucmd!(); diff --git a/tests/by-util/test_touch.rs b/tests/by-util/test_touch.rs index ca2a8b7d85..44a198452e 100644 --- a/tests/by-util/test_touch.rs +++ b/tests/by-util/test_touch.rs @@ -192,7 +192,14 @@ fn test_touch_set_cymdhms_time() { #[test] fn test_touch_set_only_atime() { - let atime_args = ["-a", "--time=access", "--time=atime", "--time=use"]; + let atime_args = [ + "-a", + "--time=access", + "--time=atime", + "--time=atim", // spell-checker:disable-line + "--time=a", + "--time=use", + ]; let file = "test_touch_set_only_atime"; for atime_arg in atime_args { @@ -293,7 +300,7 @@ fn test_touch_set_both_time_and_date() { #[test] fn test_touch_set_only_mtime() { - let mtime_args = ["-m", "--time=modify", "--time=mtime"]; + let mtime_args = ["-m", "--time=modify", "--time=mtime", "--time=m"]; let file = "test_touch_set_only_mtime"; for mtime_arg in mtime_args { diff --git a/tests/by-util/test_uniq.rs b/tests/by-util/test_uniq.rs index 911f144908..8585ebd835 100644 --- a/tests/by-util/test_uniq.rs +++ b/tests/by-util/test_uniq.rs @@ -145,6 +145,21 @@ fn test_stdin_all_repeated() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("sorted-all-repeated.expected"); + new_ucmd!() + .args(&["--all-repeated=none"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated.expected"); + new_ucmd!() + .args(&["--all-repeated=non"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated.expected"); + new_ucmd!() + .args(&["--all-repeated=n"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated.expected"); } #[test] @@ -167,6 +182,16 @@ fn test_stdin_all_repeated_separate() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("sorted-all-repeated-separate.expected"); + new_ucmd!() + .args(&["--all-repeated=separat"]) // spell-checker:disable-line + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated-separate.expected"); + new_ucmd!() + .args(&["--all-repeated=s"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated-separate.expected"); } #[test] @@ -176,6 +201,16 @@ fn test_stdin_all_repeated_prepend() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("sorted-all-repeated-prepend.expected"); + new_ucmd!() + .args(&["--all-repeated=prepen"]) // spell-checker:disable-line + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated-prepend.expected"); + new_ucmd!() + .args(&["--all-repeated=p"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("sorted-all-repeated-prepend.expected"); } #[test] @@ -253,6 +288,11 @@ fn test_group_prepend() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("group-prepend.expected"); + new_ucmd!() + .args(&["--group=p"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("group-prepend.expected"); } #[test] @@ -262,6 +302,11 @@ fn test_group_append() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("group-append.expected"); + new_ucmd!() + .args(&["--group=a"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("group-append.expected"); } #[test] @@ -271,6 +316,16 @@ fn test_group_both() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("group-both.expected"); + new_ucmd!() + .args(&["--group=bot"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("group-both.expected"); + new_ucmd!() + .args(&["--group=b"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("group-both.expected"); } #[test] @@ -280,6 +335,11 @@ fn test_group_separate() { .pipe_in_fixture(INPUT) .run() .stdout_is_fixture("group.expected"); + new_ucmd!() + .args(&["--group=s"]) + .pipe_in_fixture(INPUT) + .run() + .stdout_is_fixture("group.expected"); } #[test] diff --git a/tests/by-util/test_wc.rs b/tests/by-util/test_wc.rs index 0202ba4e88..d5d955c9b4 100644 --- a/tests/by-util/test_wc.rs +++ b/tests/by-util/test_wc.rs @@ -531,6 +531,10 @@ fn test_total_auto() { .args(&["lorem_ipsum.txt", "--total=auto"]) .run() .stdout_is(" 13 109 772 lorem_ipsum.txt\n"); + new_ucmd!() + .args(&["lorem_ipsum.txt", "--tot=au"]) + .run() + .stdout_is(" 13 109 772 lorem_ipsum.txt\n"); new_ucmd!() .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=auto"]) @@ -551,6 +555,13 @@ fn test_total_always() { " 13 109 772 lorem_ipsum.txt\n", " 13 109 772 total\n", )); + new_ucmd!() + .args(&["lorem_ipsum.txt", "--total=al"]) + .run() + .stdout_is(concat!( + " 13 109 772 lorem_ipsum.txt\n", + " 13 109 772 total\n", + )); new_ucmd!() .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=always"]) @@ -576,6 +587,13 @@ fn test_total_never() { " 13 109 772 lorem_ipsum.txt\n", " 18 204 1115 moby_dick.txt\n", )); + new_ucmd!() + .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=n"]) + .run() + .stdout_is(concat!( + " 13 109 772 lorem_ipsum.txt\n", + " 18 204 1115 moby_dick.txt\n", + )); } #[test] @@ -589,6 +607,10 @@ fn test_total_only() { .args(&["lorem_ipsum.txt", "moby_dick.txt", "--total=only"]) .run() .stdout_is("31 313 1887\n"); + new_ucmd!() + .args(&["lorem_ipsum.txt", "moby_dick.txt", "--t=o"]) + .run() + .stdout_is("31 313 1887\n"); } #[test]