1+ use core:: fmt;
2+
13/// Conditions under which a test should fail or be skipped.
24///
35/// By passing a `FailureCase` to [`TestParameters::expect_fail`][expect_fail], you can
1517/// vendor: None,
1618/// adapter: Some("RTX"),
1719/// driver: None,
18- /// reasons: vec![FailureReason::ValidationError(Some ("Some error substring") )],
20+ /// reasons: vec![FailureReason::validation_error().with_message ("Some error substring")],
1921/// behavior: FailureBehavior::AssertFailure,
2022/// }
2123/// # ;
@@ -158,7 +160,7 @@ impl FailureCase {
158160 /// Return the reasons why this case should fail.
159161 pub fn reasons ( & self ) -> & [ FailureReason ] {
160162 if self . reasons . is_empty ( ) {
161- std:: array:: from_ref ( & FailureReason :: Any )
163+ std:: array:: from_ref ( & FailureReason :: ANY )
162164 } else {
163165 & self . reasons
164166 }
@@ -170,7 +172,8 @@ impl FailureCase {
170172 ///
171173 /// If multiple reasons are pushed, will match any of them.
172174 pub fn validation_error ( mut self , msg : & ' static str ) -> Self {
173- self . reasons . push ( FailureReason :: ValidationError ( Some ( msg) ) ) ;
175+ self . reasons
176+ . push ( FailureReason :: validation_error ( ) . with_message ( msg) ) ;
174177 self
175178 }
176179
@@ -180,7 +183,7 @@ impl FailureCase {
180183 ///
181184 /// If multiple reasons are pushed, will match any of them.
182185 pub fn panic ( mut self , msg : & ' static str ) -> Self {
183- self . reasons . push ( FailureReason :: Panic ( Some ( msg) ) ) ;
186+ self . reasons . push ( FailureReason :: panic ( ) . with_message ( msg) ) ;
184187 self
185188 }
186189
@@ -247,43 +250,16 @@ impl FailureCase {
247250 /// Returns true if the given failure "satisfies" this failure case.
248251 pub ( crate ) fn matches_failure ( & self , failure : & FailureResult ) -> bool {
249252 for reason in self . reasons ( ) {
250- let result = match ( reason, failure) {
251- ( FailureReason :: Any , _) => {
252- log:: error!( "Matched failure case: Wildcard" ) ;
253- true
254- }
255- ( FailureReason :: ValidationError ( None ) , FailureResult :: ValidationError ( _) ) => {
256- log:: error!( "Matched failure case: Any Validation Error" ) ;
257- true
258- }
259- (
260- FailureReason :: ValidationError ( Some ( expected) ) ,
261- FailureResult :: ValidationError ( Some ( actual) ) ,
262- ) => {
263- let result = actual. to_lowercase ( ) . contains ( & expected. to_lowercase ( ) ) ;
264- if result {
265- log:: error!(
266- "Matched failure case: Validation Error containing \" {}\" " ,
267- expected
268- ) ;
269- }
270- result
271- }
272- ( FailureReason :: Panic ( None ) , FailureResult :: Panic ( _) ) => {
273- log:: error!( "Matched failure case: Any Panic" ) ;
274- true
275- }
276- ( FailureReason :: Panic ( Some ( expected) ) , FailureResult :: Panic ( Some ( actual) ) ) => {
277- let result = actual. to_lowercase ( ) . contains ( & expected. to_lowercase ( ) ) ;
278- if result {
279- log:: error!( "Matched failure case: Panic containing \" {}\" " , expected) ;
280- }
281- result
282- }
283- _ => false ,
284- } ;
285-
286- if result {
253+ let kind_matched = reason. kind . map_or ( true , |kind| kind == failure. kind ) ;
254+
255+ let message_matched =
256+ reason
257+ . message
258+ . map_or ( true , |message| matches ! ( & failure. message, Some ( actual) if actual. to_lowercase( ) . contains( & message. to_lowercase( ) ) ) ) ;
259+
260+ if kind_matched && message_matched {
261+ let message = failure. message . as_deref ( ) . unwrap_or ( "*no message*" ) ;
262+ log:: error!( "Matched {} {message}" , failure. kind) ;
287263 return true ;
288264 }
289265 }
@@ -308,18 +284,54 @@ bitflags::bitflags! {
308284///
309285/// If the test fails for a different reason, the given FailureCase will be ignored.
310286#[ derive( Default , Debug , Clone , PartialEq ) ]
311- pub enum FailureReason {
312- /// Matches any failure.
313- #[ default]
314- Any ,
315- /// Matches validation errors raised from the backend validation.
287+ pub struct FailureReason {
288+ /// Match a particular kind of failure result.
316289 ///
317- /// If a string is provided, matches only validation errors that contain the string .
318- ValidationError ( Option < & ' static str > ) ,
319- /// A panic was raised .
290+ /// If `None`, match any result kind .
291+ kind : Option < FailureResultKind > ,
292+ /// Match a particular message of a failure result .
320293 ///
321- /// If a string is provided, matches only panics that contain the string.
322- Panic ( Option < & ' static str > ) ,
294+ /// If `None`, matches any message. If `Some`, a case-insensitive sub-string
295+ /// test is performed. Allowing `"error occured"` to match a message like
296+ /// `"An unexpected Error occured!"`.
297+ message : Option < & ' static str > ,
298+ }
299+
300+ impl FailureReason {
301+ /// Match any failure reason.
302+ const ANY : Self = Self {
303+ kind : None ,
304+ message : None ,
305+ } ;
306+
307+ /// Match a validation error.
308+ #[ allow( dead_code) ] // Not constructed on wasm
309+ pub fn validation_error ( ) -> Self {
310+ Self {
311+ kind : Some ( FailureResultKind :: ValidationError ) ,
312+ message : None ,
313+ }
314+ }
315+
316+ /// Match a panic.
317+ pub fn panic ( ) -> Self {
318+ Self {
319+ kind : Some ( FailureResultKind :: Panic ) ,
320+ message : None ,
321+ }
322+ }
323+
324+ /// Match an error with a message.
325+ ///
326+ /// If specified, a case-insensitive sub-string test is performed. Allowing
327+ /// `"error occured"` to match a message like `"An unexpected Error
328+ /// occured!"`.
329+ pub fn with_message ( self , message : & ' static str ) -> Self {
330+ Self {
331+ message : Some ( message) ,
332+ ..self
333+ }
334+ }
323335}
324336
325337#[ derive( Default , Clone ) ]
@@ -336,11 +348,53 @@ pub enum FailureBehavior {
336348 Ignore ,
337349}
338350
351+ #[ derive( Debug , Clone , Copy , PartialEq ) ]
352+ pub ( crate ) enum FailureResultKind {
353+ #[ allow( dead_code) ] // Not constructed on wasm
354+ ValidationError ,
355+ Panic ,
356+ }
357+
358+ impl fmt:: Display for FailureResultKind {
359+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
360+ match self {
361+ FailureResultKind :: ValidationError => write ! ( f, "Validation Error" ) ,
362+ FailureResultKind :: Panic => write ! ( f, "Panic" ) ,
363+ }
364+ }
365+ }
366+
339367#[ derive( Debug ) ]
340- pub ( crate ) enum FailureResult {
368+ pub ( crate ) struct FailureResult {
369+ kind : FailureResultKind ,
370+ message : Option < String > ,
371+ }
372+
373+ impl FailureResult {
374+ /// Failure result is a panic.
375+ pub ( super ) fn panic ( ) -> Self {
376+ Self {
377+ kind : FailureResultKind :: Panic ,
378+ message : None ,
379+ }
380+ }
381+
382+ /// Failure result is a validation error.
341383 #[ allow( dead_code) ] // Not constructed on wasm
342- ValidationError ( Option < String > ) ,
343- Panic ( Option < String > ) ,
384+ pub ( super ) fn validation_error ( ) -> Self {
385+ Self {
386+ kind : FailureResultKind :: ValidationError ,
387+ message : None ,
388+ }
389+ }
390+
391+ /// Message associated with a failure result.
392+ pub ( super ) fn with_message ( self , message : impl fmt:: Display ) -> Self {
393+ Self {
394+ kind : self . kind ,
395+ message : Some ( message. to_string ( ) ) ,
396+ }
397+ }
344398}
345399
346400#[ derive( PartialEq , Clone , Copy , Debug ) ]
@@ -393,7 +447,8 @@ pub(crate) fn expectations_match_failures(
393447 if !actual. is_empty ( ) {
394448 result = ExpectationMatchResult :: Panic ;
395449 for failure in actual {
396- log:: error!( "Unexpected failure due to: {:?}" , failure) ;
450+ let message = failure. message . as_deref ( ) . unwrap_or ( "*no message*" ) ;
451+ log:: error!( "{}: {message}" , failure. kind) ;
397452 }
398453 }
399454
@@ -409,11 +464,11 @@ mod test {
409464 } ;
410465
411466 fn validation_err ( msg : & ' static str ) -> FailureResult {
412- FailureResult :: ValidationError ( Some ( String :: from ( msg) ) )
467+ FailureResult :: validation_error ( ) . with_message ( msg)
413468 }
414469
415470 fn panic ( msg : & ' static str ) -> FailureResult {
416- FailureResult :: Panic ( Some ( String :: from ( msg) ) )
471+ FailureResult :: panic ( ) . with_message ( msg)
417472 }
418473
419474 #[ test]
@@ -423,7 +478,7 @@ mod test {
423478 // -- Unexpected failure --
424479
425480 let expectation = vec ! [ ] ;
426- let actual = vec ! [ FailureResult :: ValidationError ( None ) ] ;
481+ let actual = vec ! [ FailureResult :: validation_error ( ) ] ;
427482
428483 assert_eq ! (
429484 super :: expectations_match_failures( & expectation, actual) ,
@@ -443,7 +498,7 @@ mod test {
443498 // -- Expected failure (validation) --
444499
445500 let expectation = vec ! [ FailureCase :: always( ) ] ;
446- let actual = vec ! [ FailureResult :: ValidationError ( None ) ] ;
501+ let actual = vec ! [ FailureResult :: validation_error ( ) ] ;
447502
448503 assert_eq ! (
449504 super :: expectations_match_failures( & expectation, actual) ,
@@ -453,7 +508,7 @@ mod test {
453508 // -- Expected failure (panic) --
454509
455510 let expectation = vec ! [ FailureCase :: always( ) ] ;
456- let actual = vec ! [ FailureResult :: Panic ( None ) ] ;
511+ let actual = vec ! [ FailureResult :: panic ( ) ] ;
457512
458513 assert_eq ! (
459514 super :: expectations_match_failures( & expectation, actual) ,
@@ -469,9 +524,9 @@ mod test {
469524
470525 let expectation: Vec < FailureCase > =
471526 vec ! [ FailureCase :: always( ) . validation_error( "Some StrIng" ) ] ;
472- let actual = vec ! [ FailureResult :: ValidationError ( Some ( String :: from (
527+ let actual = vec ! [ FailureResult :: validation_error ( ) . with_message (
473528 "a very long string that contains sOmE sTrInG of different capitalization" ,
474- ) ) ) ] ;
529+ ) ] ;
475530
476531 assert_eq ! (
477532 super :: expectations_match_failures( & expectation, actual) ,
0 commit comments