feat: Add process package to monorepo
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
- Add `sal-process` package for cross-platform process management. - Update workspace members in `Cargo.toml`. - Mark process package as complete in MONOREPO_CONVERSION_PLAN.md - Remove license information from `mycelium` and `os` READMEs.
This commit is contained in:
278
process/tests/mgmt_tests.rs
Normal file
278
process/tests/mgmt_tests.rs
Normal file
@@ -0,0 +1,278 @@
|
||||
use sal_process::{kill, process_get, process_list, which, ProcessError};
|
||||
|
||||
#[test]
|
||||
fn test_which_existing_command() {
|
||||
// Test with a command that should exist on all systems
|
||||
#[cfg(target_os = "windows")]
|
||||
let cmd = "cmd";
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let cmd = "sh";
|
||||
|
||||
let result = which(cmd);
|
||||
assert!(result.is_some());
|
||||
assert!(!result.unwrap().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_which_nonexistent_command() {
|
||||
let result = which("nonexistent_command_12345");
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_which_common_commands() {
|
||||
// Test common commands that should exist
|
||||
let common_commands = if cfg!(target_os = "windows") {
|
||||
vec!["cmd", "powershell"]
|
||||
} else {
|
||||
vec!["sh", "ls", "echo"]
|
||||
};
|
||||
|
||||
for cmd in common_commands {
|
||||
let result = which(cmd);
|
||||
assert!(result.is_some(), "Command '{}' should be found", cmd);
|
||||
assert!(!result.unwrap().is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_list_all() {
|
||||
let result = process_list("").unwrap();
|
||||
assert!(
|
||||
!result.is_empty(),
|
||||
"Should find at least one running process"
|
||||
);
|
||||
|
||||
// Verify process info structure
|
||||
let first_process = &result[0];
|
||||
assert!(first_process.pid > 0, "Process PID should be positive");
|
||||
assert!(
|
||||
!first_process.name.is_empty(),
|
||||
"Process name should not be empty"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_list_with_pattern() {
|
||||
// Try to find processes with common names
|
||||
let patterns = if cfg!(target_os = "windows") {
|
||||
vec!["explorer", "winlogon", "System"]
|
||||
} else {
|
||||
vec!["init", "kernel", "systemd"]
|
||||
};
|
||||
|
||||
let mut found_any = false;
|
||||
for pattern in patterns {
|
||||
if let Ok(processes) = process_list(pattern) {
|
||||
if !processes.is_empty() {
|
||||
found_any = true;
|
||||
for process in processes {
|
||||
assert!(
|
||||
process.name.contains(pattern)
|
||||
|| process
|
||||
.name
|
||||
.to_lowercase()
|
||||
.contains(&pattern.to_lowercase())
|
||||
);
|
||||
assert!(process.pid > 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// At least one pattern should match some processes
|
||||
assert!(
|
||||
found_any,
|
||||
"Should find at least one process with common patterns"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_list_nonexistent_pattern() {
|
||||
let result = process_list("nonexistent_process_12345").unwrap();
|
||||
assert!(
|
||||
result.is_empty(),
|
||||
"Should not find any processes with nonexistent pattern"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_info_structure() {
|
||||
let processes = process_list("").unwrap();
|
||||
assert!(!processes.is_empty());
|
||||
|
||||
let process = &processes[0];
|
||||
|
||||
// Test ProcessInfo fields
|
||||
assert!(process.pid > 0);
|
||||
assert!(!process.name.is_empty());
|
||||
// memory and cpu are placeholders, so we just check they exist
|
||||
assert!(process.memory >= 0.0);
|
||||
assert!(process.cpu >= 0.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_get_single_match() {
|
||||
// Find a process that should be unique
|
||||
let processes = process_list("").unwrap();
|
||||
assert!(!processes.is_empty());
|
||||
|
||||
// Try to find a process with a unique enough name
|
||||
let mut unique_process = None;
|
||||
for process in &processes {
|
||||
let matches = process_list(&process.name).unwrap();
|
||||
if matches.len() == 1 {
|
||||
unique_process = Some(process.clone());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(process) = unique_process {
|
||||
let result = process_get(&process.name).unwrap();
|
||||
assert_eq!(result.pid, process.pid);
|
||||
assert_eq!(result.name, process.name);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_get_no_match() {
|
||||
let result = process_get("nonexistent_process_12345");
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
ProcessError::NoProcessFound(pattern) => {
|
||||
assert_eq!(pattern, "nonexistent_process_12345");
|
||||
}
|
||||
_ => panic!("Expected NoProcessFound error"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_get_multiple_matches() {
|
||||
// Find a pattern that matches multiple processes
|
||||
let all_processes = process_list("").unwrap();
|
||||
assert!(!all_processes.is_empty());
|
||||
|
||||
// Try common patterns that might match multiple processes
|
||||
let patterns = if cfg!(target_os = "windows") {
|
||||
vec!["svchost", "conhost"]
|
||||
} else {
|
||||
vec!["kthread", "ksoftirqd"]
|
||||
};
|
||||
|
||||
let mut _found_multiple = false;
|
||||
for pattern in patterns {
|
||||
if let Ok(processes) = process_list(pattern) {
|
||||
if processes.len() > 1 {
|
||||
let result = process_get(pattern);
|
||||
assert!(result.is_err());
|
||||
match result.unwrap_err() {
|
||||
ProcessError::MultipleProcessesFound(p, count) => {
|
||||
assert_eq!(p, pattern);
|
||||
assert_eq!(count, processes.len());
|
||||
_found_multiple = true;
|
||||
break;
|
||||
}
|
||||
_ => panic!("Expected MultipleProcessesFound error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we can't find multiple matches with common patterns, that's okay
|
||||
// The test validates the error handling works correctly
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_kill_nonexistent_process() {
|
||||
let result = kill("nonexistent_process_12345").unwrap();
|
||||
assert!(result.contains("No matching processes") || result.contains("Successfully killed"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_list_performance() {
|
||||
use std::time::Instant;
|
||||
|
||||
let start = Instant::now();
|
||||
let _processes = process_list("").unwrap();
|
||||
let duration = start.elapsed();
|
||||
|
||||
// Process listing should complete within reasonable time (5 seconds)
|
||||
assert!(
|
||||
duration.as_secs() < 5,
|
||||
"Process listing took too long: {:?}",
|
||||
duration
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_which_performance() {
|
||||
use std::time::Instant;
|
||||
|
||||
let start = Instant::now();
|
||||
let _result = which("echo");
|
||||
let duration = start.elapsed();
|
||||
|
||||
// Which command should be very fast (1 second)
|
||||
assert!(
|
||||
duration.as_secs() < 1,
|
||||
"Which command took too long: {:?}",
|
||||
duration
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_list_filtering_accuracy() {
|
||||
// Test that filtering actually works correctly
|
||||
let all_processes = process_list("").unwrap();
|
||||
assert!(!all_processes.is_empty());
|
||||
|
||||
// Pick a process name and filter by it
|
||||
let test_process = &all_processes[0];
|
||||
let filtered_processes = process_list(&test_process.name).unwrap();
|
||||
|
||||
// All filtered processes should contain the pattern
|
||||
for process in filtered_processes {
|
||||
assert!(process.name.contains(&test_process.name));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_process_error_display() {
|
||||
let error = ProcessError::NoProcessFound("test".to_string());
|
||||
let error_string = format!("{}", error);
|
||||
assert!(error_string.contains("No processes found matching 'test'"));
|
||||
|
||||
let error = ProcessError::MultipleProcessesFound("test".to_string(), 5);
|
||||
let error_string = format!("{}", error);
|
||||
assert!(error_string.contains("Multiple processes (5) found matching 'test'"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_cross_platform_process_operations() {
|
||||
// Test operations that should work on all platforms
|
||||
|
||||
// Test which with platform-specific commands
|
||||
#[cfg(target_os = "windows")]
|
||||
{
|
||||
assert!(which("cmd").is_some());
|
||||
assert!(which("notepad").is_some());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
assert!(which("sh").is_some());
|
||||
assert!(which("ls").is_some());
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
assert!(which("sh").is_some());
|
||||
assert!(which("ls").is_some());
|
||||
}
|
||||
|
||||
// Test process listing works on all platforms
|
||||
let processes = process_list("").unwrap();
|
||||
assert!(!processes.is_empty());
|
||||
}
|
Reference in New Issue
Block a user