use crate::state::{ShellState, Job, JobStatus, enqueue_signal, check_background_jobs};
#[test]
fn test_sigchld_handler_registration() {
enqueue_signal("CHLD", 17);
}
#[test]
fn test_job_status_update() {
let shell_state = ShellState::new();
let job = Job::new(1, Some(1234), "sleep 10 &".to_string(), vec![1234], false);
shell_state.job_table.borrow_mut().add_job(job);
let updated = shell_state.job_table.borrow_mut().update_job_status(1234, JobStatus::Done(0));
assert!(updated);
let job = shell_state.job_table.borrow().get_job(1).unwrap().clone();
assert_eq!(job.status, JobStatus::Done(0));
assert_eq!(job.exit_code, Some(0));
}
#[test]
fn test_job_status_update_stopped() {
let shell_state = ShellState::new();
let job = Job::new(1, Some(1234), "sleep 10 &".to_string(), vec![1234], false);
shell_state.job_table.borrow_mut().add_job(job);
let updated = shell_state.job_table.borrow_mut().update_job_status(1234, JobStatus::Stopped);
assert!(updated);
let job = shell_state.job_table.borrow().get_job(1).unwrap().clone();
assert_eq!(job.status, JobStatus::Stopped);
assert!(job.is_active());
}
#[test]
fn test_job_status_update_signaled() {
let shell_state = ShellState::new();
let job = Job::new(1, Some(1234), "sleep 10 &".to_string(), vec![1234], false);
shell_state.job_table.borrow_mut().add_job(job);
let updated = shell_state.job_table.borrow_mut().update_job_status(1234, JobStatus::Done(143));
assert!(updated);
let job = shell_state.job_table.borrow().get_job(1).unwrap().clone();
assert_eq!(job.status, JobStatus::Done(143));
assert_eq!(job.exit_code, Some(143));
assert!(!job.is_active());
}
#[test]
fn test_check_background_jobs_completed() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "echo test &".to_string(), vec![1234], false);
job.update_status(JobStatus::Done(0));
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none());
}
#[test]
fn test_check_background_jobs_stopped() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "sleep 10 &".to_string(), vec![1234], false);
job.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_some());
let job = shell_state.job_table.borrow().get_job(1).unwrap().clone();
assert_eq!(job.status, JobStatus::Stopped);
}
#[test]
fn test_check_background_jobs_multiple() {
let mut shell_state = ShellState::new();
let mut job1 = Job::new(1, Some(1234), "echo test1 &".to_string(), vec![1234], false);
job1.update_status(JobStatus::Done(0));
shell_state.job_table.borrow_mut().add_job(job1);
let mut job2 = Job::new(2, Some(1235), "sleep 10 &".to_string(), vec![1235], false);
job2.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job2);
let mut job3 = Job::new(3, Some(1236), "false &".to_string(), vec![1236], false);
job3.update_status(JobStatus::Done(1));
shell_state.job_table.borrow_mut().add_job(job3);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none());
assert!(shell_state.job_table.borrow().get_job(3).is_none());
assert!(shell_state.job_table.borrow().get_job(2).is_some());
}
#[test]
fn test_check_background_jobs_running() {
let mut shell_state = ShellState::new();
let job = Job::new(1, Some(1234), "sleep 10 &".to_string(), vec![1234], false);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_some());
let job = shell_state.job_table.borrow().get_job(1).unwrap().clone();
assert_eq!(job.status, JobStatus::Running);
}
#[test]
fn test_check_background_jobs_empty_table() {
let mut shell_state = ShellState::new();
check_background_jobs(&mut shell_state);
assert_eq!(shell_state.job_table.borrow().get_all_jobs().len(), 0);
}
#[test]
fn test_job_notification_format_success() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "echo hello &".to_string(), vec![1234], false);
job.update_status(JobStatus::Done(0));
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none());
}
#[test]
fn test_job_notification_format_failure() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "false &".to_string(), vec![1234], false);
job.update_status(JobStatus::Done(1));
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none());
}
#[test]
fn test_job_notification_current_marker() {
let mut shell_state = ShellState::new();
let mut job1 = Job::new(1, Some(1234), "echo test1 &".to_string(), vec![1234], false);
job1.update_status(JobStatus::Done(0));
shell_state.job_table.borrow_mut().add_job(job1);
let mut job2 = Job::new(2, Some(1235), "echo test2 &".to_string(), vec![1235], false);
job2.update_status(JobStatus::Done(0));
shell_state.job_table.borrow_mut().add_job(job2);
assert_eq!(shell_state.job_table.borrow().get_current_job(), Some(2));
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none());
assert!(shell_state.job_table.borrow().get_job(2).is_none());
}
#[test]
fn test_update_job_status_nonexistent_pid() {
let shell_state = ShellState::new();
let updated = shell_state.job_table.borrow_mut().update_job_status(9999, JobStatus::Done(0));
assert!(!updated);
}
#[test]
fn test_sigchld_enqueue() {
enqueue_signal("CHLD", 17);
enqueue_signal("CHLD", 17);
enqueue_signal("CHLD", 17);
}
#[test]
fn test_stopped_job_notification_printed() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "vim file.txt &".to_string(), vec![1234], false);
job.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
let job_table = shell_state.job_table.borrow();
let job = job_table.get_job(1).expect("Stopped job should remain in table");
assert_eq!(job.status, JobStatus::Stopped);
assert!(job.is_active(), "Stopped job should be active");
}
#[test]
fn test_multiple_stopped_jobs_remain_in_table() {
let mut shell_state = ShellState::new();
let mut job1 = Job::new(1, Some(1234), "vim file1.txt &".to_string(), vec![1234], false);
job1.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job1);
let mut job2 = Job::new(2, Some(1235), "vim file2.txt &".to_string(), vec![1235], false);
job2.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job2);
let mut job3 = Job::new(3, Some(1236), "vim file3.txt &".to_string(), vec![1236], false);
job3.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job3);
check_background_jobs(&mut shell_state);
let job_table = shell_state.job_table.borrow();
assert!(job_table.get_job(1).is_some(), "Job 1 should remain");
assert!(job_table.get_job(2).is_some(), "Job 2 should remain");
assert!(job_table.get_job(3).is_some(), "Job 3 should remain");
assert_eq!(job_table.get_job(1).unwrap().status, JobStatus::Stopped);
assert_eq!(job_table.get_job(2).unwrap().status, JobStatus::Stopped);
assert_eq!(job_table.get_job(3).unwrap().status, JobStatus::Stopped);
}
#[test]
fn test_stopped_and_done_jobs_mixed() {
let mut shell_state = ShellState::new();
let mut job1 = Job::new(1, Some(1234), "vim file.txt &".to_string(), vec![1234], false);
job1.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job1);
let mut job2 = Job::new(2, Some(1235), "echo done &".to_string(), vec![1235], false);
job2.update_status(JobStatus::Done(0));
shell_state.job_table.borrow_mut().add_job(job2);
let mut job3 = Job::new(3, Some(1236), "vim file2.txt &".to_string(), vec![1236], false);
job3.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job3);
let mut job4 = Job::new(4, Some(1237), "false &".to_string(), vec![1237], false);
job4.update_status(JobStatus::Done(1));
shell_state.job_table.borrow_mut().add_job(job4);
check_background_jobs(&mut shell_state);
let job_table = shell_state.job_table.borrow();
assert!(job_table.get_job(1).is_some(), "Stopped job 1 should remain");
assert!(job_table.get_job(3).is_some(), "Stopped job 3 should remain");
assert!(job_table.get_job(2).is_none(), "Done job 2 should be removed");
assert!(job_table.get_job(4).is_none(), "Done job 4 should be removed");
}
#[test]
fn test_stopped_job_later_completes() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "vim file.txt &".to_string(), vec![1234], false);
job.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_some());
shell_state.job_table.borrow_mut().update_job_status(1234, JobStatus::Done(0));
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none(), "Completed job should be removed");
}
#[test]
fn test_stopped_job_with_nonzero_exit() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "vim file.txt &".to_string(), vec![1234], false);
job.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_some());
shell_state.job_table.borrow_mut().update_job_status(1234, JobStatus::Done(1));
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_none());
}
#[test]
fn test_stopped_job_is_active() {
let shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "sleep 10 &".to_string(), vec![1234], false);
job.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job);
let job_table = shell_state.job_table.borrow();
let job = job_table.get_job(1).unwrap();
assert!(job.is_active(), "Stopped job should be active (not done)");
assert_eq!(job.status, JobStatus::Stopped);
}
#[test]
fn test_running_job_not_notified() {
let mut shell_state = ShellState::new();
let job = Job::new(1, Some(1234), "sleep 100 &".to_string(), vec![1234], false);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
let job_table = shell_state.job_table.borrow();
let job = job_table.get_job(1).expect("Running job should remain");
assert_eq!(job.status, JobStatus::Running);
assert!(job.is_active());
}
#[test]
fn test_stopped_job_notification_multiple_calls() {
let mut shell_state = ShellState::new();
let mut job = Job::new(1, Some(1234), "vim file.txt &".to_string(), vec![1234], false);
job.update_status(JobStatus::Stopped);
shell_state.job_table.borrow_mut().add_job(job);
check_background_jobs(&mut shell_state);
check_background_jobs(&mut shell_state);
check_background_jobs(&mut shell_state);
assert!(shell_state.job_table.borrow().get_job(1).is_some());
}