From 39aa5312edd768f88a8307adcfc339ae5f20c41a Mon Sep 17 00:00:00 2001 From: Jan Scheer Date: Wed, 16 Jun 2021 20:45:46 +0200 Subject: [PATCH] id/tests: skip tests for multiple_user feature if there's not at least coreutils `id` version 8.31 in `$PATH` --- .github/workflows/GNU.yml | 9 +--- src/uu/id/src/id.rs | 10 ++-- tests/by-util/test_id.rs | 111 +++++++++++++++++++++++--------------- 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/.github/workflows/GNU.yml b/.github/workflows/GNU.yml index 1202de87f..1f9250900 100644 --- a/.github/workflows/GNU.yml +++ b/.github/workflows/GNU.yml @@ -45,14 +45,7 @@ jobs: - name: Run GNU tests shell: bash run: | - # bash uutils/util/run-gnu-test.sh - bash uutils/util/run-gnu-test.sh tests/id/context.sh # TODO: remove after debugging - sudo bash uutils/util/run-gnu-test.sh tests/id/setgid.sh # TODO: remove after debugging - bash uutils/util/run-gnu-test.sh tests/id/smack.sh # TODO: remove after debugging - bash uutils/util/run-gnu-test.sh tests/id/uid.sh # TODO: remove after debugging - bash uutils/util/run-gnu-test.sh tests/id/zero.sh # TODO: remove after debugging - bash uutils/util/run-gnu-test.sh tests/id/no-context.sh # TODO: remove after debugging - bash uutils/util/run-gnu-test.sh tests/id/gnu-zero-uids.sh # todo: remove after debugging + bash uutils/util/run-gnu-test.sh - name: Extract tests info shell: bash run: | diff --git a/src/uu/id/src/id.rs b/src/uu/id/src/id.rs index 35f641b3f..9037745eb 100644 --- a/src/uu/id/src/id.rs +++ b/src/uu/id/src/id.rs @@ -15,13 +15,15 @@ // * This was partially rewritten in order for stdout/stderr/exit_code // to be conform with GNU coreutils (8.32) testsuite for `id`. // -// * This passes GNU's coreutils Testsuite (8.32.161-370c2-dirty) +// * This supports multiple users (a feature that was introduced in coreutils 8.31) +// +// * This passes GNU's coreutils Testsuite (8.32) // for "tests/id/uid.sh" and "tests/id/zero/sh". // -// * Option '--zero' does not exist for BSD's `id`, therefor '--zero' is only +// * Option '--zero' does not exist for BSD's `id`, therefore '--zero' is only // allowed together with other options that are available on GNU's `id`. // -// * Help text based on BSD's `id`. +// * Help text based on BSD's `id` manpage and GNU's `id` manpage. // // spell-checker:ignore (ToDO) asid auditid auditinfo auid cstr egid emod euid getaudit getlogin gflag nflag pline rflag termid uflag gsflag zflag testsuite @@ -516,7 +518,7 @@ fn id_print(state: &State, groups: Vec) { .join(",") ); - // NOTE: placeholder ("-Z" is NotImplemented): + // NOTE: (SELinux NotImplemented) placeholder: // if !state.user_specified { // // print SElinux context (does not depend on "-Z") // print!(" context={}", get_selinux_contexts().join(":")); diff --git a/tests/by-util/test_id.rs b/tests/by-util/test_id.rs index 4c41e3131..b4b929a2c 100644 --- a/tests/by-util/test_id.rs +++ b/tests/by-util/test_id.rs @@ -1,20 +1,15 @@ use crate::common::util::*; -// Apparently some CI environments have configuration issues, e.g. with 'whoami' and 'id'. -// -// From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)" -// whoami: cannot find name for user ID 1001 -// id --name: cannot find name for user ID 1001 -// id --name: cannot find name for group ID 116 -// -// However, when running "id" from within "/bin/bash" it looks fine: -// id: "uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),101(systemd-journal)" -// whoami: "runner" -// - // spell-checker:ignore (ToDO) testsuite coreutil -const VERSION_EXPECTED: &str = "8.30"; // 8.32 +// These tests run the GNU coreutils `(g)id` binary in `$PATH` in order to gather reference values. +// If the `(g)id` in `$PATH` doesn't include a coreutils version string, +// or the version is too low, the test is skipped. + +// The reference version is 8.32. Here 8.30 was chosen because right now there's no +// ubuntu image for github action available with a higher version than 8.30. +const VERSION_EXPECTED: &str = "8.30"; // Version expected for the reference `id` in $PATH +const VERSION_MULTIPLE_USERS: &str = "8.31"; const UUTILS_WARNING: &str = "uutils-tests-warning"; const UUTILS_INFO: &str = "uutils-tests-info"; @@ -31,6 +26,17 @@ macro_rules! unwrap_or_return { } fn whoami() -> String { + // Apparently some CI environments have configuration issues, e.g. with 'whoami' and 'id'. + // + // From the Logs: "Build (ubuntu-18.04, x86_64-unknown-linux-gnu, feat_os_unix, use-cross)" + // whoami: cannot find name for user ID 1001 + // id --name: cannot find name for user ID 1001 + // id --name: cannot find name for group ID 116 + // + // However, when running "id" from within "/bin/bash" it looks fine: + // id: "uid=1001(runner) gid=118(docker) groups=118(docker),4(adm),101(systemd-journal)" + // whoami: "runner" + // Use environment variable to get current user instead of // invoking `whoami` and fall back to user "nobody" on error. std::env::var("USER").unwrap_or_else(|e| { @@ -48,12 +54,10 @@ fn test_id_no_specified_user() { #[cfg(target_os = "linux")] { - // NOTE: Strip 'context' part from exp_stdout (remove if SElinux gets added): - let context_offset = exp_result - .stdout_str() - .find(" context=") - .unwrap_or_else(|| _exp_stdout.len()); - _exp_stdout.replace_range(context_offset.., "\n"); + // NOTE: (SELinux NotImplemented) strip 'context' part from exp_stdout: + if let Some(context_offset) = exp_result.stdout_str().find(" context=") { + _exp_stdout.replace_range(context_offset.._exp_stdout.len() - 1, ""); + } } result @@ -126,10 +130,10 @@ fn test_id_single_user_non_existing() { let result = new_ucmd!().args(args).run(); let exp_result = unwrap_or_return!(expected_result(args)); + // It is unknown why on macOS (and possibly others?) `id` adds "Invalid argument". // coreutils 8.32: $ LC_ALL=C id foobar // macOS: stderr: "id: 'foobar': no such user: Invalid argument" // linux: stderr: "id: 'foobar': no such user" - // It is unkown why the output on macOS is different. result .stdout_is(exp_result.stdout_str()) .stderr_is(exp_result.stderr_str().replace(": Invalid argument", "")) @@ -202,6 +206,16 @@ fn test_id_password_style() { #[test] #[cfg(unix)] fn test_id_multiple_users() { + #[cfg(target_os = "linux")] + let util_name = util_name!(); + #[cfg(all(unix, not(target_os = "linux")))] + let util_name = &format!("g{}", util_name!()); + let version_check_string = check_coreutil_version(util_name, VERSION_MULTIPLE_USERS); + if version_check_string.starts_with(UUTILS_WARNING) { + println!("{}\ntest skipped", version_check_string); + return; + } + // Same typical users that GNU testsuite is using. let test_users = ["root", "man", "postfix", "sshd", &whoami()]; @@ -260,6 +274,16 @@ fn test_id_multiple_users() { #[test] #[cfg(unix)] fn test_id_multiple_users_non_existing() { + #[cfg(target_os = "linux")] + let util_name = util_name!(); + #[cfg(all(unix, not(target_os = "linux")))] + let util_name = &format!("g{}", util_name!()); + let version_check_string = check_coreutil_version(util_name, VERSION_MULTIPLE_USERS); + if version_check_string.starts_with(UUTILS_WARNING) { + println!("{}\ntest skipped", version_check_string); + return; + } + let test_users = [ "root", "hopefully_non_existing_username1", @@ -401,58 +425,61 @@ fn test_id_zero() { } } -#[allow(clippy::needless_borrow)] -#[cfg(unix)] -fn expected_result(args: &[&str]) -> Result { - // version for reference coreutil binary - - #[cfg(target_os = "linux")] - let util_name = util_name!(); - #[cfg(all(unix, not(target_os = "linux")))] - let util_name = format!("g{}", util_name!()); - - let scene = TestScenario::new(&util_name); +fn check_coreutil_version(util_name: &str, version_expected: &str) -> String { + // example: + // $ id --version | head -n 1 + // id (GNU coreutils) 8.32.162-4eda + let scene = TestScenario::new(util_name); let version_check = scene .cmd_keepenv(&util_name) .env("LANGUAGE", "C") .arg("--version") .run(); - let version_check_string: String = version_check + version_check .stdout_str() .split('\n') .collect::>() .get(0) .map_or_else( - || format!("{}: unexpected output format for reference coreutils '{} --version'", UUTILS_WARNING, util_name), + || format!("{}: unexpected output format for reference coreutil: '{} --version'", UUTILS_WARNING, util_name), |s| { - if s.contains(&format!("(GNU coreutils) {}", VERSION_EXPECTED)) { + if s.contains(&format!("(GNU coreutils) {}", version_expected)) { s.to_string() } else if s.contains("(GNU coreutils)") { - // example: id (GNU coreutils) 8.32.162-4eda let version_found = s.split_whitespace().last().unwrap()[..4].parse::().unwrap_or_default(); - let version_expected = VERSION_EXPECTED.parse::().unwrap_or_default(); + let version_expected = version_expected.parse::().unwrap_or_default(); if version_found > version_expected { - format!("{}: version for the reference coreutil '{}' is higher than expected; expected: {}, found: {}", UUTILS_INFO, util_name, VERSION_EXPECTED, version_found) + format!("{}: version for the reference coreutil '{}' is higher than expected; expected: {}, found: {}", UUTILS_INFO, util_name, version_expected, version_found) } else { - format!("{}: version for the reference coreutil '{}' does not match; expected: {}, found: {}", UUTILS_WARNING, util_name, VERSION_EXPECTED, version_found) } + format!("{}: version for the reference coreutil '{}' does not match; expected: {}, found: {}", UUTILS_WARNING, util_name, version_expected, version_found) } } else { format!("{}: no coreutils version string found for reference coreutils '{} --version'", UUTILS_WARNING, util_name) } }, - ); + ) +} + +#[allow(clippy::needless_borrow)] +#[cfg(unix)] +fn expected_result(args: &[&str]) -> Result { + #[cfg(target_os = "linux")] + let util_name = util_name!(); + #[cfg(all(unix, not(target_os = "linux")))] + let util_name = &format!("g{}", util_name!()); + + let version_check_string = check_coreutil_version(util_name, VERSION_EXPECTED); if version_check_string.starts_with(UUTILS_WARNING) { return Err(version_check_string); } println!("{}", version_check_string); + let scene = TestScenario::new(util_name); let result = scene - .cmd_keepenv(&util_name) + .cmd_keepenv(util_name) .env("LANGUAGE", "C") .args(args) .run(); - // #[cfg(all(unix, not(target_os = "linux")))] - // if cfg!(target_os = "macos") { let (stdout, stderr): (String, String) = if cfg!(target_os = "linux") { ( result.stdout_str().to_string(),