rusty_forkfork/
fork_test.rs1use std::process::Command;
16
17use crate::child_wrapper::ChildWrapper;
18
19#[macro_export]
71macro_rules! rusty_fork_test {
72 (#![rusty_fork(timeout_ms = $timeout:expr)]
73 $(
74 $(#[$meta:meta])*
75 fn $test_name:ident() $( -> $test_return:ty )? $body:block
76 )*) => { $(
77 $(#[$meta])*
78 fn $test_name() {
79 fn body_fn() $( -> $test_return )? $body
82 let body: fn () $( -> $test_return )? = body_fn;
83
84 fn supervise_fn(child: &mut $crate::ChildWrapper,
85 _file: &mut ::std::fs::File) {
86 $crate::fork_test::supervise_child(child, $timeout)
87 }
88 let supervise:
89 fn (&mut $crate::ChildWrapper, &mut ::std::fs::File) =
90 supervise_fn;
91
92 $crate::fork(
93 $crate::rusty_fork_test_name!($test_name),
94 $crate::rusty_fork_id!(),
95 $crate::fork_test::no_configure_child,
96 supervise, body).expect("forking test failed")
97 }
98 )* };
99
100 ($(
101 $(#[$meta:meta])*
102 fn $test_name:ident() $( -> $test_return:ty )? $body:block
103 )*) => {
104 rusty_fork_test! {
105 #![rusty_fork(timeout_ms = 0)]
106
107 $($(#[$meta])* fn $test_name() $( -> $test_return )? $body)*
108 }
109 };
110}
111
112#[macro_export]
121macro_rules! rusty_fork_test_name {
122 ($function_name:ident) => {
123 $crate::fork_test::fix_module_path(concat!(
124 module_path!(),
125 "::",
126 stringify!($function_name)
127 ))
128 };
129}
130
131#[allow(missing_docs)]
132#[doc(hidden)]
133pub fn supervise_child(child: &mut ChildWrapper, timeout_ms: u64) {
134 if timeout_ms > 0 {
135 wait_timeout(child, timeout_ms)
136 } else {
137 let status = child.wait().expect("failed to wait for child");
138 assert!(
139 status.success(),
140 "child exited unsuccessfully with {}",
141 status
142 );
143 }
144}
145
146#[allow(missing_docs)]
147#[doc(hidden)]
148pub fn no_configure_child(_child: &mut Command) {}
149
150pub fn fix_module_path(path: &str) -> &str {
154 path.find("::").map(|ix| &path[ix + 2..]).unwrap_or(path)
155}
156
157#[cfg(feature = "timeout")]
158fn wait_timeout(child: &mut ChildWrapper, timeout_ms: u64) {
159 use std::time::Duration;
160
161 let timeout = Duration::from_millis(timeout_ms);
162 let status = child
163 .wait_timeout(timeout)
164 .expect("failed to wait for child");
165 if let Some(status) = status {
166 assert!(
167 status.success(),
168 "child exited unsuccessfully with {}",
169 status
170 );
171 } else {
172 panic!("child process exceeded {} ms timeout", timeout_ms);
173 }
174}
175
176#[cfg(not(feature = "timeout"))]
177fn wait_timeout(_: &mut ChildWrapper, _: u64) {
178 panic!(
179 "Using the timeout feature of rusty_fork_test! requires \
180 enabling the `timeout` feature on the rusty-fork crate."
181 );
182}
183
184#[cfg(test)]
185mod test {
186 rusty_fork_test! {
187 #[test]
188 fn trivial() { }
189
190 #[test]
191 fn trivial_with_ok() -> Result<(), &'static str> { Ok(()) }
192
193 #[test]
194 #[should_panic]
195 fn trivial_with_err() -> Result<(), &'static str> { Err("should fail.") }
196
197 #[test]
198 #[should_panic]
199 fn panicking_child() {
200 panic!("just testing a panic, nothing to see here");
201 }
202
203 #[test]
204 #[should_panic]
205 fn aborting_child() {
206 ::std::process::abort();
207 }
208 }
209
210 rusty_fork_test! {
211 #![rusty_fork(timeout_ms = 1000)]
212
213 #[test]
214 #[cfg(feature = "timeout")]
215 fn timeout_passes() { }
216
217 #[test]
218 #[should_panic]
219 #[cfg(feature = "timeout")]
220 fn timeout_fails() {
221 println!("hello from child");
222 ::std::thread::sleep(
223 ::std::time::Duration::from_millis(10000));
224 println!("goodbye from child");
225 }
226 }
227}