Skip to main content

libcnb_test/
macros.rs

1/// Asserts that `left` contains `right`.
2///
3/// Commonly used when asserting `pack` output in integration tests. Expands to a [`str::contains`]
4/// call and logs `left` (in unescaped and escaped form) as well as `right` on failure.
5///
6/// # Example
7///
8/// ```
9/// use libcnb_test::assert_contains;
10///
11/// let output = "Hello World!\nHello Integration Test!";
12/// assert_contains!(output, "Integration");
13/// ```
14#[macro_export]
15macro_rules! assert_contains {
16    ($left:expr, $right:expr $(,)?) => {{
17        if !$left.contains($right) {
18            ::std::panic!(
19                r"assertion failed: `(left contains right)`
20left (unescaped):
21{}
22
23left (escaped): `{:?}`
24right: `{:?}`",
25                $left,
26                $left,
27                $right,
28            )
29        }
30    }};
31
32    ($left:expr, $right:expr, $($arg:tt)+) => {{
33        if !$left.contains($right) {
34            ::std::panic!(
35                r"assertion failed: `(left contains right)`
36left (unescaped):
37{}
38
39left (escaped): `{:?}`
40right: `{:?}`: {}",
41                $left,
42                $left,
43                $right,
44                ::core::format_args!($($arg)+)
45            )
46        }
47    }};
48}
49
50/// Asserts that `left` does not contain `right`.
51///
52/// Commonly used when asserting `pack` output in integration tests. Expands to a [`str::contains`]
53/// call and logs `left` (in unescaped and escaped form) as well as `right` on failure.
54///
55/// # Example
56///
57/// ```
58/// use libcnb_test::assert_not_contains;
59///
60/// let output = "Hello World!\nHello Integration Test!";
61/// assert_not_contains!(output, "Bahamas");
62/// ```
63#[macro_export]
64macro_rules! assert_not_contains {
65    ($left:expr, $right:expr $(,)?) => {{
66        if $left.contains($right) {
67            ::std::panic!(
68                r"assertion failed: `(left does not contain right)`
69left (unescaped):
70{}
71
72left (escaped): `{:?}`
73right: `{:?}`",
74                $left,
75                $left,
76                $right,
77            )
78        }
79    }};
80
81    ($left:expr, $right:expr, $($arg:tt)+) => {{
82        if $left.contains($right) {
83            ::std::panic!(
84                r"assertion failed: `(left does not contain right)`
85left (unescaped):
86{}
87
88left (escaped): `{:?}`
89right: `{:?}`: {}",
90                $left,
91                $left,
92                $right,
93                ::core::format_args!($($arg)+)
94            )
95        }
96    }};
97}
98
99/// Asserts that the provided value is empty.
100///
101/// Commonly used when asserting `pack` output in integration tests. Expands to a [`str::is_empty`]
102/// call and logs the value (in unescaped and escaped form) on failure.
103///
104/// # Example
105///
106/// ```
107/// use libcnb_test::assert_empty;
108///
109/// let output = "";
110/// assert_empty!(output);
111/// ```
112#[macro_export]
113macro_rules! assert_empty {
114    ($value:expr $(,)?) => {{
115        if !$value.is_empty() {
116            ::std::panic!(
117                r"assertion failed: `(is empty)`
118value (unescaped):
119{}
120
121value (escaped): `{:?}`",
122                $value,
123                $value,
124            )
125        }
126    }};
127
128    ($value:expr, $($arg:tt)+) => {{
129        if !$value.is_empty() {
130            ::std::panic!(
131                r"assertion failed: `(is empty)`
132value (unescaped):
133{}
134
135value (escaped): `{:?}`: {}",
136                $value,
137                $value,
138                ::core::format_args!($($arg)+)
139            )
140        }
141    }};
142}
143
144/// Asserts that `left` contains the `right` pattern (regular expression).
145///
146/// Commonly used when asserting `pack` output in integration tests. Expands to a regular
147/// expression match test and logs `left` (in unescaped and escaped form) as well as `right`
148/// on failure.
149///
150/// Multi-line mode is automatically enabled on regular expressions. If this is not what you
151/// want it can be disabled by adding `(?-m)` to the start of your pattern.
152///
153/// # Example
154///
155/// ```
156/// use libcnb_test::assert_contains_match;
157///
158/// let output = "Hello World!\nHello Integration Test!";
159/// assert_contains_match!(output, "Test!$");
160/// ```
161#[macro_export]
162macro_rules! assert_contains_match {
163    ($left:expr, $right:expr $(,)?) => {{
164        let regex = regex::Regex::new(&format!("(?m){}", $right)).expect("should be a valid regex");
165        if !regex.is_match(&$left) {
166            ::std::panic!(
167                r"assertion failed: `(left matches right pattern)`
168left (unescaped):
169{}
170
171left (escaped): `{:?}`
172right: `{:?}`",
173                $left,
174                $left,
175                regex
176            )
177        }
178    }};
179
180    ($left:expr, $right:expr, $($arg:tt)+) => {{
181        let regex = regex::Regex::new(&format!("(?m){}", $right)).expect("should be a valid regex");
182        if !regex.is_match(&$left) {
183            ::std::panic!(
184                r"assertion failed: `(left matches right pattern)`
185left (unescaped):
186{}
187
188left (escaped): `{:?}`
189right: `{:?}`: {}",
190                $left,
191                $left,
192                regex,
193                ::core::format_args!($($arg)+)
194            )
195        }
196    }};
197}
198
199/// Asserts that `left` does not contain the `right` pattern (regular expression).
200///
201/// Commonly used when asserting `pack` output in integration tests. Expands to a regular
202/// expression match test and logs `left` (in unescaped and escaped form) as well as `right`
203/// on failure.
204///
205/// Multi-line mode is automatically enabled on regular expressions. If this is not what you
206/// want it can be disabled by adding `(?-m)` to the start of your pattern.
207///
208/// # Example
209///
210/// ```
211/// use libcnb_test::assert_not_contains_match;
212///
213/// let output = "Hello World!\nHello Integration Test!";
214/// assert_not_contains_match!(output, "^Test!");
215/// ```
216#[macro_export]
217macro_rules! assert_not_contains_match {
218    ($left:expr, $right:expr $(,)?) => {{
219        let regex = regex::Regex::new(&format!("(?m){}", $right)).expect("should be a valid regex");
220        if regex.is_match(&$left) {
221            ::std::panic!(
222                r"assertion failed: `(left does not match right pattern)`
223left (unescaped):
224{}
225
226left (escaped): `{:?}`
227right: `{:?}`",
228                $left,
229                $left,
230                regex
231            )
232        }
233    }};
234
235    ($left:expr, $right:expr, $($arg:tt)+) => {{
236        let regex = regex::Regex::new(&format!("(?m){}", $right)).expect("should be a valid regex");
237        if regex.is_match(&$left) {
238            ::std::panic!(
239                r"assertion failed: `(left does not match right pattern)`
240left (unescaped):
241{}
242
243left (escaped): `{:?}`
244right: `{:?}`: {}",
245                $left,
246                $left,
247                regex,
248                ::core::format_args!($($arg)+)
249            )
250        }
251    }};
252}
253
254/// Asserts that an expression matches a given pattern.
255///
256/// This is a stable polyfill for `assert_matches!` that ensures 100% region coverage.
257/// Unlike the crate or standard library macros, this implementation avoids generating
258/// unreachable panic branches that `llvm-cov` flags as uncovered regions.
259///
260/// # Examples
261///
262/// ```
263/// use libcnb_test::assert_matches;
264///
265/// let result: Result<i32, String> = Ok(42);
266///
267/// // Simple pattern matching
268/// assert_matches!(result, Ok(_));
269///
270/// // With guard
271/// assert_matches!(result, Ok(x) if x > 40);
272///
273/// // With custom error message
274/// assert_matches!(result, Ok(x) if x > 40, "value should be greater than 40");
275/// ```
276#[macro_export]
277macro_rules! assert_matches {
278    // With a guard and custom message
279    ($expression:expr, $pattern:pat if $guard:expr, $($arg:tt)+) => {
280        match $expression {
281            $pattern if $guard => {}
282            ref _actual => {
283                ::std::panic!(
284                    "Expected match pattern: {} where {}, but got {:?}: {}",
285                    stringify!($pattern),
286                    stringify!($guard),
287                    _actual,
288                    ::core::format_args!($($arg)+)
289                );
290            }
291        }
292    };
293
294    // With a guard (e.g. `Ok(x) if x > 10`)
295    ($expression:expr, $pattern:pat if $guard:expr $(,)?) => {
296        match $expression {
297            $pattern if $guard => {}
298            ref _actual => {
299                ::std::panic!(
300                    "Expected match pattern: {} where {}, but got {:?}",
301                    stringify!($pattern),
302                    stringify!($guard),
303                    _actual
304                );
305            }
306        }
307    };
308
309    // Without a guard, with custom message
310    ($expression:expr, $pattern:pat, $($arg:tt)+) => {
311        match $expression {
312            $pattern if true => {}
313            ref _actual => {
314                ::std::panic!(
315                    "Expected match pattern: {}, but got {:?}: {}",
316                    stringify!($pattern),
317                    _actual,
318                    ::core::format_args!($($arg)+)
319                );
320            }
321        }
322    };
323
324    // Without a guard (injects `if true` to force branch coverage)
325    ($expression:expr, $pattern:pat $(,)?) => {
326        match $expression {
327            $pattern if true => {}
328            ref _actual => {
329                ::std::panic!(
330                    "Expected match pattern: {}, but got {:?}",
331                    stringify!($pattern),
332                    _actual
333                );
334            }
335        }
336    };
337}
338
339#[cfg(test)]
340mod tests {
341    #[test]
342    fn contains_simple() {
343        assert_contains!("Hello World!", "World");
344    }
345
346    #[test]
347    fn contains_simple_with_args() {
348        assert_contains!("Hello World!", "World", "World must be greeted!");
349    }
350
351    #[test]
352    #[should_panic(expected = "assertion failed: `(left contains right)`
353left (unescaped):
354foo
355
356left (escaped): `\"foo\"`
357right: `\"bar\"`")]
358    fn contains_simple_failure() {
359        assert_contains!("foo", "bar");
360    }
361
362    #[test]
363    #[should_panic(expected = "assertion failed: `(left contains right)`
364left (unescaped):
365Hello Germany!
366
367left (escaped): `\"Hello Germany!\"`
368right: `\"World\"`: World must be greeted!")]
369    fn contains_simple_failure_with_args() {
370        assert_contains!("Hello Germany!", "World", "World must be greeted!");
371    }
372
373    #[test]
374    fn contains_multiline() {
375        assert_contains!("Hello World!\nFoo\nBar\nBaz", "Bar");
376    }
377
378    #[test]
379    #[should_panic(expected = "assertion failed: `(left contains right)`
380left (unescaped):
381Hello World!
382Foo
383Bar
384Baz
385
386left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`
387right: `\"Eggs\"`")]
388    fn contains_multiline_failure() {
389        assert_contains!("Hello World!\nFoo\nBar\nBaz", "Eggs");
390    }
391
392    #[test]
393    #[should_panic(expected = "assertion failed: `(left contains right)`
394left (unescaped):
395Hello World!
396Foo
397Bar
398Baz
399
400left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`
401right: `\"Eggs\"`: We need eggs!")]
402    fn contains_multiline_failure_with_args() {
403        assert_contains!("Hello World!\nFoo\nBar\nBaz", "Eggs", "We need eggs!");
404    }
405
406    #[test]
407    fn not_contains_simple() {
408        assert_not_contains!("Hello World!", "Bahamas");
409    }
410
411    #[test]
412    fn not_contains_simple_with_args() {
413        assert_not_contains!("Hello World!", "Bahamas", "Bahamas must not be greeted!");
414    }
415
416    #[test]
417    #[should_panic(expected = "assertion failed: `(left does not contain right)`
418left (unescaped):
419foobar
420
421left (escaped): `\"foobar\"`
422right: `\"bar\"`")]
423    fn not_contains_simple_failure() {
424        assert_not_contains!("foobar", "bar");
425    }
426
427    #[test]
428    #[should_panic(expected = "assertion failed: `(left does not contain right)`
429left (unescaped):
430Hello Germany!
431
432left (escaped): `\"Hello Germany!\"`
433right: `\"Germany\"`: Germany must be greeted!")]
434    fn not_contains_simple_failure_with_args() {
435        assert_not_contains!("Hello Germany!", "Germany", "Germany must be greeted!");
436    }
437
438    #[test]
439    fn not_contains_multiline() {
440        assert_not_contains!("Hello World!\nFoo\nBar\nBaz", "Germany");
441    }
442
443    #[test]
444    #[should_panic(expected = "assertion failed: `(left does not contain right)`
445left (unescaped):
446Hello World!
447Foo
448Bar
449Baz
450
451left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`
452right: `\"Bar\"`")]
453    fn not_contains_multiline_failure() {
454        assert_not_contains!("Hello World!\nFoo\nBar\nBaz", "Bar");
455    }
456
457    #[test]
458    #[should_panic(expected = "assertion failed: `(left does not contain right)`
459left (unescaped):
460Hello Eggs!
461Foo
462Bar
463Baz
464
465left (escaped): `\"Hello Eggs!\\nFoo\\nBar\\nBaz\"`
466right: `\"Eggs\"`: We must not have eggs!")]
467    fn not_contains_multiline_failure_with_args() {
468        assert_not_contains!(
469            "Hello Eggs!\nFoo\nBar\nBaz",
470            "Eggs",
471            "We must not have eggs!"
472        );
473    }
474
475    #[test]
476    fn empty_simple() {
477        assert_empty!("");
478    }
479
480    #[test]
481    fn empty_simple_with_args() {
482        assert_empty!("", "Value must be empty!");
483    }
484
485    #[test]
486    #[should_panic(expected = "assertion failed: `(is empty)`
487value (unescaped):
488foo
489
490value (escaped): `\"foo\"`")]
491    fn empty_simple_failure() {
492        assert_empty!("foo");
493    }
494
495    #[test]
496    #[should_panic(expected = "assertion failed: `(is empty)`
497value (unescaped):
498Hello World!
499
500value (escaped): `\"Hello World!\"`: Greeting must be empty!")]
501    fn empty_simple_failure_with_args() {
502        assert_empty!("Hello World!", "Greeting must be empty!");
503    }
504
505    #[test]
506    #[should_panic(expected = "assertion failed: `(is empty)`
507value (unescaped):
508Hello World!
509Foo
510Bar
511Baz
512
513value (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`")]
514    fn empty_multiline_failure() {
515        assert_empty!("Hello World!\nFoo\nBar\nBaz");
516    }
517
518    #[test]
519    #[should_panic(expected = "assertion failed: `(is empty)`
520value (unescaped):
521Hello World!
522Foo
523Bar
524Baz
525
526value (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`: Greeting must be empty!")]
527    fn empty_multiline_failure_with_args() {
528        assert_empty!("Hello World!\nFoo\nBar\nBaz", "Greeting must be empty!");
529    }
530
531    #[test]
532    fn contains_match_simple() {
533        assert_contains_match!("Hello World!", "(?i)hello world!");
534    }
535
536    #[test]
537    fn contains_match_simple_with_args() {
538        assert_contains_match!("Hello World!", "(?i)hello world!", "World must be greeted");
539    }
540
541    #[test]
542    #[should_panic(expected = "assertion failed: `(left matches right pattern)`
543left (unescaped):
544foo
545
546left (escaped): `\"foo\"`
547right: `Regex(\"(?m)bar\")`")]
548    fn contains_match_simple_failure() {
549        assert_contains_match!("foo", "bar");
550    }
551
552    #[test]
553    #[should_panic(expected = "assertion failed: `(left matches right pattern)`
554left (unescaped):
555Hello World!
556
557left (escaped): `\"Hello World!\"`
558right: `Regex(\"(?m)(?-i)world\")`: World must be case-sensitively greeted!")]
559    fn contains_match_simple_failure_with_args() {
560        assert_contains_match!(
561            "Hello World!",
562            "(?-i)world",
563            "World must be case-sensitively greeted!"
564        );
565    }
566
567    #[test]
568    fn contains_match_multiline() {
569        assert_contains_match!("Hello World!\nFoo\nBar\nBaz", "^Bar$");
570    }
571
572    #[test]
573    #[should_panic(expected = "assertion failed: `(left matches right pattern)`
574left (unescaped):
575Hello World!
576Foo
577Bar
578Baz
579
580left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`
581right: `Regex(\"(?m)Eggs\")`")]
582    fn contains_match_multiline_failure() {
583        assert_contains_match!("Hello World!\nFoo\nBar\nBaz", "Eggs");
584    }
585
586    #[test]
587    #[should_panic(expected = "assertion failed: `(left matches right pattern)`
588left (unescaped):
589Hello World!
590Foo
591Bar
592Baz
593
594left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`
595right: `Regex(\"(?m)Eggs\")`: We need eggs!")]
596    fn contains_match_multiline_failure_with_args() {
597        assert_contains_match!("Hello World!\nFoo\nBar\nBaz", "Eggs", "We need eggs!");
598    }
599
600    #[test]
601    #[should_panic(expected = "should be a valid regex: Syntax(
602~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
603regex parse error:
604    (?m)(unclosed group
605        ^
606error: unclosed group
607~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
608)")]
609    fn contains_match_with_invalid_regex() {
610        assert_contains_match!("Hello World!", "(unclosed group");
611    }
612
613    #[test]
614    #[should_panic(expected = "should be a valid regex: Syntax(
615~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
616regex parse error:
617    (?m)(unclosed group
618        ^
619error: unclosed group
620~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
621)")]
622    fn contains_match_with_invalid_regex_and_args() {
623        assert_contains_match!("Hello World!", "(unclosed group", "This should fail.");
624    }
625
626    #[test]
627    fn not_contains_match_simple() {
628        assert_not_contains_match!("Hello World!", "^World");
629    }
630
631    #[test]
632    fn not_contains_match_simple_with_args() {
633        assert_not_contains_match!("Hello World!", "^World", "World must not be at the start!");
634    }
635
636    #[test]
637    #[should_panic(expected = "assertion failed: `(left does not match right pattern)`
638left (unescaped):
639foobar
640
641left (escaped): `\"foobar\"`
642right: `Regex(\"(?m)bar\")`")]
643    fn not_contains_match_simple_failure() {
644        assert_not_contains_match!("foobar", "bar");
645    }
646
647    #[test]
648    #[should_panic(expected = "assertion failed: `(left does not match right pattern)`
649left (unescaped):
650Hello Germany!
651
652left (escaped): `\"Hello Germany!\"`
653right: `Regex(\"(?m)Germany!$\")`: Germany must not be greeted!")]
654    fn not_contains_match_simple_failure_with_args() {
655        assert_not_contains_match!(
656            "Hello Germany!",
657            "Germany!$",
658            "Germany must not be greeted!"
659        );
660    }
661
662    #[test]
663    fn not_contains_match_multiline() {
664        assert_not_contains_match!("Hello World!\nFoo\nBar\nBaz", "^Germany$");
665    }
666
667    #[test]
668    #[should_panic(expected = "assertion failed: `(left does not match right pattern)`
669left (unescaped):
670Hello World!
671Foo
672Bar
673Baz
674
675left (escaped): `\"Hello World!\\nFoo\\nBar\\nBaz\"`
676right: `Regex(\"(?m)^Bar$\")`")]
677    fn not_contains_match_multiline_failure() {
678        assert_not_contains_match!("Hello World!\nFoo\nBar\nBaz", "^Bar$");
679    }
680
681    #[test]
682    #[should_panic(expected = "assertion failed: `(left does not match right pattern)`
683left (unescaped):
684Hello Eggs!
685Foo
686Bar
687Baz
688
689left (escaped): `\"Hello Eggs!\\nFoo\\nBar\\nBaz\"`
690right: `Regex(\"(?m)Eggs!$\")`: We must not have eggs!")]
691    fn not_contains_match_multiline_failure_with_args() {
692        assert_not_contains_match!(
693            "Hello Eggs!\nFoo\nBar\nBaz",
694            "Eggs!$",
695            "We must not have eggs!"
696        );
697    }
698
699    #[test]
700    #[should_panic(expected = "should be a valid regex: Syntax(
701~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
702regex parse error:
703    (?m)(unclosed group
704        ^
705error: unclosed group
706~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
707)")]
708    fn not_contains_match_with_invalid_regex() {
709        assert_not_contains_match!("Hello World!", "(unclosed group");
710    }
711
712    #[test]
713    #[should_panic(expected = "should be a valid regex: Syntax(
714~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
715regex parse error:
716    (?m)(unclosed group
717        ^
718error: unclosed group
719~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
720)")]
721    fn not_contains_match_with_invalid_regex_and_args() {
722        assert_not_contains_match!("Hello World!", "(unclosed group", "This will fail");
723    }
724
725    #[test]
726    fn assert_matches_ok_simple() {
727        let result: Result<i32, String> = Ok(42);
728        assert_matches!(result, Ok(_));
729    }
730
731    #[test]
732    fn assert_matches_ok_with_guard() {
733        let result: Result<i32, String> = Ok(42);
734        assert_matches!(result, Ok(x) if x > 40);
735    }
736
737    #[test]
738    fn assert_matches_err_simple() {
739        let result: Result<i32, String> = Err("error".to_string());
740        assert_matches!(result, Err(_));
741    }
742
743    #[test]
744    #[should_panic(expected = "Expected match pattern: Ok(_), but got Err(\"error\")")]
745    fn assert_matches_failure_simple() {
746        let result: Result<i32, String> = Err("error".to_string());
747        assert_matches!(result, Ok(_));
748    }
749
750    #[test]
751    #[should_panic(expected = "Expected match pattern: Ok(x) where x > 50, but got Ok(42)")]
752    fn assert_matches_failure_with_guard() {
753        let result: Result<i32, String> = Ok(42);
754        assert_matches!(result, Ok(x) if x > 50);
755    }
756
757    #[test]
758    fn assert_matches_option_some() {
759        let value: Option<&str> = Some("hello");
760        assert_matches!(value, Some("hello"));
761    }
762
763    #[test]
764    fn assert_matches_option_none() {
765        let value: Option<&str> = None;
766        assert_matches!(value, None);
767    }
768
769    #[test]
770    fn assert_matches_ok_simple_with_message() {
771        let result: Result<i32, String> = Ok(42);
772        assert_matches!(result, Ok(_), "result should be Ok");
773    }
774
775    #[test]
776    fn assert_matches_ok_with_guard_and_message() {
777        let result: Result<i32, String> = Ok(42);
778        assert_matches!(result, Ok(x) if x > 40, "value should be greater than 40");
779    }
780
781    #[test]
782    #[should_panic(
783        expected = "Expected match pattern: Ok(_), but got Err(\"error\"): result must be Ok"
784    )]
785    fn assert_matches_failure_simple_with_message() {
786        let result: Result<i32, String> = Err("error".to_string());
787        assert_matches!(result, Ok(_), "result must be Ok");
788    }
789
790    #[test]
791    #[should_panic(
792        expected = "Expected match pattern: Ok(x) where x > 50, but got Ok(42): value must exceed threshold"
793    )]
794    fn assert_matches_failure_with_guard_and_message() {
795        let result: Result<i32, String> = Ok(42);
796        assert_matches!(result, Ok(x) if x > 50, "value must exceed threshold");
797    }
798}