Merge branch 'master' into refactor_expected_result

This commit is contained in:
Jan Scheer 2021-07-08 00:44:32 +02:00
commit d59cd4fc4a
No known key found for this signature in database
GPG key ID: C62AD4C29E2B9828
11 changed files with 283 additions and 54 deletions

View file

@ -1,4 +1,4 @@
# spell-checker:ignore (misc) testsuite runtest (targets) busytest distclean manpages pkgs ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MANDIR MULTICALL
# spell-checker:ignore (misc) testsuite runtest findstring (targets) busytest distclean manpages pkgs ; (vars/env) BINDIR BUILDDIR CARGOFLAGS DESTDIR DOCSDIR INSTALLDIR INSTALLEES MANDIR MULTICALL
# Config options
PROFILE ?= debug
@ -307,10 +307,12 @@ ifeq (${MULTICALL}, y)
$(INSTALL) $(BUILDDIR)/coreutils $(INSTALLDIR_BIN)/$(PROG_PREFIX)coreutils
cd $(INSTALLDIR_BIN) && $(foreach prog, $(filter-out coreutils, $(INSTALLEES)), \
ln -fs $(PROG_PREFIX)coreutils $(PROG_PREFIX)$(prog) &&) :
$(if $(findstring test,$(INSTALLEES)), ln -fs $(PROG_PREFIX)coreutils $(PROG_PREFIX)[)
cat $(DOCSDIR)/_build/man/coreutils.1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)coreutils.1.gz
else
$(foreach prog, $(INSTALLEES), \
$(INSTALL) $(BUILDDIR)/$(prog) $(INSTALLDIR_BIN)/$(PROG_PREFIX)$(prog);)
$(if $(findstring test,$(INSTALLEES)), $(INSTALL) $(BUILDDIR)/test $(INSTALLDIR_BIN)/$(PROG_PREFIX)[)
endif
$(foreach man, $(filter $(INSTALLEES), $(basename $(notdir $(wildcard $(DOCSDIR)/_build/man/*)))), \
cat $(DOCSDIR)/_build/man/$(man).1 | gzip > $(INSTALLDIR_MAN)/$(PROG_PREFIX)$(man).1.gz &&) :
@ -326,6 +328,7 @@ ifeq (${MULTICALL}, y)
endif
rm -f $(addprefix $(INSTALLDIR_MAN)/,$(PROG_PREFIX)coreutils.1.gz)
rm -f $(addprefix $(INSTALLDIR_BIN)/$(PROG_PREFIX),$(PROGS))
rm -f $(INSTALLDIR_BIN)/$(PROG_PREFIX)[
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/bash-completion/completions/$(PROG_PREFIX),$(PROGS))
rm -f $(addprefix $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/$(PROG_PREFIX),$(addsuffix .fish,$(PROGS)))

View file

@ -5,6 +5,9 @@
// For the full copyright and license information, please view the LICENSE
// file that was distributed with this source code.
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;

View file

@ -31,6 +31,8 @@ use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::time::{Duration, UNIX_EPOCH};
use std::{error::Error, fmt::Display};
use uucore::error::{UCustomError, UResult};
use uucore::parse_size::{parse_size, ParseSizeError};
use uucore::InvalidEncodingHandling;
#[cfg(windows)]
@ -399,8 +401,61 @@ fn get_usage() -> String {
)
}
#[derive(Debug)]
enum DuError {
InvalidMaxDepthArg(String),
SummarizeDepthConflict(String),
InvalidTimeStyleArg(String),
InvalidTimeArg(String),
}
impl Display for DuError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DuError::InvalidMaxDepthArg(s) => write!(f, "invalid maximum depth '{}'", s),
DuError::SummarizeDepthConflict(s) => {
write!(f, "summarizing conflicts with --max-depth={}", s)
}
DuError::InvalidTimeStyleArg(s) => {
write!(
f,
"invalid argument '{}' for 'time style'
Valid arguments are:
- 'full-iso'
- 'long-iso'
- 'iso'
Try '{} --help' for more information.",
s, NAME
)
}
DuError::InvalidTimeArg(s) => {
write!(
f,
"Invalid argument '{}' for --time.
'birth' and 'creation' arguments are not supported on this platform.",
s
)
}
}
}
}
impl Error for DuError {}
impl UCustomError for DuError {
fn code(&self) -> i32 {
match self {
Self::InvalidMaxDepthArg(_) => 1,
Self::SummarizeDepthConflict(_) => 1,
Self::InvalidTimeStyleArg(_) => 1,
Self::InvalidTimeArg(_) => 1,
}
}
}
#[uucore_procs::gen_uumain]
#[allow(clippy::cognitive_complexity)]
pub fn uumain(args: impl uucore::Args) -> i32 {
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
let args = args
.collect_str(InvalidEncodingHandling::Ignore)
.accept_any();
@ -411,19 +466,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
let summarize = matches.is_present(options::SUMMARIZE);
let max_depth_str = matches.value_of(options::MAX_DEPTH);
let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
match (max_depth_str, max_depth) {
(Some(s), _) if summarize => {
show_error!("summarizing conflicts with --max-depth={}", s);
return 1;
}
(Some(s), None) => {
show_error!("invalid maximum depth '{}'", s);
return 1;
}
(Some(_), Some(_)) | (None, _) => { /* valid */ }
}
let max_depth = parse_depth(matches.value_of(options::MAX_DEPTH), summarize)?;
let options = Options {
all: matches.is_present(options::ALL),
@ -480,27 +523,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
}
};
let time_format_str = match matches.value_of("time-style") {
Some(s) => match s {
"full-iso" => "%Y-%m-%d %H:%M:%S.%f %z",
"long-iso" => "%Y-%m-%d %H:%M",
"iso" => "%Y-%m-%d",
_ => {
show_error!(
"invalid argument '{}' for 'time style'
Valid arguments are:
- 'full-iso'
- 'long-iso'
- 'iso'
Try '{} --help' for more information.",
s,
NAME
);
return 1;
}
},
None => "%Y-%m-%d %H:%M",
};
let time_format_str = parse_time_style(matches.value_of("time-style"))?;
let line_separator = if matches.is_present(options::NULL) {
"\0"
@ -534,18 +557,9 @@ Try '{} --help' for more information.",
Some(s) => match s {
"ctime" | "status" => stat.modified,
"access" | "atime" | "use" => stat.accessed,
"birth" | "creation" => {
if let Some(time) = stat.created {
time
} else {
show_error!(
"Invalid argument '{}' for --time.
'birth' and 'creation' arguments are not supported on this platform.",
s
);
return 1;
}
}
"birth" | "creation" => stat
.created
.ok_or_else(|| DuError::InvalidTimeArg(s.into()))?,
// below should never happen as clap already restricts the values.
_ => unreachable!("Invalid field for --time"),
},
@ -590,7 +604,28 @@ Try '{} --help' for more information.",
print!("{}", line_separator);
}
0
Ok(())
}
fn parse_time_style(s: Option<&str>) -> UResult<&str> {
match s {
Some(s) => match s {
"full-iso" => Ok("%Y-%m-%d %H:%M:%S.%f %z"),
"long-iso" => Ok("%Y-%m-%d %H:%M"),
"iso" => Ok("%Y-%m-%d"),
_ => Err(DuError::InvalidTimeStyleArg(s.into()).into()),
},
None => Ok("%Y-%m-%d %H:%M"),
}
}
fn parse_depth(max_depth_str: Option<&str>, summarize: bool) -> UResult<Option<usize>> {
let max_depth = max_depth_str.as_ref().and_then(|s| s.parse::<usize>().ok());
match (max_depth_str, max_depth) {
(Some(s), _) if summarize => Err(DuError::SummarizeDepthConflict(s.into()).into()),
(Some(s), None) => Err(DuError::InvalidMaxDepthArg(s.into()).into()),
(Some(_), Some(_)) | (None, _) => Ok(max_depth),
}
}
pub fn uu_app() -> App<'static, 'static> {

View file

@ -7,6 +7,9 @@
// spell-checker:ignore (ToDO) gethostid
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;

View file

@ -7,6 +7,9 @@
// spell-checker:ignore (ToDO) MAKEWORD addrs hashset
// Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
#![allow(clippy::nonstandard_macro_braces)]
#[macro_use]
extern crate uucore;
@ -14,6 +17,8 @@ use clap::{crate_version, App, Arg, ArgMatches};
use std::collections::hash_set::HashSet;
use std::net::ToSocketAddrs;
use std::str;
#[cfg(windows)]
use uucore::error::UUsageError;
use uucore::error::{UResult, USimpleError};
#[cfg(windows)]
@ -37,7 +42,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
#[allow(deprecated)]
let mut data = std::mem::uninitialized();
if WSAStartup(MAKEWORD(2, 2), &mut data as *mut _) != 0 {
return Err(USimpleError::new(1, format!("Failed to start Winsock 2.2")));
return Err(UUsageError::new(
1,
"Failed to start Winsock 2.2".to_string(),
));
}
}
let result = execute(args);

View file

@ -1267,6 +1267,7 @@ pub fn uu_app() -> App<'static, 'static> {
.help("sort by a key")
.long_help(LONG_HELP_KEYS)
.multiple(true)
.number_of_values(1)
.takes_value(true),
)
.arg(

View file

@ -10,12 +10,82 @@
mod parser;
use clap::{App, AppSettings};
use clap::{crate_version, App, AppSettings};
use parser::{parse, Symbol};
use std::ffi::{OsStr, OsString};
use std::path::Path;
use uucore::executable;
const USAGE: &str = "test EXPRESSION
or: test
or: [ EXPRESSION ]
or: [ ]
or: [ OPTION";
// We use after_help so that this comes after the usage string (it would come before if we used about)
const AFTER_HELP: &str = "
Exit with the status determined by EXPRESSION.
An omitted EXPRESSION defaults to false. Otherwise,
EXPRESSION is true or false and sets exit status. It is one of:
( EXPRESSION ) EXPRESSION is true
! EXPRESSION EXPRESSION is false
EXPRESSION1 -a EXPRESSION2 both EXPRESSION1 and EXPRESSION2 are true
EXPRESSION1 -o EXPRESSION2 either EXPRESSION1 or EXPRESSION2 is true
-n STRING the length of STRING is nonzero
STRING equivalent to -n STRING
-z STRING the length of STRING is zero
STRING1 = STRING2 the strings are equal
STRING1 != STRING2 the strings are not equal
INTEGER1 -eq INTEGER2 INTEGER1 is equal to INTEGER2
INTEGER1 -ge INTEGER2 INTEGER1 is greater than or equal to INTEGER2
INTEGER1 -gt INTEGER2 INTEGER1 is greater than INTEGER2
INTEGER1 -le INTEGER2 INTEGER1 is less than or equal to INTEGER2
INTEGER1 -lt INTEGER2 INTEGER1 is less than INTEGER2
INTEGER1 -ne INTEGER2 INTEGER1 is not equal to INTEGER2
FILE1 -ef FILE2 FILE1 and FILE2 have the same device and inode numbers
FILE1 -nt FILE2 FILE1 is newer (modification date) than FILE2
FILE1 -ot FILE2 FILE1 is older than FILE2
-b FILE FILE exists and is block special
-c FILE FILE exists and is character special
-d FILE FILE exists and is a directory
-e FILE FILE exists
-f FILE FILE exists and is a regular file
-g FILE FILE exists and is set-group-ID
-G FILE FILE exists and is owned by the effective group ID
-h FILE FILE exists and is a symbolic link (same as -L)
-k FILE FILE exists and has its sticky bit set
-L FILE FILE exists and is a symbolic link (same as -h)
-N FILE FILE exists and has been modified since it was last read
-O FILE FILE exists and is owned by the effective user ID
-p FILE FILE exists and is a named pipe
-r FILE FILE exists and read permission is granted
-s FILE FILE exists and has a size greater than zero
-S FILE FILE exists and is a socket
-t FD file descriptor FD is opened on a terminal
-u FILE FILE exists and its set-user-ID bit is set
-w FILE FILE exists and write permission is granted
-x FILE FILE exists and execute (or search) permission is granted
Except for -h and -L, all FILE-related tests dereference symbolic links.
Beware that parentheses need to be escaped (e.g., by backslashes) for shells.
INTEGER may also be -l STRING, which evaluates to the length of STRING.
NOTE: Binary -a and -o are inherently ambiguous. Use 'test EXPR1 && test
EXPR2' or 'test EXPR1 || test EXPR2' instead.
NOTE: [ honors the --help and --version options, but test does not.
test treats each of those as it treats any other nonempty STRING.
NOTE: your shell may have its own version of test and/or [, which usually supersedes
the version described here. Please refer to your shell's documentation
for details about the options it supports.";
pub fn uu_app() -> App<'static, 'static> {
App::new(executable!())
.setting(AppSettings::DisableHelpFlags)
@ -30,8 +100,22 @@ pub fn uumain(mut args: impl uucore::Args) -> i32 {
.to_string_lossy();
let mut args: Vec<_> = args.collect();
// If invoked via name '[', matching ']' must be in the last arg
if binary_name == "[" {
if binary_name.ends_with('[') {
// If invoked as [ we should recognize --help and --version (but not -h or -v)
if args.len() == 1 && (args[0] == "--help" || args[0] == "--version") {
// Let clap pretty-print help and version
App::new(binary_name)
.version(crate_version!())
.usage(USAGE)
.after_help(AFTER_HELP)
// Disable printing of -h and -v as valid alternatives for --help and --version,
// since we don't recognize -h and -v as help/version flags.
.setting(AppSettings::NeedsLongHelp)
.setting(AppSettings::NeedsLongVersion)
.get_matches_from(std::iter::once(program).chain(args.into_iter()));
return 0;
}
// If invoked via name '[', matching ']' must be in the last arg
let last = args.pop();
if last != Some(OsString::from("]")) {
eprintln!("[: missing ']'");

View file

@ -30,12 +30,16 @@ pub mod options {
pub static OS: &str = "operating-system";
}
#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "")))]
const HOST_OS: &str = "GNU/Linux";
#[cfg(all(target_os = "linux", not(any(target_env = "gnu", target_env = ""))))]
const HOST_OS: &str = "Linux";
#[cfg(target_os = "windows")]
const HOST_OS: &str = "Windows NT";
#[cfg(target_os = "freebsd")]
const HOST_OS: &str = "FreeBSD";
#[cfg(target_os = "netbsd")]
const HOST_OS: &str = "NetBSD";
#[cfg(target_os = "openbsd")]
const HOST_OS: &str = "OpenBSD";
#[cfg(target_vendor = "apple")]

View file

@ -951,3 +951,11 @@ fn test_conflict_check_out() {
);
}
}
#[test]
fn test_key_takes_one_arg() {
new_ucmd!()
.args(&["-k", "2.3", "keys_open_ended.txt"])
.succeeds()
.stdout_is_fixture("keys_open_ended.expected");
}

View file

@ -718,3 +718,21 @@ fn test_bracket_syntax_missing_right_bracket() {
.status_code(2)
.stderr_is("[: missing ']'");
}
#[test]
fn test_bracket_syntax_help() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
ucmd.arg("--help").succeeds().stdout_contains("USAGE:");
}
#[test]
fn test_bracket_syntax_version() {
let scenario = TestScenario::new("[");
let mut ucmd = scenario.ucmd();
ucmd.arg("--version")
.succeeds()
.stdout_matches(&r"\[ \d+\.\d+\.\d+".parse().unwrap());
}

View file

@ -1,5 +1,10 @@
use crate::common::util::*;
#[test]
fn test_uname() {
new_ucmd!().succeeds();
}
#[test]
fn test_uname_compatible() {
new_ucmd!().arg("-a").succeeds();
@ -45,3 +50,60 @@ fn test_uname_kernel() {
#[cfg(not(target_os = "linux"))]
ucmd.arg("-o").succeeds();
}
#[test]
fn test_uname_operating_system() {
#[cfg(target_vendor = "apple")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("Darwin\n");
#[cfg(target_os = "freebsd")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("FreeBSD\n");
#[cfg(target_os = "fuchsia")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("Fuchsia\n");
#[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "")))]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("GNU/Linux\n");
#[cfg(all(target_os = "linux", not(any(target_env = "gnu", target_env = ""))))]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("Linux\n");
#[cfg(target_os = "netbsd")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("NetBSD\n");
#[cfg(target_os = "openbsd")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("OpenBSD\n");
#[cfg(target_os = "redox")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("Redox\n");
#[cfg(target_os = "windows")]
new_ucmd!()
.arg("--operating-system")
.succeeds()
.stdout_is("Windows NT\n");
}
#[test]
fn test_uname_help() {
new_ucmd!()
.arg("--help")
.succeeds()
.stdout_contains("system information");
}