assertables/assert_status/
assert_status_code_value_eq.rs

1//! Assert a status code value is equal to another.
2//!
3//! Pseudocode:<br>
4//! a ⇒ status ⇒ code ⇒ value = b ⇒ status ⇒ code ⇒ value
5//!
6//! # Example
7//!
8//! ```rust
9//! use assertables::*;
10//! use std::process::Command;
11//!
12//! let mut a = Command::new("bin/exit-with-arg"); a.arg("1");
13//! let mut b = Command::new("bin/exit-with-arg"); b.arg("1");
14//! assert_status_code_value_eq!(a, b);
15//! ```
16//!
17//! # Module macros
18//!
19//! * [`assert_status_code_value_eq`](macro@crate::assert_status_code_value_eq)
20//! * [`assert_status_code_value_eq_as_result`](macro@crate::assert_status_code_value_eq_as_result)
21//! * [`debug_assert_status_code_value_eq`](macro@crate::debug_assert_status_code_value_eq)
22
23/// Assert a status code value is equal to another.
24///
25/// Pseudocode:<br>
26/// a ⇒ status ⇒ code ⇒ value = b ⇒ status ⇒ code ⇒ value
27///
28/// * If true, return Result `Ok((a value, b value)))`.
29///
30/// * Otherwise, return Result `Err(message)`.
31///
32/// This macro is useful for runtime checks, such as checking parameters,
33/// or sanitizing inputs, or handling different results in different ways.
34///
35/// # Module macros
36///
37/// * [`assert_status_code_value_eq`](macro@crate::assert_status_code_value_eq)
38/// * [`assert_status_code_value_eq_as_result`](macro@crate::assert_status_code_value_eq_as_result)
39/// * [`debug_assert_status_code_value_eq`](macro@crate::debug_assert_status_code_value_eq)
40///
41#[macro_export]
42macro_rules! assert_status_code_value_eq_as_result {
43    ($a_process:expr, $b_process:expr $(,)?) => {
44        match ($a_process.status(), $b_process.status()) {
45            (Ok(a_status), Ok(b_status)) => match (a_status.code(), b_status.code()) {
46                (Some(a_code), Some(b_code)) => {
47                    if a_code == b_code {
48                        Ok((a_code, b_code))
49                    } else {
50                        Err(format!(
51                            concat!(
52                                "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
53                                "https://docs.rs/assertables/",
54                                env!("CARGO_PKG_VERSION"),
55                                "/assertables/macro.assert_status_code_value_eq.html\n",
56                                " a label: `{}`,\n",
57                                " a debug: `{:?}`,\n",
58                                "  a code: `{:?}`,\n",
59                                " b label: `{}`,\n",
60                                " b debug: `{:?}`\n",
61                                "  b code: `{:?}`",
62                            ),
63                            stringify!($a_process),
64                            $a_process,
65                            a_code,
66                            stringify!($b_process),
67                            $b_process,
68                            b_code,
69                        ))
70                    }
71                }
72                _ => Err(format!(
73                    concat!(
74                        "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
75                        "https://docs.rs/assertables/",
76                        env!("CARGO_PKG_VERSION"),
77                        "/assertables/macro.assert_status_code_value_eq.html\n",
78                        " a label: `{}`,\n",
79                        " a debug: `{:?}`,\n",
80                        " b label: `{}`,\n",
81                        " b debug: `{:?}`",
82                    ),
83                    stringify!($a_process),
84                    $a_process,
85                    stringify!($b),
86                    $b_process
87                )),
88            },
89            _ => Err(format!(
90                concat!(
91                    "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
92                    "https://docs.rs/assertables/",
93                    env!("CARGO_PKG_VERSION"),
94                    "/assertables/macro.assert_status_code_value_eq.html\n",
95                    "  a label: `{}`,\n",
96                    "  a debug: `{:?}`,\n",
97                    "  b label: `{}`,\n",
98                    "  b debug: `{:?}`",
99                ),
100                stringify!($a_process),
101                $a_process,
102                stringify!($b),
103                $b_process,
104            )),
105        }
106    };
107}
108
109#[cfg(test)]
110mod test_assert_status_code_value_eq_as_result {
111    use std::process::Command;
112    use std::sync::Once;
113
114    #[test]
115    fn eq() {
116        let mut a = Command::new("bin/exit-with-arg");
117        a.arg("1");
118        let mut b = Command::new("bin/exit-with-arg");
119        b.arg("1");
120        for _ in 0..1 {
121            let actual = assert_status_code_value_eq_as_result!(a, b);
122            assert_eq!(actual.unwrap(), (1, 1));
123        }
124    }
125
126    #[test]
127    fn eq_once() {
128        static A: Once = Once::new();
129        fn a() -> Command {
130            if A.is_completed() {
131                panic!("A.is_completed()")
132            } else {
133                A.call_once(|| {})
134            }
135            let mut a = Command::new("bin/exit-with-arg");
136            a.arg("1");
137            a
138        }
139
140        static B: Once = Once::new();
141        fn b() -> Command {
142            if B.is_completed() {
143                panic!("B.is_completed()")
144            } else {
145                B.call_once(|| {})
146            }
147            let mut b = Command::new("bin/exit-with-arg");
148            b.arg("1");
149            b
150        }
151
152        assert_eq!(A.is_completed(), false);
153        assert_eq!(B.is_completed(), false);
154        let result = assert_status_code_value_eq_as_result!(a(), b());
155        assert!(result.is_ok());
156        assert_eq!(A.is_completed(), true);
157        assert_eq!(B.is_completed(), true);
158    }
159
160    #[test]
161    fn lt() {
162        let mut a = Command::new("bin/exit-with-arg");
163        a.arg("1");
164        let mut b = Command::new("bin/exit-with-arg");
165        b.arg("2");
166        let actual = assert_status_code_value_eq_as_result!(a, b);
167        let message = concat!(
168            "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
169            "https://docs.rs/assertables/",
170            env!("CARGO_PKG_VERSION"),
171            "/assertables/macro.assert_status_code_value_eq.html\n",
172            " a label: `a`,\n",
173            " a debug: `\"bin/exit-with-arg\" \"1\"`,\n",
174            "  a code: `1`,\n",
175            " b label: `b`,\n",
176            " b debug: `\"bin/exit-with-arg\" \"2\"`\n",
177            "  b code: `2`"
178        );
179        assert_eq!(actual.unwrap_err(), message);
180    }
181
182    #[test]
183    fn gt() {
184        let mut a = Command::new("bin/exit-with-arg");
185        a.arg("2");
186        let mut b = Command::new("bin/exit-with-arg");
187        b.arg("1");
188        let actual = assert_status_code_value_eq_as_result!(a, b);
189        let message = concat!(
190            "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
191            "https://docs.rs/assertables/",
192            env!("CARGO_PKG_VERSION"),
193            "/assertables/macro.assert_status_code_value_eq.html\n",
194            " a label: `a`,\n",
195            " a debug: `\"bin/exit-with-arg\" \"2\"`,\n",
196            "  a code: `2`,\n",
197            " b label: `b`,\n",
198            " b debug: `\"bin/exit-with-arg\" \"1\"`\n",
199            "  b code: `1`"
200        );
201        assert_eq!(actual.unwrap_err(), message);
202    }
203}
204
205/// Assert a status code value is equal to another.
206///
207/// Pseudocode:<br>
208/// a ⇒ status ⇒ code ⇒ value = b ⇒ status ⇒ code ⇒ value
209///
210/// * If true, return `(a value, b value)`.
211///
212/// * Otherwise, call [`panic!`] with a message and the values of the
213///   expressions with their debug representations.
214///
215/// # Examples
216///
217/// ```rust
218/// use assertables::*;
219/// use std::process::Command;
220/// # use std::panic;
221///
222/// # fn main() {
223/// let mut a = Command::new("bin/exit-with-arg"); a.arg("1");
224/// let mut b = Command::new("bin/exit-with-arg"); b.arg("1");
225/// assert_status_code_value_eq!(a, b);
226///
227/// # let result = panic::catch_unwind(|| {
228/// // This will panic
229/// let mut a = Command::new("bin/exit-with-arg"); a.arg("1");
230/// let mut b = Command::new("bin/exit-with-arg"); b.arg("2");
231/// assert_status_code_value_eq!(a, b);
232/// # });
233/// // assertion failed: `assert_status_code_value_eq!(a, b)`
234/// // https://docs.rs/assertables/9.7.0/assertables/macro.assert_status_code_value_eq.html
235/// //  a label: `a`,
236/// //  a debug: `\"bin/exit-with-arg\" \"1\"`,
237/// //  a value: `1`",
238/// //  b label: `b`,
239/// //  b debug: `\"bin/exit-with-arg\" \"2\"`,
240/// //  b value: `2`"
241/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
242/// # let message = concat!(
243/// #     "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
244/// #     "https://docs.rs/assertables/", env!("CARGO_PKG_VERSION"), "/assertables/macro.assert_status_code_value_eq.html\n",
245/// #     " a label: `a`,\n",
246/// #     " a debug: `\"bin/exit-with-arg\" \"1\"`,\n",
247/// #     "  a code: `1`,\n",
248/// #     " b label: `b`,\n",
249/// #     " b debug: `\"bin/exit-with-arg\" \"2\"`\n",
250/// #     "  b code: `2`",
251/// # );
252/// # assert_eq!(actual, message);
253/// # }
254/// ```
255///
256/// # Module macros
257///
258/// * [`assert_status_code_value_eq`](macro@crate::assert_status_code_value_eq)
259/// * [`assert_status_code_value_eq_as_result`](macro@crate::assert_status_code_value_eq_as_result)
260/// * [`debug_assert_status_code_value_eq`](macro@crate::debug_assert_status_code_value_eq)
261///
262#[macro_export]
263macro_rules! assert_status_code_value_eq {
264    ($a_process:expr, $b_process:expr $(,)?) => {
265        match $crate::assert_status_code_value_eq_as_result!($a_process, $b_process) {
266            Ok(x) => x,
267            Err(err) => panic!("{}", err),
268        }
269    };
270    ($a_process:expr, $b_process:expr, $($message:tt)+) => {
271        match $crate::assert_status_code_value_eq_as_result!($a_process, $b_process) {
272            Ok(x) => x,
273            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
274        }
275    };
276}
277
278#[cfg(test)]
279mod test_assert_status_code_value_eq {
280    use std::panic;
281    use std::process::Command;
282
283    #[test]
284    fn eq() {
285        let mut a = Command::new("bin/exit-with-arg");
286        a.arg("1");
287        let mut b = Command::new("bin/exit-with-arg");
288        b.arg("1");
289        for _ in 0..1 {
290            let actual = assert_status_code_value_eq!(a, b);
291            assert_eq!(actual, (1, 1));
292        }
293    }
294
295    #[test]
296    fn lt() {
297        let result = panic::catch_unwind(|| {
298            let mut a = Command::new("bin/exit-with-arg");
299            a.arg("1");
300            let mut b = Command::new("bin/exit-with-arg");
301            b.arg("2");
302            let _actual = assert_status_code_value_eq!(a, b);
303        });
304        let message = concat!(
305            "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
306            "https://docs.rs/assertables/",
307            env!("CARGO_PKG_VERSION"),
308            "/assertables/macro.assert_status_code_value_eq.html\n",
309            " a label: `a`,\n",
310            " a debug: `\"bin/exit-with-arg\" \"1\"`,\n",
311            "  a code: `1`,\n",
312            " b label: `b`,\n",
313            " b debug: `\"bin/exit-with-arg\" \"2\"`\n",
314            "  b code: `2`"
315        );
316        assert_eq!(
317            result
318                .unwrap_err()
319                .downcast::<String>()
320                .unwrap()
321                .to_string(),
322            message
323        );
324    }
325
326    #[test]
327    fn gt() {
328        let result = panic::catch_unwind(|| {
329            let mut a = Command::new("bin/exit-with-arg");
330            a.arg("2");
331            let mut b = Command::new("bin/exit-with-arg");
332            b.arg("1");
333            let _actual = assert_status_code_value_eq!(a, b);
334        });
335        let message = concat!(
336            "assertion failed: `assert_status_code_value_eq!(a, b)`\n",
337            "https://docs.rs/assertables/",
338            env!("CARGO_PKG_VERSION"),
339            "/assertables/macro.assert_status_code_value_eq.html\n",
340            " a label: `a`,\n",
341            " a debug: `\"bin/exit-with-arg\" \"2\"`,\n",
342            "  a code: `2`,\n",
343            " b label: `b`,\n",
344            " b debug: `\"bin/exit-with-arg\" \"1\"`\n",
345            "  b code: `1`"
346        );
347        assert_eq!(
348            result
349                .unwrap_err()
350                .downcast::<String>()
351                .unwrap()
352                .to_string(),
353            message
354        );
355    }
356}
357
358/// Assert a status code value is equal to another.
359///
360/// Pseudocode:<br>
361/// a ⇒ status ⇒ code ⇒ value = b ⇒ status ⇒ code ⇒ value
362///
363/// This macro provides the same statements as [`assert_status_code_value_eq`](macro.assert_status_code_value_eq.html),
364/// except this macro's statements are only enabled in non-optimized
365/// builds by default. An optimized build will not execute this macro's
366/// statements unless `-C debug-assertions` is passed to the compiler.
367///
368/// This macro is useful for checks that are too expensive to be present
369/// in a release build but may be helpful during development.
370///
371/// The result of expanding this macro is always type checked.
372///
373/// An unchecked assertion allows a "bin/exit-with-arg" in an inconsistent state to
374/// keep running, which might have unexpected consequences but does not
375/// introduce unsafety as long as this only happens in safe code. The
376/// performance cost of assertions, however, is not measurable in general.
377/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
378/// after thorough profiling, and more importantly, only in safe code!
379///
380/// This macro is intended to work in a similar way to
381/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
382///
383/// # Module macros
384///
385/// * [`assert_status_code_value_eq`](macro@crate::assert_status_code_value_eq)
386/// * [`assert_status_code_value_eq`](macro@crate::assert_status_code_value_eq)
387/// * [`debug_assert_status_code_value_eq`](macro@crate::debug_assert_status_code_value_eq)
388///
389#[macro_export]
390macro_rules! debug_assert_status_code_value_eq {
391    ($($arg:tt)*) => {
392        if $crate::cfg!(debug_assertions) {
393            $crate::assert_status_code_value_eq!($($arg)*);
394        }
395    };
396}