@@ -47,6 +47,7 @@ use self::TestEvent::*;
4747use self :: NamePadding :: * ;
4848use self :: OutputLocation :: * ;
4949
50+ use std:: any:: { Any , AnyRefExt } ;
5051use std:: collections:: TreeMap ;
5152use stats:: Stats ;
5253use getopts:: { OptGroup , optflag, optopt} ;
@@ -78,7 +79,7 @@ pub mod test {
7879 MetricChange , Improvement , Regression , LikelyNoise ,
7980 StaticTestFn , StaticTestName , DynTestName , DynTestFn ,
8081 run_test, test_main, test_main_static, filter_tests,
81- parse_opts, StaticBenchFn } ;
82+ parse_opts, StaticBenchFn , ShouldFail } ;
8283}
8384
8485pub mod stats;
@@ -184,13 +185,19 @@ pub struct Bencher {
184185 pub bytes : u64 ,
185186}
186187
188+ #[ deriving( Clone , Show , PartialEq , Eq , Hash ) ]
189+ pub enum ShouldFail {
190+ No ,
191+ Yes ( Option < & ' static str > )
192+ }
193+
187194// The definition of a single test. A test runner will run a list of
188195// these.
189196#[ deriving( Clone , Show , PartialEq , Eq , Hash ) ]
190197pub struct TestDesc {
191198 pub name : TestName ,
192199 pub ignore : bool ,
193- pub should_fail : bool ,
200+ pub should_fail : ShouldFail ,
194201}
195202
196203#[ deriving( Show ) ]
@@ -346,7 +353,7 @@ fn optgroups() -> Vec<getopts::OptGroup> {
346353
347354fn usage ( binary : & str ) {
348355 let message = format ! ( "Usage: {} [OPTIONS] [FILTER]" , binary) ;
349- println ! ( r"{usage}
356+ println ! ( r# "{usage}
350357
351358The FILTER regex is tested against the name of all tests to run, and
352359only those tests that match are run.
@@ -366,10 +373,12 @@ Test Attributes:
366373 function takes one argument (test::Bencher).
367374 #[should_fail] - This function (also labeled with #[test]) will only pass if
368375 the code causes a failure (an assertion failure or panic!)
376+ A message may be provided, which the failure string must
377+ contain: #[should_fail(expected = "foo")].
369378 #[ignore] - When applied to a function which is already attributed as a
370379 test, then the test runner will ignore these tests during
371380 normal test runs. Running with --ignored will run these
372- tests." ,
381+ tests."# ,
373382 usage = getopts:: usage( message. as_slice( ) ,
374383 optgroups( ) . as_slice( ) ) ) ;
375384}
@@ -902,13 +911,13 @@ fn should_sort_failures_before_printing_them() {
902911 let test_a = TestDesc {
903912 name : StaticTestName ( "a" ) ,
904913 ignore : false ,
905- should_fail : false
914+ should_fail : ShouldFail :: No
906915 } ;
907916
908917 let test_b = TestDesc {
909918 name : StaticTestName ( "b" ) ,
910919 ignore : false ,
911- should_fail : false
920+ should_fail : ShouldFail :: No
912921 } ;
913922
914923 let mut st = ConsoleTestState {
@@ -1114,7 +1123,7 @@ pub fn run_test(opts: &TestOpts,
11141123
11151124 let stdout = reader. read_to_end ( ) . unwrap ( ) . into_iter ( ) . collect ( ) ;
11161125 let task_result = result_future. into_inner ( ) ;
1117- let test_result = calc_result ( & desc, task_result. is_ok ( ) ) ;
1126+ let test_result = calc_result ( & desc, task_result) ;
11181127 monitor_ch. send ( ( desc. clone ( ) , test_result, stdout) ) ;
11191128 } )
11201129 }
@@ -1148,13 +1157,17 @@ pub fn run_test(opts: &TestOpts,
11481157 }
11491158}
11501159
1151- fn calc_result ( desc : & TestDesc , task_succeeded : bool ) -> TestResult {
1152- if task_succeeded {
1153- if desc. should_fail { TrFailed }
1154- else { TrOk }
1155- } else {
1156- if desc. should_fail { TrOk }
1157- else { TrFailed }
1160+ fn calc_result ( desc : & TestDesc , task_result : Result < ( ) , Box < Any +Send > > ) -> TestResult {
1161+ match ( & desc. should_fail , task_result) {
1162+ ( & ShouldFail :: No , Ok ( ( ) ) ) |
1163+ ( & ShouldFail :: Yes ( None ) , Err ( _) ) => TrOk ,
1164+ ( & ShouldFail :: Yes ( Some ( msg) ) , Err ( ref err) )
1165+ if err. downcast_ref :: < String > ( )
1166+ . map ( |e| & * * e)
1167+ . or_else ( || err. downcast_ref :: < & ' static str > ( ) . map ( |e| * e) )
1168+ . map ( |e| e. contains ( msg) )
1169+ . unwrap_or ( false ) => TrOk ,
1170+ _ => TrFailed ,
11581171 }
11591172}
11601173
@@ -1437,7 +1450,7 @@ mod tests {
14371450 TestDesc , TestDescAndFn , TestOpts , run_test,
14381451 Metric , MetricMap , MetricAdded , MetricRemoved ,
14391452 Improvement , Regression , LikelyNoise ,
1440- StaticTestName , DynTestName , DynTestFn } ;
1453+ StaticTestName , DynTestName , DynTestFn , ShouldFail } ;
14411454 use std:: io:: TempDir ;
14421455
14431456 #[ test]
@@ -1447,7 +1460,7 @@ mod tests {
14471460 desc : TestDesc {
14481461 name : StaticTestName ( "whatever" ) ,
14491462 ignore : true ,
1450- should_fail : false
1463+ should_fail : ShouldFail :: No ,
14511464 } ,
14521465 testfn : DynTestFn ( proc ( ) f( ) ) ,
14531466 } ;
@@ -1464,7 +1477,7 @@ mod tests {
14641477 desc : TestDesc {
14651478 name : StaticTestName ( "whatever" ) ,
14661479 ignore : true ,
1467- should_fail : false
1480+ should_fail : ShouldFail :: No ,
14681481 } ,
14691482 testfn : DynTestFn ( proc ( ) f( ) ) ,
14701483 } ;
@@ -1481,7 +1494,24 @@ mod tests {
14811494 desc : TestDesc {
14821495 name : StaticTestName ( "whatever" ) ,
14831496 ignore : false ,
1484- should_fail : true
1497+ should_fail : ShouldFail :: Yes ( None )
1498+ } ,
1499+ testfn : DynTestFn ( proc ( ) f( ) ) ,
1500+ } ;
1501+ let ( tx, rx) = channel ( ) ;
1502+ run_test ( & TestOpts :: new ( ) , false , desc, tx) ;
1503+ let ( _, res, _) = rx. recv ( ) ;
1504+ assert ! ( res == TrOk ) ;
1505+ }
1506+
1507+ #[ test]
1508+ fn test_should_fail_good_message ( ) {
1509+ fn f ( ) { panic ! ( "an error message" ) ; }
1510+ let desc = TestDescAndFn {
1511+ desc : TestDesc {
1512+ name : StaticTestName ( "whatever" ) ,
1513+ ignore : false ,
1514+ should_fail : ShouldFail :: Yes ( Some ( "error message" ) )
14851515 } ,
14861516 testfn : DynTestFn ( proc ( ) f( ) ) ,
14871517 } ;
@@ -1491,14 +1521,31 @@ mod tests {
14911521 assert ! ( res == TrOk ) ;
14921522 }
14931523
1524+ #[ test]
1525+ fn test_should_fail_bad_message ( ) {
1526+ fn f ( ) { panic ! ( "an error message" ) ; }
1527+ let desc = TestDescAndFn {
1528+ desc : TestDesc {
1529+ name : StaticTestName ( "whatever" ) ,
1530+ ignore : false ,
1531+ should_fail : ShouldFail :: Yes ( Some ( "foobar" ) )
1532+ } ,
1533+ testfn : DynTestFn ( proc ( ) f( ) ) ,
1534+ } ;
1535+ let ( tx, rx) = channel ( ) ;
1536+ run_test ( & TestOpts :: new ( ) , false , desc, tx) ;
1537+ let ( _, res, _) = rx. recv ( ) ;
1538+ assert ! ( res == TrFailed ) ;
1539+ }
1540+
14941541 #[ test]
14951542 fn test_should_fail_but_succeeds ( ) {
14961543 fn f ( ) { }
14971544 let desc = TestDescAndFn {
14981545 desc : TestDesc {
14991546 name : StaticTestName ( "whatever" ) ,
15001547 ignore : false ,
1501- should_fail : true
1548+ should_fail : ShouldFail :: Yes ( None )
15021549 } ,
15031550 testfn : DynTestFn ( proc ( ) f( ) ) ,
15041551 } ;
@@ -1544,15 +1591,15 @@ mod tests {
15441591 desc: TestDesc {
15451592 name: StaticTestName ( "1" ) ,
15461593 ignore: true ,
1547- should_fail: false ,
1594+ should_fail: ShouldFail :: No ,
15481595 } ,
15491596 testfn: DynTestFn ( proc( ) { } ) ,
15501597 } ,
15511598 TestDescAndFn {
15521599 desc: TestDesc {
15531600 name: StaticTestName ( "2" ) ,
15541601 ignore: false ,
1555- should_fail: false
1602+ should_fail: ShouldFail :: No ,
15561603 } ,
15571604 testfn: DynTestFn ( proc( ) { } ) ,
15581605 } ) ;
@@ -1588,7 +1635,7 @@ mod tests {
15881635 desc : TestDesc {
15891636 name : DynTestName ( ( * name) . clone ( ) ) ,
15901637 ignore : false ,
1591- should_fail : false
1638+ should_fail : ShouldFail :: No ,
15921639 } ,
15931640 testfn : DynTestFn ( testfn) ,
15941641 } ;
@@ -1629,7 +1676,7 @@ mod tests {
16291676 desc : TestDesc {
16301677 name : DynTestName ( name. to_string ( ) ) ,
16311678 ignore : false ,
1632- should_fail : false
1679+ should_fail : ShouldFail :: No ,
16331680 } ,
16341681 testfn : DynTestFn ( test_fn)
16351682 }
0 commit comments