flash-watcher 0.1.0

An impossibly fast file watcher that executes commands when files change
use std::process::{Child, Command};

// Mock CommandRunner implementation similar to the one in main.rs
struct CommandRunner {
    command: Vec<String>,
    restart: bool,
    clear: bool,
    current_process: Option<Child>,
}

impl CommandRunner {
    fn new(command: Vec<String>, restart: bool, clear: bool) -> Self {
        Self {
            command,
            restart,
            clear,
            current_process: None,
        }
    }

    // Simplified run method for testing that doesn't actually spawn processes
    fn dry_run(&mut self) -> Result<(), String> {
        // In a real implementation, this would kill previous processes in restart mode
        if self.restart {
            if self.current_process.is_some() {
                // This would kill the previous process
                self.current_process = None;
            }
        }

        // Simulate successful command execution
        if self.command.is_empty() {
            return Err("Empty command".to_string());
        }

        // In restart mode, we would store the child process
        if self.restart {
            // Simulate storing a process
            self.current_process = Some(mock_child_process());
        }

        Ok(())
    }
}

// Helper to create a mock child process for testing
fn mock_child_process() -> Child {
    Command::new("echo").arg("test").spawn().unwrap()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_command_runner_new() {
        let cmd = vec!["cargo".to_string(), "test".to_string()];
        let runner = CommandRunner::new(cmd.clone(), true, false);

        assert_eq!(runner.command, cmd);
        assert_eq!(runner.restart, true);
        assert_eq!(runner.clear, false);
        assert!(runner.current_process.is_none());
    }

    #[test]
    fn test_command_runner_dry_run_empty_command() {
        let mut runner = CommandRunner::new(vec![], false, false);
        let result = runner.dry_run();

        assert!(result.is_err());
        assert_eq!(result.unwrap_err(), "Empty command");
    }

    #[test]
    fn test_command_runner_dry_run_success() {
        let mut runner =
            CommandRunner::new(vec!["echo".to_string(), "test".to_string()], false, false);
        let result = runner.dry_run();

        assert!(result.is_ok());
        assert!(runner.current_process.is_none()); // Non-restart mode doesn't save process
    }

    #[test]
    fn test_command_runner_restart_mode() {
        let mut runner =
            CommandRunner::new(vec!["echo".to_string(), "test".to_string()], true, false);

        // First run should succeed and save the process
        let result = runner.dry_run();
        assert!(result.is_ok());
        assert!(runner.current_process.is_some());

        // Keep a reference to identify if process changes
        let _first_proc_id = runner.current_process.as_ref().unwrap().id();

        // Second run should replace the process
        let result = runner.dry_run();
        assert!(result.is_ok());
        assert!(runner.current_process.is_some());

        // Process should be different (in real implementation)
        // We can't test this fully in dry_run, but we can at least verify one exists
        assert!(runner.current_process.is_some());

        // Clean up the mock process
        if let Some(mut child) = runner.current_process.take() {
            let _ = child.kill();
        }
    }
}