use std::collections::{HashMap, HashSet};
use std::ffi::{OsStr, OsString};
use std::fmt;
use std::path::Path;
use std::process::ExitStatus;
use std::str::FromStr;
use crate::common::DiskUsage;
use crate::common::impl_get_set::impl_get_set;
use crate::{CpuInner, Gid, MotherboardInner, ProcessInner, ProductInner, SystemInner, Uid};
pub struct System {
pub(crate) inner: SystemInner,
}
impl Default for System {
fn default() -> System {
System::new()
}
}
impl System {
pub fn new() -> Self {
Self::new_with_specifics(RefreshKind::nothing())
}
pub fn new_all() -> Self {
Self::new_with_specifics(RefreshKind::everything())
}
pub fn new_with_specifics(refreshes: RefreshKind) -> Self {
let mut s = Self {
inner: SystemInner::new(),
};
s.refresh_specifics(refreshes);
s
}
pub fn refresh_specifics(&mut self, refreshes: RefreshKind) {
if let Some(kind) = refreshes.memory() {
self.refresh_memory_specifics(kind);
}
if let Some(kind) = refreshes.cpu() {
self.refresh_cpu_specifics(kind);
}
if let Some(kind) = refreshes.processes() {
self.refresh_processes_specifics(ProcessesToUpdate::All, true, kind);
}
}
pub fn refresh_all(&mut self) {
self.refresh_specifics(RefreshKind::everything());
}
pub fn refresh_memory(&mut self) {
self.refresh_memory_specifics(MemoryRefreshKind::everything())
}
pub fn refresh_memory_specifics(&mut self, refresh_kind: MemoryRefreshKind) {
self.inner.refresh_memory_specifics(refresh_kind)
}
pub fn refresh_cpu_usage(&mut self) {
self.refresh_cpu_specifics(CpuRefreshKind::nothing().with_cpu_usage())
}
pub fn refresh_cpu_frequency(&mut self) {
self.refresh_cpu_specifics(CpuRefreshKind::nothing().with_frequency())
}
pub fn refresh_cpu_list(&mut self, refresh_kind: CpuRefreshKind) {
self.inner.refresh_cpu_list(refresh_kind);
}
pub fn refresh_cpu_all(&mut self) {
self.refresh_cpu_specifics(CpuRefreshKind::everything())
}
pub fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
self.inner.refresh_cpu_specifics(refresh_kind)
}
pub fn refresh_processes(
&mut self,
processes_to_update: ProcessesToUpdate<'_>,
remove_dead_processes: bool,
) -> usize {
self.refresh_processes_specifics(
processes_to_update,
remove_dead_processes,
ProcessRefreshKind::nothing()
.with_memory()
.with_cpu()
.with_disk_usage()
.with_exe(UpdateKind::OnlyIfNotSet)
.with_tasks(),
)
}
pub fn refresh_processes_specifics(
&mut self,
processes_to_update: ProcessesToUpdate<'_>,
remove_dead_processes: bool,
refresh_kind: ProcessRefreshKind,
) -> usize {
fn update_and_remove(pid: &Pid, processes: &mut HashMap<Pid, Process>) {
let updated = if let Some(proc) = processes.get_mut(pid) {
proc.inner.switch_updated()
} else {
return;
};
if !updated {
processes.remove(pid);
}
}
fn update(pid: &Pid, processes: &mut HashMap<Pid, Process>) {
if let Some(proc) = processes.get_mut(pid)
&& !proc.inner.switch_updated()
{
proc.inner.set_nonexistent();
}
}
let nb_updated = self
.inner
.refresh_processes_specifics(processes_to_update, refresh_kind);
let processes = self.inner.processes_mut();
match processes_to_update {
ProcessesToUpdate::All => {
if remove_dead_processes {
processes.retain(|_, v| v.inner.switch_updated());
} else {
for proc in processes.values_mut() {
proc.inner.switch_updated();
}
}
}
ProcessesToUpdate::Some(pids) => {
let call = if remove_dead_processes {
update_and_remove
} else {
update
};
for pid in pids {
call(pid, processes);
}
}
}
nb_updated
}
pub fn processes(&self) -> &HashMap<Pid, Process> {
self.inner.processes()
}
pub fn process(&self, pid: Pid) -> Option<&Process> {
self.inner.process(pid)
}
pub fn processes_by_name<'a: 'b, 'b>(
&'a self,
name: &'b OsStr,
) -> impl Iterator<Item = &'a Process> + 'b {
let finder = memchr::memmem::Finder::new(name.as_encoded_bytes());
self.processes()
.values()
.filter(move |val: &&Process| finder.find(val.name().as_encoded_bytes()).is_some())
}
pub fn processes_by_exact_name<'a: 'b, 'b>(
&'a self,
name: &'b OsStr,
) -> impl Iterator<Item = &'a Process> + 'b {
self.processes()
.values()
.filter(move |val: &&Process| val.name() == name)
}
pub fn global_cpu_usage(&self) -> f32 {
self.inner.global_cpu_usage()
}
pub fn cpus(&self) -> &[Cpu] {
self.inner.cpus()
}
pub fn total_memory(&self) -> u64 {
self.inner.total_memory()
}
pub fn free_memory(&self) -> u64 {
self.inner.free_memory()
}
pub fn available_memory(&self) -> u64 {
self.inner.available_memory()
}
pub fn used_memory(&self) -> u64 {
self.inner.used_memory()
}
pub fn total_swap(&self) -> u64 {
self.inner.total_swap()
}
pub fn free_swap(&self) -> u64 {
self.inner.free_swap()
}
pub fn used_swap(&self) -> u64 {
self.inner.used_swap()
}
pub fn cgroup_limits(&self) -> Option<CGroupLimits> {
self.inner.cgroup_limits()
}
pub fn uptime() -> u64 {
SystemInner::uptime()
}
pub fn boot_time() -> u64 {
SystemInner::boot_time()
}
pub fn load_average() -> LoadAvg {
SystemInner::load_average()
}
pub fn name() -> Option<String> {
SystemInner::name()
}
pub fn kernel_version() -> Option<String> {
SystemInner::kernel_version()
}
pub fn os_version() -> Option<String> {
SystemInner::os_version()
}
pub fn long_os_version() -> Option<String> {
SystemInner::long_os_version()
}
pub fn distribution_id() -> String {
SystemInner::distribution_id()
}
pub fn distribution_id_like() -> Vec<String> {
SystemInner::distribution_id_like()
}
pub fn kernel_long_version() -> String {
let kernel_version = match System::kernel_version() {
None => "unknown".to_string(),
Some(s) => s,
};
let kernel_name = SystemInner::kernel_name().unwrap_or("Unknown");
if cfg!(windows) {
format!("{kernel_name} OS Build {kernel_version}")
} else {
format!("{kernel_name} {kernel_version}")
}
}
pub fn host_name() -> Option<String> {
SystemInner::host_name()
}
pub fn cpu_arch() -> String {
SystemInner::cpu_arch().unwrap_or_else(|| std::env::consts::ARCH.to_owned())
}
pub fn physical_core_count() -> Option<usize> {
SystemInner::physical_core_count()
}
pub fn open_files_limit() -> Option<usize> {
SystemInner::open_files_limit()
}
}
pub struct Motherboard {
pub(crate) inner: MotherboardInner,
}
impl Motherboard {
pub fn new() -> Option<Self> {
Some(Self {
inner: MotherboardInner::new()?,
})
}
pub fn name(&self) -> Option<String> {
self.inner.name()
}
pub fn vendor_name(&self) -> Option<String> {
self.inner.vendor_name()
}
pub fn version(&self) -> Option<String> {
self.inner.version()
}
pub fn serial_number(&self) -> Option<String> {
self.inner.serial_number()
}
pub fn asset_tag(&self) -> Option<String> {
self.inner.asset_tag()
}
}
pub struct Product;
impl Product {
pub fn name() -> Option<String> {
ProductInner::name()
}
pub fn family() -> Option<String> {
ProductInner::family()
}
pub fn serial_number() -> Option<String> {
ProductInner::serial_number()
}
#[doc(alias = "sku")]
pub fn stock_keeping_unit() -> Option<String> {
ProductInner::stock_keeping_unit()
}
pub fn uuid() -> Option<String> {
ProductInner::uuid()
}
pub fn version() -> Option<String> {
ProductInner::version()
}
pub fn vendor_name() -> Option<String> {
ProductInner::vendor_name()
}
}
#[repr(C)]
#[derive(Default, Debug, Clone)]
pub struct LoadAvg {
pub one: f64,
pub five: f64,
pub fifteen: f64,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub enum Signal {
Hangup,
Interrupt,
Quit,
Illegal,
Trap,
Abort,
IOT,
Bus,
FloatingPointException,
Kill,
User1,
Segv,
User2,
Pipe,
Alarm,
Term,
Child,
Continue,
Stop,
TSTP,
TTIN,
TTOU,
Urgent,
XCPU,
XFSZ,
VirtualAlarm,
Profiling,
Winch,
IO,
Poll,
Power,
Sys,
}
impl std::fmt::Display for Signal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match *self {
Self::Hangup => "Hangup",
Self::Interrupt => "Interrupt",
Self::Quit => "Quit",
Self::Illegal => "Illegal",
Self::Trap => "Trap",
Self::Abort => "Abort",
Self::IOT => "IOT",
Self::Bus => "Bus",
Self::FloatingPointException => "FloatingPointException",
Self::Kill => "Kill",
Self::User1 => "User1",
Self::Segv => "Segv",
Self::User2 => "User2",
Self::Pipe => "Pipe",
Self::Alarm => "Alarm",
Self::Term => "Term",
Self::Child => "Child",
Self::Continue => "Continue",
Self::Stop => "Stop",
Self::TSTP => "TSTP",
Self::TTIN => "TTIN",
Self::TTOU => "TTOU",
Self::Urgent => "Urgent",
Self::XCPU => "XCPU",
Self::XFSZ => "XFSZ",
Self::VirtualAlarm => "VirtualAlarm",
Self::Profiling => "Profiling",
Self::Winch => "Winch",
Self::IO => "IO",
Self::Poll => "Poll",
Self::Power => "Power",
Self::Sys => "Sys",
};
f.write_str(s)
}
}
#[derive(Default, Debug, Clone)]
pub struct CGroupLimits {
pub total_memory: u64,
pub free_memory: u64,
pub free_swap: u64,
pub rss: u64,
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub enum ProcessStatus {
Idle,
Run,
Sleep,
Stop,
Zombie,
Tracing,
Dead,
Wakekill,
Waking,
Parked,
LockBlocked,
UninterruptibleDiskSleep,
Unknown(u32),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub enum ThreadKind {
Kernel,
Userland,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize))]
pub enum KillError {
SignalDoesNotExist,
FailedToSendSignal,
}
pub struct Process {
pub(crate) inner: ProcessInner,
}
impl Process {
pub fn kill(&self) -> bool {
self.kill_with(Signal::Kill).unwrap_or(false)
}
pub fn kill_with(&self, signal: Signal) -> Option<bool> {
self.inner.kill_with(signal)
}
pub fn kill_and_wait(&self) -> Result<Option<ExitStatus>, KillError> {
self.kill_with_and_wait(Signal::Kill)
}
pub fn kill_with_and_wait(&self, signal: Signal) -> Result<Option<ExitStatus>, KillError> {
match self.inner.kill_with(signal) {
Some(sent) => {
if !sent {
return Err(KillError::FailedToSendSignal);
}
}
None => return Err(KillError::SignalDoesNotExist),
}
Ok(self.inner.wait())
}
pub fn wait(&self) -> Option<ExitStatus> {
self.inner.wait()
}
pub fn name(&self) -> &OsStr {
self.inner.name()
}
pub fn cmd(&self) -> &[OsString] {
self.inner.cmd()
}
pub fn exe(&self) -> Option<&Path> {
self.inner.exe()
}
pub fn pid(&self) -> Pid {
self.inner.pid()
}
pub fn environ(&self) -> &[OsString] {
self.inner.environ()
}
pub fn cwd(&self) -> Option<&Path> {
self.inner.cwd()
}
pub fn root(&self) -> Option<&Path> {
self.inner.root()
}
pub fn memory(&self) -> u64 {
self.inner.memory()
}
pub fn virtual_memory(&self) -> u64 {
self.inner.virtual_memory()
}
pub fn parent(&self) -> Option<Pid> {
self.inner.parent()
}
pub fn status(&self) -> ProcessStatus {
self.inner.status()
}
pub fn start_time(&self) -> u64 {
self.inner.start_time()
}
pub fn run_time(&self) -> u64 {
self.inner.run_time()
}
pub fn cpu_usage(&self) -> f32 {
self.inner.cpu_usage()
}
pub fn accumulated_cpu_time(&self) -> u64 {
self.inner.accumulated_cpu_time()
}
pub fn disk_usage(&self) -> DiskUsage {
self.inner.disk_usage()
}
pub fn user_id(&self) -> Option<&Uid> {
self.inner.user_id()
}
pub fn effective_user_id(&self) -> Option<&Uid> {
self.inner.effective_user_id()
}
pub fn group_id(&self) -> Option<Gid> {
self.inner.group_id()
}
pub fn effective_group_id(&self) -> Option<Gid> {
self.inner.effective_group_id()
}
pub fn session_id(&self) -> Option<Pid> {
self.inner.session_id()
}
pub fn tasks(&self) -> Option<&HashSet<Pid>> {
cfg_if! {
if #[cfg(all(
any(target_os = "linux", target_os = "android"),
not(feature = "unknown-ci")
))] {
self.inner.tasks.as_ref()
} else {
None
}
}
}
pub fn thread_kind(&self) -> Option<ThreadKind> {
cfg_if! {
if #[cfg(all(
any(target_os = "linux", target_os = "android"),
not(feature = "unknown-ci")
))] {
self.inner.thread_kind()
} else {
None
}
}
}
pub fn exists(&self) -> bool {
self.inner.exists()
}
pub fn open_files(&self) -> Option<usize> {
self.inner.open_files()
}
pub fn open_files_limit(&self) -> Option<usize> {
self.inner.open_files_limit()
}
}
macro_rules! pid_decl {
($typ:ty) => {
#[doc = include_str!("../../md_doc/pid.md")]
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[repr(transparent)]
pub struct Pid(pub(crate) $typ);
impl From<usize> for Pid {
fn from(v: usize) -> Self {
Self(v as _)
}
}
impl From<Pid> for usize {
fn from(v: Pid) -> Self {
v.0 as _
}
}
impl FromStr for Pid {
type Err = <$typ as FromStr>::Err;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(Self(<$typ>::from_str(s)?))
}
}
impl fmt::Display for Pid {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
impl Pid {
pub fn as_u32(self) -> u32 {
self.0 as _
}
pub fn from_u32(v: u32) -> Self {
Self(v as _)
}
}
};
}
cfg_if! {
if #[cfg(all(
not(feature = "unknown-ci"),
any(
target_os = "freebsd",
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
)
))] {
use libc::pid_t;
pid_decl!(pid_t);
} else {
pid_decl!(usize);
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum UpdateKind {
#[default]
Never,
Always,
OnlyIfNotSet,
}
impl UpdateKind {
#[allow(dead_code)] pub(crate) fn needs_update(self, f: impl Fn() -> bool) -> bool {
match self {
Self::Never => false,
Self::Always => true,
Self::OnlyIfNotSet => f(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ProcessesToUpdate<'a> {
All,
Some(&'a [Pid]),
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ProcessRefreshKind {
cpu: bool,
disk_usage: bool,
memory: bool,
user: UpdateKind,
cwd: UpdateKind,
root: UpdateKind,
environ: UpdateKind,
cmd: UpdateKind,
exe: UpdateKind,
tasks: bool,
}
impl Default for ProcessRefreshKind {
fn default() -> Self {
Self {
cpu: false,
disk_usage: false,
memory: false,
user: UpdateKind::default(),
cwd: UpdateKind::default(),
root: UpdateKind::default(),
environ: UpdateKind::default(),
cmd: UpdateKind::default(),
exe: UpdateKind::default(),
tasks: true, }
}
}
impl ProcessRefreshKind {
pub fn nothing() -> Self {
Self::default()
}
pub fn everything() -> Self {
Self {
cpu: true,
disk_usage: true,
memory: true,
user: UpdateKind::OnlyIfNotSet,
cwd: UpdateKind::OnlyIfNotSet,
root: UpdateKind::OnlyIfNotSet,
environ: UpdateKind::OnlyIfNotSet,
cmd: UpdateKind::OnlyIfNotSet,
exe: UpdateKind::OnlyIfNotSet,
tasks: true,
}
}
impl_get_set!(
ProcessRefreshKind,
cpu,
with_cpu,
without_cpu,
"\
It will retrieve both CPU usage and CPU accumulated time,"
);
impl_get_set!(
ProcessRefreshKind,
disk_usage,
with_disk_usage,
without_disk_usage
);
impl_get_set!(
ProcessRefreshKind,
user,
with_user,
without_user,
UpdateKind,
"\
It will retrieve the following information:
* user ID
* user effective ID (if available on the platform)
* user group ID (if available on the platform)
* user effective ID (if available on the platform)"
);
impl_get_set!(ProcessRefreshKind, memory, with_memory, without_memory);
impl_get_set!(ProcessRefreshKind, cwd, with_cwd, without_cwd, UpdateKind);
impl_get_set!(
ProcessRefreshKind,
root,
with_root,
without_root,
UpdateKind
);
impl_get_set!(
ProcessRefreshKind,
environ,
with_environ,
without_environ,
UpdateKind
);
impl_get_set!(ProcessRefreshKind, cmd, with_cmd, without_cmd, UpdateKind);
impl_get_set!(ProcessRefreshKind, exe, with_exe, without_exe, UpdateKind);
impl_get_set!(ProcessRefreshKind, tasks, with_tasks, without_tasks);
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct CpuRefreshKind {
cpu_usage: bool,
frequency: bool,
}
impl CpuRefreshKind {
pub fn nothing() -> Self {
Self::default()
}
pub fn everything() -> Self {
Self {
cpu_usage: true,
frequency: true,
}
}
impl_get_set!(CpuRefreshKind, cpu_usage, with_cpu_usage, without_cpu_usage);
impl_get_set!(CpuRefreshKind, frequency, with_frequency, without_frequency);
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct MemoryRefreshKind {
ram: bool,
swap: bool,
}
impl MemoryRefreshKind {
pub fn nothing() -> Self {
Self::default()
}
pub fn everything() -> Self {
Self {
ram: true,
swap: true,
}
}
impl_get_set!(MemoryRefreshKind, ram, with_ram, without_ram);
impl_get_set!(MemoryRefreshKind, swap, with_swap, without_swap);
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct RefreshKind {
processes: Option<ProcessRefreshKind>,
memory: Option<MemoryRefreshKind>,
cpu: Option<CpuRefreshKind>,
}
impl RefreshKind {
pub fn nothing() -> Self {
Self::default()
}
pub fn everything() -> Self {
Self {
processes: Some(ProcessRefreshKind::everything()),
memory: Some(MemoryRefreshKind::everything()),
cpu: Some(CpuRefreshKind::everything()),
}
}
impl_get_set!(
RefreshKind,
processes,
with_processes,
without_processes,
ProcessRefreshKind
);
impl_get_set!(
RefreshKind,
memory,
with_memory,
without_memory,
MemoryRefreshKind
);
impl_get_set!(RefreshKind, cpu, with_cpu, without_cpu, CpuRefreshKind);
}
#[allow(clippy::unnecessary_wraps)]
pub fn get_current_pid() -> Result<Pid, &'static str> {
cfg_if! {
if #[cfg(feature = "unknown-ci")] {
fn inner() -> Result<Pid, &'static str> {
Err("Unknown platform (CI)")
}
} else if #[cfg(any(
target_os = "freebsd",
target_os = "linux",
target_os = "android",
target_os = "macos",
target_os = "ios",
))] {
fn inner() -> Result<Pid, &'static str> {
unsafe { Ok(Pid(libc::getpid())) }
}
} else if #[cfg(windows)] {
fn inner() -> Result<Pid, &'static str> {
use windows::Win32::System::Threading::GetCurrentProcessId;
unsafe { Ok(Pid(GetCurrentProcessId() as _)) }
}
} else {
fn inner() -> Result<Pid, &'static str> {
Err("Unknown platform")
}
}
}
inner()
}
pub struct Cpu {
pub(crate) inner: CpuInner,
}
impl Cpu {
pub fn cpu_usage(&self) -> f32 {
self.inner.cpu_usage()
}
pub fn name(&self) -> &str {
self.inner.name()
}
pub fn vendor_id(&self) -> &str {
self.inner.vendor_id()
}
pub fn brand(&self) -> &str {
self.inner.brand()
}
pub fn frequency(&self) -> u64 {
self.inner.frequency()
}
}
#[cfg(test)]
mod test {
use crate::*;
use std::str::FromStr;
#[test]
fn check_refresh_process_update() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new_all();
let total = s.processes().len() as isize;
s.refresh_processes(ProcessesToUpdate::All, false);
let new_total = s.processes().len() as isize;
assert!(
(new_total - total).abs() <= 5,
"{} <= 5",
(new_total - total).abs()
);
}
#[test]
fn check_cpu_arch() {
assert!(!System::cpu_arch().is_empty());
}
#[test]
fn check_cpu_frequency() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new();
s.refresh_processes(ProcessesToUpdate::All, false);
for proc_ in s.cpus() {
assert_eq!(proc_.frequency(), 0);
}
s.refresh_cpu_usage();
for proc_ in s.cpus() {
assert_eq!(proc_.frequency(), 0);
}
if std::env::var("APPLE_CI").is_err() && std::env::var("FREEBSD_CI").is_err() {
s.refresh_cpu_specifics(CpuRefreshKind::everything());
for proc_ in s.cpus() {
assert_ne!(proc_.frequency(), 0);
}
}
}
#[test]
fn check_process_memory_usage() {
let mut s = System::new();
s.refresh_specifics(RefreshKind::everything());
if IS_SUPPORTED_SYSTEM {
#[cfg(not(feature = "apple-sandbox"))]
assert!(!s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
} else {
assert!(s.processes().iter().all(|(_, proc_)| proc_.memory() == 0));
}
}
#[test]
fn check_system_implemented_traits() {
fn check<T: Sized + std::fmt::Debug + Default + Send + Sync>(_: T) {}
check(System::new());
}
#[test]
fn check_memory_usage() {
let mut s = System::new();
assert_eq!(s.total_memory(), 0);
assert_eq!(s.free_memory(), 0);
assert_eq!(s.available_memory(), 0);
assert_eq!(s.used_memory(), 0);
assert_eq!(s.total_swap(), 0);
assert_eq!(s.free_swap(), 0);
assert_eq!(s.used_swap(), 0);
s.refresh_memory();
if IS_SUPPORTED_SYSTEM {
assert!(s.total_memory() > 0);
assert!(s.used_memory() > 0);
if s.total_swap() > 0 {
assert!(s.free_swap() > 0);
}
} else {
assert_eq!(s.total_memory(), 0);
assert_eq!(s.used_memory(), 0);
assert_eq!(s.total_swap(), 0);
assert_eq!(s.free_swap(), 0);
}
}
#[cfg(target_os = "linux")]
#[test]
fn check_processes_cpu_usage() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new();
s.refresh_processes(ProcessesToUpdate::All, false);
assert!(
s.processes()
.iter()
.all(|(_, proc_)| proc_.cpu_usage() == 0.0)
);
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL);
s.refresh_processes(ProcessesToUpdate::All, true);
assert!(
s.processes()
.iter()
.all(|(_, proc_)| proc_.cpu_usage() >= 0.0
&& proc_.cpu_usage() <= (s.cpus().len() as f32) * 100.0)
);
assert!(
s.processes()
.iter()
.any(|(_, proc_)| proc_.cpu_usage() > 0.0)
);
}
#[test]
fn check_cpu_usage() {
if !IS_SUPPORTED_SYSTEM {
return;
}
let mut s = System::new();
for _ in 0..10 {
s.refresh_cpu_usage();
std::thread::sleep(MINIMUM_CPU_UPDATE_INTERVAL);
if s.cpus().iter().any(|c| c.cpu_usage() > 0.0) {
return;
}
}
panic!("CPU usage is always zero...");
}
#[test]
fn check_system_info() {
if IS_SUPPORTED_SYSTEM {
assert!(
!System::name()
.expect("Failed to get system name")
.is_empty()
);
assert!(
!System::kernel_version()
.expect("Failed to get kernel version")
.is_empty()
);
assert!(
!System::os_version()
.expect("Failed to get os version")
.is_empty()
);
assert!(
!System::long_os_version()
.expect("Failed to get long OS version")
.is_empty()
);
}
assert!(!System::distribution_id().is_empty());
}
#[test]
fn check_host_name() {
if IS_SUPPORTED_SYSTEM {
assert!(System::host_name().is_some());
}
}
#[test]
fn check_refresh_process_return_value() {
if IS_SUPPORTED_SYSTEM {
let _pid = get_current_pid().expect("Failed to get current PID");
#[cfg(not(feature = "apple-sandbox"))]
{
let mut s = System::new();
assert_eq!(
s.refresh_processes(ProcessesToUpdate::Some(&[_pid]), true),
1
);
assert_eq!(
s.refresh_processes(ProcessesToUpdate::Some(&[_pid]), true),
1
);
}
}
}
#[test]
fn check_cpus_number() {
let mut s = System::new();
assert!(s.cpus().is_empty());
if IS_SUPPORTED_SYSTEM {
let physical_cores_count =
System::physical_core_count().expect("failed to get number of physical cores");
s.refresh_cpu_usage();
assert!(!s.cpus().is_empty());
let physical_cores_count2 =
System::physical_core_count().expect("failed to get number of physical cores");
assert!(physical_cores_count2 <= s.cpus().len());
assert_eq!(physical_cores_count, physical_cores_count2);
} else {
assert_eq!(System::physical_core_count(), None);
}
assert!(System::physical_core_count().unwrap_or(0) <= s.cpus().len());
}
#[test]
fn check_display_impl_process_status() {
println!("{} {:?}", ProcessStatus::Parked, ProcessStatus::Idle);
}
#[test]
#[allow(clippy::unnecessary_fallible_conversions)]
fn check_pid_from_impls() {
assert!(crate::Pid::try_from(0usize).is_ok());
let _ = crate::Pid::from(0);
assert!(crate::Pid::from_str("0").is_ok());
}
#[test]
#[allow(clippy::const_is_empty)]
fn check_nb_supported_signals() {
if IS_SUPPORTED_SYSTEM {
assert!(
!SUPPORTED_SIGNALS.is_empty(),
"SUPPORTED_SIGNALS shouldn't be empty on supported systems!"
);
} else {
assert!(
SUPPORTED_SIGNALS.is_empty(),
"SUPPORTED_SIGNALS should be empty on not support systems!"
);
}
}
}
#[cfg(doctest)]
mod doctest {
mod process_clone {}
mod system_clone {}
}