From 5efaa0bf32d2dd59c7585a6bae7800fd8380efc1 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sat, 21 Nov 2020 09:52:50 +0100 Subject: [PATCH] refactor(id) - move to clap and add more tests (#1628) --- Cargo.lock | 1 + src/uu/id/Cargo.toml | 1 + src/uu/id/src/id.rs | 118 ++++++++++++++++++++++++++++----------- tests/by-util/test_id.rs | 64 ++++++++++++++++++--- 4 files changed, 143 insertions(+), 41 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e9f8c63b5..a5cc30f3e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1669,6 +1669,7 @@ dependencies = [ name = "uu_id" version = "0.0.1" dependencies = [ + "clap 2.33.3 (registry+https://github.com/rust-lang/crates.io-index)", "uucore 0.0.4", "uucore_procs 0.0.4", ] diff --git a/src/uu/id/Cargo.toml b/src/uu/id/Cargo.toml index c5509e71d..6acae0274 100644 --- a/src/uu/id/Cargo.toml +++ b/src/uu/id/Cargo.toml @@ -15,6 +15,7 @@ edition = "2018" path = "src/id.rs" [dependencies] +clap = "2.33" uucore = { version=">=0.0.4", package="uucore", path="../../uucore", features=["entries", "process"] } uucore_procs = { version=">=0.0.4", package="uucore_procs", path="../../uucore_procs" } diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 252f0b395..c374de050 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -15,8 +15,12 @@ #![allow(non_camel_case_types)] #![allow(dead_code)] +extern crate clap; + #[macro_use] extern crate uucore; + +use clap::{App, Arg}; use std::ffi::CStr; use uucore::entries::{self, Group, Locate, Passwd}; pub use uucore::libc; @@ -68,50 +72,100 @@ mod audit { } } -static SYNTAX: &str = "[OPTION]... [USER]"; -static SUMMARY: &str = "Print user and group information for the specified USER,\n or (when USER omitted) for the current user."; +static ABOUT: &str = "Display user and group information for the specified USER,\n or (when USER omitted) for the current user."; +static VERSION: &str = env!("CARGO_PKG_VERSION"); + +static OPT_AUDIT: &str = "audit"; +static OPT_EFFECTIVE_USER: &str = "effective-user"; +static OPT_GROUP: &str = "group"; +static OPT_GROUPS: &str = "groups"; +static OPT_HUMAN_READABLE: &str = "human-readable"; +static OPT_NAME: &str = "name"; +static OPT_PASSWORD: &str = "password"; +static OPT_REAL_ID: &str = "real-id"; + +static ARG_USERS: &str = "users"; + +fn get_usage() -> String { + format!("{0} [OPTION]... [USER]", executable!()) +} pub fn uumain(args: impl uucore::Args) -> i32 { - let args = args.collect_str(); + let usage = get_usage(); - let mut opts = app!(SYNTAX, SUMMARY, ""); - opts.optflag( - "A", - "", - "Display the process audit (not available on Linux)", - ); - opts.optflag("G", "groups", "Display the different group IDs"); - opts.optflag("g", "group", "Display the effective group ID as a number"); - opts.optflag( - "n", - "", - "Display the name of the user or group ID for the -G, -g and -u options", - ); - opts.optflag("P", "", "Display the id as a password file entry"); - opts.optflag("p", "", "Make the output human-readable"); - opts.optflag("r", "", "Display the real ID for the -g and -u options"); - opts.optflag("u", "user", "Display the effective user ID as a number"); + let matches = App::new(executable!()) + .version(VERSION) + .about(ABOUT) + .usage(&usage[..]) + .arg( + Arg::with_name(OPT_AUDIT) + .short("A") + .help("Display the process audit (not available on Linux)"), + ) + .arg( + Arg::with_name(OPT_EFFECTIVE_USER) + .short("u") + .long("user") + .help("Display the effective user ID as a number"), + ) + .arg( + Arg::with_name(OPT_GROUP) + .short("g") + .long(OPT_GROUP) + .help("Display the effective group ID as a number"), + ) + .arg( + Arg::with_name(OPT_GROUPS) + .short("G") + .long(OPT_GROUPS) + .help("Display the different group IDs"), + ) + .arg( + Arg::with_name(OPT_HUMAN_READABLE) + .short("p") + .help("Make the output human-readable"), + ) + .arg( + Arg::with_name(OPT_NAME) + .short("n") + .help("Display the name of the user or group ID for the -G, -g and -u options"), + ) + .arg( + Arg::with_name(OPT_PASSWORD) + .short("P") + .help("Display the id as a password file entry"), + ) + .arg( + Arg::with_name(OPT_REAL_ID) + .short("r") + .help("Display the real ID for the -g and -u options"), + ) + .arg(Arg::with_name(ARG_USERS).multiple(true).takes_value(true)) + .get_matches_from(args); - let matches = opts.parse(args); + let users: Vec = matches + .values_of(ARG_USERS) + .map(|v| v.map(ToString::to_string).collect()) + .unwrap_or_default(); - if matches.opt_present("A") { + if matches.is_present(OPT_AUDIT) { auditid(); return 0; } - let possible_pw = if matches.free.is_empty() { + let possible_pw = if users.is_empty() { None } else { - match Passwd::locate(matches.free[0].as_str()) { + match Passwd::locate(users[0].as_str()) { Ok(p) => Some(p), - Err(_) => crash!(1, "No such user/group: {}", matches.free[0]), + Err(_) => crash!(1, "No such user/group: {}", users[0]), } }; - let nflag = matches.opt_present("n"); - let uflag = matches.opt_present("u"); - let gflag = matches.opt_present("g"); - let rflag = matches.opt_present("r"); + let nflag = matches.is_present(OPT_NAME); + let uflag = matches.is_present(OPT_EFFECTIVE_USER); + let gflag = matches.is_present(OPT_GROUP); + let rflag = matches.is_present(OPT_REAL_ID); if gflag { let id = possible_pw @@ -143,7 +197,7 @@ pub fn uumain(args: impl uucore::Args) -> i32 { return 0; } - if matches.opt_present("G") { + if matches.is_present(OPT_GROUPS) { println!( "{}", if nflag { @@ -167,12 +221,12 @@ pub fn uumain(args: impl uucore::Args) -> i32 { return 0; } - if matches.opt_present("P") { + if matches.is_present(OPT_PASSWORD) { pline(possible_pw.map(|v| v.uid())); return 0; }; - if matches.opt_present("p") { + if matches.is_present(OPT_HUMAN_READABLE) { pretty(possible_pw); return 0; } diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index 0541a2a89..116c73995 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -1,5 +1,17 @@ use crate::common::util::*; +fn return_whoami_username() -> String { + let scene = TestScenario::new("whoami"); + let result = scene.cmd("whoami").run(); + if is_ci() && result.stderr.contains("cannot find name for user ID") { + // In the CI, some server are failing to return whoami. + // As seems to be a configuration issue, ignoring it + return String::from(""); + } + + result.stdout.trim().to_string() +} + #[test] fn test_id() { let scene = TestScenario::new(util_name!()); @@ -29,18 +41,14 @@ fn test_id() { #[test] fn test_id_from_name() { - let mut scene = TestScenario::new("whoami"); - let result = scene.cmd("whoami").run(); - if is_ci() && result.stderr.contains("cannot find name for user ID") { - // In the CI, some server are failing to return whoami. - // As seems to be a configuration issue, ignoring it + let username = return_whoami_username(); + if username == "" { + // Sometimes, the CI is failing here return; } - let username = result.stdout.trim(); - - scene = TestScenario::new(util_name!()); - let result = scene.ucmd().arg(username).succeeds(); + let scene = TestScenario::new(util_name!()); + let result = scene.ucmd().arg(&username).succeeds(); println!("result.stdout = {}", result.stdout); println!("result.stderr = {}", result.stderr); assert!(result.success); @@ -139,3 +147,41 @@ fn test_id_user() { let s1 = String::from(result.stdout.trim()); assert!(s1.parse::().is_ok()); } + +#[test] +fn test_id_pretty_print() { + let username = return_whoami_username(); + if username == "" { + // Sometimes, the CI is failing here + return; + } + + let scene = TestScenario::new(util_name!()); + let result = scene.ucmd().arg("-p").run(); + if result.stdout.trim() == "" { + // Sometimes, the CI is failing here with + // old rust versions on Linux + return; + } + println!("result.stdout = {}", result.stdout); + println!("result.stderr = {}", result.stderr); + assert!(result.success); + assert!(result.stdout.contains(&username)); +} + +#[test] +fn test_id_password_style() { + let username = return_whoami_username(); + if username == "" { + // Sometimes, the CI is failing here + return; + } + let scene = TestScenario::new(util_name!()); + + let result = scene.ucmd().arg("-P").succeeds(); + + println!("result.stdout = {}", result.stdout); + println!("result.stderr = {}", result.stderr); + assert!(result.success); + assert!(result.stdout.starts_with(&username)); +}