xref: /drstd/src/std/sys/unix/process/process_common/tests.rs (revision 9670759b785600bf6315e4173e46a602f16add7a)
1 use super::*;
2 
3 use crate::std::ffi::OsStr;
4 use crate::std::mem;
5 use crate::std::ptr;
6 use crate::std::sys::{cvt, cvt_nz};
7 use dlibc;
8 
9 macro_rules! t {
10     ($e:expr) => {
11         match $e {
12             Ok(t) => t,
13             Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
14         }
15     };
16 }
17 
18 #[test]
19 #[cfg_attr(
20     any(
21         // See #14232 for more information, but it appears that signal delivery to a
22         // newly spawned process may just be raced in the macOS, so to prevent this
23         // test from being flaky we ignore it on macOS.
24         target_os = "macos",
25         // When run under our current QEMU emulation test suite this test fails,
26         // although the reason isn't very clear as to why. For now this test is
27         // ignored there.
28         target_arch = "arm",
29         target_arch = "aarch64",
30         target_arch = "riscv64",
31     ),
32     ignore
33 )]
test_process_mask()34 fn test_process_mask() {
35     // Test to make sure that a signal mask *does* get inherited.
36     fn test_inner(mut cmd: Command) {
37         unsafe {
38             let mut set = mem::MaybeUninit::<dlibc::sigset_t>::uninit();
39             let mut old_set = mem::MaybeUninit::<dlibc::sigset_t>::uninit();
40             t!(cvt(sigemptyset(set.as_mut_ptr())));
41             t!(cvt(sigaddset(set.as_mut_ptr(), dlibc::SIGINT)));
42             t!(cvt_nz(dlibc::pthread_sigmask(
43                 dlibc::SIG_SETMASK,
44                 set.as_ptr(),
45                 old_set.as_mut_ptr()
46             )));
47 
48             cmd.stdin(Stdio::MakePipe);
49             cmd.stdout(Stdio::MakePipe);
50 
51             let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
52             let stdin_write = pipes.stdin.take().unwrap();
53             let stdout_read = pipes.stdout.take().unwrap();
54 
55             t!(cvt_nz(dlibc::pthread_sigmask(
56                 dlibc::SIG_SETMASK,
57                 old_set.as_ptr(),
58                 ptr::null_mut()
59             )));
60 
61             t!(cvt(dlibc::kill(cat.id() as dlibc::pid_t, dlibc::SIGINT)));
62             // We need to wait until SIGINT is definitely delivered. The
63             // easiest way is to write something to cat, and try to read it
64             // back: if SIGINT is unmasked, it'll get delivered when cat is
65             // next scheduled.
66             let _ = stdin_write.write(b"Hello");
67             drop(stdin_write);
68 
69             // Exactly 5 bytes should be read.
70             let mut buf = [0; 5];
71             let ret = t!(stdout_read.read(&mut buf));
72             assert_eq!(ret, 5);
73             assert_eq!(&buf, b"Hello");
74 
75             t!(cat.wait());
76         }
77     }
78 
79     // A plain `Command::new` uses the posix_spawn path on many platforms.
80     let cmd = Command::new(OsStr::new("cat"));
81     test_inner(cmd);
82 
83     // Specifying `pre_exec` forces the fork/exec path.
84     let mut cmd = Command::new(OsStr::new("cat"));
85     unsafe { cmd.pre_exec(Box::new(|| Ok(()))) };
86     test_inner(cmd);
87 }
88 
89 #[test]
90 #[cfg_attr(
91     any(
92         // See test_process_mask
93         target_os = "macos",
94         target_arch = "arm",
95         target_arch = "aarch64",
96         target_arch = "riscv64",
97     ),
98     ignore
99 )]
test_process_group_posix_spawn()100 fn test_process_group_posix_spawn() {
101     unsafe {
102         // Spawn a cat subprocess that's just going to hang since there is no I/O.
103         let mut cmd = Command::new(OsStr::new("cat"));
104         cmd.pgroup(0);
105         cmd.stdin(Stdio::MakePipe);
106         cmd.stdout(Stdio::MakePipe);
107         let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
108 
109         // Check that we can kill its process group, which means there *is* one.
110         t!(cvt(dlibc::kill(-(cat.id() as dlibc::pid_t), dlibc::SIGINT)));
111 
112         t!(cat.wait());
113     }
114 }
115 
116 #[test]
117 #[cfg_attr(
118     any(
119         // See test_process_mask
120         target_os = "macos",
121         target_arch = "arm",
122         target_arch = "aarch64",
123         target_arch = "riscv64",
124     ),
125     ignore
126 )]
test_process_group_no_posix_spawn()127 fn test_process_group_no_posix_spawn() {
128     unsafe {
129         // Same as above, create hang-y cat. This time, force using the non-posix_spawnp path.
130         let mut cmd = Command::new(OsStr::new("cat"));
131         cmd.pgroup(0);
132         cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec
133         cmd.stdin(Stdio::MakePipe);
134         cmd.stdout(Stdio::MakePipe);
135         let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
136 
137         // Check that we can kill its process group, which means there *is* one.
138         t!(cvt(dlibc::kill(-(cat.id() as dlibc::pid_t), dlibc::SIGINT)));
139 
140         t!(cat.wait());
141     }
142 }
143 
144 #[test]
test_program_kind()145 fn test_program_kind() {
146     let vectors = &[
147         ("foo", ProgramKind::PathLookup),
148         ("foo.out", ProgramKind::PathLookup),
149         ("./foo", ProgramKind::Relative),
150         ("../foo", ProgramKind::Relative),
151         ("dir/foo", ProgramKind::Relative),
152         // Note that paths on Unix can't contain / in them, so this is actually the directory "fo\\"
153         // followed by the file "o".
154         ("fo\\/o", ProgramKind::Relative),
155         ("/foo", ProgramKind::Absolute),
156         ("/dir/../foo", ProgramKind::Absolute),
157     ];
158 
159     for (program, expected_kind) in vectors {
160         assert_eq!(
161             ProgramKind::new(program.as_ref()),
162             *expected_kind,
163             "actual != expected program kind for input {program}",
164         );
165     }
166 }
167