diff options
author | Anirban Halder <anirbanhalder752@gmail.com> | 2024-06-22 16:36:03 +0530 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-22 13:06:03 +0200 |
commit | 2774274cc24b775c60edac2975b2b8b6e5acd269 (patch) | |
tree | 5b9fc1629db4bf4ce8000dbcdb27df9202ee9a56 /tests/by-util | |
parent | 32c5d23f914bed093075e81e013e7f4ac719beba (diff) |
``uptime``: Support files in uptime (#6400)
Diffstat (limited to 'tests/by-util')
-rw-r--r-- | tests/by-util/test_uptime.rs | 233 |
1 files changed, 233 insertions, 0 deletions
diff --git a/tests/by-util/test_uptime.rs b/tests/by-util/test_uptime.rs index 89e5567fb..382d5f2ef 100644 --- a/tests/by-util/test_uptime.rs +++ b/tests/by-util/test_uptime.rs @@ -2,8 +2,22 @@ // // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +// +// spell-checker:ignore bincode serde utmp runlevel testusr testx + use crate::common::util::TestScenario; + +#[cfg(not(target_os = "macos"))] +use bincode::serialize; use regex::Regex; +#[cfg(not(target_os = "macos"))] +use serde::Serialize; +#[cfg(not(target_os = "macos"))] +use serde_big_array::BigArray; +#[cfg(not(target_os = "macos"))] +use std::fs::File; +#[cfg(not(target_os = "macos"))] +use std::{io::Write, path::PathBuf}; #[test] fn test_invalid_arg() { @@ -22,6 +36,225 @@ fn test_uptime() { // Don't check for users as it doesn't show in some CI } +/// Checks for files without utmpx records for which boot time cannot be calculated +#[test] +#[cfg(not(any(target_os = "openbsd", target_os = "freebsd")))] +// Disabled for freebsd, since it doesn't use the utmpxname() sys call to change the default utmpx +// file that is accessed using getutxent() +fn test_uptime_for_file_without_utmpx_records() { + let (at, mut ucmd) = at_and_ucmd!(); + at.write("file1", "hello"); + + ucmd.arg(at.plus_as_string("file1")) + .fails() + .stderr_contains("uptime: couldn't get boot time") + .stdout_contains("up ???? days ??:??") + .stdout_contains("load average"); +} + +/// Checks whether uptime displays the correct stderr msg when its called with a fifo +#[test] +#[cfg(all(unix, feature = "cp"))] +fn test_uptime_with_fifo() { + // This test can go on forever in the CI in some cases, might need aborting + // Sometimes writing to the pipe is broken + let ts = TestScenario::new(util_name!()); + + let at = &ts.fixtures; + at.mkfifo("fifo1"); + + at.write("a", "hello"); + // Creating a child process to write to the fifo + let mut child = ts + .ccmd("cp") + .arg(at.plus_as_string("a")) + .arg(at.plus_as_string("fifo1")) + .run_no_wait(); + + ts.ucmd() + .arg("fifo1") + .fails() + .stderr_contains("uptime: couldn't get boot time: Illegal seek") + .stdout_contains("up ???? days ??:??") + .stdout_contains("load average"); + + child.kill(); +} + +#[test] +#[cfg(not(any(target_os = "openbsd", target_os = "freebsd")))] +fn test_uptime_with_non_existent_file() { + // Disabled for freebsd, since it doesn't use the utmpxname() sys call to change the default utmpx + // file that is accessed using getutxent() + let ts = TestScenario::new(util_name!()); + + ts.ucmd() + .arg("file1") + .fails() + .stderr_contains("uptime: couldn't get boot time: No such file or directory") + .stdout_contains("up ???? days ??:??"); +} + +// TODO create a similar test for macos +// This will pass +#[test] +#[cfg(not(any(target_os = "openbsd", target_os = "macos")))] +fn test_uptime_with_file_containing_valid_boot_time_utmpx_record() { + // This test will pass for freebsd but we currently don't support changing the utmpx file for + // freebsd. + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + // Regex matches for "up 00::00" ,"up 12 days 00::00", the time can be any valid time and + // the days can be more than 1 digit or not there. This will match even if the amount of whitespace is + // wrong between the days and the time. + + let re = Regex::new(r"up [(\d){1,} days]*\d{1,2}:\d\d").unwrap(); + utmp(&at.plus("testx")); + ts.ucmd() + .arg("testx") + .succeeds() + .stdout_matches(&re) + .stdout_contains("load average"); + + // Helper function to create byte sequences + fn slice_32(slice: &[u8]) -> [i8; 32] { + let mut arr: [i8; 32] = [0; 32]; + + for (i, val) in slice.iter().enumerate() { + arr[i] = *val as i8; + } + arr + } + // Creates a file utmp records of three different types including a valid BOOT_TIME entry + fn utmp(path: &PathBuf) { + // Definitions of our utmpx structs + const BOOT_TIME: i32 = 2; + const RUN_LVL: i32 = 1; + const USER_PROCESS: i32 = 7; + #[derive(Serialize)] + #[repr(C)] + pub struct TimeVal { + pub tv_sec: i32, + pub tv_usec: i32, + } + + #[derive(Serialize)] + #[repr(C)] + pub struct ExitStatus { + e_termination: i16, + e_exit: i16, + } + #[derive(Serialize)] + #[repr(C, align(4))] + pub struct Utmp { + pub ut_type: i32, + pub ut_pid: i32, + pub ut_line: [i8; 32], + pub ut_id: [i8; 4], + + pub ut_user: [i8; 32], + #[serde(with = "BigArray")] + pub ut_host: [i8; 256], + pub ut_exit: ExitStatus, + pub ut_session: i32, + pub ut_tv: TimeVal, + + pub ut_addr_v6: [i32; 4], + glibc_reserved: [i8; 20], + } + + let utmp = Utmp { + ut_type: BOOT_TIME, + ut_pid: 0, + ut_line: slice_32("~".as_bytes()), + ut_id: [126, 126, 0, 0], + ut_user: slice_32("reboot".as_bytes()), + ut_host: [0; 256], + ut_exit: ExitStatus { + e_termination: 0, + e_exit: 0, + }, + ut_session: 0, + ut_tv: TimeVal { + tv_sec: 1716371201, + tv_usec: 290913, + }, + ut_addr_v6: [127, 0, 0, 1], + glibc_reserved: [0; 20], + }; + let utmp1 = Utmp { + ut_type: RUN_LVL, + ut_pid: std::process::id() as i32, + ut_line: slice_32("~".as_bytes()), + ut_id: [126, 126, 0, 0], + ut_user: slice_32("runlevel".as_bytes()), + ut_host: [0; 256], + ut_exit: ExitStatus { + e_termination: 0, + e_exit: 0, + }, + ut_session: 0, + ut_tv: TimeVal { + tv_sec: 1716371209, + tv_usec: 162250, + }, + ut_addr_v6: [0, 0, 0, 0], + glibc_reserved: [0; 20], + }; + let utmp2 = Utmp { + ut_type: USER_PROCESS, + ut_pid: std::process::id() as i32, + ut_line: slice_32(":1".as_bytes()), + ut_id: [126, 126, 0, 0], + ut_user: slice_32("testusr".as_bytes()), + ut_host: [0; 256], + ut_exit: ExitStatus { + e_termination: 0, + e_exit: 0, + }, + ut_session: 0, + ut_tv: TimeVal { + tv_sec: 1716371283, + tv_usec: 858764, + }, + ut_addr_v6: [0, 0, 0, 0], + glibc_reserved: [0; 20], + }; + + let mut buf = serialize(&utmp).unwrap(); + buf.append(&mut serialize(&utmp1).unwrap()); + buf.append(&mut serialize(&utmp2).unwrap()); + let mut f = File::create(path).unwrap(); + f.write_all(&buf).unwrap(); + } +} + +#[test] +#[cfg(not(target_os = "openbsd"))] +fn test_uptime_with_extra_argument() { + let ts = TestScenario::new(util_name!()); + + ts.ucmd() + .arg("a") + .arg("b") + .fails() + .stderr_contains("extra operand 'b'"); +} +/// Checks whether uptime displays the correct stderr msg when its called with a directory +#[test] +#[cfg(not(target_os = "openbsd"))] +fn test_uptime_with_dir() { + let ts = TestScenario::new(util_name!()); + let at = &ts.fixtures; + at.mkdir("dir1"); + + ts.ucmd() + .arg("dir1") + .fails() + .stderr_contains("uptime: couldn't get boot time: Is a directory") + .stdout_contains("up ???? days ??:??"); +} + #[test] #[cfg(not(target_os = "openbsd"))] fn test_uptime_since() { |