File: pid_tests.rs

package info (click to toggle)
rust-fork 0.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 376 kB
  • sloc: makefile: 2
file content (294 lines) | stat: -rw-r--r-- 9,148 bytes parent folder | download
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"),
    }
}