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