diff --git a/src/comp/front/test.rs b/src/comp/front/test.rs index 7c0d11b8fd63..4008725b6872 100644 --- a/src/comp/front/test.rs +++ b/src/comp/front/test.rs @@ -14,7 +14,7 @@ export modify_for_testing; type node_id_gen = fn@() -> ast::node_id; -type test = {span: span, path: [ast::ident], ignore: bool}; +type test = {span: span, path: [ast::ident], ignore: bool, should_fail: bool}; type test_ctxt = @{sess: session::session, @@ -105,7 +105,8 @@ fn fold_item(cx: test_ctxt, &&i: @ast::item, fld: fold::ast_fold) -> _ { log "this is a test function"; let test = {span: i.span, - path: cx.path, ignore: is_ignored(cx, i)}; + path: cx.path, ignore: is_ignored(cx, i), + should_fail: should_fail(i)}; cx.testfns += [test]; log #fmt["have %u test functions", vec::len(cx.testfns)]; } @@ -148,6 +149,10 @@ fn is_ignored(cx: test_ctxt, i: @ast::item) -> bool { } } +fn should_fail(i: @ast::item) -> bool { + vec::len(attr::find_attrs_by_name(i.attrs, "should_fail")) > 0u +} + fn add_test_module(cx: test_ctxt, m: ast::_mod) -> ast::_mod { let testmod = mk_test_module(cx); ret {items: m.items + [testmod] with m}; @@ -299,8 +304,19 @@ fn mk_test_desc_rec(cx: test_ctxt, test: test) -> @ast::expr { let ignore_field: ast::field = nospan({mut: ast::imm, ident: "ignore", expr: @ignore_expr}); + let fail_lit: ast::lit = nospan(ast::lit_bool(test.should_fail)); + + let fail_expr: ast::expr = + {id: cx.next_node_id(), + node: ast::expr_lit(@fail_lit), + span: span}; + + let fail_field: ast::field = + nospan({mut: ast::imm, ident: "should_fail", expr: @fail_expr}); + let desc_rec_: ast::expr_ = - ast::expr_rec([name_field, fn_field, ignore_field], option::none); + ast::expr_rec([name_field, fn_field, ignore_field, fail_field], + option::none); let desc_rec: ast::expr = {id: cx.next_node_id(), node: desc_rec_, span: span}; ret @desc_rec; diff --git a/src/compiletest/compiletest.rs b/src/compiletest/compiletest.rs index aa77febd2288..a582aa57b0d5 100644 --- a/src/compiletest/compiletest.rs +++ b/src/compiletest/compiletest.rs @@ -170,7 +170,8 @@ fn make_test(cx: cx, testfile: str, configport: port<[u8]>) -> test::test_desc { {name: make_test_name(cx.config, testfile), fn: make_test_closure(testfile, chan(configport)), - ignore: header::is_test_ignored(cx.config, testfile)} + ignore: header::is_test_ignored(cx.config, testfile), + should_fail: false} } fn make_test_name(config: config, testfile: str) -> str { diff --git a/src/lib/test.rs b/src/lib/test.rs index 8c76d397d958..af1e15880afd 100644 --- a/src/lib/test.rs +++ b/src/lib/test.rs @@ -50,7 +50,8 @@ type default_test_fn = test_fn; type test_desc = { name: test_name, fn: test_fn, - ignore: bool + ignore: bool, + should_fail: bool }; // The default console test runner. It accepts the command line @@ -218,7 +219,6 @@ fn run_tests(opts: test_opts, tests: [test_desc], callback: fn@(testevent)) { let filtered_tests = filter_tests(opts, tests); - callback(te_filtered(filtered_tests)); // It's tempting to just spawn all the tests at once but that doesn't @@ -282,7 +282,8 @@ fn filter_tests(opts: test_opts, if test.ignore { ret option::some({name: test.name, fn: test.fn, - ignore: false}); + ignore: false, + should_fail: test.should_fail}); } else { ret option::none; } }; @@ -305,17 +306,25 @@ type test_future = {test: test_desc, wait: fn@() -> test_result}; fn run_test(test: test_desc, to_task: test_to_task) -> test_future { - if !test.ignore { - let test_task = to_task(test.fn); - ret {test: test, - wait: - bind fn (test_task: joinable) -> test_result { - alt task::join(test_task) { - task::tr_success. { tr_ok } - task::tr_failure. { tr_failed } - } - }(test_task)}; - } else { ret {test: test, wait: fn () -> test_result { tr_ignored }}; } + if test.ignore { + ret {test: test, wait: fn () -> test_result { tr_ignored }}; + } + + let test_task = to_task(test.fn); + ret {test: test, + wait: + bind fn (test_task: joinable, should_fail: bool) -> test_result { + alt task::join(test_task) { + task::tr_success. { + if should_fail { tr_failed } + else { tr_ok } + } + task::tr_failure. { + if should_fail { tr_ok } + else { tr_failed } + } + } + }(test_task, test.should_fail)}; } // We need to run our tests in another task in order to trap test failures. diff --git a/src/test/stdtest/char.rs b/src/test/stdtest/char.rs index b2724ca325d6..79b803dc6c8b 100644 --- a/src/test/stdtest/char.rs +++ b/src/test/stdtest/char.rs @@ -26,3 +26,15 @@ fn test_to_digit() { assert (char::to_digit('z') == 35u8); assert (char::to_digit('Z') == 35u8); } + +#[test] +#[should_fail] +fn test_to_digit_fail_1() { + char::to_digit(' '); +} + +#[test] +#[should_fail] +fn test_to_digit_fail_2() { + char::to_digit('$'); +} diff --git a/src/test/stdtest/int.rs b/src/test/stdtest/int.rs index 1bd67d5c45ec..28b9b94c2ff4 100644 --- a/src/test/stdtest/int.rs +++ b/src/test/stdtest/int.rs @@ -19,6 +19,18 @@ fn test_from_str() { assert(int::from_str("-00100") == -100); } +#[test] +#[should_fail] +fn test_from_str_fail_1() { + int::from_str(" "); +} + +#[test] +#[should_fail] +fn test_from_str_fail_2() { + int::from_str("x"); +} + #[test] fn test_parse_buf() { assert (int::parse_buf(bytes("123"), 10u) == 123); @@ -40,6 +52,18 @@ fn test_parse_buf() { assert (int::parse_buf(bytes("-Z"), 36u) == -35); } +#[test] +#[should_fail] +fn test_parse_buf_fail_1() { + int::parse_buf(bytes("Z"), 35u); +} + +#[test] +#[should_fail] +fn test_parse_buf_fail_2() { + int::parse_buf(bytes("-9"), 2u); +} + #[test] fn test_to_str() { assert (eq(int::to_str(0, 10u), "0")); diff --git a/src/test/stdtest/test.rs b/src/test/stdtest/test.rs index ad833949ee78..48c28243c920 100644 --- a/src/test/stdtest/test.rs +++ b/src/test/stdtest/test.rs @@ -7,7 +7,7 @@ import std::vec; #[test] fn do_not_run_ignored_tests() { fn f() { fail; } - let desc = {name: "whatever", fn: f, ignore: true}; + let desc = {name: "whatever", fn: f, ignore: true, should_fail: false}; let future = test::run_test(desc, test::default_test_to_task); let result = future.wait(); assert result != test::tr_ok; @@ -16,11 +16,27 @@ fn do_not_run_ignored_tests() { #[test] fn ignored_tests_result_in_ignored() { fn f() { } - let desc = {name: "whatever", fn: f, ignore: true}; + let desc = {name: "whatever", fn: f, ignore: true, should_fail: false}; let res = test::run_test(desc, test::default_test_to_task).wait(); assert (res == test::tr_ignored); } +#[test] +fn test_should_fail() { + fn f() { fail; } + let desc = {name: "whatever", fn: f, ignore: false, should_fail: true}; + let res = test::run_test(desc, test::default_test_to_task).wait(); + assert res == test::tr_ok; +} + +#[test] +fn test_should_fail_but_succeeds() { + fn f() { } + let desc = {name: "whatever", fn: f, ignore: false, should_fail: true}; + let res = test::run_test(desc, test::default_test_to_task).wait(); + assert res == test::tr_failed; +} + #[test] fn first_free_arg_should_be_a_filter() { let args = ["progname", "filter"]; @@ -44,8 +60,8 @@ fn filter_for_ignored_option() { let opts = {filter: option::none, run_ignored: true}; let tests = - [{name: "1", fn: fn () { }, ignore: true}, - {name: "2", fn: fn () { }, ignore: false}]; + [{name: "1", fn: fn () { }, ignore: true, should_fail: false}, + {name: "2", fn: fn () { }, ignore: false, should_fail: false}]; let filtered = test::filter_tests(opts, tests); assert (vec::len(filtered) == 1u); @@ -69,7 +85,8 @@ fn sort_tests() { let testfn = fn () { }; let tests = []; for name: str in names { - let test = {name: name, fn: testfn, ignore: false}; + let test = {name: name, fn: testfn, ignore: false, + should_fail: false}; tests += [test]; } tests diff --git a/src/test/stdtest/uint.rs b/src/test/stdtest/uint.rs index 35bd65c0c057..6a2b5bdbd3ae 100644 --- a/src/test/stdtest/uint.rs +++ b/src/test/stdtest/uint.rs @@ -14,6 +14,18 @@ fn test_from_str() { assert (uint::from_str("00100") == 100u); } +#[test] +#[should_fail] +fn test_from_str_fail_1() { + uint::from_str(" "); +} + +#[test] +#[should_fail] +fn test_from_str_fail_2() { + uint::from_str("x"); +} + #[test] fn test_parse_buf() { assert (uint::parse_buf(bytes("123"), 10u) == 123u); @@ -24,6 +36,18 @@ fn test_parse_buf() { assert (uint::parse_buf(bytes("z"), 36u) == 35u); } +#[test] +#[should_fail] +fn test_parse_buf_fail_1() { + uint::parse_buf(bytes("Z"), 10u); +} + +#[test] +#[should_fail] +fn test_parse_buf_fail_2() { + uint::parse_buf(bytes("_"), 2u); +} + #[test] fn test_next_power_of_two() { assert (uint::next_power_of_two(0u) == 0u);