Merge pull request #2240 from jhscheer/macos_test_coreutils

who/stat/pinky: adjust tests to be compatible with running on macOS
This commit is contained in:
Sylvestre Ledru 2021-05-22 12:39:05 +02:00 committed by GitHub
commit 4d3be19de3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 217 additions and 196 deletions

View file

@ -235,6 +235,9 @@ jobs:
arm-unknown-linux-gnueabihf) sudo apt-get -y update ; sudo apt-get -y install gcc-arm-linux-gnueabihf ;;
aarch64-unknown-linux-gnu) sudo apt-get -y update ; sudo apt-get -y install gcc-aarch64-linux-gnu ;;
esac
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
- name: Initialize workflow variables
id: vars
shell: bash
@ -486,6 +489,13 @@ jobs:
- { os: windows-latest , features: windows }
steps:
- uses: actions/checkout@v1
- name: Install/setup prerequisites
shell: bash
run: |
## install/setup prerequisites
case '${{ matrix.job.os }}' in
macos-latest) brew install coreutils ;; # needed for testing
esac
# - name: Reattach HEAD ## may be needed for accurate code coverage info
# run: git checkout ${{ github.head_ref }}
- name: Initialize workflow variables

View file

@ -657,7 +657,7 @@ impl Stater {
dst.to_string_lossy()
);
} else {
arg = format!("`{}'", file);
arg = file.to_string();
}
otype = OutputType::Str;
}

View file

@ -46,9 +46,10 @@ fn get_usage() -> String {
}
fn get_long_usage() -> String {
String::from(
"If FILE is not specified, use /var/run/utmp. /var/log/wtmp as FILE is common.\n\
If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.",
format!(
"If FILE is not specified, use {}. /var/log/wtmp as FILE is common.\n\
If ARG1 ARG2 given, -m presumed: 'am i' or 'mom likes' are usual.",
utmpx::DEFAULT_FILE,
)
}

View file

@ -54,6 +54,8 @@ pub unsafe extern "C" fn utmpxname(_file: *const libc::c_char) -> libc::c_int {
0
}
pub use crate::*; // import macros from `../../macros.rs`
// In case the c_char array doesn't end with NULL
macro_rules! chars2string {
($arr:expr) => {
@ -240,7 +242,7 @@ impl UtmpxIter {
utmpxname(cstr.as_ptr())
};
if res != 0 {
println!("Warning: {}", IOError::last_os_error());
show_warning!("utmpxname: {}", IOError::last_os_error());
}
unsafe {
setutxent();

View file

@ -20,42 +20,37 @@ fn test_long_format() {
let ulogin = "root";
let pw: Passwd = Passwd::locate(ulogin).unwrap();
let real_name = pw.user_info().replace("&", &pw.name().capitalize());
new_ucmd!().arg("-l").arg(ulogin).run().stdout_is(format!(
"Login name: {:<28}In real life: {}\nDirectory: {:<29}Shell: {}\n\n",
ulogin,
real_name,
pw.user_dir(),
pw.user_shell()
));
new_ucmd!()
.arg("-l")
.arg(ulogin)
.succeeds()
.stdout_is(format!(
"Login name: {:<28}In real life: {}\nDirectory: {:<29}Shell: {}\n\n",
ulogin,
real_name,
pw.user_dir(),
pw.user_shell()
));
new_ucmd!().arg("-lb").arg(ulogin).run().stdout_is(format!(
"Login name: {:<28}In real life: {1}\n\n",
ulogin, real_name
));
new_ucmd!()
.arg("-lb")
.arg(ulogin)
.succeeds()
.stdout_is(format!(
"Login name: {:<28}In real life: {1}\n\n",
ulogin, real_name
));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_long_format_multiple_users() {
let scene = TestScenario::new(util_name!());
let args = ["-l", "root", "root", "root"];
let expected = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.arg("-l")
.arg("root")
.arg("root")
.arg("root")
.succeeds();
scene
.ucmd()
.arg("-l")
.arg("root")
.arg("root")
.arg("root")
new_ucmd!()
.args(&args)
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
}
#[test]
@ -64,63 +59,53 @@ fn test_long_format_wo_user() {
new_ucmd!().arg("-l").fails().code_is(1);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short_format_i() {
// allow whitespace variation
// * minor whitespace differences occur between platform built-in outputs; specifically, the number of trailing TABs may be variant
let args = ["-i"];
let actual = TestScenario::new(util_name!())
.ucmd()
.args(&args)
.succeeds()
.stdout_move_str();
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short_format_q() {
// allow whitespace variation
// * minor whitespace differences occur between platform built-in outputs; specifically, the number of trailing TABs may be variant
let args = ["-q"];
let actual = TestScenario::new(util_name!())
.ucmd()
.args(&args)
.succeeds()
.stdout_move_str();
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_no_flag() {
let scene = TestScenario::new(util_name!());
let actual = scene.ucmd().succeeds().stdout_move_str();
let expect = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.succeeds()
.stdout_move_str();
let actual = new_ucmd!().succeeds().stdout_move_str();
let expect = expected_result(&[]);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
let v_expect: Vec<&str> = expect.split_whitespace().collect();
assert_eq!(v_actual, v_expect);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn expected_result(args: &[&str]) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.args(args)
.run()
.succeeds()
.stdout_move_str()
}

View file

@ -96,10 +96,10 @@ fn test_invalid_option() {
new_ucmd!().arg("-w").arg("-q").arg("/").fails();
}
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
const NORMAL_FMTSTR: &'static str =
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s %u %U %x %X %y %Y %z %Z"; // avoid "%w %W" (birth/creation) due to `stat` limitations and linux kernel & rust version capability variations
#[cfg(target_os = "linux")]
#[cfg(any(target_os = "linux"))]
const DEV_FMTSTR: &'static str =
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (%t/%T) %u %U %w %W %x %X %y %Y %z %Z";
#[cfg(target_os = "linux")]
@ -125,8 +125,8 @@ fn test_fs_format() {
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_terse_normal_format() {
// note: contains birth/creation date which increases test fragility
// * results may vary due to built-in `stat` limitations as well as linux kernel and rust version capability variations
@ -156,10 +156,10 @@ fn test_terse_normal_format() {
);
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_format_created_time() {
let args = ["-c", "%w", "/boot"];
let args = ["-c", "%w", "/bin"];
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
@ -180,10 +180,10 @@ fn test_format_created_time() {
);
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_format_created_seconds() {
let args = ["-c", "%W", "/boot"];
let args = ["-c", "%W", "/bin"];
let actual = new_ucmd!().args(&args).succeeds().stdout_move_str();
let expect = expected_result(&args);
println!("actual: {:?}", actual);
@ -204,79 +204,97 @@ fn test_format_created_seconds() {
);
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_normal_format() {
let args = ["-c", NORMAL_FMTSTR, "/boot"];
let args = ["-c", NORMAL_FMTSTR, "/bin"];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_follow_symlink() {
let args = ["-L", "-c", DEV_FMTSTR, "/dev/cdrom"];
new_ucmd!()
.args(&args)
.run()
.stdout_is(expected_result(&args));
fn test_symlinks() {
let scene = TestScenario::new(util_name!());
let at = &scene.fixtures;
let mut tested: bool = false;
// arbitrarily chosen symlinks with hope that the CI environment provides at least one of them
for file in vec![
"/bin/sh",
"/bin/sudoedit",
"/usr/bin/ex",
"/etc/localtime",
"/etc/aliases",
] {
if at.file_exists(file) && at.is_symlink(file) {
tested = true;
let args = ["-c", NORMAL_FMTSTR, file];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
// -L, --dereference follow links
let args = ["-L", "-c", NORMAL_FMTSTR, file];
scene
.ucmd()
.args(&args)
.succeeds()
.stdout_is(expected_result(&args));
}
}
if !tested {
panic!("No symlink found to test in this environment");
}
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_symlink() {
let args = ["-c", DEV_FMTSTR, "/dev/cdrom"];
new_ucmd!()
.args(&args)
.run()
.stdout_is(expected_result(&args));
}
#[test]
#[cfg(target_os = "linux")]
fn test_char() {
let args = ["-c", DEV_FMTSTR, "/dev/pts/ptmx"];
// TODO: "(%t) (%x) (%w)" deviate from GNU stat for `character special file` on macOS
// Diff < left / right > :
// <"(f0000) (2021-05-20 23:08:03.442555000 +0200) (1970-01-01 01:00:00.000000000 +0100)\n"
// >"(f) (2021-05-20 23:08:03.455598000 +0200) (-)\n"
let args = [
"-c",
#[cfg(target_os = "linux")]
DEV_FMTSTR,
#[cfg(target_os = "linux")]
"/dev/pts/ptmx",
#[cfg(any(target_vendor = "apple"))]
"%a %A %b %B %d %D %f %F %g %G %h %i %m %n %o %s (/%T) %u %U %W %X %y %Y %z %Z",
#[cfg(any(target_vendor = "apple"))]
"/dev/ptmx",
];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_vendor = "apple"))]
#[test]
#[cfg(target_os = "linux")]
fn test_multi_files() {
let args = [
"-c",
NORMAL_FMTSTR,
"/dev",
"/usr/lib",
#[cfg(target_os = "linux")]
"/etc/fstab",
"/var",
];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(any(target_os = "linux", target_os = "freebsd", target_vendor = "apple"))]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_one_file() {
let (at, mut ucmd) = at_and_ucmd!();
let file = "TEST_FILE.mp4";
at.touch(file);
ucmd.arg(file)
.succeeds()
.stdout_contains(format!("File: `{}'", file))
.stdout_contains(format!("Size: 0"))
.stdout_contains(format!("Access: (0644/-rw-r--r--)"));
}
#[test]
#[cfg(target_os = "linux")]
fn test_printf() {
let args = [
"--printf=123%-# 15q\\r\\\"\\\\\\a\\b\\e\\f\\v%+020.23m\\x12\\167\\132\\112\\n",
@ -284,16 +302,21 @@ fn test_printf() {
];
new_ucmd!()
.args(&args)
.run()
.succeeds()
.stdout_is(expected_result(&args));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn expected_result(args: &[&str]) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.args(args)
.run()
.succeeds()
.stdout_move_str()
}

View file

@ -1,28 +1,28 @@
use crate::common::util::*;
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_count() {
for opt in vec!["-q", "--count"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_boot() {
for opt in vec!["-b", "--boot"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_heading() {
for opt in vec!["-H", "--heading"] {
@ -30,7 +30,7 @@ fn test_heading() {
// * 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).succeeds().stdout_move_str();
let expect = expected_result(opt);
let expect = expected_result(&[opt]);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
let v_actual: Vec<&str> = actual.split_whitespace().collect();
@ -39,205 +39,205 @@ fn test_heading() {
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_short() {
for opt in vec!["-s", "--short"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_login() {
for opt in vec!["-l", "--login"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_m() {
for opt in vec!["-m"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_process() {
for opt in vec!["-p", "--process"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_runlevel() {
for opt in vec!["-r", "--runlevel"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_time() {
for opt in vec!["-t", "--time"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_mesg() {
for opt in vec!["-w", "-T", "--users", "--message", "--writable"] {
// -T, -w, --mesg
// add user's message status as +, - or ?
// --message
// same as -T
// --writable
// same as -T
for opt in vec!["-T", "-w", "--mesg", "--message", "--writable"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[test]
fn test_arg1_arg2() {
let scene = TestScenario::new(util_name!());
let args = ["am", "i"];
let expected = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.arg("am")
.arg("i")
.succeeds();
scene
.ucmd()
.arg("am")
.arg("i")
new_ucmd!()
.args(&args)
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
}
#[test]
fn test_too_many_args() {
let expected =
const EXPECTED: &str =
"error: The value 'u' was provided to '<FILE>...', but it wasn't expecting any more values";
new_ucmd!()
.arg("am")
.arg("i")
.arg("u")
.fails()
.stderr_contains(expected);
let args = ["am", "i", "u"];
new_ucmd!().args(&args).fails().stderr_contains(EXPECTED);
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_users() {
for opt in vec!["-u", "--users"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
let actual = new_ucmd!().arg(opt).succeeds().stdout_move_str();
let expect = expected_result(&[opt]);
println!("actual: {:?}", actual);
println!("expect: {:?}", expect);
let mut v_actual: Vec<&str> = actual.split_whitespace().collect();
let mut v_expect: Vec<&str> = expect.split_whitespace().collect();
// TODO: `--users` differs from GNU's output on manOS running in CI
// Diff < left / right > :
// <"runner console 2021-05-20 22:03 00:08 196\n"
// >"runner console 2021-05-20 22:03 old 196\n"
if is_ci() && cfg!(target_os = "macos") {
v_actual.remove(4);
v_expect.remove(4);
}
assert_eq!(v_actual, v_expect);
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_lookup() {
for opt in vec!["--lookup"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_dead() {
for opt in vec!["-d", "--dead"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_all_separately() {
if is_ci() && cfg!(target_os = "macos") {
// TODO: fix `-u`, see: test_users
return;
}
// -a, --all same as -b -d --login -p -r -t -T -u
let args = ["-b", "-d", "--login", "-p", "-r", "-t", "-T", "-u"];
let scene = TestScenario::new(util_name!());
let expected = scene
.cmd_keepenv(util_name!())
.env("LANGUAGE", "C")
.arg("-b")
.arg("-d")
.arg("--login")
.arg("-p")
.arg("-r")
.arg("-t")
.arg("-T")
.arg("-u")
.succeeds();
scene
.ucmd()
.arg("-b")
.arg("-d")
.arg("--login")
.arg("-p")
.arg("-r")
.arg("-t")
.arg("-T")
.arg("-u")
.args(&args)
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
scene
.ucmd()
.arg("--all")
.succeeds()
.stdout_is(expected.stdout_str());
.stdout_is(expected_result(&args));
}
#[cfg(target_os = "linux")]
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
#[test]
fn test_all() {
if is_ci() && cfg!(target_os = "macos") {
// TODO: fix `-u`, see: test_users
return;
}
for opt in vec!["-a", "--all"] {
new_ucmd!()
.arg(opt)
.succeeds()
.stdout_is(expected_result(opt));
.stdout_is(expected_result(&[opt]));
}
}
#[cfg(target_os = "linux")]
fn expected_result(arg: &str) -> String {
TestScenario::new(util_name!())
.cmd_keepenv(util_name!())
#[cfg(any(target_vendor = "apple", target_os = "linux"))]
fn expected_result(args: &[&str]) -> String {
#[cfg(target_os = "linux")]
let util_name = util_name!();
#[cfg(target_vendor = "apple")]
let util_name = format!("g{}", util_name!());
TestScenario::new(&util_name)
.cmd_keepenv(util_name)
.env("LANGUAGE", "C")
.args(&[arg])
.args(args)
.succeeds()
.stdout_move_str()
}