1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
|
//! Tests for PID helper functions (getpid, getppid)
//!
//! This module tests the convenience wrappers for getting process IDs:
//! - `getpid()` - Get current process ID
//! - `getppid()` - Get parent process ID
//!
//! These tests verify that the wrappers correctly hide unsafe code
//! and return valid process IDs in both parent and child processes.
#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
#![allow(clippy::match_wild_err_arm)]
use std::process::exit;
use fork::{Fork, fork, getpid, getppid, waitpid};
#[test]
fn test_getpid_returns_valid_pid() {
// Tests that getpid() returns a valid positive process ID
// Expected behavior:
// 1. getpid() returns current process ID
// 2. PID should be positive
// 3. PID should be consistent across multiple calls
let pid1 = getpid();
let pid2 = getpid();
assert!(pid1 > 0, "PID should be positive");
assert_eq!(pid1, pid2, "PID should be consistent");
}
#[test]
fn test_getppid_returns_valid_pid() {
// Tests that getppid() returns a valid parent process ID
// Expected behavior:
// 1. getppid() returns parent process ID
// 2. Parent PID should be positive
// 3. Parent PID should be consistent
let ppid1 = getppid();
let ppid2 = getppid();
assert!(ppid1 > 0, "Parent PID should be positive");
assert_eq!(ppid1, ppid2, "Parent PID should be consistent");
}
#[test]
fn test_getpid_different_in_child() {
// Tests that child process has different PID from parent
// Expected behavior:
// 1. Parent gets its PID
// 2. Child gets its PID
// 3. Child PID != Parent PID
// 4. Child's parent PID == Parent's PID
let parent_pid = getpid();
match fork() {
Ok(Fork::Parent(child_pid)) => {
// Verify fork returned correct child PID
assert!(child_pid > 0, "Child PID from fork should be positive");
assert_ne!(
child_pid, parent_pid,
"Child PID should differ from parent PID"
);
waitpid(child_pid).expect("waitpid failed");
}
Ok(Fork::Child) => {
let child_pid = getpid();
let child_parent_pid = getppid();
// Child's PID should be different from parent's
assert_ne!(
child_pid, parent_pid,
"Child should have different PID from parent"
);
// Child's parent PID should match original parent's PID
assert_eq!(
child_parent_pid, parent_pid,
"Child's parent PID should match parent's PID"
);
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_getpid_matches_fork_result() {
// Tests that getpid() in child matches PID returned by fork() in parent
// Expected behavior:
// 1. Parent gets child PID from fork()
// 2. Child calls getpid()
// 3. Both should match
match fork() {
Ok(Fork::Parent(fork_child_pid)) => {
// Parent waits for child to complete
let status = waitpid(fork_child_pid).expect("waitpid failed");
assert!(libc::WIFEXITED(status), "Child should exit normally");
// We can't directly compare here, but child will verify
}
Ok(Fork::Child) => {
// Child verifies its PID
let my_pid = getpid();
assert!(my_pid > 0, "Child PID should be positive");
// Note: We can't pass this back to parent easily,
// but the fact that both calls succeed validates the wrapper
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_getppid_returns_parent_pid() {
// Tests that child's getppid() returns the parent's getpid()
// Expected behavior:
// 1. Parent records its PID
// 2. Child calls getppid()
// 3. Child's parent PID matches parent's PID
let parent_pid = getpid();
match fork() {
Ok(Fork::Parent(child_pid)) => {
waitpid(child_pid).expect("waitpid failed");
}
Ok(Fork::Child) => {
let my_parent = getppid();
assert_eq!(
my_parent, parent_pid,
"Child's parent PID should match parent's actual PID"
);
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_getpid_no_unsafe_in_user_code() {
// Tests that getpid() hides unsafe from user code
// Expected behavior:
// 1. User can call getpid() without unsafe block
// 2. Function returns valid PID
// 3. Type is libc::pid_t
// This compiles without unsafe - that's the test!
let pid: libc::pid_t = getpid();
assert!(pid > 0, "PID should be positive");
}
#[test]
fn test_getppid_no_unsafe_in_user_code() {
// Tests that getppid() hides unsafe from user code
// Expected behavior:
// 1. User can call getppid() without unsafe block
// 2. Function returns valid PID
// 3. Type is libc::pid_t
// This compiles without unsafe - that's the test!
let ppid: libc::pid_t = getppid();
assert!(ppid > 0, "Parent PID should be positive");
}
#[test]
fn test_pid_functions_in_multiple_forks() {
// Tests PID functions work correctly with multiple forks
// Expected behavior:
// 1. Create multiple children
// 2. Each child has unique PID
// 3. All children have same parent PID
let parent_pid = getpid();
let mut child_pids = vec![];
// Create 3 children
for i in 0..3 {
match fork() {
Ok(Fork::Parent(child_pid)) => {
child_pids.push(child_pid);
}
Ok(Fork::Child) => {
let my_pid = getpid();
let my_parent = getppid();
// Verify child has different PID
assert_ne!(my_pid, parent_pid, "Child {i} should have different PID");
// Verify parent PID is correct
assert_eq!(
my_parent, parent_pid,
"Child {i} should have correct parent PID"
);
exit(i);
}
Err(_) => panic!("Fork {i} failed"),
}
}
// Parent waits for all children
for child_pid in child_pids {
waitpid(child_pid).expect("waitpid failed");
}
// Verify our PID is still the same
assert_eq!(getpid(), parent_pid, "Parent PID should not change");
}
#[test]
fn test_getpid_consistency_across_operations() {
// Tests that getpid() remains consistent during process lifetime
// Expected behavior:
// 1. PID remains the same throughout process execution
// 2. PID doesn't change after fork (in same process)
// 3. PID doesn't change after other operations
let pid1 = getpid();
// Do some work
let _ppid = getppid();
let pid2 = getpid();
assert_eq!(pid1, pid2, "PID should remain consistent");
// Fork and verify parent PID doesn't change
match fork() {
Ok(Fork::Parent(child_pid)) => {
let pid3 = getpid();
assert_eq!(pid1, pid3, "Parent PID should not change after fork");
waitpid(child_pid).expect("waitpid failed");
}
Ok(Fork::Child) => {
let child_pid = getpid();
assert_ne!(child_pid, pid1, "Child should have different PID");
exit(0);
}
Err(_) => panic!("Fork failed"),
}
}
#[test]
fn test_getppid_after_parent_exits() {
// Tests that grandchild gets reparented to init (PID 1)
// Expected behavior:
// 1. Create child, child creates grandchild
// 2. Child exits
// 3. Grandchild's parent becomes init (PID 1)
match fork() {
Ok(Fork::Parent(child_pid)) => {
// Wait for child to exit
waitpid(child_pid).expect("waitpid failed");
// Grandchild will be reparented, but we can't directly observe it here
}
Ok(Fork::Child) => {
let child_pid = getpid();
// Create grandchild
match fork() {
Ok(Fork::Parent(_)) => {
// Child exits immediately, orphaning grandchild
exit(0);
}
Ok(Fork::Child) => {
// Give parent time to exit
std::thread::sleep(std::time::Duration::from_millis(100));
// Grandchild's parent should now be init (PID 1)
let current_parent = getppid();
assert_eq!(
current_parent, 1,
"Orphaned grandchild should be reparented to init (PID 1)"
);
// Verify we're not the original child
let my_pid = getpid();
assert_ne!(my_pid, child_pid, "Grandchild should have different PID");
exit(0);
}
Err(_) => exit(1),
}
}
Err(_) => panic!("Fork failed"),
}
}
|