#![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::{Command, exit},
thread,
time::Duration,
};
use fork::{Fork, fork, waitpid};
use common::{get_test_dir, setup_test_dir};
#[test]
fn test_fork_basic() {
match fork() {
Ok(Fork::Parent(child)) => {
assert!(child > 0, "Child PID should be positive");
assert!(waitpid(child).is_ok(), "waitpid should succeed");
}
Ok(Fork::Child) => {
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_fork_parent_child_communication() {
let test_dir = setup_test_dir(get_test_dir("fork_communication"));
let message_file = test_dir.join("message.txt");
match fork() {
Ok(Fork::Parent(child)) => {
thread::sleep(Duration::from_millis(50));
let message = fs::read_to_string(&message_file).expect("Failed to read message file");
assert_eq!(message.trim(), "hello from child");
waitpid(child).expect("Failed to wait for child");
fs::remove_file(&message_file).ok();
}
Ok(Fork::Child) => {
fs::write(&message_file, "hello from child").expect("Failed to write message");
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_fork_multiple_children() {
let mut children = Vec::new();
for i in 0..3 {
match fork() {
Ok(Fork::Parent(child)) => {
children.push(child);
}
Ok(Fork::Child) => {
exit(i);
}
Err(_) => panic!("Fork {} failed", i),
}
}
assert_eq!(children.len(), 3, "Should have 3 children");
for child in children {
assert!(waitpid(child).is_ok(), "Failed to wait for child {}", child);
}
}
#[test]
fn test_fork_child_inherits_environment() {
let test_dir = setup_test_dir(get_test_dir("fork_env"));
let env_file = test_dir.join("env.txt");
let test_var = "FORK_TEST_VAR";
let test_value = "test_value_12345";
unsafe {
env::set_var(test_var, test_value);
}
match fork() {
Ok(Fork::Parent(child)) => {
thread::sleep(Duration::from_millis(50));
let content = fs::read_to_string(&env_file).expect("Failed to read env file");
assert_eq!(content.trim(), test_value);
waitpid(child).expect("Failed to wait for child");
fs::remove_file(&env_file).ok();
unsafe {
env::remove_var(test_var);
}
}
Ok(Fork::Child) => {
let value = env::var(test_var).expect("Environment variable not found");
fs::write(&env_file, value).expect("Failed to write env file");
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_fork_child_can_execute_commands() {
let test_dir = get_test_dir("fork");
fs::create_dir_all(&test_dir).expect("Failed to create test directory");
let output_file = test_dir.join("command_output.txt");
match fork() {
Ok(Fork::Parent(child)) => {
thread::sleep(Duration::from_millis(100));
assert!(output_file.exists(), "Output file should exist");
let content = fs::read_to_string(&output_file).expect("Failed to read output");
assert!(!content.is_empty(), "Output should not be empty");
waitpid(child).expect("Failed to wait for child");
fs::remove_file(&output_file).ok();
}
Ok(Fork::Child) => {
let output = Command::new("echo")
.arg("child executed command")
.output()
.expect("Failed to execute command");
fs::write(&output_file, &output.stdout).expect("Failed to write output");
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_fork_child_has_different_pid() {
let test_dir = get_test_dir("fork");
fs::create_dir_all(&test_dir).expect("Failed to create test directory");
let pid_file = test_dir.join("pids.txt");
let parent_pid = unsafe { libc::getpid() };
match fork() {
Ok(Fork::Parent(child)) => {
thread::sleep(Duration::from_millis(50));
let content = fs::read_to_string(&pid_file).expect("Failed to read pid file");
let child_pid: i32 = content.trim().parse().expect("Failed to parse PID");
assert_ne!(
parent_pid, child_pid,
"Parent and child should have different PIDs"
);
assert_eq!(
child, child_pid,
"Child PID from fork() should match actual child PID"
);
waitpid(child).expect("Failed to wait for child");
fs::remove_file(&pid_file).ok();
}
Ok(Fork::Child) => {
let child_pid = unsafe { libc::getpid() };
fs::write(&pid_file, format!("{}", child_pid)).expect("Failed to write PID");
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_waitpid_waits_for_child() {
let test_dir = get_test_dir("fork");
fs::create_dir_all(&test_dir).expect("Failed to create test directory");
let marker_file = test_dir.join("wait_marker.txt");
match fork() {
Ok(Fork::Parent(child)) => {
assert!(
!marker_file.exists(),
"Marker should not exist before child runs"
);
waitpid(child).expect("Failed to wait for child");
assert!(
marker_file.exists(),
"Marker should exist after child completes"
);
fs::remove_file(&marker_file).ok();
}
Ok(Fork::Child) => {
thread::sleep(Duration::from_millis(50));
fs::write(&marker_file, "done").expect("Failed to write marker");
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}