Files
another-boids-in-rust/vendor/sysinfo/tests/process.rs

1148 lines
36 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 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::nothing().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::nothing().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,
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 <https://github.com/GuillaumeGomez/sysinfo/issues/886>.
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::nothing().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::<Vec<_>>();
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());
// We will use this `System` instance for another check.
let mut old_system = System::new();
old_system.refresh_processes(ProcessesToUpdate::All, false);
assert!(old_system.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));
let mut new_system = sysinfo::System::new_with_specifics(RefreshKind::nothing());
new_system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[pid]),
true,
ProcessRefreshKind::nothing(),
);
// `new_system` should not have this removed process.
assert!(new_system.process(pid).is_none());
s.refresh_processes(ProcessesToUpdate::All, true);
// Checks that the process isn't listed anymore.
assert!(s.process(pid).is_none());
// And we ensure that refreshing it this way will work too (ie, not listed anymore).
old_system.refresh_processes_specifics(
ProcessesToUpdate::Some(&[pid]),
true,
ProcessRefreshKind::nothing(),
);
assert!(old_system.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::nothing().with_processes(ProcessRefreshKind::nothing()),
);
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() {
// Skip if unsupported.
if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") {
return;
}
// 1) Spawn a thread that waits on a channel, so we control when it exits.
let task_name = "controlled_test_thread";
let (tx, rx) = std::sync::mpsc::channel::<()>();
std::thread::Builder::new()
.name(task_name.to_string())
.spawn(move || {
// Wait until the main thread signals we can exit.
let _ = rx.recv();
})
.unwrap();
let pid = Pid::from_u32(std::process::id() as _);
let mut sys = System::new();
// Wait until the new thread shows up in the process/tasks list.
// We do a short loop and check each time by refreshing processes.
const MAX_POLLS: usize = 20;
const POLL_INTERVAL: std::time::Duration = std::time::Duration::from_millis(100);
for _ in 0..MAX_POLLS {
sys.refresh_processes(ProcessesToUpdate::All, /*refresh_users=*/ false);
// Check if our thread is present in two ways:
// (a) via parent's tasks
// (b) by exact name
let parent_proc = sys.process(pid);
let tasks_contain_thread = parent_proc
.and_then(|p| p.tasks())
.map(|tids| {
tids.iter().any(|tid| {
sys.process(*tid)
.map(|t| t.name() == task_name)
.unwrap_or(false)
})
})
.unwrap_or(false);
let by_exact_name_exists = sys
.processes_by_exact_name(task_name.as_ref())
.next()
.is_some();
if tasks_contain_thread && by_exact_name_exists {
// We confirmed the thread is now visible
break;
}
std::thread::sleep(POLL_INTERVAL);
}
// At this point we know the task is visible in the system's process/tasks list.
// Let's validate a few more things:
// * ProcessRefreshKind::nothing() should have task information.
// * ProcessRefreshKind::nothing().with_tasks() should have task information.
// * ProcessRefreshKind::nothing().without_tasks() shouldn't have task information.
// * ProcessRefreshKind::everything() should have task information.
// * ProcessRefreshKind::everything() should have task information.
// * ProcessRefreshKind::everything().without_tasks() should not have task information.
let expectations = [
(ProcessRefreshKind::nothing(), true),
(ProcessRefreshKind::nothing().with_tasks(), true),
(ProcessRefreshKind::nothing().without_tasks(), false),
(ProcessRefreshKind::everything(), true),
(ProcessRefreshKind::everything().with_tasks(), true),
(ProcessRefreshKind::everything().without_tasks(), false),
];
for (kind, expect_tasks) in expectations.iter() {
let mut sys_new = System::new();
sys_new.refresh_processes_specifics(ProcessesToUpdate::All, true, *kind);
let proc = sys_new.process(pid).unwrap();
assert_eq!(proc.tasks().is_some(), *expect_tasks);
}
// 3) Signal the thread to exit.
drop(tx);
// 4) Wait until the thread is gone from the systems process/tasks list.
for _ in 0..MAX_POLLS {
sys.refresh_processes(ProcessesToUpdate::All, /*refresh_users=*/ true);
let parent_proc = sys.process(pid as sysinfo::Pid);
let tasks_contain_thread = parent_proc
.and_then(|p| p.tasks())
.map(|tids| {
tids.iter().any(|tid| {
sys.process(*tid)
.map(|t| t.name() == task_name)
.unwrap_or(false)
})
})
.unwrap_or(false);
let by_exact_name_exists = sys
.processes_by_exact_name(task_name.as_ref())
.next()
.is_some();
// If it's gone from both checks, we're good.
if !tasks_contain_thread && !by_exact_name_exists {
break;
}
std::thread::sleep(POLL_INTERVAL);
}
}
// 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(
RefreshKind::nothing().with_processes(ProcessRefreshKind::nothing()),
);
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 <https://github.com/GuillaumeGomez/sysinfo/issues/918>.
#[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() {
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(), sysinfo::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::nothing());
{
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::nothing().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::nothing());
{
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::nothing());
{
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::nothing().$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::nothing());
{
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::nothing(),
);
check_empty(&s, pid);
s.refresh_processes_specifics(
ProcessesToUpdate::Some(&[pid]),
false,
ProcessRefreshKind::nothing(),
);
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::nothing().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);
}
#[test]
fn accumulated_cpu_time() {
fn generate_cpu_usage() {
use std::sync::atomic::{AtomicBool, Ordering};
let atomic = std::sync::Arc::new(AtomicBool::new(false));
let thread_atomic = atomic.clone();
std::thread::spawn(move || {
while !thread_atomic.load(Ordering::Relaxed) {
System::new_all();
}
});
std::thread::sleep(std::time::Duration::from_millis(250));
atomic.store(true, Ordering::Relaxed);
}
if !sysinfo::IS_SUPPORTED_SYSTEM
|| cfg!(feature = "apple-sandbox")
|| cfg!(target_os = "freebsd")
{
return;
}
let mut s = System::new();
let current_pid = sysinfo::get_current_pid().expect("failed to get current pid");
let refresh_kind = ProcessRefreshKind::nothing().with_cpu();
generate_cpu_usage();
s.refresh_processes_specifics(ProcessesToUpdate::Some(&[current_pid]), false, refresh_kind);
let acc_time = s
.process(current_pid)
.expect("no process found")
.accumulated_cpu_time();
assert_ne!(acc_time, 0);
generate_cpu_usage();
s.refresh_processes_specifics(ProcessesToUpdate::Some(&[current_pid]), true, refresh_kind);
let new_acc_time = s
.process(current_pid)
.expect("no process found")
.accumulated_cpu_time();
assert!(
new_acc_time > acc_time,
"{} not superior to {}",
new_acc_time,
acc_time
);
}
#[test]
fn test_exists() {
if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") {
return;
}
let file_name = "target/test_binary4";
build_test_binary(file_name);
let mut p = std::process::Command::new(format!("./{file_name}"))
.arg("1")
.spawn()
.unwrap();
let pid = Pid::from_u32(p.id() as _);
let mut s = System::new();
let process_refresh_kind = ProcessRefreshKind::nothing().with_memory();
s.refresh_processes_specifics(ProcessesToUpdate::Some(&[pid]), false, process_refresh_kind);
assert!(s.process(pid).unwrap().exists());
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_specifics(ProcessesToUpdate::Some(&[pid]), false, process_refresh_kind);
assert!(!s.process(pid).unwrap().exists());
}
#[cfg(target_os = "linux")]
#[test]
fn test_tasks() {
use std::collections::HashSet;
fn get_tasks(system: &System) -> HashSet<Pid> {
let pid_to_process = system.processes();
let mut task_pids: HashSet<Pid> = HashSet::new();
for process in pid_to_process.values() {
if let Some(tasks) = process.tasks() {
task_pids.extend(tasks);
}
}
task_pids
}
let mut system = System::new_with_specifics(RefreshKind::nothing());
system.refresh_processes_specifics(ProcessesToUpdate::All, true, ProcessRefreshKind::nothing());
// Spawn a thread to increase the task count
let scheduler = std::thread::spawn(move || {
system.refresh_processes_specifics(
ProcessesToUpdate::All,
true,
ProcessRefreshKind::nothing(),
);
let mut system_new = System::new_with_specifics(RefreshKind::nothing());
system_new.refresh_processes_specifics(
ProcessesToUpdate::All,
true,
ProcessRefreshKind::nothing(),
);
assert_eq!(get_tasks(&system), get_tasks(&system_new));
});
scheduler.join().expect("Scheduler panicked");
}
#[test]
fn open_files() {
if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") {
return;
}
let pid = sysinfo::get_current_pid().expect("failed to get current pid");
let _file =
std::fs::File::create(std::env::temp_dir().join("sysinfo-open-files.test")).unwrap();
let mut s = System::new();
s.refresh_processes(ProcessesToUpdate::Some(&[pid]), false);
let cur_process = s.process(pid).unwrap();
assert!(cur_process
.open_files()
.is_some_and(|open_files| open_files > 0));
assert!(cur_process
.open_files_limit()
.is_some_and(|open_files| open_files > 0));
}