// Take a look at the license at the top of the repository in the LICENSE file. #![cfg(feature = "system")] use bstr::ByteSlice; use sysinfo::{Pid, ProcessRefreshKind, ProcessesToUpdate, RefreshKind, System, UpdateKind}; macro_rules! start_proc { ($time:literal, $name:literal) => { if cfg!(target_os = "windows") { std::process::Command::new("waitfor") .arg("/t") .arg($time) .arg($name) .stdout(std::process::Stdio::null()) .spawn() .unwrap() } else { std::process::Command::new("sleep") .arg($time) .stdout(std::process::Stdio::null()) .spawn() .unwrap() } }; } #[test] fn test_cwd() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut p = start_proc!("3", "CwdSignal"); let pid = Pid::from_u32(p.id() as _); std::thread::sleep(std::time::Duration::from_secs(1)); let mut s = System::new(); s.refresh_processes_specifics( ProcessesToUpdate::All, false, ProcessRefreshKind::new().with_cwd(UpdateKind::Always), ); p.kill().expect("Unable to kill process."); let processes = s.processes(); let p = processes.get(&pid); if let Some(p) = p { assert_eq!(p.pid(), pid); assert_eq!(p.cwd().unwrap(), &std::env::current_dir().unwrap()); } else { panic!("Process not found!"); } } #[test] fn test_cmd() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut p = start_proc!("3", "CmdSignal"); std::thread::sleep(std::time::Duration::from_millis(500)); let mut s = System::new(); assert!(s.processes().is_empty()); s.refresh_processes_specifics( ProcessesToUpdate::All, false, ProcessRefreshKind::new().with_cmd(UpdateKind::Always), ); p.kill().expect("Unable to kill process"); assert!(!s.processes().is_empty()); if let Some(process) = s.process(Pid::from_u32(p.id() as _)) { if cfg!(target_os = "windows") { // Sometimes, we get the full path instead for some reasons... So just in case, // we check for the command independently that from the arguments. assert!(process.cmd()[0].as_encoded_bytes().contains_str("waitfor")); assert_eq!(&process.cmd()[1..], &["/t", "3", "CmdSignal"]); } else { assert_eq!(process.cmd(), &["sleep", "3"]); } } else { panic!("Process not found!"); } } fn build_test_binary(file_name: &str) { std::process::Command::new("rustc") .arg("test_bin/main.rs") .arg("-o") .arg(file_name) .stdout(std::process::Stdio::null()) .spawn() .unwrap() .wait() .unwrap(); } #[test] #[allow(clippy::zombie_processes)] fn test_environ() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let file_name = "target/test_binary"; build_test_binary(file_name); let mut p = std::process::Command::new(format!("./{file_name}")) .env("FOO", "BAR") .env("OTHER", "VALUE") .spawn() .unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); let pid = Pid::from_u32(p.id() as _); let mut s = System::new(); s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid]), false, sysinfo::ProcessRefreshKind::everything(), ); p.kill().expect("Unable to kill process."); let processes = s.processes(); let proc_ = processes.get(&pid); if let Some(proc_) = proc_ { assert_eq!(proc_.pid(), pid); assert!(proc_.environ().iter().any(|e| e == "FOO=BAR")); assert!(proc_.environ().iter().any(|e| e == "OTHER=VALUE")); } else { panic!("Process not found!"); } // Test to ensure that a process with a lot of environment variables doesn't get truncated. // More information in . const SIZE: usize = 30_000; let mut big_env = String::with_capacity(SIZE); for _ in 0..SIZE { big_env.push('a'); } let mut p = std::process::Command::new("./target/test_binary") .env("FOO", &big_env) .spawn() .unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); let pid = Pid::from_u32(p.id() as _); let mut s = System::new(); s.refresh_processes_specifics( ProcessesToUpdate::All, false, ProcessRefreshKind::new().with_environ(UpdateKind::Always), ); let processes = s.processes(); let proc_ = processes.get(&pid); if let Some(proc_) = proc_ { p.kill().expect("Unable to kill process."); assert_eq!(proc_.pid(), pid); let env = format!("FOO={big_env}"); assert!(proc_.environ().iter().any(|e| *e == *env)); } else { panic!("Process not found!"); } } #[test] fn test_process_refresh() { let mut s = System::new(); assert_eq!(s.processes().len(), 0); if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } s.refresh_processes( ProcessesToUpdate::Some(&[sysinfo::get_current_pid().expect("failed to get current pid")]), false, ); assert!(s .process(sysinfo::get_current_pid().expect("failed to get current pid")) .is_some()); assert!(s .processes() .iter() .all(|(_, p)| p.environ().is_empty() && p.cwd().is_none() && p.cmd().is_empty())); assert!(s .processes() .iter() .any(|(_, p)| !p.name().is_empty() && p.memory() != 0)); } #[test] fn test_process_disk_usage() { use std::fs; use std::fs::File; use std::io::prelude::*; use sysinfo::get_current_pid; if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } if std::env::var("FREEBSD_CI").is_ok() { // For an unknown reason, when running this test on Cirrus CI, it fails. It works perfectly // locally though... Dark magic... return; } fn inner() -> System { { let mut file = File::create("test.txt").expect("failed to create file"); file.write_all(b"This is a test file\nwith test data.\n") .expect("failed to write to file"); } fs::remove_file("test.txt").expect("failed to remove file"); // Waiting a bit just in case... std::thread::sleep(std::time::Duration::from_millis(250)); let mut system = System::new(); assert!(system.processes().is_empty()); system.refresh_processes(ProcessesToUpdate::All, false); assert!(!system.processes().is_empty()); system } let mut system = inner(); let mut p = system .process(get_current_pid().expect("Failed retrieving current pid.")) .expect("failed to get process"); if cfg!(any(target_os = "macos", target_os = "ios")) && p.disk_usage().total_written_bytes == 0 { // For whatever reason, sometimes, mac doesn't work on the first time when running // `cargo test`. Two solutions, either run with "cargo test -- --test-threads 1", or // check twice... system = inner(); p = system .process(get_current_pid().expect("Failed retrieving current pid.")) .expect("failed to get process"); } assert!( p.disk_usage().total_written_bytes > 0, "found {} total written bytes...", p.disk_usage().total_written_bytes ); assert!( p.disk_usage().written_bytes > 0, "found {} written bytes...", p.disk_usage().written_bytes ); } #[test] fn cpu_usage_is_not_nan() { let mut system = System::new(); system.refresh_processes(ProcessesToUpdate::All, false); if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } // We need `collect` otherwise we can't have mutable access to `system`. #[allow(clippy::needless_collect)] let first_pids = system .processes() .iter() .take(10) .map(|(&pid, _)| pid) .collect::>(); let mut checked = 0; first_pids.into_iter().for_each(|pid| { system.refresh_processes(ProcessesToUpdate::Some(&[pid]), true); if let Some(p) = system.process(pid) { assert!(!p.cpu_usage().is_nan()); checked += 1; } }); assert!(checked > 0); } #[test] fn test_process_times() { use std::time::{SystemTime, UNIX_EPOCH}; if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let boot_time = System::boot_time(); assert!(boot_time > 0); let mut p = start_proc!("3", "ProcessTimes"); let pid = Pid::from_u32(p.id() as _); std::thread::sleep(std::time::Duration::from_secs(1)); let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::All, false); p.kill().expect("Unable to kill process."); if let Some(p) = s.process(pid) { assert_eq!(p.pid(), pid); assert!(p.run_time() >= 1); assert!(p.run_time() <= 2); assert!(p.start_time() > p.run_time()); // On linux, for whatever reason, the uptime seems to be older than the boot time, leading // to this weird `+ 5` to ensure the test is passing as it should... assert!( p.start_time() + 5 > SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(), ); assert!(p.start_time() >= boot_time); } else { panic!("Process not found!"); } } // Checks that `session_id` is working. #[test] fn test_process_session_id() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::All, false); assert!(s.processes().values().any(|p| p.session_id().is_some())); } // Checks that `refresh_processes` is removing dead processes. #[test] fn test_refresh_processes() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut p = start_proc!("300", "RefreshProcesses"); let pid = Pid::from_u32(p.id() as _); std::thread::sleep(std::time::Duration::from_secs(1)); // Checks that the process is listed as it should. let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::All, false); assert!(s.process(pid).is_some()); // Check that the process name is not empty. assert!(!s.process(pid).unwrap().name().is_empty()); p.kill().expect("Unable to kill process."); // We need this, otherwise the process will still be around as a zombie on linux. let _ = p.wait(); // Let's give some time to the system to clean up... std::thread::sleep(std::time::Duration::from_secs(1)); s.refresh_processes(ProcessesToUpdate::All, true); // Checks that the process isn't listed anymore. assert!(s.process(pid).is_none()); } // This test ensures that if we refresh only one process, then only this process is removed. #[test] fn test_refresh_process_doesnt_remove() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut p1 = start_proc!("300", "RefreshProcessRemove1"); let mut p2 = start_proc!("300", "RefreshProcessRemove2"); let pid1 = Pid::from_u32(p1.id() as _); let pid2 = Pid::from_u32(p2.id() as _); std::thread::sleep(std::time::Duration::from_secs(1)); // Checks that the process is listed as it should. let mut s = System::new_with_specifics( RefreshKind::new().with_processes(sysinfo::ProcessRefreshKind::new()), ); s.refresh_processes(ProcessesToUpdate::All, false); assert!(s.process(pid1).is_some()); assert!(s.process(pid2).is_some()); p1.kill().expect("Unable to kill process."); p2.kill().expect("Unable to kill process."); // We need this, otherwise the process will still be around as a zombie on linux. let _ = p1.wait(); let _ = p2.wait(); // Let's give some time to the system to clean up... std::thread::sleep(std::time::Duration::from_secs(1)); assert_eq!( s.refresh_processes(ProcessesToUpdate::Some(&[pid1]), false), 0 ); // We check that none of the two processes were removed. assert!(s.process(pid1).is_some()); assert!(s.process(pid2).is_some()); assert_eq!( s.refresh_processes(ProcessesToUpdate::Some(&[pid1]), true), 0 ); // We check that only `pid1` was removed. assert!(s.process(pid1).is_none()); assert!(s.process(pid2).is_some()); } // Checks that `refresh_processes` is adding and removing task. #[test] #[cfg(all( any(target_os = "linux", target_os = "android"), not(feature = "unknown-ci") ))] fn test_refresh_tasks() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let task_name = "task_1_second"; std::thread::Builder::new() .name(task_name.into()) .spawn(|| { std::thread::sleep(std::time::Duration::from_secs(1)); }) .unwrap(); let pid = Pid::from_u32(std::process::id() as _); // Checks that the task is listed as it should. let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::All, false); assert!(s .process(pid) .unwrap() .tasks() .map(|tasks| tasks.iter().any(|task_pid| s .process(*task_pid) .map(|task| task.name() == task_name) .unwrap_or(false))) .unwrap_or(false)); assert!(s .processes_by_exact_name(task_name.as_ref()) .next() .is_some()); // Let's give some time to the system to clean up... std::thread::sleep(std::time::Duration::from_secs(2)); s.refresh_processes(ProcessesToUpdate::All, true); assert!(!s .process(pid) .unwrap() .tasks() .map(|tasks| tasks.iter().any(|task_pid| s .process(*task_pid) .map(|task| task.name() == task_name) .unwrap_or(false))) .unwrap_or(false)); assert!(s .processes_by_exact_name(task_name.as_ref()) .next() .is_none()); } // Checks that `refresh_process` is removing dead processes when asked. #[test] fn test_refresh_process() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut p = start_proc!("300", "RefreshProcess"); let pid = Pid::from_u32(p.id() as _); std::thread::sleep(std::time::Duration::from_secs(1)); // Checks that the process is listed as it should. let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::Some(&[pid]), false); assert!(s.process(pid).is_some()); // Check that the process name is not empty. assert!(!s.process(pid).unwrap().name().is_empty()); p.kill().expect("Unable to kill process."); // We need this, otherwise the process will still be around as a zombie on linux. let _ = p.wait(); // Let's give some time to the system to clean up... std::thread::sleep(std::time::Duration::from_secs(1)); assert_eq!( s.refresh_processes(ProcessesToUpdate::Some(&[pid]), false), 0 ); // Checks that the process is still listed. assert!(s.process(pid).is_some()); assert_eq!( s.refresh_processes(ProcessesToUpdate::Some(&[pid]), true), 0 ); // Checks that the process is not listed anymore. assert!(s.process(pid).is_none()); } #[test] fn test_wait_child() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let p = start_proc!("300", "WaitChild"); let before = std::time::Instant::now(); let pid = Pid::from_u32(p.id() as _); let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::Some(&[pid]), false); let process = s.process(pid).unwrap(); // Kill the child process. process.kill(); // Wait for child process should work. process.wait(); // Child process should not be present. assert_eq!( s.refresh_processes(ProcessesToUpdate::Some(&[pid]), true), 0 ); assert!(before.elapsed() < std::time::Duration::from_millis(1000)); } #[test] fn test_wait_non_child() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let before = std::time::Instant::now(); // spawn non child process. let p = if !cfg!(target_os = "linux") { return; } else { std::process::Command::new("setsid") .arg("-w") .arg("sleep") .arg("2") .stdout(std::process::Stdio::null()) .spawn() .unwrap() }; let pid = Pid::from_u32(p.id()); let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::Some(&[pid]), false); let process = s.process(pid).expect("Process not found!"); // Wait for a non child process. process.wait(); // Child process should not be present. assert_eq!( s.refresh_processes(ProcessesToUpdate::Some(&[pid]), true), 0 ); // should wait for 2s. assert!( before.elapsed() > std::time::Duration::from_millis(1900), "Elapsed time {:?} is not greater than 1900ms", before.elapsed() ); assert!( before.elapsed() < std::time::Duration::from_millis(3000), "Elapsed time {:?} is not less than 3000ms", before.elapsed() ); } #[test] fn test_process_iterator_lifetimes() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let s = System::new_with_specifics( sysinfo::RefreshKind::new().with_processes(sysinfo::ProcessRefreshKind::new()), ); let process: Option<&sysinfo::Process>; { let name = String::from(""); // errors before PR #904: name does not live long enough process = s.processes_by_name(name.as_ref()).next(); } process.unwrap(); let process: Option<&sysinfo::Process>; { // worked fine before and after: &'static str lives longer than System, error couldn't appear process = s.processes_by_name("".as_ref()).next(); } process.unwrap(); } // Regression test for . #[test] fn test_process_cpu_usage() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut sys = System::new_all(); std::thread::sleep(sysinfo::MINIMUM_CPU_UPDATE_INTERVAL); sys.refresh_all(); let max_usage = sys.cpus().len() as f32 * 100.; for process in sys.processes().values() { assert!(process.cpu_usage() <= max_usage); } } #[test] fn test_process_creds() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut sys = System::new_all(); sys.refresh_all(); // Just ensure there is at least one process on the system whose credentials can be retrieved. assert!(sys.processes().values().any(|process| { if process.user_id().is_none() { return false; } #[cfg(not(windows))] { if process.group_id().is_none() || process.effective_user_id().is_none() || process.effective_group_id().is_none() { return false; } } true })); // On Windows, make sure no process has real group ID and no effective IDs. #[cfg(windows)] assert!(sys.processes().values().all(|process| { if process.group_id().is_some() || process.effective_user_id().is_some() || process.effective_group_id().is_some() { return false; } true })); } // This test ensures that only the requested information is retrieved. #[test] fn test_process_specific_refresh() { use sysinfo::{DiskUsage, ProcessRefreshKind}; if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } fn check_empty(s: &System, pid: Pid) { let p = s.process(pid).unwrap(); // Name should never be empty. assert!(!p.name().is_empty()); if cfg!(target_os = "windows") { assert_eq!(p.user_id(), None); } assert_eq!(p.environ().len(), 0); assert_eq!(p.cmd().len(), 0); assert_eq!(p.exe(), None); assert_eq!(p.cwd(), None); assert_eq!(p.root(), None); assert_eq!(p.memory(), 0); assert_eq!(p.virtual_memory(), 0); // These two won't be checked, too much lazyness in testing them... assert_eq!(p.disk_usage(), DiskUsage::default()); assert_eq!(p.cpu_usage(), 0.); } let mut s = System::new(); let pid = Pid::from_u32(std::process::id()); macro_rules! update_specific_and_check { (memory) => { s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new()); { let p = s.process(pid).unwrap(); assert_eq!(p.memory(), 0, "failed 0 check for memory"); assert_eq!(p.virtual_memory(), 0, "failed 0 check for virtual memory"); } s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new().with_memory()); { let p = s.process(pid).unwrap(); assert_ne!(p.memory(), 0, "failed non-0 check for memory"); assert_ne!(p.virtual_memory(), 0, "failed non-0 check for virtual memory"); } // And now we check that re-refreshing nothing won't remove the // information. s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new()); { let p = s.process(pid).unwrap(); assert_ne!(p.memory(), 0, "failed non-0 check (number 2) for memory"); assert_ne!(p.virtual_memory(), 0, "failed non-0 check(number 2) for virtual memory"); } }; ($name:ident, $method:ident, $($extra:tt)+) => { s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new()); { let p = s.process(pid).unwrap(); assert_eq!( p.$name()$($extra)+, concat!("failed 0 check check for ", stringify!($name)), ); } s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new().$method(UpdateKind::Always)); { let p = s.process(pid).unwrap(); assert_ne!( p.$name()$($extra)+, concat!("failed non-0 check check for ", stringify!($name)),); } // And now we check that re-refreshing nothing won't remove the // information. s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new()); { let p = s.process(pid).unwrap(); assert_ne!( p.$name()$($extra)+, concat!("failed non-0 check (number 2) check for ", stringify!($name)),); } } } s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new(), ); check_empty(&s, pid); s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid]), false, ProcessRefreshKind::new(), ); check_empty(&s, pid); update_specific_and_check!(memory); update_specific_and_check!(environ, with_environ, .len(), 0); update_specific_and_check!(cmd, with_cmd, .len(), 0); if !cfg!(any( target_os = "macos", target_os = "ios", feature = "apple-sandbox", )) { update_specific_and_check!(root, with_root, , None); } update_specific_and_check!(exe, with_exe, , None); update_specific_and_check!(cwd, with_cwd, , None); } #[test] fn test_refresh_pids() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let self_pid = sysinfo::get_current_pid().expect("failed to get current pid"); let mut s = System::new(); let mut p = start_proc!("3", "RefreshPids"); let child_pid = Pid::from_u32(p.id() as _); let pids = &[child_pid, self_pid]; std::thread::sleep(std::time::Duration::from_millis(500)); s.refresh_processes(ProcessesToUpdate::Some(pids), false); p.kill().expect("Unable to kill process."); assert_eq!(s.processes().len(), 2); for pid in s.processes().keys() { assert!(pids.contains(pid)); } } #[test] fn test_process_run_time() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { return; } let mut s = System::new(); let current_pid = sysinfo::get_current_pid().expect("failed to get current pid"); s.refresh_processes(ProcessesToUpdate::Some(&[current_pid]), false); let run_time = s.process(current_pid).expect("no process found").run_time(); std::thread::sleep(std::time::Duration::from_secs(2)); s.refresh_processes(ProcessesToUpdate::Some(&[current_pid]), true); let new_run_time = s.process(current_pid).expect("no process found").run_time(); assert!( new_run_time > run_time, "{} not superior to {}", new_run_time, run_time ); } // Test that if the parent of a process is removed, then the child PID will be // updated as well. #[test] fn test_parent_change() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") || cfg!(windows) { // Windows never updates its parent PID so no need to check anything. return; } let file_name = "target/test_binary2"; build_test_binary(file_name); let mut p = std::process::Command::new(format!("./{file_name}")) .arg("1") .spawn() .unwrap(); std::thread::sleep(std::time::Duration::from_secs(1)); let pid = Pid::from_u32(p.id() as _); let mut s = System::new(); s.refresh_processes(ProcessesToUpdate::All, false); assert_eq!( s.process(pid).expect("process was not created").parent(), sysinfo::get_current_pid().ok(), ); let child_pid = s .processes() .iter() .find(|(_, proc_)| proc_.parent() == Some(pid)) .map(|(pid, _)| *pid) .expect("failed to get child process"); // Waiting for the parent process to stop. p.wait().expect("wait failed"); s.refresh_processes(ProcessesToUpdate::All, true); // Parent should not be around anymore. assert!(s.process(pid).is_none()); let child = s.process(child_pid).expect("child is dead"); // Child should have a different parent now. assert_ne!(child.parent(), Some(pid)); // We kill the child to clean up. child.kill(); } // We want to ensure that if `System::refresh_process*` methods are called // one after the other, it won't badly impact the CPU usage computation. #[test] fn test_multiple_single_process_refresh() { if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") || cfg!(windows) { // Windows never updates its parent PID so no need to check anything. return; } let file_name = "target/test_binary3"; build_test_binary(file_name); let mut p_a = std::process::Command::new(format!("./{file_name}")) .arg("1") .spawn() .unwrap(); let mut p_b = std::process::Command::new(format!("./{file_name}")) .arg("1") .spawn() .unwrap(); let pid_a = Pid::from_u32(p_a.id() as _); let pid_b = Pid::from_u32(p_b.id() as _); let mut s = System::new(); let process_refresh_kind = ProcessRefreshKind::new().with_cpu(); s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid_a]), false, process_refresh_kind, ); s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid_b]), false, process_refresh_kind, ); std::thread::sleep(std::time::Duration::from_secs(1)); s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid_a]), true, process_refresh_kind, ); s.refresh_processes_specifics( ProcessesToUpdate::Some(&[pid_b]), true, process_refresh_kind, ); let cpu_a = s.process(pid_a).unwrap().cpu_usage(); let cpu_b = s.process(pid_b).unwrap().cpu_usage(); p_a.kill().expect("failed to kill process a"); p_b.kill().expect("failed to kill process b"); let _ = p_a.wait(); let _ = p_b.wait(); assert!(cpu_b - 5. < cpu_a && cpu_b + 5. > cpu_a); }