From 616af6eb83630b76bb3a9bde57a00f5ebe5dbd6c Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Thu, 4 Dec 2014 23:02:36 -0800 Subject: Allow message specification for should_fail The test harness will make sure that the panic message contains the specified string. This is useful to help make `#[should_fail]` tests a bit less brittle by decreasing the chance that the test isn't "accidentally" passing due to a panic occurring earlier than expected. The behavior is in some ways similar to JUnit's `expected` feature: `@Test(expected=NullPointerException.class)`. Without the message assertion, this test would pass even though it's not actually reaching the intended part of the code: ```rust #[test] #[should_fail(message = "out of bounds")] fn test_oob_array_access() { let idx: uint = from_str("13o").unwrap(); // oops, this will panic [1i32, 2, 3][idx]; } ``` --- src/libsyntax/test.rs | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 05828fc05f8..3d2cb988434 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -37,12 +37,17 @@ use {ast, ast_util}; use ptr::P; use util::small_vector::SmallVector; +enum ShouldFail { + No, + Yes(Option), +} + struct Test { span: Span, path: Vec , bench: bool, ignore: bool, - should_fail: bool + should_fail: ShouldFail } struct TestCtxt<'a> { @@ -360,8 +365,16 @@ fn is_ignored(i: &ast::Item) -> bool { i.attrs.iter().any(|attr| attr.check_name("ignore")) } -fn should_fail(i: &ast::Item) -> bool { - attr::contains_name(i.attrs.as_slice(), "should_fail") +fn should_fail(i: &ast::Item) -> ShouldFail { + match i.attrs.iter().find(|attr| attr.check_name("should_fail")) { + Some(attr) => { + let msg = attr.meta_item_list() + .and_then(|list| list.iter().find(|mi| mi.check_name("message"))) + .and_then(|mi| mi.value_str()); + ShouldFail::Yes(msg) + } + None => ShouldFail::No, + } } /* @@ -550,7 +563,20 @@ fn mk_test_desc_and_fn_rec(cx: &TestCtxt, test: &Test) -> P { vec![name_expr]); let ignore_expr = ecx.expr_bool(span, test.ignore); - let fail_expr = ecx.expr_bool(span, test.should_fail); + let should_fail_path = |name| { + ecx.path(span, vec![self_id, test_id, ecx.ident_of("ShouldFail"), ecx.ident_of(name)]) + }; + let fail_expr = match test.should_fail { + ShouldFail::No => ecx.expr_path(should_fail_path("No")), + ShouldFail::Yes(ref msg) => { + let path = should_fail_path("Yes"); + let arg = match *msg { + Some(ref msg) => ecx.expr_some(span, ecx.expr_str(span, msg.clone())), + None => ecx.expr_none(span), + }; + ecx.expr_call(span, ecx.expr_path(path), vec![arg]) + } + }; // self::test::TestDesc { ... } let desc_expr = ecx.expr_struct( -- cgit 1.4.1-3-g733a5 From 3246d4f36979658c61c9e2bcfe524b1d45b3257a Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 6 Dec 2014 15:16:38 -0800 Subject: Change from message to expected --- src/libsyntax/test.rs | 2 +- src/libtest/lib.rs | 2 +- src/test/run-fail/test-should-fail-bad-message.rs | 2 +- src/test/run-pass/test-should-fail-good-message.rs | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/libsyntax') diff --git a/src/libsyntax/test.rs b/src/libsyntax/test.rs index 3d2cb988434..ca2f190ce76 100644 --- a/src/libsyntax/test.rs +++ b/src/libsyntax/test.rs @@ -369,7 +369,7 @@ fn should_fail(i: &ast::Item) -> ShouldFail { match i.attrs.iter().find(|attr| attr.check_name("should_fail")) { Some(attr) => { let msg = attr.meta_item_list() - .and_then(|list| list.iter().find(|mi| mi.check_name("message"))) + .and_then(|list| list.iter().find(|mi| mi.check_name("expected"))) .and_then(|mi| mi.value_str()); ShouldFail::Yes(msg) } diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index 5cdd346f981..0c71dcee2e5 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -374,7 +374,7 @@ Test Attributes: #[should_fail] - This function (also labeled with #[test]) will only pass if the code causes a failure (an assertion failure or panic!) A message may be provided, which the failure string must - contain: #[should_fail(message = "foo")]. + contain: #[should_fail(expected = "foo")]. #[ignore] - When applied to a function which is already attributed as a test, then the test runner will ignore these tests during normal test runs. Running with --ignored will run these diff --git a/src/test/run-fail/test-should-fail-bad-message.rs b/src/test/run-fail/test-should-fail-bad-message.rs index dc703de489c..76a5022e3be 100644 --- a/src/test/run-fail/test-should-fail-bad-message.rs +++ b/src/test/run-fail/test-should-fail-bad-message.rs @@ -14,7 +14,7 @@ // ignore-pretty: does not work well with `--test` #[test] -#[should_fail(message = "foobar")] +#[should_fail(expected = "foobar")] fn test_foo() { panic!("blah") } diff --git a/src/test/run-pass/test-should-fail-good-message.rs b/src/test/run-pass/test-should-fail-good-message.rs index 9d422700fbf..dcb2fe6dfc0 100644 --- a/src/test/run-pass/test-should-fail-good-message.rs +++ b/src/test/run-pass/test-should-fail-good-message.rs @@ -12,13 +12,13 @@ // ignore-pretty: does not work well with `--test` #[test] -#[should_fail(message = "foo")] +#[should_fail(expected = "foo")] fn test_foo() { panic!("foo bar") } #[test] -#[should_fail(message = "foo")] +#[should_fail(expected = "foo")] fn test_foo_dynamic() { panic!("{} bar", "foo") } -- cgit 1.4.1-3-g733a5