#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
#![allow(clippy::match_wild_err_arm)]
#![allow(clippy::similar_names)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::indexing_slicing)]
mod common;
use std::{env, fs, process::exit, thread, time::Duration};
use fork::{Fork, chdir, fork, getpgrp, getpid, setsid, waitpid};
use common::{get_test_dir, get_unique_test_dir, setup_test_dir, wait_for_file};
#[test]
fn test_double_fork_daemon_pattern() {
let test_dir = setup_test_dir(get_unique_test_dir("int_double_fork"));
let daemon_pid_file = test_dir.join("daemon.pid");
match fork().expect("First fork failed") {
Fork::Parent(_child) => {
assert!(
wait_for_file(&daemon_pid_file, 3000),
"Daemon PID file should exist"
);
let pid_str = fs::read_to_string(&daemon_pid_file).expect("Failed to read PID file");
let daemon_pid: i32 = pid_str.trim().parse().expect("Failed to parse daemon PID");
assert!(daemon_pid > 0, "Daemon PID should be positive");
fs::remove_file(&daemon_pid_file).ok();
}
Fork::Child => {
setsid().expect("setsid failed");
match fork().expect("Second fork failed") {
Fork::Parent(_) => {
exit(0);
}
Fork::Child => {
let pid = getpid();
let pgid = getpgrp();
fs::write(&daemon_pid_file, format!("{}", pid))
.expect("Failed to write daemon PID");
assert!(pgid > 0);
exit(0);
}
}
}
}
}
#[test]
fn test_setsid_creates_new_session() {
let test_dir = setup_test_dir(get_unique_test_dir("int_setsid"));
let session_file = test_dir.join("session.info");
match fork().expect("Fork failed") {
Fork::Parent(_child) => {
thread::sleep(Duration::from_millis(50));
let content = fs::read_to_string(&session_file).expect("Failed to read session file");
let parts: Vec<&str> = content.trim().split(',').collect();
let sid: i32 = parts[0].parse().expect("Failed to parse SID");
let pid: i32 = parts[1].parse().expect("Failed to parse PID");
let pgid: i32 = parts[2].parse().expect("Failed to parse PGID");
assert_eq!(pid, pgid, "Process should be session leader");
assert_eq!(sid, pid, "SID should equal PID for session leader");
fs::remove_file(&session_file).ok();
}
Fork::Child => {
let sid = setsid().expect("setsid failed");
let pid = unsafe { libc::getpid() };
let pgid = getpgrp();
fs::write(&session_file, format!("{},{},{}", sid, pid, pgid))
.expect("Failed to write session info");
exit(0);
}
}
}
#[test]
fn test_chdir_changes_directory() {
let test_dir = setup_test_dir(get_test_dir("int_chdir"));
let dir_file = test_dir.join("directory.info");
match fork().expect("Fork failed") {
Fork::Parent(_child) => {
thread::sleep(Duration::from_millis(50));
let content = fs::read_to_string(&dir_file).expect("Failed to read dir file");
assert_eq!(content.trim(), "/", "Directory should be root");
fs::remove_file(&dir_file).ok();
}
Fork::Child => {
chdir().expect("chdir failed");
let current = env::current_dir().expect("Failed to get current dir");
fs::write(&dir_file, current.to_str().unwrap())
.expect("Failed to write directory info");
exit(0);
}
}
}
#[test]
fn test_process_isolation() {
let test_dir = setup_test_dir(get_test_dir("int_isolation"));
let parent_file = test_dir.join("parent.txt");
let child_file = test_dir.join("child.txt");
fs::write(&parent_file, "parent data").expect("Failed to write parent file");
match fork().expect("Fork failed") {
Fork::Parent(_child) => {
thread::sleep(Duration::from_millis(50));
assert!(parent_file.exists(), "Parent file should exist");
assert!(child_file.exists(), "Child file should exist");
let child_content = fs::read_to_string(&child_file).expect("Failed to read child file");
assert_eq!(child_content.trim(), "child data");
fs::remove_file(&parent_file).ok();
fs::remove_file(&child_file).ok();
}
Fork::Child => {
assert!(parent_file.exists(), "Child should see parent file");
fs::write(&child_file, "child data").expect("Failed to write child file");
exit(0);
}
}
}
#[test]
fn test_getpgrp_returns_process_group() {
match fork().expect("Fork failed") {
Fork::Parent(_child) => {
let parent_pgid = getpgrp();
assert!(parent_pgid > 0, "Parent PGID should be positive");
thread::sleep(Duration::from_millis(50));
}
Fork::Child => {
let child_pgid = getpgrp();
assert!(child_pgid > 0, "Child PGID should be positive");
exit(0);
}
}
}
#[test]
fn test_chdir_error_handling() {
match fork().expect("Fork failed") {
Fork::Parent(child) => {
waitpid(child).expect("waitpid failed");
}
Fork::Child => {
let result = chdir();
assert!(result.is_ok(), "chdir to root should succeed");
let cwd = env::current_dir().expect("Failed to get current dir");
assert_eq!(cwd.to_str().unwrap(), "/", "Should be in root directory");
exit(0);
}
}
}
#[test]
fn test_chdir_returns_io_error() {
match fork().expect("Fork failed") {
Fork::Parent(child) => {
waitpid(child).expect("waitpid failed");
}
Fork::Child => {
let result: std::io::Result<()> = chdir();
assert!(result.is_ok());
if let Err(e) = result {
let _errno = e.raw_os_error();
let _msg = format!("{}", e);
}
exit(0);
}
}
}