mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 03:27:44 +00:00
refactor(chown): move to clap & add tests (#1648)
This commit is contained in:
parent
068fee2ebd
commit
49b32ea68d
4 changed files with 495 additions and 81 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1442,6 +1442,7 @@ dependencies = [
|
|||
name = "uu_chown"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"glob 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"uucore 0.0.4",
|
||||
"uucore_procs 0.0.4",
|
||||
|
|
|
@ -15,6 +15,7 @@ edition = "2018"
|
|||
path = "src/chown.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = "2.33"
|
||||
glob = "0.3.0"
|
||||
uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "fs"] }
|
||||
uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" }
|
||||
|
|
|
@ -13,6 +13,9 @@ pub use uucore::entries::{self, Group, Locate, Passwd};
|
|||
use uucore::fs::resolve_relative_path;
|
||||
use uucore::libc::{self, gid_t, lchown, uid_t};
|
||||
|
||||
extern crate clap;
|
||||
use clap::{App, Arg};
|
||||
|
||||
extern crate walkdir;
|
||||
use walkdir::WalkDir;
|
||||
|
||||
|
@ -28,77 +31,165 @@ use std::path::Path;
|
|||
use std::ffi::CString;
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
|
||||
static SYNTAX: &str =
|
||||
"[OPTION]... [OWNER][:[GROUP]] FILE...\n chown [OPTION]... --reference=RFILE FILE...";
|
||||
static SUMMARY: &str = "change file owner and group";
|
||||
static ABOUT: &str = "change file owner and group";
|
||||
static VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
|
||||
static OPT_CHANGES: &str = "changes";
|
||||
static OPT_DEREFERENCE: &str = "dereference";
|
||||
static OPT_NO_DEREFERENCE: &str = "no-dereference";
|
||||
static OPT_FROM: &str = "from";
|
||||
static OPT_PRESERVE_ROOT: &str = "preserve-root";
|
||||
static OPT_NO_PRESERVE_ROOT: &str = "no-preserve-root";
|
||||
static OPT_QUIET: &str = "quiet";
|
||||
static OPT_RECURSIVE: &str = "recursive";
|
||||
static OPT_REFERENCE: &str = "reference";
|
||||
static OPT_SILENT: &str = "silent";
|
||||
static OPT_TRAVERSE: &str = "H";
|
||||
static OPT_NO_TRAVERSE: &str = "P";
|
||||
static OPT_TRAVERSE_EVERY: &str = "L";
|
||||
static OPT_VERBOSE: &str = "verbose";
|
||||
|
||||
static ARG_OWNER: &str = "owner";
|
||||
static ARG_FILES: &str = "files";
|
||||
|
||||
const FTS_COMFOLLOW: u8 = 1;
|
||||
const FTS_PHYSICAL: u8 = 1 << 1;
|
||||
const FTS_LOGICAL: u8 = 1 << 2;
|
||||
|
||||
fn get_usage() -> String {
|
||||
format!(
|
||||
"{0} [OPTION]... [OWNER][:[GROUP]] FILE...\n{0} [OPTION]... --reference=RFILE FILE...",
|
||||
executable!()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn uumain(args: impl uucore::Args) -> i32 {
|
||||
let args = args.collect_str();
|
||||
|
||||
let mut opts = app!(SYNTAX, SUMMARY, "");
|
||||
opts.optflag("c",
|
||||
"changes",
|
||||
"like verbose but report only when a change is made")
|
||||
.optflag("f", "silent", "")
|
||||
.optflag("", "quiet", "suppress most error messages")
|
||||
.optflag("v",
|
||||
"verbose",
|
||||
"output a diagnostic for every file processed")
|
||||
.optflag("", "dereference", "affect the referent of each symbolic link (this is the default), rather than the symbolic link itself")
|
||||
.optflag("h", "no-dereference", "affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)")
|
||||
let usage = get_usage();
|
||||
|
||||
.optopt("", "from", "change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute", "CURRENT_OWNER:CURRENT_GROUP")
|
||||
.optopt("",
|
||||
"reference",
|
||||
"use RFILE's owner and group rather than specifying OWNER:GROUP values",
|
||||
"RFILE")
|
||||
.optflag("",
|
||||
"no-preserve-root",
|
||||
"do not treat '/' specially (the default)")
|
||||
.optflag("", "preserve-root", "fail to operate recursively on '/'")
|
||||
let matches = App::new(executable!())
|
||||
.version(VERSION)
|
||||
.about(ABOUT)
|
||||
.usage(&usage[..])
|
||||
.arg(
|
||||
Arg::with_name(OPT_CHANGES)
|
||||
.short("c")
|
||||
.long(OPT_CHANGES)
|
||||
.help("like verbose but report only when a change is made"),
|
||||
)
|
||||
.arg(Arg::with_name(OPT_DEREFERENCE).long(OPT_DEREFERENCE).help(
|
||||
"affect the referent of each symbolic link (this is the default), rather than the symbolic link itself",
|
||||
))
|
||||
.arg(
|
||||
Arg::with_name(OPT_NO_DEREFERENCE)
|
||||
.short("h")
|
||||
.long(OPT_NO_DEREFERENCE)
|
||||
.help(
|
||||
"affect symbolic links instead of any referenced file (useful only on systems that can change the ownership of a symlink)",
|
||||
),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_FROM)
|
||||
.long(OPT_FROM)
|
||||
.help(
|
||||
"change the owner and/or group of each file only if its current owner and/or group match those specified here. Either may be omitted, in which case a match is not required for the omitted attribute",
|
||||
)
|
||||
.value_name("CURRENT_OWNER:CURRENT_GROUP"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_PRESERVE_ROOT)
|
||||
.long(OPT_PRESERVE_ROOT)
|
||||
.help("fail to operate recursively on '/'"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_NO_PRESERVE_ROOT)
|
||||
.long(OPT_NO_PRESERVE_ROOT)
|
||||
.help("do not treat '/' specially (the default)"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_QUIET)
|
||||
.long(OPT_QUIET)
|
||||
.help("suppress most error messages"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_RECURSIVE)
|
||||
.short("R")
|
||||
.long(OPT_RECURSIVE)
|
||||
.help("operate on files and directories recursively"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_REFERENCE)
|
||||
.long(OPT_REFERENCE)
|
||||
.help("use RFILE's owner and group rather than specifying OWNER:GROUP values")
|
||||
.value_name("RFILE")
|
||||
.min_values(1),
|
||||
)
|
||||
.arg(Arg::with_name(OPT_SILENT).short("f").long(OPT_SILENT))
|
||||
.arg(
|
||||
Arg::with_name(OPT_TRAVERSE)
|
||||
.short(OPT_TRAVERSE)
|
||||
.help("if a command line argument is a symbolic link to a directory, traverse it")
|
||||
.overrides_with_all(&[OPT_TRAVERSE_EVERY, OPT_NO_TRAVERSE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_TRAVERSE_EVERY)
|
||||
.short(OPT_TRAVERSE_EVERY)
|
||||
.help("traverse every symbolic link to a directory encountered")
|
||||
.overrides_with_all(&[OPT_TRAVERSE, OPT_NO_TRAVERSE]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_NO_TRAVERSE)
|
||||
.short(OPT_NO_TRAVERSE)
|
||||
.help("do not traverse any symbolic links (default)")
|
||||
.overrides_with_all(&[OPT_TRAVERSE, OPT_TRAVERSE_EVERY]),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(OPT_VERBOSE)
|
||||
.long(OPT_VERBOSE)
|
||||
.help("output a diagnostic for every file processed"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ARG_OWNER)
|
||||
.multiple(false)
|
||||
.takes_value(true)
|
||||
.required(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name(ARG_FILES)
|
||||
.multiple(true)
|
||||
.takes_value(true)
|
||||
.required(true)
|
||||
.min_values(1),
|
||||
)
|
||||
.get_matches_from(args);
|
||||
|
||||
.optflag("R",
|
||||
"recursive",
|
||||
"operate on files and directories recursively")
|
||||
.optflag("H",
|
||||
"",
|
||||
"if a command line argument is a symbolic link to a directory, traverse it")
|
||||
.optflag("L",
|
||||
"",
|
||||
"traverse every symbolic link to a directory encountered")
|
||||
.optflag("P", "", "do not traverse any symbolic links (default)");
|
||||
/* First arg is the owner/group */
|
||||
let owner = matches.value_of(ARG_OWNER).unwrap();
|
||||
|
||||
let mut bit_flag = FTS_PHYSICAL;
|
||||
let mut preserve_root = false;
|
||||
let mut derefer = -1;
|
||||
let flags: &[char] = &['H', 'L', 'P'];
|
||||
for opt in &args {
|
||||
match opt.as_str() {
|
||||
// If more than one is specified, only the final one takes effect.
|
||||
s if s.contains(flags) => {
|
||||
if let Some(idx) = s.rfind(flags) {
|
||||
match s.chars().nth(idx).unwrap() {
|
||||
'H' => bit_flag = FTS_COMFOLLOW | FTS_PHYSICAL,
|
||||
'L' => bit_flag = FTS_LOGICAL,
|
||||
'P' => bit_flag = FTS_PHYSICAL,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
"--no-preserve-root" => preserve_root = false,
|
||||
"--preserve-root" => preserve_root = true,
|
||||
"--dereference" => derefer = 1,
|
||||
"--no-dereference" => derefer = 0,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
/* Then the list of files */
|
||||
let files: Vec<String> = matches
|
||||
.values_of(ARG_FILES)
|
||||
.map(|v| v.map(ToString::to_string).collect())
|
||||
.unwrap_or_default();
|
||||
|
||||
let matches = opts.parse(args);
|
||||
let recursive = matches.opt_present("recursive");
|
||||
let preserve_root = matches.is_present(OPT_PRESERVE_ROOT);
|
||||
|
||||
let mut derefer = if matches.is_present(OPT_NO_DEREFERENCE) {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
let mut bit_flag = if matches.is_present(OPT_TRAVERSE) {
|
||||
FTS_COMFOLLOW | FTS_PHYSICAL
|
||||
} else if matches.is_present(OPT_TRAVERSE_EVERY) {
|
||||
FTS_LOGICAL
|
||||
} else {
|
||||
FTS_PHYSICAL
|
||||
};
|
||||
|
||||
let recursive = matches.is_present(OPT_RECURSIVE);
|
||||
if recursive {
|
||||
if bit_flag == FTS_PHYSICAL {
|
||||
if derefer == 1 {
|
||||
|
@ -111,17 +202,17 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
bit_flag = FTS_PHYSICAL;
|
||||
}
|
||||
|
||||
let verbosity = if matches.opt_present("changes") {
|
||||
let verbosity = if matches.is_present(OPT_CHANGES) {
|
||||
Verbosity::Changes
|
||||
} else if matches.opt_present("silent") || matches.opt_present("quiet") {
|
||||
} else if matches.is_present(OPT_SILENT) || matches.is_present(OPT_QUIET) {
|
||||
Verbosity::Silent
|
||||
} else if matches.opt_present("verbose") {
|
||||
} else if matches.is_present(OPT_VERBOSE) {
|
||||
Verbosity::Verbose
|
||||
} else {
|
||||
Verbosity::Normal
|
||||
};
|
||||
|
||||
let filter = if let Some(spec) = matches.opt_str("from") {
|
||||
let filter = if let Some(spec) = matches.value_of(OPT_FROM) {
|
||||
match parse_spec(&spec) {
|
||||
Ok((Some(uid), None)) => IfFrom::User(uid),
|
||||
Ok((None, Some(gid))) => IfFrom::Group(gid),
|
||||
|
@ -136,18 +227,9 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
IfFrom::All
|
||||
};
|
||||
|
||||
if matches.free.is_empty() {
|
||||
show_usage_error!("missing operand");
|
||||
return 1;
|
||||
} else if matches.free.len() < 2 && !matches.opt_present("reference") {
|
||||
show_usage_error!("missing operand after ‘{}’", matches.free[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
let mut files;
|
||||
let dest_uid: Option<u32>;
|
||||
let dest_gid: Option<u32>;
|
||||
if let Some(file) = matches.opt_str("reference") {
|
||||
if let Some(file) = matches.value_of(OPT_REFERENCE) {
|
||||
match fs::metadata(&file) {
|
||||
Ok(meta) => {
|
||||
dest_gid = Some(meta.gid());
|
||||
|
@ -158,9 +240,8 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
files = matches.free;
|
||||
} else {
|
||||
match parse_spec(&matches.free[0]) {
|
||||
match parse_spec(&owner) {
|
||||
Ok((u, g)) => {
|
||||
dest_uid = u;
|
||||
dest_gid = g;
|
||||
|
@ -170,8 +251,6 @@ pub fn uumain(args: impl uucore::Args) -> i32 {
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
files = matches.free;
|
||||
files.remove(0);
|
||||
}
|
||||
let executor = Chowner {
|
||||
bit_flag,
|
||||
|
@ -197,7 +276,7 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
|||
Ok((
|
||||
Some(match Passwd::locate(args[0]) {
|
||||
Ok(v) => v.uid(),
|
||||
_ => return Err(format!("invalid user: ‘{}’", spec)),
|
||||
_ => return Err(format!("invalid user: '{}'", spec)),
|
||||
}),
|
||||
None,
|
||||
))
|
||||
|
@ -206,18 +285,18 @@ fn parse_spec(spec: &str) -> Result<(Option<u32>, Option<u32>), String> {
|
|||
None,
|
||||
Some(match Group::locate(args[1]) {
|
||||
Ok(v) => v.gid(),
|
||||
_ => return Err(format!("invalid group: ‘{}’", spec)),
|
||||
_ => return Err(format!("invalid group: '{}'", spec)),
|
||||
}),
|
||||
))
|
||||
} else if usr_grp {
|
||||
Ok((
|
||||
Some(match Passwd::locate(args[0]) {
|
||||
Ok(v) => v.uid(),
|
||||
_ => return Err(format!("invalid user: ‘{}’", spec)),
|
||||
_ => return Err(format!("invalid user: '{}'", spec)),
|
||||
}),
|
||||
Some(match Group::locate(args[1]) {
|
||||
Ok(v) => v.gid(),
|
||||
_ => return Err(format!("invalid group: ‘{}’", spec)),
|
||||
_ => return Err(format!("invalid group: '{}'", spec)),
|
||||
}),
|
||||
))
|
||||
} else {
|
||||
|
|
|
@ -46,3 +46,336 @@ mod test_passgrp {
|
|||
fn test_invalid_option() {
|
||||
new_ucmd!().arg("-w").arg("-q").arg("/").fails();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_myself() {
|
||||
// test chown username file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("results {}", result.stdout);
|
||||
let username = result.stdout.trim_end();
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(username).arg(file1).run();
|
||||
println!("results stdout {}", result.stdout);
|
||||
println!("results stderr {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_myself_second() {
|
||||
// test chown username: file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("results {}", result.stdout);
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let result = ucmd
|
||||
.arg(result.stdout.trim_end().to_owned() + ":")
|
||||
.arg(file1)
|
||||
.run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_myself_group() {
|
||||
// test chown username:group file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("user name = {}", result.stdout);
|
||||
let username = result.stdout.trim_end();
|
||||
|
||||
let result = scene.cmd("id").arg("-gn").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("group name = {}", result.stdout);
|
||||
let group = result.stdout.trim_end();
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
let perm = username.to_owned() + ":" + group;
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("chown: invalid group:") {
|
||||
// With some Ubuntu into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_only_group() {
|
||||
// test chown :group file.txt
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("results {}", result.stdout);
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
let perm = ":".to_owned() + result.stdout.trim_end();
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
|
||||
if is_ci() && result.stderr.contains("Operation not permitted") {
|
||||
// With ubuntu with old Rust in the CI, we can get an error
|
||||
return;
|
||||
}
|
||||
if is_ci() && result.stderr.contains("chown: invalid group:") {
|
||||
// With mac into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_only_id() {
|
||||
// test chown 1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id = String::from(result.stdout.trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let result = ucmd.arg(id).arg(file1).run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("chown: invalid user:") {
|
||||
// With some Ubuntu into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_only_group_id() {
|
||||
// test chown :1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-g").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id = String::from(result.stdout.trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let perm = ":".to_owned() + &id;
|
||||
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
|
||||
println!("result.stdout = {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("chown: invalid group:") {
|
||||
// With mac into the CI, we can get this answer
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_both_id() {
|
||||
// test chown 1111:1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id_user = String::from(result.stdout.trim());
|
||||
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-g").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id_group = String::from(result.stdout.trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let perm = id_user + &":".to_owned() + &id_group;
|
||||
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_both_mix() {
|
||||
// test chown 1111:1111 file.txt
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-u").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let id_user = String::from(result.stdout.trim());
|
||||
|
||||
let result = TestScenario::new("id").ucmd_keepenv().arg("-gn").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let group_name = String::from(result.stdout.trim());
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
let file1 = "test_install_target_dir_file_a1";
|
||||
|
||||
at.touch(file1);
|
||||
let perm = id_user + &":".to_owned() + &group_name;
|
||||
|
||||
let result = ucmd.arg(perm).arg(file1).run();
|
||||
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_chown_recursive() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let username = result.stdout.trim_end();
|
||||
|
||||
let (at, mut ucmd) = at_and_ucmd!();
|
||||
at.mkdir("a");
|
||||
at.mkdir("a/b");
|
||||
at.mkdir("a/b/c");
|
||||
at.mkdir("z");
|
||||
at.touch(&at.plus_as_string("a/a"));
|
||||
at.touch(&at.plus_as_string("a/b/b"));
|
||||
at.touch(&at.plus_as_string("a/b/c/c"));
|
||||
at.touch(&at.plus_as_string("z/y"));
|
||||
|
||||
let result = ucmd
|
||||
.arg("-R")
|
||||
.arg("--verbose")
|
||||
.arg(username)
|
||||
.arg("a")
|
||||
.arg("z")
|
||||
.run();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
assert!(result.stdout.contains("ownership of a/a retained as"));
|
||||
assert!(result.success);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_root_preserve() {
|
||||
let scene = TestScenario::new(util_name!());
|
||||
let result = scene.cmd("whoami").run();
|
||||
if is_ci() && result.stderr.contains("No such user/group") {
|
||||
// In the CI, some server are failing to return whoami.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
let username = result.stdout.trim_end();
|
||||
|
||||
let result = new_ucmd!()
|
||||
.arg("--preserve-root")
|
||||
.arg("-R")
|
||||
.arg(username)
|
||||
.arg("/")
|
||||
.fails();
|
||||
println!("result.stdout {}", result.stdout);
|
||||
println!("result.stderr = {}", result.stderr);
|
||||
if is_ci() && result.stderr.contains("invalid user") {
|
||||
// In the CI, some server are failing to return id.
|
||||
// As seems to be a configuration issue, ignoring it
|
||||
return;
|
||||
}
|
||||
assert!(result
|
||||
.stderr
|
||||
.contains("chown: it is dangerous to operate recursively"));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue