1
Fork 0
mirror of https://github.com/RGBCube/uutils-coreutils synced 2025-08-02 05:57:46 +00:00

Merge branch 'master' into ls/skip_metadata

This commit is contained in:
Terts Diepraam 2021-04-24 10:45:43 +02:00
commit ce8c58b93e
45 changed files with 793 additions and 891 deletions

44
Cargo.lock generated
View file

@ -28,6 +28,15 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi 0.3.9",
]
[[package]] [[package]]
name = "arrayvec" name = "arrayvec"
version = "0.4.12" version = "0.4.12"
@ -127,9 +136,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]] [[package]]
name = "cast" name = "cast"
version = "0.2.3" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0" checksum = "cc38c385bfd7e444464011bb24820f40dd1c76bcdfa1b78611cb7c2e5cafab75"
dependencies = [ dependencies = [
"rustc_version", "rustc_version",
] ]
@ -169,7 +178,7 @@ version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [ dependencies = [
"ansi_term", "ansi_term 0.11.0",
"atty", "atty",
"bitflags", "bitflags",
"strsim", "strsim",
@ -452,9 +461,9 @@ dependencies = [
[[package]] [[package]]
name = "crossbeam-channel" name = "crossbeam-channel"
version = "0.5.0" version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dca26ee1f8d361640700bde38b2c37d8c22b3ce2d360e1fc1c74ea4b0aa7d775" checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"crossbeam-utils", "crossbeam-utils",
@ -580,7 +589,7 @@ checksum = "1d34cfa13a63ae058bfa601fe9e313bbdb3746427c1459185464ce0fcf62e1e8"
dependencies = [ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
"libc", "libc",
"redox_syscall 0.2.5", "redox_syscall 0.2.6",
"winapi 0.3.9", "winapi 0.3.9",
] ]
@ -783,6 +792,15 @@ dependencies = [
"cfg-if 1.0.0", "cfg-if 1.0.0",
] ]
[[package]]
name = "lscolors"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d24b894c45c9da468621cdd615a5a79ee5e5523dd4f75c76ebc03d458940c16e"
dependencies = [
"ansi_term 0.12.1",
]
[[package]] [[package]]
name = "match_cfg" name = "match_cfg"
version = "0.1.0" version = "0.1.0"
@ -1182,9 +1200,9 @@ checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.5" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" checksum = "8270314b5ccceb518e7e578952f0b72b88222d02e8f77f5ecf7abbb673539041"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
@ -1195,7 +1213,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f" checksum = "8440d8acb4fd3d277125b4bd01a6f38aee8d814b3b5fc09b3f2b825d37d3fe8f"
dependencies = [ dependencies = [
"redox_syscall 0.2.5", "redox_syscall 0.2.6",
] ]
[[package]] [[package]]
@ -1400,9 +1418,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.69" version = "1.0.70"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "48fe99c6bd8b1cc636890bcc071842de909d902c81ac7dab53ba33c421ab8ffb" checksum = "b9505f307c872bab8eb46f77ae357c8eba1fdacead58ee5a850116b1d7f82883"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote 1.0.9", "quote 1.0.9",
@ -1460,7 +1478,7 @@ checksum = "077185e2eac69c3f8379a4298e1e07cd36beb962290d4a51199acf0fdc10607e"
dependencies = [ dependencies = [
"libc", "libc",
"numtoa", "numtoa",
"redox_syscall 0.2.5", "redox_syscall 0.2.6",
"redox_termios", "redox_termios",
] ]
@ -1996,12 +2014,12 @@ dependencies = [
"clap", "clap",
"globset", "globset",
"lazy_static", "lazy_static",
"lscolors",
"number_prefix", "number_prefix",
"once_cell", "once_cell",
"term_grid", "term_grid",
"termsize", "termsize",
"time", "time",
"unicode-width",
"uucore", "uucore",
"uucore_procs", "uucore_procs",
] ]

View file

@ -210,7 +210,6 @@ pub struct Options {
overwrite: OverwriteMode, overwrite: OverwriteMode,
parents: bool, parents: bool,
strip_trailing_slashes: bool, strip_trailing_slashes: bool,
reflink: bool,
reflink_mode: ReflinkMode, reflink_mode: ReflinkMode,
preserve_attributes: Vec<Attribute>, preserve_attributes: Vec<Attribute>,
recursive: bool, recursive: bool,
@ -633,12 +632,12 @@ impl Options {
update: matches.is_present(OPT_UPDATE), update: matches.is_present(OPT_UPDATE),
verbose: matches.is_present(OPT_VERBOSE), verbose: matches.is_present(OPT_VERBOSE),
strip_trailing_slashes: matches.is_present(OPT_STRIP_TRAILING_SLASHES), strip_trailing_slashes: matches.is_present(OPT_STRIP_TRAILING_SLASHES),
reflink: matches.is_present(OPT_REFLINK),
reflink_mode: { reflink_mode: {
if let Some(reflink) = matches.value_of(OPT_REFLINK) { if let Some(reflink) = matches.value_of(OPT_REFLINK) {
match reflink { match reflink {
"always" => ReflinkMode::Always, "always" => ReflinkMode::Always,
"auto" => ReflinkMode::Auto, "auto" => ReflinkMode::Auto,
"never" => ReflinkMode::Never,
value => { value => {
return Err(Error::InvalidArgument(format!( return Err(Error::InvalidArgument(format!(
"invalid argument '{}' for \'reflink\'", "invalid argument '{}' for \'reflink\'",
@ -1196,7 +1195,7 @@ fn copy_file(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
///Copy the file from `source` to `dest` either using the normal `fs::copy` or the ///Copy the file from `source` to `dest` either using the normal `fs::copy` or the
///`FICLONE` ioctl if --reflink is specified and the filesystem supports it. ///`FICLONE` ioctl if --reflink is specified and the filesystem supports it.
fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> { fn copy_helper(source: &Path, dest: &Path, options: &Options) -> CopyResult<()> {
if options.reflink { if options.reflink_mode != ReflinkMode::Never {
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
return Err("--reflink is only supported on linux".to_string().into()); return Err("--reflink is only supported on linux".to_string().into());

View file

@ -16,19 +16,19 @@ path = "src/ls.rs"
[dependencies] [dependencies]
clap = "2.33" clap = "2.33"
lazy_static = "1.0.1"
number_prefix = "0.4" number_prefix = "0.4"
term_grid = "0.1.5" term_grid = "0.1.5"
termsize = "0.1.6" termsize = "0.1.6"
time = "0.1.40" time = "0.1.40"
unicode-width = "0.1.5"
globset = "0.4.6" globset = "0.4.6"
lscolors = { version="0.7.1", features=["ansi_term"] }
uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs"] } uucore = { version=">=0.0.8", package="uucore", path="../../uucore", features=["entries", "fs"] }
uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" } uucore_procs = { version=">=0.0.5", package="uucore_procs", path="../../uucore_procs" }
once_cell = "1.7.2" once_cell = "1.7.2"
atty = "0.2"
[target.'cfg(unix)'.dependencies] [target.'cfg(unix)'.dependencies]
atty = "0.2" lazy_static = "1.4.0"
[[bin]] [[bin]]
name = "ls" name = "ls"

View file

@ -7,27 +7,26 @@
// spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf // spell-checker:ignore (ToDO) cpio svgz webm somegroup nlink rmvb xspf
#[macro_use]
extern crate uucore;
#[cfg(unix)] #[cfg(unix)]
#[macro_use] #[macro_use]
extern crate lazy_static; extern crate lazy_static;
#[macro_use]
extern crate uucore;
mod quoting_style; mod quoting_style;
mod version_cmp; mod version_cmp;
use clap::{App, Arg}; use clap::{App, Arg};
use globset::{self, Glob, GlobSet, GlobSetBuilder}; use globset::{self, Glob, GlobSet, GlobSetBuilder};
use lscolors::LsColors;
use number_prefix::NumberPrefix; use number_prefix::NumberPrefix;
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use quoting_style::{escape_name, QuotingStyle}; use quoting_style::{escape_name, QuotingStyle};
#[cfg(unix)] #[cfg(unix)]
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::{self, DirEntry, FileType, Metadata}; use std::fs::{self, DirEntry, FileType, Metadata};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
#[cfg(any(unix, target_os = "redox"))] #[cfg(any(unix, target_os = "redox"))]
use std::os::unix::fs::MetadataExt; use std::os::unix::fs::{FileTypeExt, MetadataExt};
#[cfg(windows)] #[cfg(windows)]
use std::os::windows::fs::MetadataExt; use std::os::windows::fs::MetadataExt;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@ -39,9 +38,7 @@ use std::{cmp::Reverse, process::exit};
use term_grid::{Cell, Direction, Filling, Grid, GridOptions}; use term_grid::{Cell, Direction, Filling, Grid, GridOptions};
use time::{strftime, Timespec}; use time::{strftime, Timespec};
#[cfg(unix)] #[cfg(unix)]
use unicode_width::UnicodeWidthStr; use uucore::libc::{S_IXGRP, S_IXOTH, S_IXUSR};
#[cfg(unix)]
use uucore::libc::{mode_t, S_ISGID, S_ISUID, S_ISVTX, S_IWOTH, S_IXGRP, S_IXOTH, S_IXUSR};
static VERSION: &str = env!("CARGO_PKG_VERSION"); static VERSION: &str = env!("CARGO_PKG_VERSION");
static ABOUT: &str = " static ABOUT: &str = "
@ -54,30 +51,6 @@ fn get_usage() -> String {
format!("{0} [OPTION]... [FILE]...", executable!()) format!("{0} [OPTION]... [FILE]...", executable!())
} }
#[cfg(unix)]
static DEFAULT_COLORS: &str = "rs=0:di=01;34:ln=01;36:mh=00:pi=40;33:so=01;35:do=01;35:bd=40;33;01:cd=40;33;01:or=40;31;01:mi=00:su=37;41:sg=30;43:ca=30;41:tw=30;42:ow=34;42:st=37;44:ex=01;32:*.tar=01;31:*.tgz=01;31:*.arc=01;31:*.arj=01;31:*.taz=01;31:*.lha=01;31:*.lz4=01;31:*.lzh=01;31:*.lzma=01;31:*.tlz=01;31:*.txz=01;31:*.tzo=01;31:*.t7z=01;31:*.zip=01;31:*.z=01;31:*.Z=01;31:*.dz=01;31:*.gz=01;31:*.lrz=01;31:*.lz=01;31:*.lzo=01;31:*.xz=01;31:*.bz2=01;31:*.bz=01;31:*.tbz=01;31:*.tbz2=01;31:*.tz=01;31:*.deb=01;31:*.rpm=01;31:*.jar=01;31:*.war=01;31:*.ear=01;31:*.sar=01;31:*.rar=01;31:*.alz=01;31:*.ace=01;31:*.zoo=01;31:*.cpio=01;31:*.7z=01;31:*.rz=01;31:*.cab=01;31:*.jpg=01;35:*.jpeg=01;35:*.gif=01;35:*.bmp=01;35:*.pbm=01;35:*.pgm=01;35:*.ppm=01;35:*.tga=01;35:*.xbm=01;35:*.xpm=01;35:*.tif=01;35:*.tiff=01;35:*.png=01;35:*.svg=01;35:*.svgz=01;35:*.mng=01;35:*.pcx=01;35:*.mov=01;35:*.mpg=01;35:*.mpeg=01;35:*.m2v=01;35:*.mkv=01;35:*.webm=01;35:*.ogm=01;35:*.mp4=01;35:*.m4v=01;35:*.mp4v=01;35:*.vob=01;35:*.qt=01;35:*.nuv=01;35:*.wmv=01;35:*.asf=01;35:*.rm=01;35:*.rmvb=01;35:*.flc=01;35:*.avi=01;35:*.fli=01;35:*.flv=01;35:*.gl=01;35:*.dl=01;35:*.xcf=01;35:*.xwd=01;35:*.yuv=01;35:*.cgm=01;35:*.emf=01;35:*.ogv=01;35:*.ogx=01;35:*.aac=00;36:*.au=00;36:*.flac=00;36:*.m4a=00;36:*.mid=00;36:*.midi=00;36:*.mka=00;36:*.mp3=00;36:*.mpc=00;36:*.ogg=00;36:*.ra=00;36:*.wav=00;36:*.oga=00;36:*.opus=00;36:*.spx=00;36:*.xspf=00;36:";
#[cfg(unix)]
lazy_static! {
static ref LS_COLORS: String =
std::env::var("LS_COLORS").unwrap_or_else(|_| DEFAULT_COLORS.to_string());
static ref COLOR_MAP: HashMap<&'static str, &'static str> = {
let codes = LS_COLORS.split(':');
let mut map = HashMap::new();
for c in codes {
let p: Vec<_> = c.splitn(2, '=').collect();
if p.len() == 2 {
map.insert(p[0], p[1]);
}
}
map
};
static ref RESET_CODE: &'static str = COLOR_MAP.get("rs").unwrap_or(&"0");
static ref LEFT_CODE: &'static str = COLOR_MAP.get("lc").unwrap_or(&"\x1b[");
static ref RIGHT_CODE: &'static str = COLOR_MAP.get("rc").unwrap_or(&"m");
static ref END_CODE: &'static str = COLOR_MAP.get("ec").unwrap_or(&"");
}
pub mod options { pub mod options {
pub mod format { pub mod format {
pub static ONELINE: &str = "1"; pub static ONELINE: &str = "1";
@ -212,8 +185,7 @@ struct Config {
time: Time, time: Time,
#[cfg(unix)] #[cfg(unix)]
inode: bool, inode: bool,
#[cfg(unix)] color: Option<LsColors>,
color: bool,
long: LongFormat, long: LongFormat,
width: Option<u16>, width: Option<u16>,
quoting_style: QuotingStyle, quoting_style: QuotingStyle,
@ -337,8 +309,7 @@ impl Config {
Time::Modification Time::Modification
}; };
#[cfg(unix)] let needs_color = match options.value_of(options::COLOR) {
let color = match options.value_of(options::COLOR) {
None => options.is_present(options::COLOR), None => options.is_present(options::COLOR),
Some(val) => match val { Some(val) => match val {
"" | "always" | "yes" | "force" => true, "" | "always" | "yes" | "force" => true,
@ -347,6 +318,12 @@ impl Config {
}, },
}; };
let color = if needs_color {
Some(LsColors::from_env().unwrap_or_default())
} else {
None
};
let size_format = if options.is_present(options::size::HUMAN_READABLE) { let size_format = if options.is_present(options::size::HUMAN_READABLE) {
SizeFormat::Binary SizeFormat::Binary
} else if options.is_present(options::size::SI) { } else if options.is_present(options::size::SI) {
@ -524,7 +501,6 @@ impl Config {
size_format, size_format,
directory: options.is_present(options::DIRECTORY), directory: options.is_present(options::DIRECTORY),
time, time,
#[cfg(unix)]
color, color,
#[cfg(unix)] #[cfg(unix)]
inode: options.is_present(options::INODE), inode: options.is_present(options::INODE),
@ -1551,131 +1527,58 @@ fn get_file_name(name: &Path, strip: Option<&Path>) -> String {
name.to_string_lossy().into_owned() name.to_string_lossy().into_owned()
} }
#[cfg(not(unix))] #[cfg(unix)]
fn display_file_name(path: &PathData, strip: Option<&Path>, config: &Config) -> Option<Cell> { fn file_is_executable(md: &Metadata) -> bool {
let mut name = escape_name(get_file_name(&path.p_buf, strip), &config.quoting_style); // Mode always returns u32, but the flags might not be, based on the platform
// e.g. linux has u32, mac has u16.
// S_IXUSR -> user has execute permission
// S_IXGRP -> group has execute persmission
// S_IXOTH -> other users have execute permission
md.mode() & ((S_IXUSR | S_IXGRP | S_IXOTH) as u32) != 0
}
#[allow(clippy::clippy::collapsible_else_if)]
fn classify_file(path: &PathData) -> Option<char> {
let file_type = path.file_type()?; let file_type = path.file_type()?;
match config.indicator_style { if file_type.is_dir() {
IndicatorStyle::Classify | IndicatorStyle::FileType => { Some('/')
if file_type.is_dir() { } else if file_type.is_symlink() {
name.push('/'); Some('@')
}
if file_type.is_symlink() {
name.push('@');
}
}
IndicatorStyle::Slash => {
if file_type.is_dir() {
name.push('/');
}
}
_ => (),
};
if config.format == Format::Long && path.file_type()?.is_symlink() {
if let Ok(target) = path.p_buf.read_link() {
// We don't bother updating width here because it's not used for long listings
let target_name = target.to_string_lossy().to_string();
name.push_str(" -> ");
name.push_str(&target_name);
}
}
Some(name.into())
}
#[cfg(unix)]
fn color_name(name: String, typ: &str) -> String {
let mut typ = typ;
if !COLOR_MAP.contains_key(typ) {
if typ == "or" {
typ = "ln";
} else if typ == "mi" {
typ = "fi";
}
};
if let Some(code) = COLOR_MAP.get(typ) {
format!(
"{}{}{}{}{}{}{}{}",
*LEFT_CODE, code, *RIGHT_CODE, name, *END_CODE, *LEFT_CODE, *RESET_CODE, *RIGHT_CODE,
)
} else { } else {
name #[cfg(unix)]
} {
} if file_type.is_socket() {
Some('=')
#[cfg(unix)] } else if file_type.is_fifo() {
macro_rules! has { Some('|')
($mode:expr, $perm:expr) => { } else if file_type.is_file() && file_is_executable(path.md()?) {
$mode & ($perm as mode_t) != 0
};
}
#[cfg(unix)]
#[allow(clippy::cognitive_complexity)]
fn display_file_name(path: &PathData, strip: Option<&Path>, config: &Config) -> Option<Cell> {
let mut name = escape_name(get_file_name(&path.p_buf, strip), &config.quoting_style);
if config.format != Format::Long && config.inode {
name = get_inode(path.md()?) + " " + &name;
}
let mut width = UnicodeWidthStr::width(&*name);
let ext;
if config.color || config.indicator_style != IndicatorStyle::None {
let metadata = path.md()?;
let file_type = path.file_type()?;
let (code, sym) = if file_type.is_dir() {
("di", Some('/'))
} else if file_type.is_symlink() {
if path.p_buf.exists() {
("ln", Some('@'))
} else {
("or", Some('@'))
}
} else if file_type.is_socket() {
("so", Some('='))
} else if file_type.is_fifo() {
("pi", Some('|'))
} else if file_type.is_block_device() {
("bd", None)
} else if file_type.is_char_device() {
("cd", None)
} else if file_type.is_file() {
let mode = metadata.mode() as mode_t;
let sym = if has!(mode, S_IXUSR | S_IXGRP | S_IXOTH) {
Some('*') Some('*')
} else { } else {
None None
};
if has!(mode, S_ISUID) {
("su", sym)
} else if has!(mode, S_ISGID) {
("sg", sym)
} else if has!(mode, S_ISVTX) && has!(mode, S_IWOTH) {
("tw", sym)
} else if has!(mode, S_ISVTX) {
("st", sym)
} else if has!(mode, S_IWOTH) {
("ow", sym)
} else if has!(mode, S_IXUSR | S_IXGRP | S_IXOTH) {
("ex", sym)
} else if metadata.nlink() > 1 {
("mh", sym)
} else if let Some(e) = path.p_buf.extension() {
ext = format!("*.{}", e.to_string_lossy());
(ext.as_str(), None)
} else {
("fi", None)
} }
} else {
("", None)
};
if config.color {
name = color_name(name, code);
} }
#[cfg(not(unix))]
None
}
}
fn display_file_name(path: &PathData, strip: Option<&Path>, config: &Config) -> Option<Cell> {
let mut name = escape_name(get_file_name(&path.p_buf, strip), &config.quoting_style);
#[cfg(unix)]
{
if config.format != Format::Long && config.inode {
name = get_inode(path.md()?) + " " + &name;
}
}
if let Some(ls_colors) = &config.color {
name = color_name(&ls_colors, &path.p_buf, name, path.md()?);
}
if config.indicator_style != IndicatorStyle::None {
let sym = classify_file(path);
let char_opt = match config.indicator_style { let char_opt = match config.indicator_style {
IndicatorStyle::Classify => sym, IndicatorStyle::Classify => sym,
@ -1698,24 +1601,25 @@ fn display_file_name(path: &PathData, strip: Option<&Path>, config: &Config) ->
if let Some(c) = char_opt { if let Some(c) = char_opt {
name.push(c); name.push(c);
width += 1;
} }
} }
if config.format == Format::Long && path.file_type()?.is_symlink() { if config.format == Format::Long && path.file_type()?.is_symlink() {
if let Ok(target) = path.p_buf.read_link() { if let Ok(target) = path.p_buf.read_link() {
// We don't bother updating width here because it's not used for long listings // We don't bother updating width here because it's not used for long listings
let code = if target.exists() { "fi" } else { "mi" };
let target_name = color_name(target.to_string_lossy().to_string(), code);
name.push_str(" -> "); name.push_str(" -> ");
name.push_str(&target_name); name.push_str(&target.to_string_lossy());
} }
} }
Some(Cell { Some(name.into())
contents: name, }
width,
}) fn color_name(ls_colors: &LsColors, path: &Path, name: String, md: &Metadata) -> String {
match ls_colors.style_for_path_with_metadata(path, Some(&md)) {
Some(style) => style.to_ansi_term_style().paint(name).to_string(),
None => name,
}
} }
#[cfg(not(unix))] #[cfg(not(unix))]

View file

@ -1,6 +1,6 @@
use std::char::from_digit; use std::char::from_digit;
const SPECIAL_SHELL_CHARS: &str = "~`#$&*()\\|[]{};'\"<>?! "; const SPECIAL_SHELL_CHARS: &str = "~`#$&*()|[]{};\\'\"<>?! ";
pub(super) enum QuotingStyle { pub(super) enum QuotingStyle {
Shell { Shell {
@ -27,12 +27,10 @@ pub(super) enum Quotes {
// This implementation is heavily inspired by the std::char::EscapeDefault implementation // This implementation is heavily inspired by the std::char::EscapeDefault implementation
// in the Rust standard library. This custom implementation is needed because the // in the Rust standard library. This custom implementation is needed because the
// characters \a, \b, \e, \f & \v are not recognized by Rust. // characters \a, \b, \e, \f & \v are not recognized by Rust.
#[derive(Clone, Debug)]
struct EscapedChar { struct EscapedChar {
state: EscapeState, state: EscapeState,
} }
#[derive(Clone, Debug)]
enum EscapeState { enum EscapeState {
Done, Done,
Char(char), Char(char),
@ -41,14 +39,12 @@ enum EscapeState {
Octal(EscapeOctal), Octal(EscapeOctal),
} }
#[derive(Clone, Debug)]
struct EscapeOctal { struct EscapeOctal {
c: char, c: char,
state: EscapeOctalState, state: EscapeOctalState,
idx: usize, idx: usize,
} }
#[derive(Clone, Debug)]
enum EscapeOctalState { enum EscapeOctalState {
Done, Done,
Backslash, Backslash,
@ -135,7 +131,6 @@ impl EscapedChar {
'\x0B' => Backslash('v'), '\x0B' => Backslash('v'),
'\x0C' => Backslash('f'), '\x0C' => Backslash('f'),
'\r' => Backslash('r'), '\r' => Backslash('r'),
'\\' => Backslash('\\'),
'\x00'..='\x1F' | '\x7F' => Octal(EscapeOctal::from(c)), '\x00'..='\x1F' | '\x7F' => Octal(EscapeOctal::from(c)),
'\'' => match quotes { '\'' => match quotes {
Quotes::Single => Backslash('\''), Quotes::Single => Backslash('\''),
@ -511,6 +506,23 @@ mod tests {
], ],
); );
// A control character followed by a special shell character
check_names(
"one\n&two",
vec![
("one?&two", "literal"),
("one\n&two", "literal-show"),
("one\\n&two", "escape"),
("\"one\\n&two\"", "c"),
("'one?&two'", "shell"),
("'one\n&two'", "shell-show"),
("'one?&two'", "shell-always"),
("'one\n&two'", "shell-always-show"),
("'one'$'\\n''&two'", "shell-escape"),
("'one'$'\\n''&two'", "shell-escape-always"),
],
);
// The first 16 control characters. NUL is also included, even though it is of // The first 16 control characters. NUL is also included, even though it is of
// no importance for file names. // no importance for file names.
check_names( check_names(
@ -627,4 +639,22 @@ mod tests {
], ],
); );
} }
#[test]
fn test_backslash() {
// Escaped in C-style, but not in Shell-style escaping
check_names(
"one\\two",
vec![
("one\\two", "literal"),
("one\\two", "literal-show"),
("one\\\\two", "escape"),
("\"one\\\\two\"", "c"),
("'one\\two'", "shell"),
("\'one\\two\'", "shell-always"),
("'one\\two'", "shell-escape"),
("'one\\two'", "shell-escape-always"),
],
);
}
} }

View file

@ -18,18 +18,15 @@ pub fn err_msg(msg: &str) {
// by default stdout only flushes // by default stdout only flushes
// to console when a newline is passed. // to console when a newline is passed.
#[allow(unused_must_use)]
pub fn flush_char(c: char) { pub fn flush_char(c: char) {
print!("{}", c); print!("{}", c);
stdout().flush(); let _ = stdout().flush();
} }
#[allow(unused_must_use)]
pub fn flush_str(s: &str) { pub fn flush_str(s: &str) {
print!("{}", s); print!("{}", s);
stdout().flush(); let _ = stdout().flush();
} }
#[allow(unused_must_use)]
pub fn flush_bytes(bslice: &[u8]) { pub fn flush_bytes(bslice: &[u8]) {
stdout().write(bslice); let _ = stdout().write(bslice);
stdout().flush(); let _ = stdout().flush();
} }

View file

@ -1,5 +1,4 @@
#![allow(dead_code)] #![allow(dead_code)]
// spell-checker:ignore (change!) each's // spell-checker:ignore (change!) each's
// spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr // spell-checker:ignore (ToDO) LONGHELP FORMATSTRING templating parameterizing formatstr
@ -9,7 +8,6 @@ mod tokenize;
static NAME: &str = "printf"; static NAME: &str = "printf";
static VERSION: &str = env!("CARGO_PKG_VERSION"); static VERSION: &str = env!("CARGO_PKG_VERSION");
static SHORT_USAGE: &str = "printf: usage: printf [-v var] format [arguments]";
static LONGHELP_LEAD: &str = "printf static LONGHELP_LEAD: &str = "printf
USAGE: printf FORMATSTRING [ARGUMENT]... USAGE: printf FORMATSTRING [ARGUMENT]...

View file

@ -28,8 +28,7 @@ pub fn arrnum_int_mult(arr_num: &[u8], basenum: u8, base_ten_int_fact: u8) -> Ve
} }
} }
} }
#[allow(clippy::map_clone)] let ret: Vec<u8> = ret_rev.into_iter().rev().collect();
let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
ret ret
} }
@ -102,70 +101,6 @@ pub fn arrnum_int_div_step(
remainder: rem_out, remainder: rem_out,
} }
} }
// pub struct ArrFloat {
// pub leading_zeros: u8,
// pub values: Vec<u8>,
// pub basenum: u8
// }
//
// pub struct ArrFloatDivOut {
// pub quotient: u8,
// pub remainder: ArrFloat
// }
//
// pub fn arrfloat_int_div(
// arrfloat_in : &ArrFloat,
// base_ten_int_divisor : u8,
// precision : u16
// ) -> DivOut {
//
// let mut remainder = ArrFloat {
// basenum: arrfloat_in.basenum,
// leading_zeros: arrfloat_in.leading_zeroes,
// values: Vec<u8>::new()
// }
// let mut quotient = 0;
//
// let mut bufferval : u16 = 0;
// let base : u16 = arrfloat_in.basenum as u16;
// let divisor : u16 = base_ten_int_divisor as u16;
//
// let mut it_f = arrfloat_in.values.iter();
// let mut position = 0 + arrfloat_in.leading_zeroes as u16;
// let mut at_end = false;
// while position< precision {
// let next_digit = match it_f.next() {
// Some(c) => {}
// None => { 0 }
// }
// match u_cur {
// Some(u) => {
// bufferval += u.clone() as u16;
// if bufferval > divisor {
// while bufferval >= divisor {
// quotient+=1;
// bufferval -= divisor;
// }
// if bufferval == 0 {
// rem_out.position +=1;
// } else {
// rem_out.replace = Some(bufferval as u8);
// }
// break;
// } else {
// bufferval *= base;
// }
// },
// None => {
// break;
// }
// }
// u_cur = it_f.next().clone();
// rem_out.position+=1;
// }
// ArrFloatDivOut { quotient: quotient, remainder: remainder }
// }
//
pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<u8> { pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<u8> {
let mut carry: u16 = u16::from(base_ten_int_term); let mut carry: u16 = u16::from(base_ten_int_term);
let mut rem: u16; let mut rem: u16;
@ -193,8 +128,7 @@ pub fn arrnum_int_add(arrnum: &[u8], basenum: u8, base_ten_int_term: u8) -> Vec<
} }
} }
} }
#[allow(clippy::map_clone)] let ret: Vec<u8> = ret_rev.into_iter().rev().collect();
let ret: Vec<u8> = ret_rev.iter().rev().map(|x| *x).collect();
ret ret
} }
@ -219,8 +153,7 @@ pub fn unsigned_to_arrnum(src: u16) -> Vec<u8> {
} }
// temporary needs-improvement-function // temporary needs-improvement-function
#[allow(unused_variables)] pub fn base_conv_float(src: &[u8], radix_src: u8, _radix_dest: u8) -> f64 {
pub fn base_conv_float(src: &[u8], radix_src: u8, radix_dest: u8) -> f64 {
// it would require a lot of addl code // it would require a lot of addl code
// to implement this for arbitrary string input. // to implement this for arbitrary string input.
// until then, the below operates as an outline // until then, the below operates as an outline
@ -267,7 +200,6 @@ pub fn arrnum_to_str(src: &[u8], radix_def_dest: &dyn RadixDef) -> String {
str_out str_out
} }
#[allow(unused_variables)]
pub fn base_conv_str( pub fn base_conv_str(
src: &str, src: &str,
radix_def_src: &dyn RadixDef, radix_def_src: &dyn RadixDef,

View file

@ -43,45 +43,15 @@ impl Formatter for CninetyNineHexFloatf {
// c99 hex has unique requirements of all floating point subs in pretty much every part of building a primitive, from prefix and suffix to need for base conversion (in all other cases if you don't have decimal you must have decimal, here it's the other way around) // c99 hex has unique requirements of all floating point subs in pretty much every part of building a primitive, from prefix and suffix to need for base conversion (in all other cases if you don't have decimal you must have decimal, here it's the other way around)
// on the todo list is to have a trait for get_primitive that is implemented by each float formatter and can override a default. when that happens we can take the parts of get_primitive_dec specific to dec and spin them out to their own functions that can be overridden. // on the todo list is to have a trait for get_primitive that is implemented by each float formatter and can override a default. when that happens we can take the parts of get_primitive_dec specific to dec and spin them out to their own functions that can be overridden.
#[allow(unused_variables)]
#[allow(unused_assignments)]
fn get_primitive_hex( fn get_primitive_hex(
inprefix: &InPrefix, inprefix: &InPrefix,
str_in: &str, _str_in: &str,
analysis: &FloatAnalysis, _analysis: &FloatAnalysis,
last_dec_place: usize, _last_dec_place: usize,
capitalized: bool, capitalized: bool,
) -> FormatPrimitive { ) -> FormatPrimitive {
let prefix = Some(String::from(if inprefix.sign == -1 { "-0x" } else { "0x" })); let prefix = Some(String::from(if inprefix.sign == -1 { "-0x" } else { "0x" }));
// assign the digits before and after the decimal points
// to separate slices. If no digits after decimal point,
// assign 0
let (mut first_segment_raw, second_segment_raw) = match analysis.decimal_pos {
Some(pos) => (&str_in[..pos], &str_in[pos + 1..]),
None => (str_in, "0"),
};
if first_segment_raw.is_empty() {
first_segment_raw = "0";
}
// convert to string, hexifying if input is in dec.
// let (first_segment, second_segment) =
// match inprefix.radix_in {
// Base::Ten => {
// (to_hex(first_segment_raw, true),
// to_hex(second_segment_raw, false))
// }
// _ => {
// (String::from(first_segment_raw),
// String::from(second_segment_raw))
// }
// };
//
//
// f.pre_decimal = Some(first_segment);
// f.post_decimal = Some(second_segment);
//
// TODO actual conversion, make sure to get back mantissa. // TODO actual conversion, make sure to get back mantissa.
// for hex to hex, it's really just a matter of moving the // for hex to hex, it's really just a matter of moving the
// decimal point and calculating the mantissa by its initial // decimal point and calculating the mantissa by its initial

View file

@ -22,12 +22,11 @@ fn get_len_fprim(fprim: &FormatPrimitive) -> usize {
len len
} }
pub struct Decf { pub struct Decf;
as_num: f64,
}
impl Decf { impl Decf {
pub fn new() -> Decf { pub fn new() -> Decf {
Decf { as_num: 0.0 } Decf
} }
} }
impl Formatter for Decf { impl Formatter for Decf {

View file

@ -5,12 +5,10 @@ use super::super::format_field::FormatField;
use super::super::formatter::{FormatPrimitive, Formatter, InPrefix}; use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis}; use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis};
pub struct Floatf { pub struct Floatf;
as_num: f64,
}
impl Floatf { impl Floatf {
pub fn new() -> Floatf { pub fn new() -> Floatf {
Floatf { as_num: 0.0 } Floatf
} }
} }
impl Formatter for Floatf { impl Formatter for Floatf {

View file

@ -11,7 +11,7 @@ use std::i64;
use std::u64; use std::u64;
pub struct Intf { pub struct Intf {
a: u32, _a: u32,
} }
// see the Intf::analyze() function below // see the Intf::analyze() function below
@ -24,7 +24,7 @@ struct IntAnalysis {
impl Intf { impl Intf {
pub fn new() -> Intf { pub fn new() -> Intf {
Intf { a: 0 } Intf { _a: 0 }
} }
// take a ref to argument string, and basic information // take a ref to argument string, and basic information
// about prefix (offset, radix, sign), and analyze string // about prefix (offset, radix, sign), and analyze string

View file

@ -5,12 +5,11 @@ use super::super::format_field::FormatField;
use super::super::formatter::{FormatPrimitive, Formatter, InPrefix}; use super::super::formatter::{FormatPrimitive, Formatter, InPrefix};
use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis}; use super::float_common::{get_primitive_dec, primitive_to_str_common, FloatAnalysis};
pub struct Scif { pub struct Scif;
as_num: f64,
}
impl Scif { impl Scif {
pub fn new() -> Scif { pub fn new() -> Scif {
Scif { as_num: 0.0 } Scif
} }
} }
impl Formatter for Scif { impl Formatter for Scif {

View file

@ -242,18 +242,16 @@ impl UnescapedText {
} }
} }
} }
#[allow(unused_variables)]
impl token::Tokenizer for UnescapedText { impl token::Tokenizer for UnescapedText {
fn from_it( fn from_it(
it: &mut PutBackN<Chars>, it: &mut PutBackN<Chars>,
args: &mut Peekable<Iter<String>>, _: &mut Peekable<Iter<String>>,
) -> Option<Box<dyn token::Token>> { ) -> Option<Box<dyn token::Token>> {
UnescapedText::from_it_core(it, false) UnescapedText::from_it_core(it, false)
} }
} }
#[allow(unused_variables)]
impl token::Token for UnescapedText { impl token::Token for UnescapedText {
fn print(&self, pf_args_it: &mut Peekable<Iter<String>>) { fn print(&self, _: &mut Peekable<Iter<String>>) {
cli::flush_bytes(&self.0[..]); cli::flush_bytes(&self.0[..]);
} }
} }

View file

@ -351,20 +351,18 @@ fn tokenize_default(line: &str) -> Vec<Field> {
/// Split between separators. These separators are not included in fields. /// Split between separators. These separators are not included in fields.
fn tokenize_with_separator(line: &str, separator: char) -> Vec<Field> { fn tokenize_with_separator(line: &str, separator: char) -> Vec<Field> {
let mut tokens = vec![0..0]; let mut tokens = vec![];
let mut previous_was_separator = false; let separator_indices =
for (idx, char) in line.char_indices() { line.char_indices()
if previous_was_separator { .filter_map(|(i, c)| if c == separator { Some(i) } else { None });
tokens.push(idx..0); let mut start = 0;
} for sep_idx in separator_indices {
if char == separator { tokens.push(start..sep_idx);
tokens.last_mut().unwrap().end = idx; start = sep_idx + 1;
previous_was_separator = true; }
} else { if start < line.len() {
previous_was_separator = false; tokens.push(start..line.len());
}
} }
tokens.last_mut().unwrap().end = line.len();
tokens tokens
} }
@ -1383,4 +1381,14 @@ mod tests {
vec![0..0, 1..1, 2..2, 3..9, 10..18,] vec![0..0, 1..1, 2..2, 3..9, 10..18,]
); );
} }
#[test]
fn test_tokenize_fields_trailing_custom_separator() {
let line = "a";
assert_eq!(tokenize(line, Some('a')), vec![0..0]);
let line = "aa";
assert_eq!(tokenize(line, Some('a')), vec![0..0, 1..1]);
let line = "..a..a";
assert_eq!(tokenize(line, Some('a')), vec![0..2, 3..5]);
}
} }

View file

@ -91,10 +91,15 @@ fn tac(filenames: Vec<String>, before: bool, _: bool, separator: &str) -> i32 {
} else { } else {
let path = Path::new(filename); let path = Path::new(filename);
if path.is_dir() || path.metadata().is_err() { if path.is_dir() || path.metadata().is_err() {
show_error!( if path.is_dir() {
"failed to open '{}' for reading: No such file or directory", show_error!("dir: read error: Invalid argument");
filename } else {
); show_error!(
"failed to open '{}' for reading: No such file or directory",
filename
);
}
exit_code = 1;
continue; continue;
} }
match File::open(path) { match File::open(path) {

View file

@ -117,6 +117,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
.arg( .arg(
Arg::with_name(options::SLEEP_INT) Arg::with_name(options::SLEEP_INT)
.short("s") .short("s")
.takes_value(true)
.long(options::SLEEP_INT) .long(options::SLEEP_INT)
.help("Number or seconds to sleep between polling the file when running with -f"), .help("Number or seconds to sleep between polling the file when running with -f"),
) )

View file

@ -192,7 +192,8 @@ fn truncate(
} }
fn parse_size(size: &str) -> (u64, TruncateMode) { fn parse_size(size: &str) -> (u64, TruncateMode) {
let mode = match size.chars().next().unwrap() { let clean_size = size.replace(" ", "");
let mode = match clean_size.chars().next().unwrap() {
'+' => TruncateMode::Extend, '+' => TruncateMode::Extend,
'-' => TruncateMode::Reduce, '-' => TruncateMode::Reduce,
'<' => TruncateMode::AtMost, '<' => TruncateMode::AtMost,
@ -203,9 +204,9 @@ fn parse_size(size: &str) -> (u64, TruncateMode) {
}; };
let bytes = { let bytes = {
let mut slice = if mode == TruncateMode::Reference { let mut slice = if mode == TruncateMode::Reference {
size &clean_size
} else { } else {
&size[1..] &clean_size[1..]
}; };
if slice.chars().last().unwrap().is_alphabetic() { if slice.chars().last().unwrap().is_alphabetic() {
slice = &slice[..slice.len() - 1]; slice = &slice[..slice.len() - 1];
@ -220,11 +221,11 @@ fn parse_size(size: &str) -> (u64, TruncateMode) {
Ok(num) => num, Ok(num) => num,
Err(e) => crash!(1, "'{}' is not a valid number: {}", size, e), Err(e) => crash!(1, "'{}' is not a valid number: {}", size, e),
}; };
if size.chars().last().unwrap().is_alphabetic() { if clean_size.chars().last().unwrap().is_alphabetic() {
number *= match size.chars().last().unwrap().to_ascii_uppercase() { number *= match clean_size.chars().last().unwrap().to_ascii_uppercase() {
'B' => match size 'B' => match clean_size
.chars() .chars()
.nth(size.len() - 2) .nth(clean_size.len() - 2)
.unwrap() .unwrap()
.to_ascii_uppercase() .to_ascii_uppercase()
{ {

View file

@ -47,7 +47,7 @@ fn run_single_test(test: &TestCase, at: AtPath, mut ucmd: UCommand) {
ucmd.arg(arg); ucmd.arg(arg);
} }
let r = ucmd.run(); let r = ucmd.run();
if !r.success { if !r.succeeded() {
println!("{}", r.stderr_str()); println!("{}", r.stderr_str());
panic!("{:?}: failed", ucmd.raw); panic!("{:?}: failed", ucmd.raw);
} }

View file

@ -4,14 +4,11 @@ use crate::common::util::*;
fn test_missing_operand() { fn test_missing_operand() {
let result = new_ucmd!().run(); let result = new_ucmd!().run();
assert_eq!( assert!(result
true, .stderr_str()
result .starts_with("error: The following required arguments were not provided"));
.stderr
.starts_with("error: The following required arguments were not provided")
);
assert_eq!(true, result.stderr.contains("<newroot>")); assert!(result.stderr_str().contains("<newroot>"));
} }
#[test] #[test]
@ -20,14 +17,11 @@ fn test_enter_chroot_fails() {
at.mkdir("jail"); at.mkdir("jail");
let result = ucmd.arg("jail").run(); let result = ucmd.arg("jail").fails();
assert_eq!( assert!(result
true, .stderr_str()
result.stderr.starts_with( .starts_with("chroot: error: cannot chroot to jail: Operation not permitted (os error 1)"));
"chroot: error: cannot chroot to jail: Operation not permitted (os error 1)"
)
)
} }
#[test] #[test]
@ -47,19 +41,18 @@ fn test_invalid_user_spec() {
at.mkdir("a"); at.mkdir("a");
let result = ucmd.arg("a").arg("--userspec=ARABA:").run(); let result = ucmd.arg("a").arg("--userspec=ARABA:").fails();
assert_eq!( assert!(result
true, .stderr_str()
result.stderr.starts_with("chroot: error: invalid userspec") .starts_with("chroot: error: invalid userspec"));
);
} }
#[test] #[test]
fn test_preference_of_userspec() { fn test_preference_of_userspec() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let result = scene.cmd("whoami").run(); let result = scene.cmd("whoami").run();
if is_ci() && result.stderr.contains("No such user/group") { if is_ci() && result.stderr_str().contains("No such user/group") {
// In the CI, some server are failing to return whoami. // In the CI, some server are failing to return whoami.
// As seems to be a configuration issue, ignoring it // As seems to be a configuration issue, ignoring it
return; return;
@ -73,7 +66,7 @@ fn test_preference_of_userspec() {
println!("result.stdout = {}", result.stdout_str()); println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str()); println!("result.stderr = {}", result.stderr_str());
if is_ci() && result.stderr.contains("cannot find name for user ID") { if is_ci() && result.stderr_str().contains("cannot find name for user ID") {
// In the CI, some server are failing to return id. // In the CI, some server are failing to return id.
// As seems to be a configuration issue, ignoring it // As seems to be a configuration issue, ignoring it
return; return;

View file

@ -42,13 +42,9 @@ static TEST_MOUNT_OTHER_FILESYSTEM_FILE: &str = "mount/DO_NOT_copy_me.txt";
fn test_cp_cp() { fn test_cp_cp() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
// Invoke our binary to make the copy. // Invoke our binary to make the copy.
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
// Check that the exit code represents a successful copy.
assert!(result.success);
// Check the content of the destination file that was copied. // Check the content of the destination file that was copied.
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
@ -57,12 +53,9 @@ fn test_cp_cp() {
#[test] #[test]
fn test_cp_existing_target() { fn test_cp_existing_target() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_EXISTING_FILE) .arg(TEST_EXISTING_FILE)
.run(); .succeeds();
assert!(result.success);
// Check the content of the destination file // Check the content of the destination file
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
@ -74,52 +67,41 @@ fn test_cp_existing_target() {
#[test] #[test]
fn test_cp_duplicate_files() { fn test_cp_duplicate_files() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds()
.stderr_contains("specified more than once");
assert!(result.success);
assert!(result.stderr.contains("specified more than once"));
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
} }
#[test] #[test]
fn test_cp_multiple_files_target_is_file() { fn test_cp_multiple_files_target_is_file() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!()
let result = ucmd
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_EXISTING_FILE) .arg(TEST_EXISTING_FILE)
.run(); .fails()
.stderr_contains("not a directory");
assert!(!result.success);
assert!(result.stderr.contains("not a directory"));
} }
#[test] #[test]
fn test_cp_directory_not_recursive() { fn test_cp_directory_not_recursive() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!()
let result = ucmd
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .fails()
.stderr_contains("omitting directory");
assert!(!result.success);
assert!(result.stderr.contains("omitting directory"));
} }
#[test] #[test]
fn test_cp_multiple_files() { fn test_cp_multiple_files() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
assert_eq!(at.read(TEST_HOW_ARE_YOU_DEST), "How are you?\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_DEST), "How are you?\n");
} }
@ -129,14 +111,11 @@ fn test_cp_multiple_files() {
#[cfg(not(macos))] #[cfg(not(macos))]
fn test_cp_recurse() { fn test_cp_recurse() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("-r")
let result = ucmd
.arg("-r")
.arg(TEST_COPY_FROM_FOLDER) .arg(TEST_COPY_FROM_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW) .arg(TEST_COPY_TO_FOLDER_NEW)
.run(); .succeeds();
assert!(result.success);
// Check the content of the destination file that was copied. // Check the content of the destination file that was copied.
assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_COPY_TO_FOLDER_NEW_FILE), "Hello, World!\n");
} }
@ -144,14 +123,10 @@ fn test_cp_recurse() {
#[test] #[test]
fn test_cp_with_dirs_t() { fn test_cp_with_dirs_t() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("-t")
//using -t option
let result_to_dir_t = ucmd
.arg("-t")
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.run(); .succeeds();
assert!(result_to_dir_t.success);
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
} }
@ -162,63 +137,52 @@ fn test_cp_with_dirs() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures; let at = &scene.fixtures;
//using -t option scene
let result_to_dir = scene
.ucmd() .ucmd()
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
assert!(result_to_dir.success);
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
let result_from_dir = scene scene
.ucmd() .ucmd()
.arg(TEST_COPY_FROM_FOLDER_FILE) .arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
assert!(result_from_dir.success);
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
} }
#[test] #[test]
fn test_cp_arg_target_directory() { fn test_cp_arg_target_directory() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("-t") .arg("-t")
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n"); assert_eq!(at.read(TEST_COPY_TO_FOLDER_FILE), "Hello, World!\n");
} }
#[test] #[test]
fn test_cp_arg_no_target_directory() { fn test_cp_arg_no_target_directory() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!()
let result = ucmd
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg("-v") .arg("-v")
.arg("-T") .arg("-T")
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .fails()
.stderr_contains("cannot overwrite directory");
assert!(!result.success);
assert!(result.stderr.contains("cannot overwrite directory"));
} }
#[test] #[test]
fn test_cp_arg_interactive() { fn test_cp_arg_interactive() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!()
let result = ucmd
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.arg("-i") .arg("-i")
.pipe_in("N\n") .pipe_in("N\n")
.run(); .succeeds()
.stderr_contains("Not overwriting");
assert!(result.success);
assert!(result.stderr.contains("Not overwriting"));
} }
#[test] #[test]
@ -227,39 +191,33 @@ fn test_cp_arg_link() {
use std::os::linux::fs::MetadataExt; use std::os::linux::fs::MetadataExt;
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--link") .arg("--link")
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.metadata(TEST_HELLO_WORLD_SOURCE).st_nlink(), 2); assert_eq!(at.metadata(TEST_HELLO_WORLD_SOURCE).st_nlink(), 2);
} }
#[test] #[test]
fn test_cp_arg_symlink() { fn test_cp_arg_symlink() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--symbolic-link") .arg("--symbolic-link")
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
assert!(result.success);
assert!(at.is_symlink(TEST_HELLO_WORLD_DEST)); assert!(at.is_symlink(TEST_HELLO_WORLD_DEST));
} }
#[test] #[test]
fn test_cp_arg_no_clobber() { fn test_cp_arg_no_clobber() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--no-clobber") .arg("--no-clobber")
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.run(); .succeeds();
assert!(result.success);
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");
} }
@ -267,34 +225,31 @@ fn test_cp_arg_no_clobber() {
fn test_cp_arg_no_clobber_twice() { fn test_cp_arg_no_clobber_twice() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures; let at = &scene.fixtures;
at.touch("source.txt"); at.touch("source.txt");
let result = scene scene
.ucmd() .ucmd()
.arg("--no-clobber") .arg("--no-clobber")
.arg("source.txt") .arg("source.txt")
.arg("dest.txt") .arg("dest.txt")
.run(); .succeeds()
.no_stderr();
println!("stderr = {:?}", result.stderr_str());
println!("stdout = {:?}", result.stdout_str());
assert!(result.success);
assert!(result.stderr.is_empty());
assert_eq!(at.read("source.txt"), ""); assert_eq!(at.read("source.txt"), "");
at.append("source.txt", "some-content"); at.append("source.txt", "some-content");
let result = scene scene
.ucmd() .ucmd()
.arg("--no-clobber") .arg("--no-clobber")
.arg("source.txt") .arg("source.txt")
.arg("dest.txt") .arg("dest.txt")
.run(); .succeeds()
.stdout_does_not_contain("Not overwriting");
assert!(result.success);
assert_eq!(at.read("source.txt"), "some-content"); assert_eq!(at.read("source.txt"), "some-content");
// Should be empty as the "no-clobber" should keep // Should be empty as the "no-clobber" should keep
// the previous version // the previous version
assert_eq!(at.read("dest.txt"), ""); assert_eq!(at.read("dest.txt"), "");
assert!(!result.stderr.contains("Not overwriting"));
} }
#[test] #[test]
@ -311,16 +266,11 @@ fn test_cp_arg_force() {
permissions.set_readonly(true); permissions.set_readonly(true);
set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap(); set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--force") .arg("--force")
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
println!("{:?}", result.stderr_str());
println!("{:?}", result.stdout_str());
assert!(result.success);
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
} }
@ -342,13 +292,11 @@ fn test_cp_arg_remove_destination() {
permissions.set_readonly(true); permissions.set_readonly(true);
set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap(); set_permissions(at.plus(TEST_HELLO_WORLD_DEST), permissions).unwrap();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--remove-destination") .arg("--remove-destination")
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
} }
@ -356,13 +304,11 @@ fn test_cp_arg_remove_destination() {
fn test_cp_arg_backup() { fn test_cp_arg_backup() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--backup") .arg("--backup")
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!( assert_eq!(
at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)), at.read(&*format!("{}~", TEST_HOW_ARE_YOU_SOURCE)),
@ -374,14 +320,12 @@ fn test_cp_arg_backup() {
fn test_cp_arg_suffix() { fn test_cp_arg_suffix() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--suffix") .arg("--suffix")
.arg(".bak") .arg(".bak")
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
assert_eq!( assert_eq!(
at.read(&*format!("{}.bak", TEST_HOW_ARE_YOU_SOURCE)), at.read(&*format!("{}.bak", TEST_HOW_ARE_YOU_SOURCE)),
@ -391,9 +335,8 @@ fn test_cp_arg_suffix() {
#[test] #[test]
fn test_cp_deref_conflicting_options() { fn test_cp_deref_conflicting_options() {
let (_at, mut ucmd) = at_and_ucmd!(); new_ucmd!()
.arg("-LP")
ucmd.arg("-LP")
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.fails(); .fails();
@ -401,8 +344,7 @@ fn test_cp_deref_conflicting_options() {
#[test] #[test]
fn test_cp_deref() { fn test_cp_deref() {
let scene = TestScenario::new(util_name!()); let (at, mut ucmd) = at_and_ucmd!();
let at = &scene.fixtures;
#[cfg(not(windows))] #[cfg(not(windows))]
let _r = fs::symlink( let _r = fs::symlink(
@ -415,16 +357,12 @@ fn test_cp_deref() {
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK), at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
); );
//using -L option //using -L option
let result = scene ucmd.arg("-L")
.ucmd()
.arg("-L")
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK) .arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
// Check that the exit code represents a successful copy.
assert!(result.success);
let path_to_new_symlink = at let path_to_new_symlink = at
.subdir .subdir
.join(TEST_COPY_TO_FOLDER) .join(TEST_COPY_TO_FOLDER)
@ -444,8 +382,7 @@ fn test_cp_deref() {
} }
#[test] #[test]
fn test_cp_no_deref() { fn test_cp_no_deref() {
let scene = TestScenario::new(util_name!()); let (at, mut ucmd) = at_and_ucmd!();
let at = &scene.fixtures;
#[cfg(not(windows))] #[cfg(not(windows))]
let _r = fs::symlink( let _r = fs::symlink(
@ -458,16 +395,12 @@ fn test_cp_no_deref() {
at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK), at.subdir.join(TEST_HELLO_WORLD_SOURCE_SYMLINK),
); );
//using -P option //using -P option
let result = scene ucmd.arg("-P")
.ucmd()
.arg("-P")
.arg(TEST_HELLO_WORLD_SOURCE) .arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE_SYMLINK) .arg(TEST_HELLO_WORLD_SOURCE_SYMLINK)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
// Check that the exit code represents a successful copy.
assert!(result.success);
let path_to_new_symlink = at let path_to_new_symlink = at
.subdir .subdir
.join(TEST_COPY_TO_FOLDER) .join(TEST_COPY_TO_FOLDER)
@ -490,14 +423,10 @@ fn test_cp_strip_trailing_slashes() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
//using --strip-trailing-slashes option //using --strip-trailing-slashes option
let result = ucmd ucmd.arg("--strip-trailing-slashes")
.arg("--strip-trailing-slashes")
.arg(format!("{}/", TEST_HELLO_WORLD_SOURCE)) .arg(format!("{}/", TEST_HELLO_WORLD_SOURCE))
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .succeeds();
// Check that the exit code represents a successful copy.
assert!(result.success);
// Check the content of the destination file that was copied. // Check the content of the destination file that was copied.
assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n"); assert_eq!(at.read(TEST_HELLO_WORLD_DEST), "Hello, World!\n");
@ -507,14 +436,11 @@ fn test_cp_strip_trailing_slashes() {
fn test_cp_parents() { fn test_cp_parents() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg("--parents")
.arg("--parents")
.arg(TEST_COPY_FROM_FOLDER_FILE) .arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
assert!(result.success);
// Check the content of the destination file that was copied.
assert_eq!( assert_eq!(
at.read(&format!( at.read(&format!(
"{}/{}", "{}/{}",
@ -528,14 +454,12 @@ fn test_cp_parents() {
fn test_cp_parents_multiple_files() { fn test_cp_parents_multiple_files() {
let (at, mut ucmd) = at_and_ucmd!(); let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd ucmd.arg("--parents")
.arg("--parents")
.arg(TEST_COPY_FROM_FOLDER_FILE) .arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.run(); .succeeds();
assert!(result.success);
assert_eq!( assert_eq!(
at.read(&format!( at.read(&format!(
"{}/{}", "{}/{}",
@ -554,20 +478,12 @@ fn test_cp_parents_multiple_files() {
#[test] #[test]
fn test_cp_parents_dest_not_directory() { fn test_cp_parents_dest_not_directory() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!()
let result = ucmd
.arg("--parents") .arg("--parents")
.arg(TEST_COPY_FROM_FOLDER_FILE) .arg(TEST_COPY_FROM_FOLDER_FILE)
.arg(TEST_HELLO_WORLD_DEST) .arg(TEST_HELLO_WORLD_DEST)
.run(); .fails()
println!("{:?}", result); .stderr_contains("with --parents, the destination must be a directory");
// Check that we did not succeed in copying.
assert!(!result.success);
assert!(result
.stderr
.contains("with --parents, the destination must be a directory"));
} }
#[test] #[test]
@ -594,18 +510,14 @@ fn test_cp_deref_folder_to_folder() {
assert!(env::set_current_dir(&cwd).is_ok()); assert!(env::set_current_dir(&cwd).is_ok());
//using -P -R option //using -P -R option
let result = scene scene
.ucmd() .ucmd()
.arg("-L") .arg("-L")
.arg("-R") .arg("-R")
.arg("-v") .arg("-v")
.arg(TEST_COPY_FROM_FOLDER) .arg(TEST_COPY_FROM_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW) .arg(TEST_COPY_TO_FOLDER_NEW)
.run(); .succeeds();
println!("cp output {}", result.stdout_str());
// Check that the exit code represents a successful copy.
assert!(result.success);
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
@ -698,18 +610,14 @@ fn test_cp_no_deref_folder_to_folder() {
assert!(env::set_current_dir(&cwd).is_ok()); assert!(env::set_current_dir(&cwd).is_ok());
//using -P -R option //using -P -R option
let result = scene scene
.ucmd() .ucmd()
.arg("-P") .arg("-P")
.arg("-R") .arg("-R")
.arg("-v") .arg("-v")
.arg(TEST_COPY_FROM_FOLDER) .arg(TEST_COPY_FROM_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW) .arg(TEST_COPY_TO_FOLDER_NEW)
.run(); .succeeds();
println!("cp output {}", result.stdout_str());
// Check that the exit code represents a successful copy.
assert!(result.success);
#[cfg(not(windows))] #[cfg(not(windows))]
{ {
@ -791,13 +699,11 @@ fn test_cp_archive() {
previous, previous,
) )
.unwrap(); .unwrap();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--archive") .arg("--archive")
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap(); let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
@ -807,11 +713,10 @@ fn test_cp_archive() {
let creation2 = metadata2.modified().unwrap(); let creation2 = metadata2.modified().unwrap();
let scene2 = TestScenario::new("ls"); let scene2 = TestScenario::new("ls");
let result = scene2.cmd("ls").arg("-al").arg(at.subdir).run(); let result = scene2.cmd("ls").arg("-al").arg(at.subdir).succeeds();
println!("ls dest {}", result.stdout_str()); println!("ls dest {}", result.stdout_str());
assert_eq!(creation, creation2); assert_eq!(creation, creation2);
assert!(result.success);
} }
#[test] #[test]
@ -850,11 +755,10 @@ fn test_cp_archive_recursive() {
// Back to the initial cwd (breaks the other tests) // Back to the initial cwd (breaks the other tests)
assert!(env::set_current_dir(&cwd).is_ok()); assert!(env::set_current_dir(&cwd).is_ok());
let resultg = ucmd ucmd.arg("--archive")
.arg("--archive")
.arg(TEST_COPY_TO_FOLDER) .arg(TEST_COPY_TO_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW) .arg(TEST_COPY_TO_FOLDER_NEW)
.run(); .fails(); // fails for now
let scene2 = TestScenario::new("ls"); let scene2 = TestScenario::new("ls");
let result = scene2 let result = scene2
@ -865,7 +769,6 @@ fn test_cp_archive_recursive() {
println!("ls dest {}", result.stdout_str()); println!("ls dest {}", result.stdout_str());
let scene2 = TestScenario::new("ls");
let result = scene2 let result = scene2
.cmd("ls") .cmd("ls")
.arg("-al") .arg("-al")
@ -910,9 +813,6 @@ fn test_cp_archive_recursive() {
.join("2.link") .join("2.link")
.to_string_lossy() .to_string_lossy()
)); ));
// fails for now
assert!(resultg.success);
} }
#[test] #[test]
@ -928,13 +828,11 @@ fn test_cp_preserve_timestamps() {
previous, previous,
) )
.unwrap(); .unwrap();
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--preserve=timestamps") .arg("--preserve=timestamps")
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap(); let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
@ -948,7 +846,6 @@ fn test_cp_preserve_timestamps() {
println!("ls dest {}", result.stdout_str()); println!("ls dest {}", result.stdout_str());
assert_eq!(creation, creation2); assert_eq!(creation, creation2);
assert!(result.success);
} }
#[test] #[test]
@ -966,13 +863,11 @@ fn test_cp_dont_preserve_timestamps() {
.unwrap(); .unwrap();
sleep(Duration::from_secs(3)); sleep(Duration::from_secs(3));
let result = ucmd ucmd.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_HELLO_WORLD_SOURCE)
.arg("--no-preserve=timestamps") .arg("--no-preserve=timestamps")
.arg(TEST_HOW_ARE_YOU_SOURCE) .arg(TEST_HOW_ARE_YOU_SOURCE)
.run(); .succeeds();
assert!(result.success);
assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n"); assert_eq!(at.read(TEST_HOW_ARE_YOU_SOURCE), "Hello, World!\n");
let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap(); let metadata = std_fs::metadata(at.subdir.join(TEST_HELLO_WORLD_SOURCE)).unwrap();
@ -992,7 +887,6 @@ fn test_cp_dont_preserve_timestamps() {
// Some margins with time check // Some margins with time check
assert!(res.as_secs() > 3595); assert!(res.as_secs() > 3595);
assert!(res.as_secs() < 3605); assert!(res.as_secs() < 3605);
assert!(result.success);
} }
#[test] #[test]
@ -1017,7 +911,7 @@ fn test_cp_one_file_system() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
// Test must be run as root (or with `sudo -E`) // Test must be run as root (or with `sudo -E`)
if scene.cmd("whoami").run().stdout != "root\n" { if scene.cmd("whoami").run().stdout_str() != "root\n" {
return; return;
} }
@ -1042,17 +936,16 @@ fn test_cp_one_file_system() {
at_src.touch(TEST_MOUNT_OTHER_FILESYSTEM_FILE); at_src.touch(TEST_MOUNT_OTHER_FILESYSTEM_FILE);
// Begin testing -x flag // Begin testing -x flag
let result = scene scene
.ucmd() .ucmd()
.arg("-rx") .arg("-rx")
.arg(TEST_MOUNT_COPY_FROM_FOLDER) .arg(TEST_MOUNT_COPY_FROM_FOLDER)
.arg(TEST_COPY_TO_FOLDER_NEW) .arg(TEST_COPY_TO_FOLDER_NEW)
.run(); .succeeds();
// Ditch the mount before the asserts // Ditch the mount before the asserts
scene.cmd("umount").arg(mountpoint_path).succeeds(); scene.cmd("umount").arg(mountpoint_path).succeeds();
assert!(result.success);
assert!(!at_dst.file_exists(TEST_MOUNT_OTHER_FILESYSTEM_FILE)); assert!(!at_dst.file_exists(TEST_MOUNT_OTHER_FILESYSTEM_FILE));
// Check if the other files were copied from the source folder hirerarchy // Check if the other files were copied from the source folder hirerarchy
for entry in WalkDir::new(at_src.as_string()) { for entry in WalkDir::new(at_src.as_string()) {
@ -1072,3 +965,59 @@ fn test_cp_one_file_system() {
} }
} }
} }
#[test]
#[cfg(target_os = "linux")]
fn test_cp_reflink_always() {
let (at, mut ucmd) = at_and_ucmd!();
let result = ucmd
.arg("--reflink=always")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_EXISTING_FILE)
.run();
if result.success {
// Check the content of the destination file
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
} else {
// Older Linux versions do not support cloning.
}
}
#[test]
#[cfg(target_os = "linux")]
fn test_cp_reflink_auto() {
let (at, mut ucmd) = at_and_ucmd!();
ucmd.arg("--reflink=auto")
.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");
}
#[test]
#[cfg(target_os = "linux")]
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();
// Check the content of the destination file
assert_eq!(at.read(TEST_EXISTING_FILE), "Hello, World!\n");
}
#[test]
#[cfg(target_os = "linux")]
fn test_cp_reflink_bad() {
let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd
.arg("--reflink=bad")
.arg(TEST_HELLO_WORLD_SOURCE)
.arg(TEST_EXISTING_FILE)
.fails()
.stderr_contains("invalid argument");
}

View file

@ -7,174 +7,147 @@ use rust_users::*;
#[test] #[test]
fn test_date_email() { fn test_date_email() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("--rfc-email").succeeds();
let result = ucmd.arg("--rfc-email").run();
assert!(result.success);
} }
#[test] #[test]
fn test_date_email2() { fn test_date_email2() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("-R").succeeds();
let result = ucmd.arg("-R").run();
assert!(result.success);
} }
#[test] #[test]
fn test_date_rfc_3339() { fn test_date_rfc_3339() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("--rfc-3339=ns").succeeds(); let rfc_regexp = concat!(
r#"(\d+)-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])\s([01]\d|2[0-3]):"#,
r#"([0-5]\d):([0-5]\d|60)(\.\d+)?(([Zz])|([\+|\-]([01]\d|2[0-3])))"#
);
let re = Regex::new(rfc_regexp).unwrap();
// Check that the output matches the regexp // Check that the output matches the regexp
let rfc_regexp = r"(\d+)-(0[1-9]|1[012])-(0[1-9]|[12]\d|3[01])\s([01]\d|2[0-3]):([0-5]\d):([0-5]\d|60)(\.\d+)?(([Zz])|([\+|\-]([01]\d|2[0-3])))"; scene
let re = Regex::new(rfc_regexp).unwrap(); .ucmd()
assert!(re.is_match(&result.stdout_str().trim())); .arg("--rfc-3339=ns")
.succeeds()
.stdout_matches(&re);
result = scene.ucmd().arg("--rfc-3339=seconds").succeeds(); scene
.ucmd()
// Check that the output matches the regexp .arg("--rfc-3339=seconds")
let re = Regex::new(rfc_regexp).unwrap(); .succeeds()
assert!(re.is_match(&result.stdout_str().trim())); .stdout_matches(&re);
} }
#[test] #[test]
fn test_date_rfc_8601() { fn test_date_rfc_8601() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("--iso-8601=ns").succeeds();
let result = ucmd.arg("--iso-8601=ns").run();
assert!(result.success);
} }
#[test] #[test]
fn test_date_rfc_8601_second() { fn test_date_rfc_8601_second() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("--iso-8601=second").succeeds();
let result = ucmd.arg("--iso-8601=second").run();
assert!(result.success);
} }
#[test] #[test]
fn test_date_utc() { fn test_date_utc() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("--utc").succeeds();
let result = ucmd.arg("--utc").run();
assert!(result.success);
} }
#[test] #[test]
fn test_date_universal() { fn test_date_universal() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("--universal").succeeds();
let result = ucmd.arg("--universal").run();
assert!(result.success);
} }
#[test] #[test]
fn test_date_format_y() { fn test_date_format_y() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("+%Y").succeeds();
assert!(result.success);
let mut re = Regex::new(r"^\d{4}$").unwrap(); let mut re = Regex::new(r"^\d{4}$").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%Y").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%y").succeeds();
assert!(result.success);
re = Regex::new(r"^\d{2}$").unwrap(); re = Regex::new(r"^\d{2}$").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%y").succeeds().stdout_matches(&re);
} }
#[test] #[test]
fn test_date_format_m() { fn test_date_format_m() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("+%b").succeeds();
assert!(result.success);
let mut re = Regex::new(r"\S+").unwrap(); let mut re = Regex::new(r"\S+").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%b").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%m").succeeds();
assert!(result.success);
re = Regex::new(r"^\d{2}$").unwrap(); re = Regex::new(r"^\d{2}$").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%m").succeeds().stdout_matches(&re);
} }
#[test] #[test]
fn test_date_format_day() { fn test_date_format_day() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let mut result = scene.ucmd().arg("+%a").succeeds();
assert!(result.success);
let mut re = Regex::new(r"\S+").unwrap(); let mut re = Regex::new(r"\S+").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%a").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%A").succeeds();
assert!(result.success);
re = Regex::new(r"\S+").unwrap(); re = Regex::new(r"\S+").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%A").succeeds().stdout_matches(&re);
result = scene.ucmd().arg("+%u").succeeds();
assert!(result.success);
re = Regex::new(r"^\d{1}$").unwrap(); re = Regex::new(r"^\d{1}$").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); scene.ucmd().arg("+%u").succeeds().stdout_matches(&re);
} }
#[test] #[test]
fn test_date_format_full_day() { fn test_date_format_full_day() {
let scene = TestScenario::new(util_name!());
let result = scene.ucmd().arg("+'%a %Y-%m-%d'").succeeds();
assert!(result.success);
let re = Regex::new(r"\S+ \d{4}-\d{2}-\d{2}").unwrap(); let re = Regex::new(r"\S+ \d{4}-\d{2}-\d{2}").unwrap();
assert!(re.is_match(&result.stdout_str().trim())); new_ucmd!()
.arg("+'%a %Y-%m-%d'")
.succeeds()
.stdout_matches(&re);
} }
#[test] #[test]
#[cfg(all(unix, not(target_os = "macos")))] #[cfg(all(unix, not(target_os = "macos")))]
fn test_date_set_valid() { fn test_date_set_valid() {
if get_effective_uid() == 0 { if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!()
let result = ucmd
.arg("--set") .arg("--set")
.arg("2020-03-12 13:30:00+08:00") .arg("2020-03-12 13:30:00+08:00")
.succeeds(); .succeeds()
result.no_stdout().no_stderr(); .no_stdout()
.no_stderr();
} }
} }
#[test] #[test]
#[cfg(any(windows, all(unix, not(target_os = "macos"))))] #[cfg(any(windows, all(unix, not(target_os = "macos"))))]
fn test_date_set_invalid() { fn test_date_set_invalid() {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!().arg("--set").arg("123abcd").fails();
let result = ucmd.arg("--set").arg("123abcd").fails(); result.no_stdout();
let result = result.no_stdout(); assert!(result.stderr_str().starts_with("date: invalid date "));
assert!(result.stderr.starts_with("date: invalid date "));
} }
#[test] #[test]
#[cfg(all(unix, not(target_os = "macos")))] #[cfg(all(unix, not(target_os = "macos")))]
fn test_date_set_permissions_error() { fn test_date_set_permissions_error() {
if !(get_effective_uid() == 0 || is_wsl()) { if !(get_effective_uid() == 0 || is_wsl()) {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!()
let result = ucmd.arg("--set").arg("2020-03-11 21:45:00+08:00").fails(); .arg("--set")
let result = result.no_stdout(); .arg("2020-03-11 21:45:00+08:00")
assert!(result.stderr.starts_with("date: cannot set date: ")); .fails();
result.no_stdout();
assert!(result.stderr_str().starts_with("date: cannot set date: "));
} }
} }
#[test] #[test]
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
fn test_date_set_mac_unavailable() { fn test_date_set_mac_unavailable() {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!()
let result = ucmd.arg("--set").arg("2020-03-11 21:45:00+08:00").fails(); .arg("--set")
let result = result.no_stdout(); .arg("2020-03-11 21:45:00+08:00")
.fails();
result.no_stdout();
assert!(result assert!(result
.stderr .stderr_str()
.starts_with("date: setting the date is not supported by macOS")); .starts_with("date: setting the date is not supported by macOS"));
} }
@ -183,13 +156,12 @@ fn test_date_set_mac_unavailable() {
/// TODO: expected to fail currently; change to succeeds() when required. /// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_2() { fn test_date_set_valid_2() {
if get_effective_uid() == 0 { if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!()
let result = ucmd
.arg("--set") .arg("--set")
.arg("Sat 20 Mar 2021 14:53:01 AWST") .arg("Sat 20 Mar 2021 14:53:01 AWST")
.fails(); .fails();
let result = result.no_stdout(); result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date ")); assert!(result.stderr_str().starts_with("date: invalid date "));
} }
} }
@ -198,13 +170,12 @@ fn test_date_set_valid_2() {
/// TODO: expected to fail currently; change to succeeds() when required. /// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_3() { fn test_date_set_valid_3() {
if get_effective_uid() == 0 { if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!()
let result = ucmd
.arg("--set") .arg("--set")
.arg("Sat 20 Mar 2021 14:53:01") // Local timezone .arg("Sat 20 Mar 2021 14:53:01") // Local timezone
.fails(); .fails();
let result = result.no_stdout(); result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date ")); assert!(result.stderr_str().starts_with("date: invalid date "));
} }
} }
@ -213,12 +184,11 @@ fn test_date_set_valid_3() {
/// TODO: expected to fail currently; change to succeeds() when required. /// TODO: expected to fail currently; change to succeeds() when required.
fn test_date_set_valid_4() { fn test_date_set_valid_4() {
if get_effective_uid() == 0 { if get_effective_uid() == 0 {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!()
let result = ucmd
.arg("--set") .arg("--set")
.arg("2020-03-11 21:45:00") // Local timezone .arg("2020-03-11 21:45:00") // Local timezone
.fails(); .fails();
let result = result.no_stdout(); result.no_stdout();
assert!(result.stderr.starts_with("date: invalid date ")); assert!(result.stderr_str().starts_with("date: invalid date "));
} }
} }

View file

@ -2,30 +2,22 @@ use crate::common::util::*;
#[test] #[test]
fn test_df_compatible_no_size_arg() { fn test_df_compatible_no_size_arg() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("-a").succeeds();
let result = ucmd.arg("-a").run();
assert!(result.success);
} }
#[test] #[test]
fn test_df_compatible() { fn test_df_compatible() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("-ah").succeeds();
let result = ucmd.arg("-ah").run();
assert!(result.success);
} }
#[test] #[test]
fn test_df_compatible_type() { fn test_df_compatible_type() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("-aT").succeeds();
let result = ucmd.arg("-aT").run();
assert!(result.success);
} }
#[test] #[test]
fn test_df_compatible_si() { fn test_df_compatible_si() {
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg("-aH").succeeds();
let result = ucmd.arg("-aH").run();
assert!(result.success);
} }
// ToDO: more tests... // ToDO: more tests...

View file

@ -220,26 +220,36 @@ fn test_du_time() {
.arg("date_test") .arg("date_test")
.succeeds() .succeeds()
.stdout_only("0\t2015-05-15 00:00\tdate_test\n"); .stdout_only("0\t2015-05-15 00:00\tdate_test\n");
// cleanup by removing test file
scene.cmd("rm").arg("date_test").succeeds(); // TODO: is this necessary?
} }
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
#[cfg(feature = "chmod")] #[cfg(feature = "chmod")]
#[test] #[test]
fn test_du_no_permission() { fn test_du_no_permission() {
let ts = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let _chmod = ts.ccmd("chmod").arg("-r").arg(SUB_DIR_LINKS).succeeds(); at.mkdir_all(SUB_DIR_LINKS);
let result = ts.ucmd().arg(SUB_DIR_LINKS).succeeds();
ts.ccmd("chmod").arg("+r").arg(SUB_DIR_LINKS).run(); scene.ccmd("chmod").arg("-r").arg(SUB_DIR_LINKS).succeeds();
assert_eq!( let result = scene.ucmd().arg(SUB_DIR_LINKS).run(); // TODO: replace with ".fails()" once `du` is fixed
result.stderr_str(), result.stderr_contains(
"du: cannot read directory subdir/links: Permission denied (os error 13)\n" "du: cannot read directory subdir/links: Permission denied (os error 13)",
); );
#[cfg(target_os = "linux")]
{
let result_reference = scene.cmd("du").arg(SUB_DIR_LINKS).fails();
if result_reference
.stderr_str()
.contains("du: cannot read directory 'subdir/links': Permission denied")
{
assert_eq!(result.stdout_str(), result_reference.stdout_str());
return;
}
}
_du_no_permission(result.stdout_str()); _du_no_permission(result.stdout_str());
} }

View file

@ -140,8 +140,11 @@ fn test_unset_variable() {
#[test] #[test]
fn test_fail_null_with_program() { fn test_fail_null_with_program() {
let out = new_ucmd!().arg("--null").arg("cd").fails().stderr; new_ucmd!()
assert!(out.contains("cannot specify --null (-0) with command")); .arg("--null")
.arg("cd")
.fails()
.stderr_contains("cannot specify --null (-0) with command");
} }
#[cfg(not(windows))] #[cfg(not(windows))]

View file

@ -29,7 +29,7 @@ fn test_fmt_w_too_big() {
.run(); .run();
//.stdout_is_fixture("call_graph.expected"); //.stdout_is_fixture("call_graph.expected");
assert_eq!( assert_eq!(
result.stderr.trim(), result.stderr_str().trim(),
"fmt: error: invalid width: '2501': Numerical result out of range" "fmt: error: invalid width: '2501': Numerical result out of range"
); );
} }

View file

@ -10,7 +10,7 @@ fn test_groups() {
// As seems to be a configuration issue, ignoring it // As seems to be a configuration issue, ignoring it
return; return;
} }
assert!(result.success); result.success();
assert!(!result.stdout_str().trim().is_empty()); assert!(!result.stdout_str().trim().is_empty());
} }
@ -30,16 +30,12 @@ fn test_groups_arg() {
println!("result.stdout = {}", result.stdout_str()); println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str()); println!("result.stderr = {}", result.stderr_str());
assert!(result.success); result.success();
assert!(!result.stdout_str().is_empty()); assert!(!result.stdout_str().is_empty());
let username = result.stdout_str().trim(); let username = result.stdout_str().trim();
// call groups with the user name to check that we // call groups with the user name to check that we
// are getting something // are getting something
let (_, mut ucmd) = at_and_ucmd!(); new_ucmd!().arg(username).succeeds();
let result = ucmd.arg(username).run();
println!("result.stdout = {}", result.stdout_str());
println!("result.stderr = {}", result.stderr_str());
assert!(result.success);
assert!(!result.stdout_str().is_empty()); assert!(!result.stdout_str().is_empty());
} }

View file

@ -156,14 +156,10 @@ fn test_negative_zero_bytes() {
} }
#[test] #[test]
fn test_no_such_file_or_directory() { fn test_no_such_file_or_directory() {
let result = new_ucmd!().arg("no_such_file.toml").run(); new_ucmd!()
.arg("no_such_file.toml")
assert_eq!( .fails()
true, .stderr_contains("cannot open 'no_such_file.toml' for reading: No such file or directory");
result
.stderr
.contains("cannot open 'no_such_file.toml' for reading: No such file or directory")
)
} }
// there was a bug not caught by previous tests // there was a bug not caught by previous tests

View file

@ -11,12 +11,10 @@ use std::thread::sleep;
fn test_install_help() { fn test_install_help() {
let (_, mut ucmd) = at_and_ucmd!(); let (_, mut ucmd) = at_and_ucmd!();
assert!(ucmd ucmd.arg("--help")
.arg("--help")
.succeeds() .succeeds()
.no_stderr() .no_stderr()
.stdout .stdout_contains("FLAGS:");
.contains("FLAGS:"));
} }
#[test] #[test]
@ -59,13 +57,11 @@ fn test_install_failing_not_dir() {
at.touch(file1); at.touch(file1);
at.touch(file2); at.touch(file2);
at.touch(file3); at.touch(file3);
assert!(ucmd ucmd.arg(file1)
.arg(file1)
.arg(file2) .arg(file2)
.arg(file3) .arg(file3)
.fails() .fails()
.stderr .stderr_contains("not a directory");
.contains("not a directory"));
} }
#[test] #[test]
@ -77,13 +73,11 @@ fn test_install_unimplemented_arg() {
at.touch(file); at.touch(file);
at.mkdir(dir); at.mkdir(dir);
assert!(ucmd ucmd.arg(context_arg)
.arg(context_arg)
.arg(file) .arg(file)
.arg(dir) .arg(dir)
.fails() .fails()
.stderr .stderr_contains("Unimplemented");
.contains("Unimplemented"));
assert!(!at.file_exists(&format!("{}/{}", dir, file))); assert!(!at.file_exists(&format!("{}/{}", dir, file)));
} }
@ -231,13 +225,11 @@ fn test_install_mode_failing() {
at.touch(file); at.touch(file);
at.mkdir(dir); at.mkdir(dir);
assert!(ucmd ucmd.arg(file)
.arg(file)
.arg(dir) .arg(dir)
.arg(mode_arg) .arg(mode_arg)
.fails() .fails()
.stderr .stderr_contains("Invalid mode string: invalid digit found in string");
.contains("Invalid mode string: invalid digit found in string"));
let dest_file = &format!("{}/{}", dir, file); let dest_file = &format!("{}/{}", dir, file);
assert!(at.file_exists(file)); assert!(at.file_exists(file));
@ -336,7 +328,7 @@ fn test_install_target_new_file_with_owner() {
.arg(format!("{}/{}", dir, file)) .arg(format!("{}/{}", dir, file))
.run(); .run();
if is_ci() && result.stderr.contains("error: no such user:") { if is_ci() && result.stderr_str().contains("error: no such user:") {
// In the CI, some server are failing to return the user id. // In the CI, some server are failing to return the user id.
// As seems to be a configuration issue, ignoring it // As seems to be a configuration issue, ignoring it
return; return;
@ -619,35 +611,27 @@ fn test_install_and_strip_with_program() {
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn test_install_and_strip_with_invalid_program() { fn test_install_and_strip_with_invalid_program() {
let scene = TestScenario::new(util_name!()); new_ucmd!()
let stderr = scene
.ucmd()
.arg("-s") .arg("-s")
.arg("--strip-program") .arg("--strip-program")
.arg("/bin/date") .arg("/bin/date")
.arg(strip_source_file()) .arg(strip_source_file())
.arg(STRIP_TARGET_FILE) .arg(STRIP_TARGET_FILE)
.fails() .fails()
.stderr; .stderr_contains("strip program failed");
assert!(stderr.contains("strip program failed"));
} }
#[test] #[test]
#[cfg(not(windows))] #[cfg(not(windows))]
fn test_install_and_strip_with_non_existent_program() { fn test_install_and_strip_with_non_existent_program() {
let scene = TestScenario::new(util_name!()); new_ucmd!()
let stderr = scene
.ucmd()
.arg("-s") .arg("-s")
.arg("--strip-program") .arg("--strip-program")
.arg("/usr/bin/non_existent_program") .arg("/usr/bin/non_existent_program")
.arg(strip_source_file()) .arg(strip_source_file())
.arg(STRIP_TARGET_FILE) .arg(STRIP_TARGET_FILE)
.fails() .fails()
.stderr; .stderr_contains("No such file or directory");
assert!(stderr.contains("No such file or directory"));
} }
#[test] #[test]

View file

@ -299,13 +299,11 @@ fn test_symlink_overwrite_dir_fail() {
at.touch(path_a); at.touch(path_a);
at.mkdir(path_b); at.mkdir(path_b);
assert!( assert!(!ucmd
ucmd.args(&["-s", "-T", path_a, path_b]) .args(&["-s", "-T", path_a, path_b])
.fails() .fails()
.stderr .stderr_str()
.len() .is_empty());
> 0
);
} }
#[test] #[test]
@ -358,7 +356,11 @@ fn test_symlink_target_only() {
at.mkdir(dir); at.mkdir(dir);
assert!(ucmd.args(&["-s", "-t", dir]).fails().stderr.len() > 0); assert!(!ucmd
.args(&["-s", "-t", dir])
.fails()
.stderr_str()
.is_empty());
} }
#[test] #[test]

View file

@ -104,6 +104,12 @@ fn test_ls_width() {
.stdout_only("test-width-1\ntest-width-2\ntest-width-3\ntest-width-4\n"); .stdout_only("test-width-1\ntest-width-2\ntest-width-3\ntest-width-4\n");
} }
scene
.ucmd()
.arg("-w=bad")
.fails()
.stderr_contains("invalid line width");
for option in &["-w 1a", "-w=1a", "--width=1a", "--width 1a"] { for option in &["-w 1a", "-w=1a", "--width=1a", "--width 1a"] {
scene scene
.ucmd() .ucmd()
@ -444,6 +450,39 @@ fn test_ls_deref() {
assert!(!re.is_match(result.stdout_str().trim())); assert!(!re.is_match(result.stdout_str().trim()));
} }
#[test]
fn test_ls_sort_none() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("test-3");
at.touch("test-1");
at.touch("test-2");
// Order is not specified so we just check that it doesn't
// give any errors.
scene.ucmd().arg("--sort=none").succeeds();
scene.ucmd().arg("-U").succeeds();
}
#[test]
fn test_ls_sort_name() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("test-3");
at.touch("test-1");
at.touch("test-2");
let sep = if cfg!(unix) { "\n" } else { " " };
scene
.ucmd()
.arg("--sort=name")
.succeeds()
.stdout_is(["test-1", "test-2", "test-3\n"].join(sep));
}
#[test] #[test]
fn test_ls_order_size() { fn test_ls_order_size() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
@ -472,6 +511,18 @@ fn test_ls_order_size() {
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n"); result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)] #[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n"); result.stdout_only("test-1 test-2 test-3 test-4\n");
let result = scene.ucmd().arg("--sort=size").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
#[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("--sort=size").arg("-r").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n");
} }
#[test] #[test]
@ -480,13 +531,16 @@ fn test_ls_long_ctime() {
let at = &scene.fixtures; let at = &scene.fixtures;
at.touch("test-long-ctime-1"); at.touch("test-long-ctime-1");
let result = scene.ucmd().arg("-lc").succeeds();
// Should show the time on Unix, but question marks on windows. for arg in &["-c", "--time=ctime", "--time=status"] {
#[cfg(unix)] let result = scene.ucmd().arg("-l").arg(arg).succeeds();
result.stdout_contains(":");
#[cfg(not(unix))] // Should show the time on Unix, but question marks on windows.
result.stdout_contains("???"); #[cfg(unix)]
result.stdout_contains(":");
#[cfg(not(unix))]
result.stdout_contains("???");
}
} }
#[test] #[test]
@ -527,32 +581,46 @@ fn test_ls_order_time() {
#[cfg(windows)] #[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n"); result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("--sort=time").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
#[cfg(windows)]
result.stdout_only("test-4 test-3 test-2 test-1\n");
let result = scene.ucmd().arg("-tr").succeeds(); let result = scene.ucmd().arg("-tr").succeeds();
#[cfg(not(windows))] #[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n"); result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)] #[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n"); result.stdout_only("test-1 test-2 test-3 test-4\n");
let result = scene.ucmd().arg("--sort=time").arg("-r").succeeds();
#[cfg(not(windows))]
result.stdout_only("test-1\ntest-2\ntest-3\ntest-4\n");
#[cfg(windows)]
result.stdout_only("test-1 test-2 test-3 test-4\n");
// 3 was accessed last in the read // 3 was accessed last in the read
// So the order should be 2 3 4 1 // So the order should be 2 3 4 1
let result = scene.ucmd().arg("-tu").succeeds(); for arg in &["-u", "--time=atime", "--time=access", "--time=use"] {
let file3_access = at.open("test-3").metadata().unwrap().accessed().unwrap(); let result = scene.ucmd().arg("-t").arg(arg).succeeds();
let file4_access = at.open("test-4").metadata().unwrap().accessed().unwrap(); let file3_access = at.open("test-3").metadata().unwrap().accessed().unwrap();
let file4_access = at.open("test-4").metadata().unwrap().accessed().unwrap();
// It seems to be dependent on the platform whether the access time is actually set // It seems to be dependent on the platform whether the access time is actually set
if file3_access > file4_access { if file3_access > file4_access {
if cfg!(not(windows)) { if cfg!(not(windows)) {
result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n"); result.stdout_only("test-3\ntest-4\ntest-2\ntest-1\n");
} else {
result.stdout_only("test-3 test-4 test-2 test-1\n");
}
} else { } else {
result.stdout_only("test-3 test-4 test-2 test-1\n"); // Access time does not seem to be set on Windows and some other
} // systems so the order is 4 3 2 1
} else { if cfg!(not(windows)) {
// Access time does not seem to be set on Windows and some other result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n");
// systems so the order is 4 3 2 1 } else {
if cfg!(not(windows)) { result.stdout_only("test-4 test-3 test-2 test-1\n");
result.stdout_only("test-4\ntest-3\ntest-2\ntest-1\n"); }
} else {
result.stdout_only("test-4 test-3 test-2 test-1\n");
} }
} }
@ -629,20 +697,27 @@ fn test_ls_recursive() {
result.stdout_contains(&"a\\b:\nb"); result.stdout_contains(&"a\\b:\nb");
} }
#[cfg(unix)]
#[test] #[test]
fn test_ls_ls_color() { fn test_ls_color() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures; let at = &scene.fixtures;
at.mkdir("a"); at.mkdir("a");
at.mkdir("a/nested_dir"); let nested_dir = Path::new("a")
.join("nested_dir")
.to_string_lossy()
.to_string();
at.mkdir(&nested_dir);
at.mkdir("z"); at.mkdir("z");
at.touch(&at.plus_as_string("a/nested_file")); let nested_file = Path::new("a")
.join("nested_file")
.to_string_lossy()
.to_string();
at.touch(&nested_file);
at.touch("test-color"); at.touch("test-color");
let a_with_colors = "\x1b[01;34ma\x1b[0m"; let a_with_colors = "\x1b[1;34ma\x1b[0m";
let z_with_colors = "\x1b[01;34mz\x1b[0m"; let z_with_colors = "\x1b[1;34mz\x1b[0m";
let nested_dir_with_colors = "\x1b[01;34mnested_dir\x1b[0m"; let nested_dir_with_colors = "\x1b[1;34mnested_dir\x1b[0m";
// Color is disabled by default // Color is disabled by default
let result = scene.ucmd().succeeds(); let result = scene.ucmd().succeeds();
@ -678,14 +753,6 @@ fn test_ls_ls_color() {
.succeeds() .succeeds()
.stdout_contains(nested_dir_with_colors); .stdout_contains(nested_dir_with_colors);
// Color has no effect
scene
.ucmd()
.arg("--color=always")
.arg("a/nested_file")
.succeeds()
.stdout_contains("a/nested_file\n");
// No output // No output
scene scene
.ucmd() .ucmd()
@ -825,7 +892,7 @@ fn test_ls_indicator_style() {
let options = vec!["classify", "file-type", "slash"]; let options = vec!["classify", "file-type", "slash"];
for opt in options { for opt in options {
// Verify that classify and file-type both contain indicators for symlinks. // Verify that classify and file-type both contain indicators for symlinks.
let result = scene scene
.ucmd() .ucmd()
.arg(format!("--indicator-style={}", opt)) .arg(format!("--indicator-style={}", opt))
.succeeds() .succeeds()
@ -835,7 +902,7 @@ fn test_ls_indicator_style() {
// Same test as above, but with the alternate flags. // Same test as above, but with the alternate flags.
let options = vec!["--classify", "--file-type", "-p"]; let options = vec!["--classify", "--file-type", "-p"];
for opt in options { for opt in options {
let result = scene scene
.ucmd() .ucmd()
.arg(format!("{}", opt)) .arg(format!("{}", opt))
.succeeds() .succeeds()
@ -846,7 +913,7 @@ fn test_ls_indicator_style() {
let options = vec!["classify", "file-type"]; let options = vec!["classify", "file-type"];
for opt in options { for opt in options {
// Verify that classify and file-type both contain indicators for symlinks. // Verify that classify and file-type both contain indicators for symlinks.
let result = scene scene
.ucmd() .ucmd()
.arg(format!("--indicator-style={}", opt)) .arg(format!("--indicator-style={}", opt))
.succeeds() .succeeds()
@ -970,7 +1037,7 @@ fn test_ls_hidden_windows() {
let result = scene.ucmd().succeeds(); let result = scene.ucmd().succeeds();
assert!(!result.stdout_str().contains(file)); assert!(!result.stdout_str().contains(file));
let result = scene.ucmd().arg("-a").succeeds().stdout_contains(file); scene.ucmd().arg("-a").succeeds().stdout_contains(file);
} }
#[test] #[test]
@ -1060,9 +1127,11 @@ fn test_ls_quoting_style() {
at.touch("one"); at.touch("one");
// It seems that windows doesn't allow \n in filenames. // It seems that windows doesn't allow \n in filenames.
// And it also doesn't like \, of course.
#[cfg(unix)] #[cfg(unix)]
{ {
at.touch("one\ntwo"); at.touch("one\ntwo");
at.touch("one\\two");
// Default is shell-escape // Default is shell-escape
scene scene
.ucmd() .ucmd()
@ -1124,6 +1193,42 @@ fn test_ls_quoting_style() {
.succeeds() .succeeds()
.stdout_only(format!("{}\n", correct)); .stdout_only(format!("{}\n", correct));
} }
for (arg, correct) in &[
("--quoting-style=literal", "one\\two"),
("-N", "one\\two"),
("--quoting-style=c", "\"one\\\\two\""),
("-Q", "\"one\\\\two\""),
("--quote-name", "\"one\\\\two\""),
("--quoting-style=escape", "one\\\\two"),
("-b", "one\\\\two"),
("--quoting-style=shell-escape", "'one\\two'"),
("--quoting-style=shell-escape-always", "'one\\two'"),
("--quoting-style=shell", "'one\\two'"),
("--quoting-style=shell-always", "'one\\two'"),
] {
scene
.ucmd()
.arg(arg)
.arg("one\\two")
.succeeds()
.stdout_only(format!("{}\n", correct));
}
// Tests for a character that forces quotation in shell-style escaping
// after a character in a dollar expression
at.touch("one\n&two");
for (arg, correct) in &[
("--quoting-style=shell-escape", "'one'$'\\n''&two'"),
("--quoting-style=shell-escape-always", "'one'$'\\n''&two'"),
] {
scene
.ucmd()
.arg(arg)
.arg("one\n&two")
.succeeds()
.stdout_only(format!("{}\n", correct));
}
} }
scene scene
@ -1324,6 +1429,43 @@ fn test_ls_ignore_hide() {
.stdout_is("CONTRIBUTING.md\nREADME.md\nREADMECAREFULLY.md\nsome_other_file\n"); .stdout_is("CONTRIBUTING.md\nREADME.md\nREADMECAREFULLY.md\nsome_other_file\n");
} }
#[test]
fn test_ls_ignore_backups() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
at.touch("somefile");
at.touch("somebackup~");
at.touch(".somehiddenfile");
at.touch(".somehiddenbackup~");
scene.ucmd().arg("-B").succeeds().stdout_is("somefile\n");
scene
.ucmd()
.arg("--ignore-backups")
.succeeds()
.stdout_is("somefile\n");
scene
.ucmd()
.arg("-aB")
.succeeds()
.stdout_contains(".somehiddenfile")
.stdout_contains("somefile")
.stdout_does_not_contain("somebackup")
.stdout_does_not_contain(".somehiddenbackup~");
scene
.ucmd()
.arg("-a")
.arg("--ignore-backups")
.succeeds()
.stdout_contains(".somehiddenfile")
.stdout_contains("somefile")
.stdout_does_not_contain("somebackup")
.stdout_does_not_contain(".somehiddenbackup~");
}
#[test] #[test]
fn test_ls_directory() { fn test_ls_directory() {
let scene = TestScenario::new(util_name!()); let scene = TestScenario::new(util_name!());

View file

@ -19,8 +19,7 @@ fn test_create_one_fifo_with_invalid_mode() {
.arg("-m") .arg("-m")
.arg("invalid") .arg("invalid")
.fails() .fails()
.stderr .stderr_contains("invalid mode");
.contains("invalid mode");
} }
#[test] #[test]

View file

@ -2,18 +2,15 @@ use crate::common::util::*;
#[test] #[test]
fn test_more_no_arg() { fn test_more_no_arg() {
let (_, mut ucmd) = at_and_ucmd!(); // stderr = more: Reading from stdin isn't supported yet.
let result = ucmd.run(); new_ucmd!().fails();
assert!(!result.success);
} }
#[test] #[test]
fn test_more_dir_arg() { fn test_more_dir_arg() {
let (_, mut ucmd) = at_and_ucmd!(); let result = new_ucmd!().arg(".").run();
ucmd.arg("."); result.failure();
let result = ucmd.run();
assert!(!result.success);
const EXPECTED_ERROR_MESSAGE: &str = const EXPECTED_ERROR_MESSAGE: &str =
"more: '.' is a directory.\nTry 'more --help' for more information."; "more: '.' is a directory.\nTry 'more --help' for more information.";
assert_eq!(result.stderr.trim(), EXPECTED_ERROR_MESSAGE); assert_eq!(result.stderr_str().trim(), EXPECTED_ERROR_MESSAGE);
} }

View file

@ -476,16 +476,9 @@ fn test_mv_overwrite_nonempty_dir() {
// GNU: "mv: cannot move a to b: Directory not empty" // GNU: "mv: cannot move a to b: Directory not empty"
// Verbose output for the move should not be shown on failure // Verbose output for the move should not be shown on failure
assert!( let result = ucmd.arg("-vT").arg(dir_a).arg(dir_b).fails();
ucmd.arg("-vT") result.no_stdout();
.arg(dir_a) assert!(!result.stderr_str().is_empty());
.arg(dir_b)
.fails()
.no_stdout()
.stderr
.len()
> 0
);
assert!(at.dir_exists(dir_a)); assert!(at.dir_exists(dir_a));
assert!(at.dir_exists(dir_b)); assert!(at.dir_exists(dir_b));
@ -526,15 +519,15 @@ fn test_mv_errors() {
// $ mv -T -t a b // $ mv -T -t a b
// mv: cannot combine --target-directory (-t) and --no-target-directory (-T) // mv: cannot combine --target-directory (-t) and --no-target-directory (-T)
let result = scene scene
.ucmd() .ucmd()
.arg("-T") .arg("-T")
.arg("-t") .arg("-t")
.arg(dir) .arg(dir)
.arg(file_a) .arg(file_a)
.arg(file_b) .arg(file_b)
.fails(); .fails()
assert!(result.stderr.contains("cannot be used with")); .stderr_contains("cannot be used with");
// $ at.touch file && at.mkdir dir // $ at.touch file && at.mkdir dir
// $ mv -T file dir // $ mv -T file dir
@ -553,7 +546,13 @@ fn test_mv_errors() {
// $ at.mkdir dir && at.touch file // $ at.mkdir dir && at.touch file
// $ mv dir file // $ mv dir file
// err == mv: cannot overwrite non-directory file with directory dir // err == mv: cannot overwrite non-directory file with directory dir
assert!(scene.ucmd().arg(dir).arg(file_a).fails().stderr.len() > 0); assert!(!scene
.ucmd()
.arg(dir)
.arg(file_a)
.fails()
.stderr_str()
.is_empty());
} }
#[test] #[test]

View file

@ -16,7 +16,7 @@ fn test_negative_adjustment() {
let res = new_ucmd!().args(&["-n", "-1", "true"]).run(); let res = new_ucmd!().args(&["-n", "-1", "true"]).run();
assert!(res assert!(res
.stderr .stderr_str()
.starts_with("nice: warning: setpriority: Permission denied")); .starts_with("nice: warning: setpriority: Permission denied"));
} }

View file

@ -9,35 +9,28 @@ fn test_output_is_random_permutation() {
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("\n"); .join("\n");
let result = new_ucmd!() let result = new_ucmd!().pipe_in(input.as_bytes()).succeeds();
.pipe_in(input.as_bytes()) result.no_stderr();
.succeeds()
.no_stderr()
.stdout
.clone();
let mut result_seq: Vec<i32> = result let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n") .split("\n")
.filter(|x| !x.is_empty()) .filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap()) .map(|x| x.parse().unwrap())
.collect(); .collect();
result_seq.sort(); result_seq.sort();
assert_ne!(result, input, "Output is not randomised"); assert_ne!(result.stdout_str(), input, "Output is not randomised");
assert_eq!(result_seq, input_seq, "Output is not a permutation"); assert_eq!(result_seq, input_seq, "Output is not a permutation");
} }
#[test] #[test]
fn test_zero_termination() { fn test_zero_termination() {
let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let input_seq = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = new_ucmd!() let result = new_ucmd!().arg("-z").arg("-i1-10").succeeds();
.arg("-z") result.no_stderr();
.arg("-i1-10")
.succeeds()
.no_stderr()
.stdout
.clone();
let mut result_seq: Vec<i32> = result let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\0") .split("\0")
.filter(|x| !x.is_empty()) .filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap()) .map(|x| x.parse().unwrap())
@ -57,12 +50,11 @@ fn test_echo() {
.map(|x| x.to_string()) .map(|x| x.to_string())
.collect::<Vec<String>>(), .collect::<Vec<String>>(),
) )
.succeeds() .succeeds();
.no_stderr() result.no_stderr();
.stdout
.clone();
let mut result_seq: Vec<i32> = result let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n") .split("\n")
.filter(|x| !x.is_empty()) .filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap()) .map(|x| x.parse().unwrap())
@ -84,12 +76,11 @@ fn test_head_count() {
let result = new_ucmd!() let result = new_ucmd!()
.args(&["-n", &repeat_limit.to_string()]) .args(&["-n", &repeat_limit.to_string()])
.pipe_in(input.as_bytes()) .pipe_in(input.as_bytes())
.succeeds() .succeeds();
.no_stderr() result.no_stderr();
.stdout
.clone();
let mut result_seq: Vec<i32> = result let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n") .split("\n")
.filter(|x| !x.is_empty()) .filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap()) .map(|x| x.parse().unwrap())
@ -99,7 +90,7 @@ fn test_head_count() {
assert!( assert!(
result_seq.iter().all(|x| input_seq.contains(x)), result_seq.iter().all(|x| input_seq.contains(x)),
"Output includes element not from input: {}", "Output includes element not from input: {}",
result result.stdout_str()
) )
} }
@ -117,12 +108,11 @@ fn test_repeat() {
.arg("-r") .arg("-r")
.args(&["-n", &repeat_limit.to_string()]) .args(&["-n", &repeat_limit.to_string()])
.pipe_in(input.as_bytes()) .pipe_in(input.as_bytes())
.succeeds() .succeeds();
.no_stderr() result.no_stderr();
.stdout
.clone();
let result_seq: Vec<i32> = result let result_seq: Vec<i32> = result
.stdout_str()
.split("\n") .split("\n")
.filter(|x| !x.is_empty()) .filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap()) .map(|x| x.parse().unwrap())
@ -146,14 +136,11 @@ fn test_repeat() {
fn test_file_input() { fn test_file_input() {
let expected_seq = vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20]; let expected_seq = vec![11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
let result = new_ucmd!() let result = new_ucmd!().arg("file_input.txt").succeeds();
.arg("file_input.txt") result.no_stderr();
.succeeds()
.no_stderr()
.stdout
.clone();
let mut result_seq: Vec<i32> = result let mut result_seq: Vec<i32> = result
.stdout_str()
.split("\n") .split("\n")
.filter(|x| !x.is_empty()) .filter(|x| !x.is_empty())
.map(|x| x.parse().unwrap()) .map(|x| x.parse().unwrap())
@ -164,52 +151,50 @@ fn test_file_input() {
#[test] #[test]
fn test_shuf_echo_and_input_range_not_allowed() { fn test_shuf_echo_and_input_range_not_allowed() {
let result = new_ucmd!().args(&["-e", "0", "-i", "0-2"]).run(); new_ucmd!()
.args(&["-e", "0", "-i", "0-2"])
assert!(!result.success); .fails()
assert!(result .stderr_contains(
.stderr "The argument '--input-range <LO-HI>' cannot be used with '--echo <ARG>...'",
.contains("The argument '--input-range <LO-HI>' cannot be used with '--echo <ARG>...'")); );
} }
#[test] #[test]
fn test_shuf_input_range_and_file_not_allowed() { fn test_shuf_input_range_and_file_not_allowed() {
let result = new_ucmd!().args(&["-i", "0-9", "file"]).run(); new_ucmd!()
.args(&["-i", "0-9", "file"])
assert!(!result.success); .fails()
assert!(result .stderr_contains("The argument '<file>' cannot be used with '--input-range <LO-HI>'");
.stderr
.contains("The argument '<file>' cannot be used with '--input-range <LO-HI>'"));
} }
#[test] #[test]
fn test_shuf_invalid_input_range_one() { fn test_shuf_invalid_input_range_one() {
let result = new_ucmd!().args(&["-i", "0"]).run(); new_ucmd!()
.args(&["-i", "0"])
assert!(!result.success); .fails()
assert!(result.stderr.contains("invalid input range")); .stderr_contains("invalid input range");
} }
#[test] #[test]
fn test_shuf_invalid_input_range_two() { fn test_shuf_invalid_input_range_two() {
let result = new_ucmd!().args(&["-i", "a-9"]).run(); new_ucmd!()
.args(&["-i", "a-9"])
assert!(!result.success); .fails()
assert!(result.stderr.contains("invalid input range: 'a'")); .stderr_contains("invalid input range: 'a'");
} }
#[test] #[test]
fn test_shuf_invalid_input_range_three() { fn test_shuf_invalid_input_range_three() {
let result = new_ucmd!().args(&["-i", "0-b"]).run(); new_ucmd!()
.args(&["-i", "0-b"])
assert!(!result.success); .fails()
assert!(result.stderr.contains("invalid input range: 'b'")); .stderr_contains("invalid input range: 'b'");
} }
#[test] #[test]
fn test_shuf_invalid_input_line_count() { fn test_shuf_invalid_input_line_count() {
let result = new_ucmd!().args(&["-n", "a"]).run(); new_ucmd!()
.args(&["-n", "a"])
assert!(!result.success); .fails()
assert!(result.stderr.contains("invalid line count: 'a'")); .stderr_contains("invalid line count: 'a'");
} }

View file

@ -581,3 +581,12 @@ fn test_check_silent() {
.fails() .fails()
.stdout_is(""); .stdout_is("");
} }
#[test]
fn test_trailing_separator() {
new_ucmd!()
.args(&["-t", "x", "-k", "1,1"])
.pipe_in("aax\naaa\n")
.succeeds()
.stdout_is("aax\naaa\n");
}

View file

@ -337,5 +337,5 @@ fn expected_result(args: &[&str]) -> String {
.env("LANGUAGE", "C") .env("LANGUAGE", "C")
.args(args) .args(args)
.run() .run()
.stdout .stdout_move_str()
} }

View file

@ -52,18 +52,19 @@ fn test_single_non_newline_separator_before() {
#[test] #[test]
fn test_invalid_input() { fn test_invalid_input() {
let (_, mut ucmd) = at_and_ucmd!(); let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
ucmd.arg("b") scene
.run() .ucmd()
.stderr .arg("b")
.contains("tac: error: failed to open 'b' for reading"); .fails()
.stderr_contains("failed to open 'b' for reading: No such file or directory");
let (at, mut ucmd) = at_and_ucmd!();
at.mkdir("a"); at.mkdir("a");
ucmd.arg("a") scene
.run() .ucmd()
.stderr .arg("a")
.contains("tac: error: failed to read 'a'"); .fails()
.stderr_contains("dir: read error: Invalid argument");
} }

View file

@ -343,3 +343,12 @@ fn test_negative_indexing() {
assert_eq!(positive_lines_index.stdout(), negative_lines_index.stdout()); assert_eq!(positive_lines_index.stdout(), negative_lines_index.stdout());
assert_eq!(positive_bytes_index.stdout(), negative_bytes_index.stdout()); assert_eq!(positive_bytes_index.stdout(), negative_bytes_index.stdout());
} }
#[test]
fn test_sleep_interval() {
new_ucmd!()
.arg("-s")
.arg("10")
.arg(FOOBAR_TXT)
.succeeds();
}

View file

@ -120,19 +120,15 @@ fn test_truncate_with_set1_shorter_than_set2() {
#[test] #[test]
fn missing_args_fails() { fn missing_args_fails() {
let (_, mut ucmd) = at_and_ucmd!(); let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.run(); ucmd.fails().stderr_contains("missing operand");
assert!(!result.success);
assert!(result.stderr.contains("missing operand"));
} }
#[test] #[test]
fn missing_required_second_arg_fails() { fn missing_required_second_arg_fails() {
let (_, mut ucmd) = at_and_ucmd!(); let (_, mut ucmd) = at_and_ucmd!();
let result = ucmd.args(&["foo"]).run(); ucmd.args(&["foo"])
.fails()
assert!(!result.success); .stderr_contains("missing operand after");
assert!(result.stderr.contains("missing operand after"));
} }
#[test] #[test]

View file

@ -53,6 +53,16 @@ fn test_decrease_file_size() {
assert!(file.seek(SeekFrom::Current(0)).unwrap() == 6); assert!(file.seek(SeekFrom::Current(0)).unwrap() == 6);
} }
#[test]
fn test_space_in_size() {
let (at, mut ucmd) = at_and_ucmd!();
let mut file = at.make_file(TFILE2);
file.write_all(b"1234567890").unwrap();
ucmd.args(&["--size", " 4", TFILE2]).succeeds();
file.seek(SeekFrom::End(0)).unwrap();
assert!(file.seek(SeekFrom::Current(0)).unwrap() == 4);
}
#[test] #[test]
fn test_failed() { fn test_failed() {
new_ucmd!().fails(); new_ucmd!().fails();
@ -69,3 +79,4 @@ fn test_failed_incorrect_arg() {
let (_at, mut ucmd) = at_and_ucmd!(); let (_at, mut ucmd) = at_and_ucmd!();
ucmd.args(&["-s", "+5A", TFILE1]).fails(); ucmd.args(&["-s", "+5A", TFILE1]).fails();
} }

View file

@ -18,33 +18,35 @@ fn test_sort_self_loop() {
#[test] #[test]
fn test_no_such_file() { fn test_no_such_file() {
let result = new_ucmd!().arg("invalid_file_txt").run(); new_ucmd!()
.arg("invalid_file_txt")
assert_eq!(true, result.stderr.contains("No such file or directory")); .fails()
.stderr_contains("No such file or directory");
} }
#[test] #[test]
fn test_version_flag() { fn test_version_flag() {
let version_short = new_ucmd!().arg("-V").run(); let version_short = new_ucmd!().arg("-V").succeeds();
let version_long = new_ucmd!().arg("--version").run(); let version_long = new_ucmd!().arg("--version").succeeds();
assert_eq!(version_short.stdout(), version_long.stdout()); assert_eq!(version_short.stdout_str(), version_long.stdout_str());
} }
#[test] #[test]
fn test_help_flag() { fn test_help_flag() {
let help_short = new_ucmd!().arg("-h").run(); let help_short = new_ucmd!().arg("-h").succeeds();
let help_long = new_ucmd!().arg("--help").run(); let help_long = new_ucmd!().arg("--help").succeeds();
assert_eq!(help_short.stdout(), help_long.stdout()); assert_eq!(help_short.stdout_str(), help_long.stdout_str());
} }
#[test] #[test]
fn test_multiple_arguments() { fn test_multiple_arguments() {
let result = new_ucmd!() new_ucmd!()
.arg("call_graph.txt") .arg("call_graph.txt")
.arg("invalid_file.txt") .arg("invalid_file")
.run(); .fails()
.stderr_contains(
assert_eq!(true, result.stderr.contains("error: Found argument 'invalid_file.txt' which wasn't expected, or isn't valid in this context")) "Found argument 'invalid_file' which wasn't expected, or isn't valid in this context",
);
} }

View file

@ -23,7 +23,7 @@ fn test_heading() {
for opt in vec!["-H"] { for opt in vec!["-H"] {
// allow whitespace variation // allow whitespace variation
// * minor whitespace differences occur between platform built-in outputs; specifically number of TABs between "TIME" and "COMMENT" may be variant // * minor whitespace differences occur between platform built-in outputs; specifically number of TABs between "TIME" and "COMMENT" may be variant
let actual = new_ucmd!().arg(opt).run().stdout; let actual = new_ucmd!().arg(opt).run().stdout_move_str();
let expect = expected_result(opt); let expect = expected_result(opt);
println!("actual: {:?}", actual); println!("actual: {:?}", actual);
println!("expect: {:?}", expect); println!("expect: {:?}", expect);
@ -80,5 +80,5 @@ fn expected_result(arg: &str) -> String {
.env("LANGUAGE", "C") .env("LANGUAGE", "C")
.args(&[arg]) .args(&[arg])
.run() .run()
.stdout .stdout_move_str()
} }

View file

@ -74,11 +74,11 @@ pub struct CmdResult {
code: Option<i32>, code: Option<i32>,
/// zero-exit from running the Command? /// zero-exit from running the Command?
/// see [`success`] /// see [`success`]
pub success: bool, success: bool,
/// captured standard output after running the Command /// captured standard output after running the Command
pub stdout: String, stdout: String,
/// captured standard error after running the Command /// captured standard error after running the Command
pub stderr: String, stderr: String,
} }
impl CmdResult { impl CmdResult {
@ -329,14 +329,14 @@ impl CmdResult {
} }
pub fn stdout_matches(&self, regex: &regex::Regex) -> &CmdResult { pub fn stdout_matches(&self, regex: &regex::Regex) -> &CmdResult {
if !regex.is_match(self.stdout_str()) { if !regex.is_match(self.stdout_str().trim()) {
panic!("Stdout does not match regex:\n{}", self.stdout_str()) panic!("Stdout does not match regex:\n{}", self.stdout_str())
} }
self self
} }
pub fn stdout_does_not_match(&self, regex: &regex::Regex) -> &CmdResult { pub fn stdout_does_not_match(&self, regex: &regex::Regex) -> &CmdResult {
if regex.is_match(self.stdout_str()) { if regex.is_match(self.stdout_str().trim()) {
panic!("Stdout matches regex:\n{}", self.stdout_str()) panic!("Stdout matches regex:\n{}", self.stdout_str())
} }
self self