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
295
296
297
298
299
300
//! 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) => {
// Poll until reparented to init (PID 1) with a timeout
let mut reparented = false;
for _ in 0..50 {
if getppid() == 1 {
reparented = true;
break;
}
std::thread::sleep(std::time::Duration::from_millis(20));
}
assert!(
reparented,
"Orphaned grandchild should be reparented to init (PID 1), got ppid={}",
getppid()
);
// 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"),
}
}