File: chdir_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 (352 lines) | stat: -rw-r--r-- 11,164 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
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
//! Comprehensive tests for `chdir()` function
//!
//! This module thoroughly tests the `chdir()` function to ensure:
//! - Successful directory change to root (/)
//! - Proper error handling and return types
//! - Thread safety and process isolation
//! - Integration with fork and daemon patterns
//! - Multiple successive calls (idempotent behavior)
//! - Verification of actual filesystem effects

#![allow(clippy::expect_used)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::panic)]
#![allow(clippy::match_wild_err_arm)]
#![allow(clippy::uninlined_format_args)]

use std::{
    env, fs,
    path::{Path, PathBuf},
    process::exit,
    thread,
    time::Duration,
};

use fork::{Fork, WEXITSTATUS, WIFEXITED, chdir, fork, waitpid};

#[test]
fn test_chdir_basic_success() {
    // Test that chdir successfully changes to root directory
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status), "Child should exit normally");
            assert_eq!(WEXITSTATUS(status), 0, "Child should exit with code 0");
        }
        Fork::Child => {
            // Change to root directory
            match chdir() {
                Ok(()) => {
                    let cwd = env::current_dir().expect("Failed to get current dir");
                    assert_eq!(cwd.to_str().unwrap(), "/", "Should be in root directory");
                    exit(0);
                }
                Err(e) => {
                    eprintln!("chdir failed: {}", e);
                    exit(1);
                }
            }
        }
    }
}

#[test]
fn test_chdir_returns_unit() {
    // Test that chdir returns () on success
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            let result: std::io::Result<()> = chdir();
            assert!(result.is_ok());

            // Verify it returns unit type
            let _unit: () = result.unwrap();
            exit(0);
        }
    }
}

#[test]
fn test_chdir_changes_actual_working_directory() {
    // Verify chdir actually changes the working directory, not just returns Ok
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            // Get original directory
            let original = env::current_dir().expect("Failed to get original dir");

            // Change to root
            chdir().expect("chdir failed");

            // Get new directory
            let new_dir = env::current_dir().expect("Failed to get new dir");

            // Verify change occurred
            assert_eq!(new_dir, PathBuf::from("/"), "Should be in root");
            if original.as_path() != Path::new("/") {
                assert_ne!(
                    original, new_dir,
                    "Directory should change when not already at /"
                );
            }

            exit(0);
        }
    }
}

#[test]
fn test_chdir_idempotent() {
    // Test that calling chdir multiple times is safe (idempotent)
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            // Call chdir multiple times
            chdir().expect("First chdir failed");
            chdir().expect("Second chdir failed");
            chdir().expect("Third chdir failed");

            // Verify still in root
            let cwd = env::current_dir().expect("Failed to get current dir");
            assert_eq!(cwd.to_str().unwrap(), "/");

            exit(0);
        }
    }
}

#[test]
fn test_chdir_process_isolation() {
    // Test that chdir in child doesn't affect parent
    let parent_dir = env::current_dir().expect("Failed to get parent dir");

    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            // Parent waits for child
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));

            // Parent directory should be unchanged
            let current = env::current_dir().expect("Failed to get current dir");
            assert_eq!(current, parent_dir, "Parent directory should not change");
        }
        Fork::Child => {
            // Child changes directory
            chdir().expect("chdir failed");

            // Verify child is in root
            let cwd = env::current_dir().expect("Failed to get current dir");
            assert_eq!(cwd.to_str().unwrap(), "/");

            exit(0);
        }
    }
}

#[test]
fn test_chdir_with_file_operations() {
    // Test that chdir affects file operations (relative paths)
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            // Change to root
            chdir().expect("chdir failed");

            // Confirm relative operations work from new cwd
            let cwd = env::current_dir().expect("Failed to get current dir");
            assert_eq!(cwd.to_str().unwrap(), "/");
            fs::metadata(".").expect("Root directory metadata should be readable");

            exit(0);
        }
    }
}

#[test]
fn test_chdir_with_absolute_path_operations() {
    // Test that absolute paths still work after chdir
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            let temp_file = std::env::temp_dir().join("fork_test_chdir");
            fs::write(&temp_file, "test").expect("Failed to write test file");

            // Change directory
            chdir().expect("chdir failed");

            // Absolute path should still work
            let content =
                fs::read_to_string(&temp_file).expect("Failed to read with absolute path");
            assert_eq!(content, "test");

            // Cleanup
            fs::remove_file(&temp_file).ok();

            exit(0);
        }
    }
}

#[test]
fn test_chdir_error_type() {
    // Test that chdir returns proper io::Error type
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            let result: std::io::Result<()> = chdir();

            // Should succeed for root directory
            assert!(result.is_ok());

            // Type check - this is an io::Error if it were to fail
            match result {
                Ok(()) => exit(0),
                Err(e) => {
                    // Verify it's a proper io::Error
                    let _: std::io::Error = e;
                    exit(1);
                }
            }
        }
    }
}

#[test]
fn test_chdir_concurrent_forks() {
    // Test chdir behavior with multiple concurrent child processes
    let mut children = Vec::new();

    for _ in 0..3 {
        match fork().expect("Fork failed") {
            Fork::Parent(child) => {
                children.push(child);
            }
            Fork::Child => {
                // Each child changes to root independently
                chdir().expect("chdir failed");

                let cwd = env::current_dir().expect("Failed to get current dir");
                assert_eq!(cwd.to_str().unwrap(), "/");

                // Small delay to ensure concurrency
                thread::sleep(Duration::from_millis(10));

                exit(0);
            }
        }
    }

    // Parent waits for all children
    for child in children {
        let status = waitpid(child).expect("waitpid failed");
        assert!(WIFEXITED(status));
        assert_eq!(WEXITSTATUS(status), 0);
    }
}

#[test]
fn test_chdir_before_and_after_setsid() {
    // Test that chdir works correctly with setsid (common in daemon creation)
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            use fork::setsid;

            // Create new session first
            setsid().expect("setsid failed");

            // Then change directory (typical daemon pattern)
            chdir().expect("chdir failed");

            // Verify both worked
            let cwd = env::current_dir().expect("Failed to get current dir");
            assert_eq!(cwd.to_str().unwrap(), "/");

            let pgid = fork::getpgrp();
            assert!(pgid > 0);

            exit(0);
        }
    }
}

#[test]
fn test_chdir_uses_c_string_literal() {
    // This test verifies that the modern c"" string literal is used correctly
    // by ensuring chdir works without any runtime string allocation errors
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            // Call chdir multiple times rapidly
            // If there were allocation issues, this would likely fail
            for _ in 0..100 {
                chdir().expect("chdir failed");
            }

            let cwd = env::current_dir().expect("Failed to get current dir");
            assert_eq!(cwd.to_str().unwrap(), "/");

            exit(0);
        }
    }
}

#[test]
fn test_chdir_with_env_manipulation() {
    // Test that chdir works correctly even when environment is modified
    match fork().expect("Fork failed") {
        Fork::Parent(child) => {
            let status = waitpid(child).expect("waitpid failed");
            assert!(WIFEXITED(status));
            assert_eq!(WEXITSTATUS(status), 0);
        }
        Fork::Child => {
            // Modify environment
            // SAFETY: This child has no other threads and exits immediately after the test.
            unsafe {
                env::set_var("PWD", "/some/fake/path");
            }

            // chdir should still work correctly
            chdir().expect("chdir failed");

            // Verify actual directory (not PWD env var)
            let cwd = env::current_dir().expect("Failed to get current dir");
            assert_eq!(cwd.to_str().unwrap(), "/");

            exit(0);
        }
    }
}