From 61789e27f276244d487d0dcaad1e394e8ba81032 Mon Sep 17 00:00:00 2001 From: fortifiedhill <24689525+fortifiedhill@users.noreply.github.com> Date: Tue, 26 Mar 2024 21:10:28 -0500 Subject: [PATCH 1/6] w: Format time and get cmdline This change allows w to display time in a similar way to the original procps w. Additionally, w now fetches the cmdline for each entry. --- Cargo.lock | 2 ++ Cargo.toml | 2 ++ src/uu/w/Cargo.toml | 1 + src/uu/w/src/w.rs | 19 +++++++++++++++---- 4 files changed, 20 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a8dfb51..f9878809 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -410,6 +410,7 @@ dependencies = [ "sysinfo", "tempfile", "textwrap", + "time", "uu_free", "uu_pmap", "uu_pwdx", @@ -763,6 +764,7 @@ name = "uu_w" version = "0.0.1" dependencies = [ "clap", + "time", "uucore", ] diff --git a/Cargo.toml b/Cargo.toml index 1ac9d53b..de8b230e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,6 +47,7 @@ xattr = "1.3.1" tempfile = "3.9.0" rand = { version = "0.8", features = ["small_rng"] } bytesize = "1.3.0" +time = { version = "0.3", features = ["formatting"] } [workspace.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] utmpx = "0.1" @@ -75,6 +76,7 @@ tempfile = { workspace = true } libc = { workspace = true } rand = { workspace = true } uucore = { workspace = true, features = ["entries", "process", "signals"] } +time = { workspace = true, features = ["formatting"] } [target.'cfg(unix)'.dev-dependencies] xattr = { workspace = true } diff --git a/src/uu/w/Cargo.toml b/src/uu/w/Cargo.toml index 573ab0be..d5a7775e 100644 --- a/src/uu/w/Cargo.toml +++ b/src/uu/w/Cargo.toml @@ -14,6 +14,7 @@ categories = ["command-line-utilities"] [dependencies] uucore = { workspace = true, features = ["utmpx"] } clap = { workspace = true } +time = { workspace = true, features = ["formatting"] } ['cfg(any(target_os = "linux", target_os = "android"))'.dependencies] utmpx = { workspace = true } diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs index c19df490..1b59a02a 100644 --- a/src/uu/w/src/w.rs +++ b/src/uu/w/src/w.rs @@ -5,7 +5,8 @@ use clap::crate_version; use clap::{Arg, ArgAction, Command}; -use std::process; +use std::{fs, path::Path, process}; +use time; use uucore::utmpx::Utmpx; use uucore::{error::UResult, format_usage, help_about, help_usage}; @@ -22,6 +23,16 @@ struct UserInfo { command: String, } +fn format_time(time: time::OffsetDateTime) -> Result { + let time_format = time::format_description::parse("[hour]:[minute]").unwrap(); + time.format(&time_format) +} + +fn fetch_cmdline(pid: i32) -> Result { + let cmdline_path = Path::new("/proc").join(pid.to_string()).join("cmdline"); + fs::read_to_string(cmdline_path) +} + fn fetch_user_info() -> Result, std::io::Error> { let mut user_info_list = Vec::new(); for entry in Utmpx::iter_all_records() { @@ -29,11 +40,11 @@ fn fetch_user_info() -> Result, std::io::Error> { let user_info = UserInfo { user: entry.user(), terminal: entry.tty_device(), - login_time: format!("{}", entry.login_time()), // Needs formatting + login_time: format_time(entry.login_time()).unwrap_or_default(), idle_time: String::new(), // Placeholder, needs actual implementation jcpu: String::new(), // Placeholder, needs actual implementation pcpu: String::new(), // Placeholder, needs actual implementation - command: String::new(), // Placeholder, needs actual implementation + command: fetch_cmdline(entry.pid()).unwrap_or_default(), }; user_info_list.push(user_info); } @@ -50,7 +61,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { match fetch_user_info() { Ok(user_info) => { if !no_header { - println!("USER\tTTY\t\tLOGIN@\t\tIDLE\tJCPU\tPCPU\tWHAT"); + println!("USER\tTTY\tLOGIN@\tIDLE\tJCPU\tPCPU\tWHAT"); } for user in user_info { println!( From bf4c48080f27a48c72d55ddf217b42b5010b9980 Mon Sep 17 00:00:00 2001 From: fortifiedhill <24689525+fortifiedhill@users.noreply.github.com> Date: Wed, 27 Mar 2024 03:57:51 -0500 Subject: [PATCH 2/6] w: Add tests for date formatting and fetching cmdline --- src/uu/w/src/w.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs index 1b59a02a..9fc61aa1 100644 --- a/src/uu/w/src/w.rs +++ b/src/uu/w/src/w.rs @@ -142,3 +142,27 @@ pub fn uu_app() -> Command { .action(ArgAction::SetTrue), ) } + +#[cfg(test)] +mod tests { + use time::OffsetDateTime; + use crate::format_time; + use crate::fetch_cmdline; + use std::{fs::read_to_string, path::Path, process}; + + + #[test] + fn test_format_time() { + let unix_epoc = OffsetDateTime::UNIX_EPOCH; + assert_eq!(format_time(unix_epoc).unwrap(), "00:00"); + } + + #[test] + // Get PID of current process and use that for cmdline testing + fn test_fetch_cmdline() { + // uucore's utmpx returns an i32, so we cast to that to mimic it. + let pid = process::id() as i32; + let path = Path::new("/proc").join(pid.to_string()).join("cmdline"); + assert_eq!(read_to_string(path).unwrap(), fetch_cmdline(pid).unwrap()) + } +} From b7cc844650e6a8ff1300839f63e6c2b8ea2eca1b Mon Sep 17 00:00:00 2001 From: fortifiedhill <24689525+fortifiedhill@users.noreply.github.com> Date: Wed, 27 Mar 2024 05:27:18 -0500 Subject: [PATCH 3/6] w: Remove unit tests and add integration tests It makes more since to make fetch_cmdline and format_time public functions until they are moved into a library or seperate file and just use integration tests from the start. --- src/uu/w/src/w.rs | 28 ++-------------------------- tests/by-util/test_w.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 26 deletions(-) diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs index 9fc61aa1..d50e3a69 100644 --- a/src/uu/w/src/w.rs +++ b/src/uu/w/src/w.rs @@ -23,12 +23,12 @@ struct UserInfo { command: String, } -fn format_time(time: time::OffsetDateTime) -> Result { +pub fn format_time(time: time::OffsetDateTime) -> Result { let time_format = time::format_description::parse("[hour]:[minute]").unwrap(); time.format(&time_format) } -fn fetch_cmdline(pid: i32) -> Result { +pub fn fetch_cmdline(pid: i32) -> Result { let cmdline_path = Path::new("/proc").join(pid.to_string()).join("cmdline"); fs::read_to_string(cmdline_path) } @@ -142,27 +142,3 @@ pub fn uu_app() -> Command { .action(ArgAction::SetTrue), ) } - -#[cfg(test)] -mod tests { - use time::OffsetDateTime; - use crate::format_time; - use crate::fetch_cmdline; - use std::{fs::read_to_string, path::Path, process}; - - - #[test] - fn test_format_time() { - let unix_epoc = OffsetDateTime::UNIX_EPOCH; - assert_eq!(format_time(unix_epoc).unwrap(), "00:00"); - } - - #[test] - // Get PID of current process and use that for cmdline testing - fn test_fetch_cmdline() { - // uucore's utmpx returns an i32, so we cast to that to mimic it. - let pid = process::id() as i32; - let path = Path::new("/proc").join(pid.to_string()).join("cmdline"); - assert_eq!(read_to_string(path).unwrap(), fetch_cmdline(pid).unwrap()) - } -} diff --git a/tests/by-util/test_w.rs b/tests/by-util/test_w.rs index 9203273c..77cff7b2 100644 --- a/tests/by-util/test_w.rs +++ b/tests/by-util/test_w.rs @@ -4,6 +4,8 @@ // file that was distributed with this source code. // spell-checker:ignore (words) symdir somefakedir +use std::{fs, path::Path, process}; + use crate::common::util::TestScenario; #[test] @@ -19,3 +21,18 @@ fn test_no_header() { assert!(!result.contains("USER\tTTY\t\tLOGIN@\t\tIDLE\tJCPU\tPCPU\tWHAT")); } + +#[test] +fn test_format_time() { + let unix_epoc = time::OffsetDateTime::UNIX_EPOCH; + assert_eq!(w::format_time(unix_epoc).unwrap(), "00:00"); +} + +#[test] +// Get PID of current process and use that for cmdline testing +fn test_fetch_cmdline() { + // uucore's utmpx returns an i32, so we cast to that to mimic it. + let pid = process::id() as i32; + let path = Path::new("/proc").join(pid.to_string()).join("cmdline"); + assert_eq!(fs::read_to_string(path).unwrap(), w::fetch_cmdline(pid).unwrap()) +} From 7c00b219bc5e0e715e681833a1df2163ccc658e8 Mon Sep 17 00:00:00 2001 From: fortifiedhill <24689525+fortifiedhill@users.noreply.github.com> Date: Wed, 27 Mar 2024 16:09:53 -0500 Subject: [PATCH 4/6] w: Fixup tests Unit tests are where they should be and there is an added test that checks the output of w. --- src/uu/w/src/w.rs | 29 +++++++++++++++++++++++++++-- tests/by-util/test_w.rs | 36 +++++++++++++++++++++--------------- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs index d50e3a69..74ec324f 100644 --- a/src/uu/w/src/w.rs +++ b/src/uu/w/src/w.rs @@ -23,12 +23,12 @@ struct UserInfo { command: String, } -pub fn format_time(time: time::OffsetDateTime) -> Result { +fn format_time(time: time::OffsetDateTime) -> Result { let time_format = time::format_description::parse("[hour]:[minute]").unwrap(); time.format(&time_format) } -pub fn fetch_cmdline(pid: i32) -> Result { +fn fetch_cmdline(pid: i32) -> Result { let cmdline_path = Path::new("/proc").join(pid.to_string()).join("cmdline"); fs::read_to_string(cmdline_path) } @@ -142,3 +142,28 @@ pub fn uu_app() -> Command { .action(ArgAction::SetTrue), ) } + +#[cfg(test)] +mod tests { + use crate::{fetch_cmdline, format_time}; + use std::{fs, path::Path, process}; + use time::OffsetDateTime; + + #[test] + fn test_format_time() { + let unix_epoc = OffsetDateTime::UNIX_EPOCH; + assert_eq!(format_time(unix_epoc).unwrap(), "00:00"); + } + + #[test] + // Get PID of current process and use that for cmdline testing + fn test_fetch_cmdline() { + // uucore's utmpx returns an i32, so we cast to that to mimic it. + let pid = process::id() as i32; + let path = Path::new("/proc").join(pid.to_string()).join("cmdline"); + assert_eq!( + fs::read_to_string(path).unwrap(), + fetch_cmdline(pid).unwrap() + ) + } +} diff --git a/tests/by-util/test_w.rs b/tests/by-util/test_w.rs index 77cff7b2..031ab98a 100644 --- a/tests/by-util/test_w.rs +++ b/tests/by-util/test_w.rs @@ -4,9 +4,8 @@ // file that was distributed with this source code. // spell-checker:ignore (words) symdir somefakedir -use std::{fs, path::Path, process}; - use crate::common::util::TestScenario; +use std::path::Path; #[test] fn test_invalid_arg() { @@ -19,20 +18,27 @@ fn test_no_header() { let result = cmd.stdout_str(); - assert!(!result.contains("USER\tTTY\t\tLOGIN@\t\tIDLE\tJCPU\tPCPU\tWHAT")); + assert!(!result.contains("USER\tTTY\tLOGIN@\tIDLE\tJCPU\tPCPU\tWHAT")); } #[test] -fn test_format_time() { - let unix_epoc = time::OffsetDateTime::UNIX_EPOCH; - assert_eq!(w::format_time(unix_epoc).unwrap(), "00:00"); -} - -#[test] -// Get PID of current process and use that for cmdline testing -fn test_fetch_cmdline() { - // uucore's utmpx returns an i32, so we cast to that to mimic it. - let pid = process::id() as i32; - let path = Path::new("/proc").join(pid.to_string()).join("cmdline"); - assert_eq!(fs::read_to_string(path).unwrap(), w::fetch_cmdline(pid).unwrap()) +fn test_output_format() { + // Use no header to simplify testing + let cmd = new_ucmd!().arg("--no-header").succeeds(); + let output_lines = cmd.stdout_str().lines(); + // There is no guarantee that there will be a cmdline entry, but for testing purposes, we will assume so, + // since there will be one present in most cases. + for line in output_lines { + // We need to get rid of extra 0 terminators on strings, such as the cmdline, so we don't have characters at the end. + let line_vec: Vec = line + .split_whitespace() + .map(|s| String::from(s.trim_end_matches('\0'))) + .collect(); + // Check the time formatting, this should be the third entry in list + // For now, we are just going to check that that length of time is 5 and it has a colon + assert!(line_vec[2].contains(":") && line_vec[2].chars().count() == 5); + // Check to make sure that cmdline is a path that exists. + // For now, cmdline will be in index 3, until the output is complete. + assert!(Path::new(line_vec.last().unwrap().as_str()).exists()) + } } From 6f30c4a7fd0e2213e9ff708fcfad3da03d83483a Mon Sep 17 00:00:00 2001 From: fortifiedhill <24689525+fortifiedhill@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:32:48 -0500 Subject: [PATCH 5/6] w: Switch to chrono crate The time library has been switched to chrono. Also, the integration test for cmdline checking has been dropped, as there is really no expectation of what it should be. --- Cargo.lock | 152 +++++++++++++++++++++++++++++++++++++++- Cargo.toml | 4 +- src/uu/w/Cargo.toml | 2 +- src/uu/w/src/w.rs | 41 ++++++++--- tests/by-util/test_w.rs | 24 +++---- 5 files changed, 197 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f9878809..0da6d657 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,21 @@ dependencies = [ "memchr", ] +[[package]] +name = "android-tzdata" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + [[package]] name = "anstream" version = "0.6.11" @@ -59,6 +74,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "autocfg" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" + [[package]] name = "bitflags" version = "1.3.2" @@ -71,18 +92,44 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "bumpalo" +version = "3.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" + [[package]] name = "bytesize" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +[[package]] +name = "cc" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5" + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" +dependencies = [ + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-targets 0.52.0", +] + [[package]] name = "clap" version = "4.4.18" @@ -239,6 +286,29 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" +[[package]] +name = "iana-time-zone" +version = "0.1.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + [[package]] name = "io-lifetimes" version = "1.0.11" @@ -256,6 +326,15 @@ version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "js-sys" +version = "0.3.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.152" @@ -274,6 +353,12 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +[[package]] +name = "log" +version = "0.4.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" + [[package]] name = "memchr" version = "2.7.1" @@ -300,6 +385,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +dependencies = [ + "autocfg", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -397,6 +491,7 @@ dependencies = [ name = "procps" version = "0.0.1" dependencies = [ + "chrono", "clap", "clap_complete", "clap_mangen", @@ -410,7 +505,6 @@ dependencies = [ "sysinfo", "tempfile", "textwrap", - "time", "uu_free", "uu_pmap", "uu_pwdx", @@ -763,8 +857,8 @@ dependencies = [ name = "uu_w" version = "0.0.1" dependencies = [ + "chrono", "clap", - "time", "uucore", ] @@ -817,6 +911,60 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +dependencies = [ + "cfg-if", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +dependencies = [ + "bumpalo", + "log", + "once_cell", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" + [[package]] name = "wild" version = "2.2.0" diff --git a/Cargo.toml b/Cargo.toml index de8b230e..a28981ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ xattr = "1.3.1" tempfile = "3.9.0" rand = { version = "0.8", features = ["small_rng"] } bytesize = "1.3.0" -time = { version = "0.3", features = ["formatting"] } +chrono = { version = "0.4.37"} [workspace.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] utmpx = "0.1" @@ -76,7 +76,7 @@ tempfile = { workspace = true } libc = { workspace = true } rand = { workspace = true } uucore = { workspace = true, features = ["entries", "process", "signals"] } -time = { workspace = true, features = ["formatting"] } +chrono = { workspace = true } [target.'cfg(unix)'.dev-dependencies] xattr = { workspace = true } diff --git a/src/uu/w/Cargo.toml b/src/uu/w/Cargo.toml index d5a7775e..01ea9437 100644 --- a/src/uu/w/Cargo.toml +++ b/src/uu/w/Cargo.toml @@ -14,7 +14,7 @@ categories = ["command-line-utilities"] [dependencies] uucore = { workspace = true, features = ["utmpx"] } clap = { workspace = true } -time = { workspace = true, features = ["formatting"] } +chrono = { workspace = true } ['cfg(any(target_os = "linux", target_os = "android"))'.dependencies] utmpx = { workspace = true } diff --git a/src/uu/w/src/w.rs b/src/uu/w/src/w.rs index 74ec324f..afa51c06 100644 --- a/src/uu/w/src/w.rs +++ b/src/uu/w/src/w.rs @@ -3,10 +3,10 @@ // For the full copyright and license information, please view the LICENSE // file that was distributed with this source code. +use chrono::{self, Datelike}; use clap::crate_version; use clap::{Arg, ArgAction, Command}; use std::{fs, path::Path, process}; -use time; use uucore::utmpx::Utmpx; use uucore::{error::UResult, format_usage, help_about, help_usage}; @@ -23,9 +23,21 @@ struct UserInfo { command: String, } -fn format_time(time: time::OffsetDateTime) -> Result { - let time_format = time::format_description::parse("[hour]:[minute]").unwrap(); - time.format(&time_format) +fn format_time(time: String) -> Result { + let mut t: String = time; + // Trim the seconds off of timezone offset, as chrono can't parse the time with it present + if let Some(time_offset) = t.rfind(':') { + t = t.drain(..time_offset).collect(); + } + // If login time day is not current day, format like Sat16, or Fri06 + let current_dt = chrono::Local::now().fixed_offset(); + let dt = chrono::DateTime::parse_from_str(&t, "%Y-%m-%d %H:%M:%S%.f %:z")?; + + if current_dt.day() != dt.day() { + Ok(dt.format("%a%d").to_string()) + } else { + Ok(dt.format("%H:%M").to_string()) + } } fn fetch_cmdline(pid: i32) -> Result { @@ -40,7 +52,7 @@ fn fetch_user_info() -> Result, std::io::Error> { let user_info = UserInfo { user: entry.user(), terminal: entry.tty_device(), - login_time: format_time(entry.login_time()).unwrap_or_default(), + login_time: format_time(entry.login_time().to_string()).unwrap_or_default(), idle_time: String::new(), // Placeholder, needs actual implementation jcpu: String::new(), // Placeholder, needs actual implementation pcpu: String::new(), // Placeholder, needs actual implementation @@ -146,13 +158,26 @@ pub fn uu_app() -> Command { #[cfg(test)] mod tests { use crate::{fetch_cmdline, format_time}; + use chrono; use std::{fs, path::Path, process}; - use time::OffsetDateTime; #[test] fn test_format_time() { - let unix_epoc = OffsetDateTime::UNIX_EPOCH; - assert_eq!(format_time(unix_epoc).unwrap(), "00:00"); + let unix_epoc = chrono::Local::now() + .format("%Y-%m-%d %H:%M:%S%.6f %::z") + .to_string(); + let unix_formatted = format_time(unix_epoc).unwrap(); + assert!(unix_formatted.contains(':') && unix_formatted.chars().count() == 5); + // Test a date that is 5 days ago + let td = chrono::Local::now().fixed_offset() + - chrono::TimeDelta::new(60 * 60 * 24 * 5, 0).unwrap(); + // Pre-format time, so it's similar to how utmpx returns it + let pre_formatted = format!("{}", td.format("%Y-%m-%d %H:%M:%S%.6f %::z")); + + assert_eq!( + format_time(pre_formatted).unwrap(), + td.format("%a%d").to_string() + ) } #[test] diff --git a/tests/by-util/test_w.rs b/tests/by-util/test_w.rs index 031ab98a..b5580be0 100644 --- a/tests/by-util/test_w.rs +++ b/tests/by-util/test_w.rs @@ -5,7 +5,6 @@ // spell-checker:ignore (words) symdir somefakedir use crate::common::util::TestScenario; -use std::path::Path; #[test] fn test_invalid_arg() { @@ -26,19 +25,18 @@ fn test_output_format() { // Use no header to simplify testing let cmd = new_ucmd!().arg("--no-header").succeeds(); let output_lines = cmd.stdout_str().lines(); - // There is no guarantee that there will be a cmdline entry, but for testing purposes, we will assume so, - // since there will be one present in most cases. + for line in output_lines { - // We need to get rid of extra 0 terminators on strings, such as the cmdline, so we don't have characters at the end. - let line_vec: Vec = line - .split_whitespace() - .map(|s| String::from(s.trim_end_matches('\0'))) - .collect(); + let line_vec: Vec = line.split_whitespace().map(|s| String::from(s)).collect(); // Check the time formatting, this should be the third entry in list - // For now, we are just going to check that that length of time is 5 and it has a colon - assert!(line_vec[2].contains(":") && line_vec[2].chars().count() == 5); - // Check to make sure that cmdline is a path that exists. - // For now, cmdline will be in index 3, until the output is complete. - assert!(Path::new(line_vec.last().unwrap().as_str()).exists()) + // For now, we are just going to check that that length of time is 5 and it has a colon, else + // it is possible that a time can look like Fri13, so it can start with a letter and end + // with a number + assert!( + (line_vec[2].contains(":") && line_vec[2].chars().count() == 5) + || (line_vec[2].starts_with(char::is_alphabetic) + && line_vec[2].ends_with(char::is_numeric) + && line_vec[2].chars().count() == 5) + ); } } From 31404863ba7cafa1225dbd89291a2941cb8aca05 Mon Sep 17 00:00:00 2001 From: fortifiedhill <24689525+fortifiedhill@users.noreply.github.com> Date: Sun, 31 Mar 2024 21:43:23 -0500 Subject: [PATCH 6/6] w: Fix chrono entry in Cargo.toml --- Cargo.lock | 2 -- Cargo.toml | 4 +++- src/uu/w/Cargo.toml | 4 +++- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0da6d657..cd53dae9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -124,9 +124,7 @@ checksum = "8a0d04d43504c61aa6c7531f1871dd0d418d91130162063b789da00fd7057a5e" dependencies = [ "android-tzdata", "iana-time-zone", - "js-sys", "num-traits", - "wasm-bindgen", "windows-targets 0.52.0", ] diff --git a/Cargo.toml b/Cargo.toml index a28981ee..48aa51f5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,9 @@ xattr = "1.3.1" tempfile = "3.9.0" rand = { version = "0.8", features = ["small_rng"] } bytesize = "1.3.0" -chrono = { version = "0.4.37"} +chrono = { version = "0.4.37", default-features = false, features = [ + "clock", +] } [workspace.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] utmpx = "0.1" diff --git a/src/uu/w/Cargo.toml b/src/uu/w/Cargo.toml index 01ea9437..4dd7ab8d 100644 --- a/src/uu/w/Cargo.toml +++ b/src/uu/w/Cargo.toml @@ -14,7 +14,9 @@ categories = ["command-line-utilities"] [dependencies] uucore = { workspace = true, features = ["utmpx"] } clap = { workspace = true } -chrono = { workspace = true } +chrono = { workspace = true, default-features = false, features = [ + "clock", +] } ['cfg(any(target_os = "linux", target_os = "android"))'.dependencies] utmpx = { workspace = true }