about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorMatt Brubeck <mbrubeck@limpet.net>2011-11-01 10:31:23 -0700
committerMatt Brubeck <mbrubeck@limpet.net>2011-11-01 16:55:39 -0700
commit76077a9fb7f67a8af1b2eb16c2814ca703ad6c97 (patch)
tree5093785e88259f901a94c9199f6908053d4841ca /src
parenteabc9f229574a906437dd63359caec1c15fb75ad (diff)
downloadrust-76077a9fb7f67a8af1b2eb16c2814ca703ad6c97.tar.gz
rust-76077a9fb7f67a8af1b2eb16c2814ca703ad6c97.zip
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.
Diffstat (limited to 'src')
-rw-r--r--src/comp/front/test.rs22
-rw-r--r--src/compiletest/compiletest.rs3
-rw-r--r--src/lib/test.rs37
-rw-r--r--src/test/stdtest/char.rs12
-rw-r--r--src/test/stdtest/int.rs24
-rw-r--r--src/test/stdtest/test.rs27
-rw-r--r--src/test/stdtest/uint.rs24
7 files changed, 126 insertions, 23 deletions
diff --git a/src/comp/front/test.rs b/src/comp/front/test.rs
index 7c0d11b8fd6..4008725b687 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 aa77febd228..a582aa57b0d 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<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 {
diff --git a/src/lib/test.rs b/src/lib/test.rs
index 8c76d397d95..af1e15880af 100644
--- a/src/lib/test.rs
+++ b/src/lib/test.rs
@@ -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.
diff --git a/src/test/stdtest/char.rs b/src/test/stdtest/char.rs
index b2724ca325d..79b803dc6c8 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 1bd67d5c45e..28b9b94c2ff 100644
--- a/src/test/stdtest/int.rs
+++ b/src/test/stdtest/int.rs
@@ -20,6 +20,18 @@ fn test_from_str() {
 }
 
 #[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);
     assert (int::parse_buf(bytes("1001"), 2u) == 9);
@@ -41,6 +53,18 @@ fn test_parse_buf() {
 }
 
 #[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"));
     assert (eq(int::to_str(1, 10u), "1"));
diff --git a/src/test/stdtest/test.rs b/src/test/stdtest/test.rs
index ad833949ee7..48c28243c92 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,12 +16,28 @@ 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"];
     check (vec::is_not_empty(args));
@@ -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 35bd65c0c05..6a2b5bdbd3a 100644
--- a/src/test/stdtest/uint.rs
+++ b/src/test/stdtest/uint.rs
@@ -15,6 +15,18 @@ fn test_from_str() {
 }
 
 #[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);
     assert (uint::parse_buf(bytes("1001"), 2u) == 9u);
@@ -25,6 +37,18 @@ fn test_parse_buf() {
 }
 
 #[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);
     assert (uint::next_power_of_two(1u) == 1u);