From 76a56926d997b3693220b5d39ffcd017bcf0b0e0 Mon Sep 17 00:00:00 2001 From: Anirban Halder Date: Sat, 25 May 2024 14:09:08 +0530 Subject: [PATCH] Removed fixture files and changed to manual creation of utmpx entries in testing --- Cargo.lock | 29 ++- Cargo.toml | 10 +- src/uu/uptime/src/platform/unix.rs | 9 +- tests/by-util/test_uptime.rs | 166 ++++++++++++++---- .../uptime/validMultiplePartialRecords.txt | Bin 20584 -> 0 bytes .../fixtures/uptime/validMultipleRecords.txt | Bin 64128 -> 0 bytes tests/fixtures/uptime/validRecord.txt | Bin 1536 -> 0 bytes 7 files changed, 169 insertions(+), 45 deletions(-) delete mode 100644 tests/fixtures/uptime/validMultiplePartialRecords.txt delete mode 100644 tests/fixtures/uptime/validMultipleRecords.txt delete mode 100644 tests/fixtures/uptime/validRecord.txt diff --git a/Cargo.lock b/Cargo.lock index 5fd1ed75857..040158f8bf6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -156,6 +156,15 @@ dependencies = [ "compare", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.69.4" @@ -423,6 +432,7 @@ checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" name = "coreutils" version = "0.0.26" dependencies = [ + "bincode", "chrono", "clap", "clap_complete", @@ -444,6 +454,8 @@ dependencies = [ "rlimit", "rstest", "selinux", + "serde", + "serde-big-array", "sha1", "tempfile", "textwrap", @@ -2065,18 +2077,27 @@ checksum = "e25dfac463d778e353db5be2449d1cce89bd6fd23c9f1ea21310ce6e5a1b29c4" [[package]] name = "serde" -version = "1.0.193" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] +[[package]] +name = "serde-big-array" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11fc7cc2c76d73e0f27ee52abbd64eec84d46f370c88371120433196934e4b7f" +dependencies = [ + "serde", +] + [[package]] name = "serde_derive" -version = "1.0.193" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 229d6b9f57c..09d0c11ac89 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -490,8 +490,16 @@ sha1 = { version = "0.10.6", features = ["std"] } tempfile = { workspace = true } time = { workspace = true, features = ["local-offset"] } unindent = "0.2.3" -uucore = { workspace = true, features = ["entries", "process", "signals"] } +uucore = { workspace = true, features = [ + "entries", + "process", + "signals", + "utmpx", +] } +serde = { version = "1.0.202", features = ["derive"] } walkdir = { workspace = true } +bincode = { version = "1.3.3" } +serde-big-array = "0.5.1" hex-literal = "0.4.1" rstest = { workspace = true } diff --git a/src/uu/uptime/src/platform/unix.rs b/src/uu/uptime/src/platform/unix.rs index 7df34eb1a30..f8fc9eb4da4 100644 --- a/src/uu/uptime/src/platform/unix.rs +++ b/src/uu/uptime/src/platform/unix.rs @@ -109,7 +109,7 @@ fn uptime_with_file(file_path: &OsString) -> UResult<()> { print_loadavg(); return Ok(()); } - + print_time(); let (boot_time, user_count) = process_utmpx_from_file(file_path); if let Some(time) = boot_time { let upsecs = get_uptime_from_boot_time(time); @@ -211,6 +211,13 @@ fn process_utmpx_from_file(file: &OsString) -> (Option, usize) { match line.record_type() { USER_PROCESS => nusers += 1, BOOT_TIME => { + // Macos "getutxent" initializes all fields of the struct to 0, if it can't find any + // utmpx record from the file. + #[cfg(target_os = "macos")] + if line.into_inner().ut_tv.tv_sec == 0 { + continue; + } + let dt = line.login_time(); if dt.unix_timestamp() > 0 { boot_time = Some(dt.unix_timestamp() as time_t); diff --git a/tests/by-util/test_uptime.rs b/tests/by-util/test_uptime.rs index 23e492ab2db..402d4355409 100644 --- a/tests/by-util/test_uptime.rs +++ b/tests/by-util/test_uptime.rs @@ -3,7 +3,13 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. use crate::common::util::TestScenario; + use regex::Regex; +use serde::Serialize; +use serde_big_array::BigArray; +use std::fs::File; +use std::{io::Write, path::PathBuf}; +use uucore::utmpx::{BOOT_TIME, RUN_LVL, USER_PROCESS, UT_HOSTSIZE, UT_LINESIZE, UT_NAMESIZE}; #[test] fn test_invalid_arg() { @@ -75,32 +81,134 @@ fn test_uptime_with_non_existent_file() { #[test] #[cfg(not(target_os = "openbsd"))] -fn test_uptime_with_file_containing_valid_utmpx_record() { +fn test_uptime_with_file_containing_valid_boot_time_utmpx_record() { let ts = TestScenario::new(util_name!()); - let re = Regex::new(r"up {1,2}[(\d){1,} days]*\d{1,2}:\d\d").unwrap(); - ts.ucmd() - .arg("validRecord.txt") - .succeeds() - .stdout_matches(&re) - .stdout_contains("load average"); -} + 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. -/// Assuming /var/log/wtmp has multiple records, /var/log/wtmp doesn't seem to exist in macos -#[test] -#[cfg(not(target_os = "openbsd"))] -fn test_uptime_with_file_containing_multiple_valid_utmpx_record() { - let ts = TestScenario::new(util_name!()); - // Checking for up 00:00 [can be any time] - let re = Regex::new(r"up {1,2}[(\d){1,} days]*\d{1,2}:\d\d").unwrap(); - // Can be multiple users, for double digit users, only matches the last digit. - let re_users = Regex::new(r"\d user[s]?").unwrap(); + let re = Regex::new(r"up [(\d){1,} days]*\d{1,2}:\d\d").unwrap(); + + utmp(&at.plus("test")); ts.ucmd() - .arg("validMultipleRecords.txt") + .arg("test") .succeeds() .stdout_matches(&re) - .stdout_matches(&re_users) .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.into_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 + #[derive(Serialize)] + #[repr(C)] + pub struct time_val { + pub tv_sec: i32, + pub tv_usec: i32, + } + + #[derive(Serialize)] + #[repr(C, align(4))] + pub struct exit_status { + 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; UT_LINESIZE], + pub ut_id: [i8; 4], + + pub ut_user: [i8; UT_NAMESIZE], + #[serde(with = "BigArray")] + pub ut_host: [i8; UT_HOSTSIZE], + pub ut_exit: exit_status, + + pub ut_session: i32, + pub ut_tv: time_val, + + pub ut_addr_v6: [i32; 4], + glibc_reserved: [i8; 20], + } + + let utmp = utmp { + ut_type: BOOT_TIME as i32, + 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: exit_status { + e_termination: 0, + e_exit: 0, + }, + ut_session: 0, + ut_tv: time_val { + tv_sec: 1716371201, + tv_usec: 290913, + }, + ut_addr_v6: [127, 0, 0, 1], + glibc_reserved: [0; 20], + }; + let utmp1 = utmp { + ut_type: RUN_LVL as i32, + 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: exit_status { + e_termination: 0, + e_exit: 0, + }, + ut_session: 0, + ut_tv: time_val { + tv_sec: 1716371209, + tv_usec: 162250, + }, + ut_addr_v6: [0, 0, 0, 0], + glibc_reserved: [0; 20], + }; + let utmp2 = utmp { + ut_type: USER_PROCESS as i32, + 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: exit_status { + e_termination: 0, + e_exit: 0, + }, + ut_session: 0, + ut_tv: time_val { + tv_sec: 1716371283, + tv_usec: 858764, + }, + ut_addr_v6: [0, 0, 0, 0], + glibc_reserved: [0; 20], + }; + + let mut buf = bincode::serialize(&utmp).unwrap(); + buf.append(&mut bincode::serialize(&utmp1).unwrap()); + buf.append(&mut bincode::serialize(&utmp2).unwrap()); + let mut f = File::create(path).unwrap(); + f.write_all(&mut buf).unwrap(); + } } + #[test] #[cfg(not(target_os = "openbsd"))] fn test_uptime_with_extra_argument() { @@ -112,26 +220,6 @@ fn test_uptime_with_extra_argument() { .fails() .stderr_contains("extra operand 'b'"); } -/// Here we test if partial records are parsed properly and this may return an uptime of hours or -/// days, assuming /var/log/wtmp contains multiple records -#[test] -#[cfg(not(target_os = "openbsd"))] -fn test_uptime_with_file_containing_multiple_valid_utmpx_record_with_partial_records() { - let ts = TestScenario::new(util_name!()); - - let re_users = Regex::new(r"\d user[s]?").unwrap(); - // 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_uptime = Regex::new(r"up {1,2}[(\d){1,} days]*\d{1,2}:\d\d").unwrap(); - ts.ucmd() - .arg("validMultiplePartialRecords.txt") - .succeeds() - .stdout_contains("load average") - .stdout_matches(&re_users) - .stdout_matches(&re_uptime); -} - /// Checks whether uptime displays the correct stderr msg when its called with a directory #[test] #[cfg(not(target_os = "openbsd"))] diff --git a/tests/fixtures/uptime/validMultiplePartialRecords.txt b/tests/fixtures/uptime/validMultiplePartialRecords.txt deleted file mode 100644 index ef9ea9083c5d2220feb444f166c7ef8b304836e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20584 zcmeHP{Yz9q6n%Y}eOXXJ5alnW=(=l5ZK4s86hTDx0o5<1X*(sFTc!pUVFgz7MV1zU znklsjMHEJA5#0Nab!YV%NW1ThJq+YNaNq9EnX7xw&OLKy-gB`xb=;ahXFJ^zKx?u- zm1@g|@VrDUUJ*+~D|YWl#4C0rq8F1b$=1e(>>UFbD0BQperCy;3Ku^mHAhg;5 zM3WP!SOnmAXZ+-nyRUVD{1s{fxmj{+jaUIjXdE8`Ha?Pr)C4dk&TW+(^qL>7f#B)_ z5KT^?lvUt0(c}c8hoZpcLO;Fa?whfJds+?TX34RY zdTko|#FN4+aQI585ddU?5KiwYF V58>4aAex*&JNK5D2AWVGaAGY)aFqzK>jt(g zZEb+Q$v%!&1K}w^Bl}CtrY3AIswNN~@qQ#4sumwk$Nlu~zyJ5Mk3wu%cLx5>;p}&L zls`yG{56g(%V{|4qE0Xu`oieL16G1VowyV5pjHH7Fh3K6da0+(6w{<`7SC`p z&rjM8MGbvCpcTP9^Zw`8@%yMb&V6bGe<+#XXZcnE?x8d`A+zX7y&u@3+M diff --git a/tests/fixtures/uptime/validMultipleRecords.txt b/tests/fixtures/uptime/validMultipleRecords.txt deleted file mode 100644 index 344e4b4d03327ed0cec9bcfcce10965e81ab85e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 64128 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 diff --git a/tests/fixtures/uptime/validRecord.txt b/tests/fixtures/uptime/validRecord.txt deleted file mode 100644 index dcf631643943235a7a1ead2545786159e5dfdbaa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1536 zcmZQ#fPgwEjf<)SDkw@#%Fi#srGkOMOwU5kK-b*Nz|6qHz#t_tGpEwXz{teF*w9cn zJvA@2C^H$i=3yjr{fuW?su&+pK4Ju_G6iCy!oM^xC$%g!2baO4;SUL!`@rx|F=E9P zOeo^)Kwdg01A~c