Add should_fail annotation for unit tests
This allows test cases to assert that a function is expected to fail. Tests annotated with "should_fail" will succeed only if the function fails.
This commit is contained in:
parent
eabc9f2295
commit
76077a9fb7
7 changed files with 126 additions and 23 deletions
|
@ -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;
|
||||
|
|
|
@ -170,7 +170,8 @@ fn make_test(cx: cx, testfile: str, configport: port<[u8]>) ->
|
|||
test::test_desc<fn@()> {
|
||||
{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 {
|
||||
|
|
|
@ -50,7 +50,8 @@ type default_test_fn = test_fn<fn()>;
|
|||
type test_desc<T> = {
|
||||
name: test_name,
|
||||
fn: test_fn<T>,
|
||||
ignore: bool
|
||||
ignore: bool,
|
||||
should_fail: bool
|
||||
};
|
||||
|
||||
// The default console test runner. It accepts the command line
|
||||
|
@ -218,7 +219,6 @@ fn run_tests<T>(opts: test_opts, tests: [test_desc<T>],
|
|||
callback: fn@(testevent<T>)) {
|
||||
|
||||
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<T>(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<T> = {test: test_desc<T>, wait: fn@() -> test_result};
|
|||
|
||||
fn run_test<T>(test: test_desc<T>,
|
||||
to_task: test_to_task<T>) -> test_future<T> {
|
||||
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.
|
||||
|
|
|
@ -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('$');
|
||||
}
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue