process_state 1.0.0

A Rust library that lets you track, cache, and manage commands like a process manager
Documentation
use process_state::{ProcessState, ProcessStatus};
use std::process::Command;
use tempfile::TempDir;

/// Test adding and removing a process from the state.
///
/// Spawns a short-lived process, adds it to `ProcessState`,
/// verifies it is tracked, then removes it and verifies removal.
#[test]
fn test_add_and_remove_process() {
    let tmp = TempDir::new().unwrap();
    let ns = tmp.path().file_name().unwrap().to_str().unwrap();
    let mut state = ProcessState::new(ns).unwrap();

    // Spawn a short-lived process
    let child = if cfg!(windows) {
        Command::new("timeout").arg("2").spawn().unwrap()
    } else {
        Command::new("sleep").arg("2").spawn().unwrap()
    };

    // Track the process
    state.add_process(&child, "test", "sleep 2").unwrap();

    // Ensure it's added correctly
    assert_eq!(state.get_all().len(), 1);
    assert_eq!(state.get_running().len(), 1);

    // Remove the process and check removal
    let pid = child.id();
    state.remove_process(pid).unwrap();
    assert_eq!(state.get_all().len(), 0);
}

/// Test that `refresh` updates the status of processes.
///
/// Spawns a process, ensures it's running, waits for it to exit,
/// refreshes the state, and verifies that the status is updated to `Stopped`.
#[test]
fn test_refresh_updates_status() {
    let tmp = TempDir::new().unwrap();
    let ns = tmp.path().file_name().unwrap().to_str().unwrap();
    let mut state = ProcessState::new(ns).unwrap();

    // Spawn a short-lived process
    let mut child = if cfg!(windows) {
        Command::new("timeout").arg("1").spawn().unwrap()
    } else {
        Command::new("sleep").arg("1").spawn().unwrap()
    };

    // Track the process
    state.add_process(&child, "short", "sleep 1").unwrap();

    // Initially, process should be running
    state.refresh().unwrap();
    assert_eq!(state.get_running().len(), 1);

    // Wait for process to exit and refresh
    child.wait().unwrap();
    state.refresh().unwrap();

    // Status should now be updated to stopped
    let all = state.get_all();
    let info = all.first().unwrap();
    assert_eq!(info.status, ProcessStatus::Stopped);
}

/// Test that process state persists across `ProcessState` instances.
///
/// Adds a process, drops the `ProcessState`, reopens it, and verifies that
/// the process is still tracked in the loaded state.
#[test]
fn test_persistence() {
    let tmp = TempDir::new().unwrap();
    let ns = tmp.path().file_name().unwrap().to_str().unwrap();

    {
        let mut state = ProcessState::new(ns).unwrap();

        // Spawn a short-lived process
        let child = if cfg!(windows) {
            Command::new("timeout").arg("1").spawn().unwrap()
        } else {
            Command::new("sleep").arg("1").spawn().unwrap()
        };

        state.add_process(&child, "persist", "sleep 1").unwrap();

        // Check that it's added
        let all = state.get_all();
        let info = all.first().unwrap();
        assert_eq!(info.label, "persist");
    }

    // Reload state from disk
    let state2 = ProcessState::new(ns).unwrap();
    let all = state2.get_all();
    let info = all.first().unwrap();
    assert_eq!(info.label, "persist");
}