assertables/assert_status/
assert_status_success.rs

1//! Assert a status is a success.
2//!
3//! Pseudocode:<br>
4//! a ⇒ status ⇒ success = true
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("0");
13//! assert_status_success!(a);
14//! ```
15//!
16//! # Module macros
17//!
18//! * [`assert_status_success`](macro@crate::assert_status_success)
19//! * [`assert_status_success_as_result`](macro@crate::assert_status_success_as_result)
20//! * [`debug_assert_status_success`](macro@crate::debug_assert_status_success)
21
22/// Assert a status is a success.
23///
24/// Pseudocode:<br>
25/// a ⇒ status ⇒ success = true
26///
27/// * If true, return Result `Ok(a ⇒ status ⇒ code ⇒ value)`.
28///
29/// * Otherwise, return Result `Err(message)`.
30///
31/// This macro is useful for runtime checks, such as checking parameters,
32/// or sanitizing inputs, or handling different results in different ways.
33///
34/// # Module macros
35///
36/// * [`assert_status_success`](macro@crate::assert_status_success)
37/// * [`assert_status_success_as_result`](macro@crate::assert_status_success_as_result)
38/// * [`debug_assert_status_success`](macro@crate::debug_assert_status_success)
39///
40#[macro_export]
41macro_rules! assert_status_success_as_result {
42    ($a:expr $(,)?) => {
43        match ($a.status()) {
44            Ok(a1) => {
45                if a1.success() {
46                    Ok(true)
47                } else {
48                    Err(format!(
49                        concat!(
50                            "assertion failed: `assert_status_success!(a)`\n",
51                            "https://docs.rs/assertables/9.8.3/assertables/macro.assert_status_success.html\n",
52                            " a label: `{}`,\n",
53                            " a debug: `{:?}`",
54                        ),
55                        stringify!($a),
56                        $a,
57                    ))
58                }
59            }
60            a_status => Err(format!(
61                concat!(
62                    "assertion failed: `assert_status_success!(a)`\n",
63                    "https://docs.rs/assertables/9.8.3/assertables/macro.assert_status_success.html\n",
64                    "  a label: `{}`,\n",
65                    "  a debug: `{:?}`,\n",
66                    " a status: `{:?}`",
67                ),
68                stringify!($a),
69                $a,
70                a_status
71            )),
72        }
73    };
74}
75
76#[cfg(test)]
77mod test_assert_status_success_as_result {
78    use std::process::Command;
79    use std::sync::Once;
80
81    #[test]
82    fn success() {
83        let mut a = Command::new("bin/exit-with-arg");
84        a.arg("0");
85        for _ in 0..1 {
86            let actual = assert_status_success_as_result!(a);
87            assert_eq!(actual.unwrap(), true);
88        }
89    }
90
91    #[test]
92    fn success_once() {
93        static A: Once = Once::new();
94        fn a() -> Command {
95            if A.is_completed() {
96                panic!("A.is_completed()")
97            } else {
98                A.call_once(|| {})
99            }
100            let mut a = Command::new("bin/exit-with-arg");
101            a.arg("0");
102            a
103        }
104
105        assert_eq!(A.is_completed(), false);
106        let result = assert_status_success_as_result!(a());
107        assert!(result.is_ok());
108        assert_eq!(A.is_completed(), true);
109    }
110
111    #[test]
112    fn failure() {
113        let mut a = Command::new("bin/exit-with-arg");
114        a.arg("1");
115        let actual = assert_status_success_as_result!(a);
116        let message = concat!(
117            "assertion failed: `assert_status_success!(a)`\n",
118            "https://docs.rs/assertables/9.8.3/assertables/macro.assert_status_success.html\n",
119            " a label: `a`,\n",
120            " a debug: `\"bin/exit-with-arg\" \"1\"`",
121        );
122        assert_eq!(actual.unwrap_err(), message);
123    }
124}
125
126/// Assert a status is a success.
127///
128/// Pseudocode:<br>
129/// a ⇒ status ⇒ success = true
130///
131/// * If true, return `a ⇒ status ⇒ code ⇒ value``.
132///
133/// * Otherwise, call [`panic!`] with a message and the values of the
134///   expressions with their debug representations.
135///
136/// # Examples
137///
138/// ```rust
139/// use assertables::*;
140/// use std::process::Command;
141/// # use std::panic;
142///
143/// # fn main() {
144/// let mut a = Command::new("bin/exit-with-arg"); a.arg("0");
145/// assert_status_success!(a);
146///
147/// # let result = panic::catch_unwind(|| {
148/// // This will panic
149/// let mut a = Command::new("bin/exit-with-arg"); a.arg("1");
150/// assert_status_success!(a);
151/// # });
152/// // assertion failed: `assert_status_success!(a)`
153/// // https://docs.rs/assertables/…/assertables/macro.assert_status_success.html
154/// //  a label: `a`,
155/// //  a debug: `\"bin/exit-with-arg\" \"1\"`
156/// # let actual = result.unwrap_err().downcast::<String>().unwrap().to_string();
157/// # let message = concat!(
158/// #     "assertion failed: `assert_status_success!(a)`\n",
159/// #     "https://docs.rs/assertables/9.8.3/assertables/macro.assert_status_success.html\n",
160/// #     " a label: `a`,\n",
161/// #     " a debug: `\"bin/exit-with-arg\" \"1\"`"
162/// # );
163/// # assert_eq!(actual, message);
164/// # }
165/// ```
166///
167/// # Module macros
168///
169/// * [`assert_status_success`](macro@crate::assert_status_success)
170/// * [`assert_status_success_as_result`](macro@crate::assert_status_success_as_result)
171/// * [`debug_assert_status_success`](macro@crate::debug_assert_status_success)
172///
173#[macro_export]
174macro_rules! assert_status_success {
175    ($a:expr $(,)?) => {
176        match $crate::assert_status_success_as_result!($a) {
177            Ok(x) => x,
178            Err(err) => panic!("{}", err),
179        }
180    };
181    ($a:expr, $($message:tt)+) => {
182        match $crate::assert_status_success_as_result!($a) {
183            Ok(x) => x,
184            Err(err) => panic!("{}\n{}", format_args!($($message)+), err),
185        }
186    };
187}
188
189#[cfg(test)]
190mod test_assert_status_success {
191    use std::panic;
192    use std::process::Command;
193
194    #[test]
195    fn success() {
196        let mut a = Command::new("bin/exit-with-arg");
197        a.arg("0");
198        for _ in 0..1 {
199            let actual = assert_status_success!(a);
200            assert_eq!(actual, true);
201        }
202    }
203
204    #[test]
205    fn failure() {
206        let result = panic::catch_unwind(|| {
207            let mut a = Command::new("bin/exit-with-arg");
208            a.arg("1");
209            let _actual = assert_status_success!(a);
210        });
211        let message = concat!(
212            "assertion failed: `assert_status_success!(a)`\n",
213            "https://docs.rs/assertables/9.8.3/assertables/macro.assert_status_success.html\n",
214            " a label: `a`,\n",
215            " a debug: `\"bin/exit-with-arg\" \"1\"`",
216        );
217        assert_eq!(
218            result
219                .unwrap_err()
220                .downcast::<String>()
221                .unwrap()
222                .to_string(),
223            message
224        );
225    }
226}
227
228/// Assert a status is a success.
229///
230/// Pseudocode:<br>
231/// a ⇒ status ⇒ success = true
232///
233/// This macro provides the same statements as [`assert_status_success`](macro.assert_status_success.html),
234/// except this macro's statements are only enabled in non-optimized
235/// builds by default. An optimized build will not execute this macro's
236/// statements unless `-C debug-assertions` is passed to the compiler.
237///
238/// This macro is useful for checks that are too expensive to be present
239/// in a release build but may be helpful during development.
240///
241/// The result of expanding this macro is always type checked.
242///
243/// An unchecked assertion allows a "bin/exit-with-arg" in an inconsistent state to
244/// keep running, which might have unexpected consequences but does not
245/// introduce unsafety as long as this only happens in safe code. The
246/// performance cost of assertions, however, is not measurable in general.
247/// Replacing `assert*!` with `debug_assert*!` is thus only encouraged
248/// after thorough profiling, and more importantly, only in safe code!
249///
250/// This macro is intended to work in a similar way to
251/// [`::std::debug_assert`](https://doc.rust-lang.org/std/macro.debug_assert.html).
252///
253/// # Module macros
254///
255/// * [`assert_status_success`](macro@crate::assert_status_success)
256/// * [`assert_status_success`](macro@crate::assert_status_success)
257/// * [`debug_assert_status_success`](macro@crate::debug_assert_status_success)
258///
259#[macro_export]
260macro_rules! debug_assert_status_success {
261    ($($arg:tt)*) => {
262        if $crate::cfg!(debug_assertions) {
263            $crate::assert_status_success!($($arg)*);
264        }
265    };
266}