File: error_handling_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 (272 lines) | stat: -rw-r--r-- 9,041 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
//! Error handling and edge case tests
//!
//! This module tests error scenarios and edge cases for fork library functions:
//! - `setsid()` called when already a session leader (EPERM)
//! - `close_fd()` error handling
//! - Error type verification (`io::Error`)
//!
//! These tests ensure proper error propagation and handling.

#![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)]

use std::process::exit;

use fork::{Fork, close_fd, fork, getpgrp, setsid, waitpid};

#[test]
fn test_setsid_error_when_already_session_leader() {
    // Tests that setsid returns error when called by a session leader
    // Expected behavior:
    // 1. Child calls setsid() to become session leader (succeeds)
    // 2. Child immediately calls setsid() again (should fail with EPERM)
    // 3. Verifies proper error handling
    match fork() {
        Ok(Fork::Parent(child)) => {
            waitpid(child).expect("waitpid failed");
        }
        Ok(Fork::Child) => {
            // First setsid should succeed
            let sid = setsid().expect("First setsid should succeed");
            assert!(sid > 0, "SID should be positive");

            // Verify we're a session leader (PID == PGID)
            let pid = unsafe { libc::getpid() };
            let pgid = getpgrp();
            assert_eq!(pid, pgid, "Should be session leader");

            // Second setsid should fail with EPERM
            let result = setsid();
            assert!(result.is_err(), "Second setsid should fail");

            let err = result.unwrap_err();
            assert_eq!(
                err.raw_os_error(),
                Some(libc::EPERM),
                "Should return EPERM when already session leader"
            );

            exit(0);
        }
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_fork_returns_io_error_type() {
    // Tests that fork returns proper io::Error type
    // Expected behavior:
    // 1. fork() returns io::Result<Fork>
    // 2. Error type can be inspected with raw_os_error()
    // 3. Verifies type signature from v0.4.0
    match fork() {
        Ok(Fork::Parent(child)) => {
            waitpid(child).expect("waitpid failed");
        }
        Ok(Fork::Child) => exit(0),
        Err(e) => {
            // If fork somehow fails, verify it's a proper io::Error
            let _errno = e.raw_os_error();
            let _error_msg = format!("{}", e);
            panic!("Fork failed: {}", e);
        }
    }
}

#[test]
fn test_waitpid_returns_io_error_type() {
    // Tests that waitpid returns proper io::Error on failure
    // Expected behavior:
    // 1. waitpid on invalid PID returns Err(io::Error)
    // 2. Error has raw_os_error() method
    // 3. Error can be formatted as string
    match fork() {
        Ok(Fork::Parent(_)) => {
            // Try to wait on invalid PID
            let result = waitpid(999_999);
            assert!(result.is_err(), "Should fail on invalid PID");

            let err = result.unwrap_err();
            // Verify io::Error properties
            assert!(err.raw_os_error().is_some(), "Should have errno");
            let error_string = format!("{}", err);
            assert!(!error_string.is_empty(), "Should have error message");
        }
        Ok(Fork::Child) => exit(0),
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_setsid_returns_io_error_type() {
    // Tests that setsid returns proper io::Error on failure
    match fork() {
        Ok(Fork::Parent(child)) => {
            waitpid(child).expect("waitpid failed");
        }
        Ok(Fork::Child) => {
            // First call succeeds
            setsid().expect("First setsid should succeed");

            // Second call fails
            let result = setsid();
            assert!(result.is_err(), "Second setsid should fail");

            let err = result.unwrap_err();
            // Verify io::Error properties
            assert_eq!(err.raw_os_error(), Some(libc::EPERM));
            let error_string = format!("{}", err);
            assert!(
                error_string.contains("Operation not permitted") || error_string.contains("EPERM"),
                "Error message should indicate permission error: {}",
                error_string
            );

            exit(0);
        }
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_getpgrp_returns_pid_type() {
    // Tests that getpgrp returns pid_t directly (not Result)
    // getpgrp() always succeeds per POSIX and cannot fail
    match fork() {
        Ok(Fork::Parent(child)) => {
            waitpid(child).expect("waitpid failed");
        }
        Ok(Fork::Child) => {
            // getpgrp() always succeeds and returns pid_t directly (not Result)
            let pgid: libc::pid_t = getpgrp();
            assert!(pgid > 0, "PGID should be positive");

            exit(0);
        }
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_close_fd_error_handling() {
    // Tests that close_fd handles errors properly
    // Note: This is tricky because closing stdin/stdout/stderr
    // should normally succeed. This test just verifies the function
    // returns io::Result and can be error-checked.
    match fork() {
        Ok(Fork::Parent(child)) => {
            waitpid(child).expect("waitpid failed");
        }
        Ok(Fork::Child) => {
            // Close fds - should succeed normally
            let result = close_fd();
            assert!(result.is_ok(), "close_fd should succeed normally");

            // Second call might fail since fds are already closed
            // but this is implementation-dependent
            let result2 = close_fd();

            // Either succeeds (idempotent) or fails - both are acceptable
            match result2 {
                Ok(()) => {
                    // Idempotent - fine
                }
                Err(e) => {
                    // Failed - verify it's a proper io::Error
                    assert!(e.raw_os_error().is_some(), "Should have errno");
                }
            }

            exit(0);
        }
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_error_kind_matching() {
    // Tests that io::Error kinds can be matched for specific handling
    // Expected behavior:
    // 1. Errors return standard io::ErrorKind variants
    // 2. Can pattern match on error kinds
    // 3. Demonstrates error handling patterns
    match fork() {
        Ok(Fork::Parent(_)) => {
            // Try to wait on invalid PID
            let result = waitpid(999_999);

            if let Err(e) = result {
                // Can match on error kind for different handling
                if e.kind() == std::io::ErrorKind::NotFound {
                    // ECHILD can map to NotFound on some systems
                }

                // Verify we can access errno
                assert!(e.raw_os_error().is_some(), "Should have raw OS error code");
            }
        }
        Ok(Fork::Child) => exit(0),
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_fork_child_pid_method() {
    // Tests Fork::child_pid() method returns correct PID
    match fork() {
        Ok(Fork::Parent(child_pid)) => {
            let fork_result = Fork::Parent(child_pid);

            // Test child_pid() method
            let extracted_pid = fork_result.child_pid();
            assert!(extracted_pid.is_some(), "Parent should have child PID");
            assert_eq!(
                extracted_pid.unwrap(),
                child_pid,
                "child_pid() should match fork result"
            );

            waitpid(child_pid).expect("waitpid failed");
        }
        Ok(Fork::Child) => {
            let fork_result = Fork::Child;

            // Test child_pid() method
            let extracted_pid = fork_result.child_pid();
            assert!(extracted_pid.is_none(), "Child should not have child PID");

            exit(0);
        }
        Err(_) => panic!("Fork failed"),
    }
}

#[test]
fn test_fork_is_parent_is_child_methods() {
    // Tests Fork::is_parent() and Fork::is_child() methods
    match fork() {
        Ok(result) => {
            if result.is_parent() {
                // In parent
                assert!(!result.is_child(), "Parent should not be child");
                assert!(result.child_pid().is_some(), "Parent should have child PID");

                let child_pid = result.child_pid().unwrap();
                waitpid(child_pid).expect("waitpid failed");
            } else if result.is_child() {
                // In child
                assert!(!result.is_parent(), "Child should not be parent");
                assert!(
                    result.child_pid().is_none(),
                    "Child should not have child PID"
                );
                exit(0);
            }
        }
        Err(_) => panic!("Fork failed"),
    }
}