about summary refs log tree commit diff
path: root/src/libstd/test.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstd/test.rs')
-rw-r--r--src/libstd/test.rs272
1 files changed, 148 insertions, 124 deletions
diff --git a/src/libstd/test.rs b/src/libstd/test.rs
index 5a2693a42c1..e83b759f901 100644
--- a/src/libstd/test.rs
+++ b/src/libstd/test.rs
@@ -15,8 +15,6 @@
 // simplest interface possible for representing and running tests
 // while providing a base that other test frameworks may build off of.
 
-#[forbid(deprecated_mode)];
-
 use getopts;
 use sort;
 use term;
@@ -51,20 +49,24 @@ pub type TestName = ~str;
 // the test succeeds; if the function fails then the test fails. We
 // may need to come up with a more clever definition of test in order
 // to support isolation of tests into tasks.
-pub type TestFn = fn~();
+pub type TestFn = ~fn();
 
 // The definition of a single test. A test runner will run a list of
 // these.
 pub struct TestDesc {
     name: TestName,
-    testfn: TestFn,
     ignore: bool,
     should_fail: bool
 }
 
+pub struct TestDescAndFn {
+    desc: TestDesc,
+    testfn: TestFn,
+}
+
 // The default console test runner. It accepts the command line
 // arguments and a vector of test_descs (generated at compile time).
-pub fn test_main(args: &[~str], tests: &[TestDesc]) {
+pub fn test_main(args: &[~str], tests: ~[TestDescAndFn]) {
     let opts =
         match parse_opts(args) {
           either::Left(move o) => o,
@@ -92,7 +94,7 @@ pub fn parse_opts(args: &[~str]) -> OptRes {
         };
 
     let filter =
-        if vec::len(matches.free) > 0u {
+        if vec::len(matches.free) > 0 {
             option::Some(matches.free[0])
         } else { option::None };
 
@@ -111,26 +113,27 @@ pub fn parse_opts(args: &[~str]) -> OptRes {
 #[deriving_eq]
 pub enum TestResult { TrOk, TrFailed, TrIgnored, }
 
-type ConsoleTestState =
-    @{out: io::Writer,
-      log_out: Option<io::Writer>,
-      use_color: bool,
-      mut total: uint,
-      mut passed: uint,
-      mut failed: uint,
-      mut ignored: uint,
-      mut failures: ~[TestDesc]};
+struct ConsoleTestState {
+    out: io::Writer,
+    log_out: Option<io::Writer>,
+    use_color: bool,
+    mut total: uint,
+    mut passed: uint,
+    mut failed: uint,
+    mut ignored: uint,
+    mut failures: ~[TestDesc]
+}
 
 // A simple console test runner
 pub fn run_tests_console(opts: &TestOpts,
-                     tests: &[TestDesc]) -> bool {
+                         tests: ~[TestDescAndFn]) -> bool {
 
-    fn callback(event: &TestEvent, st: ConsoleTestState) {
+    fn callback(event: &TestEvent, st: @ConsoleTestState) {
         debug!("callback(event=%?)", event);
         match *event {
           TeFiltered(ref filtered_tests) => {
             st.total = filtered_tests.len();
-            let noun = if st.total != 1u { ~"tests" } else { ~"test" };
+            let noun = if st.total != 1 { ~"tests" } else { ~"test" };
             st.out.write_line(fmt!("\nrunning %u %s", st.total, noun));
           }
           TeWait(ref test) => st.out.write_str(
@@ -142,18 +145,18 @@ pub fn run_tests_console(opts: &TestOpts,
             }
             match result {
               TrOk => {
-                st.passed += 1u;
+                st.passed += 1;
                 write_ok(st.out, st.use_color);
                 st.out.write_line(~"");
               }
               TrFailed => {
-                st.failed += 1u;
+                st.failed += 1;
                 write_failed(st.out, st.use_color);
                 st.out.write_line(~"");
                 st.failures.push(move test);
               }
               TrIgnored => {
-                st.ignored += 1u;
+                st.ignored += 1;
                 write_ignored(st.out, st.use_color);
                 st.out.write_line(~"");
               }
@@ -174,19 +177,19 @@ pub fn run_tests_console(opts: &TestOpts,
     };
 
     let st =
-        @{out: io::stdout(),
+        @ConsoleTestState{out: io::stdout(),
           log_out: log_out,
           use_color: use_color(),
-          mut total: 0u,
-          mut passed: 0u,
-          mut failed: 0u,
-          mut ignored: 0u,
+          mut total: 0,
+          mut passed: 0,
+          mut failed: 0,
+          mut ignored: 0,
           mut failures: ~[]};
 
     run_tests(opts, tests, |x| callback(&x, st));
 
     assert (st.passed + st.failed + st.ignored == st.total);
-    let success = st.failed == 0u;
+    let success = st.failed == 0;
 
     if !success {
         print_failures(st);
@@ -234,7 +237,7 @@ pub fn run_tests_console(opts: &TestOpts,
     }
 }
 
-fn print_failures(st: ConsoleTestState) {
+fn print_failures(st: @ConsoleTestState) {
     st.out.write_line(~"\nfailures:");
     let failures = copy st.failures;
     let failures = vec::map(failures, |test| test.name);
@@ -246,29 +249,29 @@ fn print_failures(st: ConsoleTestState) {
 
 #[test]
 fn should_sort_failures_before_printing_them() {
+    fn dummy() {}
+
     let s = do io::with_str_writer |wr| {
         let test_a = TestDesc {
             name: ~"a",
-            testfn: fn~() { },
             ignore: false,
             should_fail: false
         };
 
         let test_b = TestDesc {
             name: ~"b",
-            testfn: fn~() { },
             ignore: false,
             should_fail: false
         };
 
         let st =
-            @{out: wr,
+            @ConsoleTestState{out: wr,
               log_out: option::None,
               use_color: false,
-              mut total: 0u,
-              mut passed: 0u,
-              mut failed: 0u,
-              mut ignored: 0u,
+              mut total: 0,
+              mut passed: 0,
+              mut failed: 0,
+              mut ignored: 0,
               mut failures: ~[move test_b, move test_a]};
 
         print_failures(st);
@@ -279,7 +282,7 @@ fn should_sort_failures_before_printing_them() {
     assert apos < bpos;
 }
 
-fn use_color() -> bool { return get_concurrency() == 1u; }
+fn use_color() -> bool { return get_concurrency() == 1; }
 
 enum TestEvent {
     TeFiltered(~[TestDesc]),
@@ -290,51 +293,50 @@ enum TestEvent {
 type MonitorMsg = (TestDesc, TestResult);
 
 fn run_tests(opts: &TestOpts,
-             tests: &[TestDesc],
+             tests: ~[TestDescAndFn],
              callback: fn@(e: TestEvent)) {
     let mut filtered_tests = filter_tests(opts, tests);
-    callback(TeFiltered(copy filtered_tests));
+
+    let filtered_descs = filtered_tests.map(|t| t.desc);
+    callback(TeFiltered(filtered_descs));
 
     // It's tempting to just spawn all the tests at once, but since we have
     // many tests that run in other processes we would be making a big mess.
     let concurrency = get_concurrency();
     debug!("using %u test tasks", concurrency);
 
-    let total = vec::len(filtered_tests);
-    let mut run_idx = 0;
-    let mut wait_idx = 0;
-    let mut done_idx = 0;
+    let mut remaining = filtered_tests;
+    vec::reverse(remaining);
+    let mut pending = 0;
 
     let (p, ch) = stream();
     let ch = SharedChan(ch);
 
-    while done_idx < total {
-        while wait_idx < concurrency && run_idx < total {
-            let test = copy filtered_tests[run_idx];
+    while pending > 0 || !remaining.is_empty() {
+        while pending < concurrency && !remaining.is_empty() {
+            let test = remaining.pop();
             if concurrency == 1 {
                 // We are doing one test at a time so we can print the name
                 // of the test before we run it. Useful for debugging tests
                 // that hang forever.
-                callback(TeWait(copy test));
+                callback(TeWait(test.desc));
             }
-            run_test(move test, ch.clone());
-            wait_idx += 1;
-            run_idx += 1;
+            run_test(test, ch.clone());
+            pending += 1;
         }
 
-        let (test, result) = p.recv();
+        let (desc, result) = p.recv();
         if concurrency != 1 {
-            callback(TeWait(copy test));
+            callback(TeWait(desc));
         }
-        callback(TeResult(move test, result));
-        wait_idx -= 1;
-        done_idx += 1;
+        callback(TeResult(desc, result));
+        pending -= 1;
     }
 }
 
 // Windows tends to dislike being overloaded with threads.
 #[cfg(windows)]
-const sched_overcommit : uint = 1u;
+const sched_overcommit : uint = 1;
 
 #[cfg(unix)]
 const sched_overcommit : uint = 4u;
@@ -342,16 +344,17 @@ const sched_overcommit : uint = 4u;
 fn get_concurrency() -> uint {
     unsafe {
         let threads = rustrt::rust_sched_threads() as uint;
-        if threads == 1u { 1u }
+        if threads == 1 { 1 }
         else { threads * sched_overcommit }
     }
 }
 
 #[allow(non_implicitly_copyable_typarams)]
-pub fn filter_tests(opts: &TestOpts,
-                    tests: &[TestDesc])
-                 -> ~[TestDesc] {
-    let mut filtered = vec::slice(tests, 0, tests.len());
+pub fn filter_tests(
+    opts: &TestOpts,
+    tests: ~[TestDescAndFn]) -> ~[TestDescAndFn]
+{
+    let mut filtered = tests;
 
     // Remove tests that don't match the test filter
     filtered = if opts.filter.is_none() {
@@ -363,10 +366,10 @@ pub fn filter_tests(opts: &TestOpts,
           option::None => ~""
         };
 
-        fn filter_fn(test: &TestDesc, filter_str: &str) ->
-            Option<TestDesc> {
-            if str::contains(test.name, filter_str) {
-                return option::Some(copy *test);
+        fn filter_fn(test: TestDescAndFn, filter_str: &str) ->
+            Option<TestDescAndFn> {
+            if str::contains(test.desc.name, filter_str) {
+                return option::Some(test);
             } else { return option::None; }
         }
 
@@ -377,26 +380,26 @@ pub fn filter_tests(opts: &TestOpts,
     filtered = if !opts.run_ignored {
         move filtered
     } else {
-        fn filter(test: &TestDesc) -> Option<TestDesc> {
-            if test.ignore {
-                return option::Some(TestDesc {
-                    name: test.name,
-                    testfn: copy test.testfn,
-                    ignore: false,
-                    should_fail: test.should_fail});
-            } else { return option::None; }
+        fn filter(test: TestDescAndFn) -> Option<TestDescAndFn> {
+            if test.desc.ignore {
+                let TestDescAndFn {desc, testfn} = test;
+                Some(TestDescAndFn {
+                    desc: TestDesc {ignore: false, ..desc},
+                    testfn: testfn
+                })
+            } else {
+                None
+            }
         };
 
         vec::filter_map(filtered, |x| filter(x))
     };
 
     // Sort the tests alphabetically
-    filtered = {
-        pure fn lteq(t1: &TestDesc, t2: &TestDesc) -> bool {
-            str::le(t1.name, t2.name)
-        }
-        sort::merge_sort(filtered, lteq)
-    };
+    pure fn lteq(t1: &TestDescAndFn, t2: &TestDescAndFn) -> bool {
+        str::le(t1.desc.name, t2.desc.name)
+    }
+    sort::quick_sort(filtered, lteq);
 
     move filtered
 }
@@ -406,37 +409,40 @@ struct TestFuture {
     wait: fn@() -> TestResult,
 }
 
-pub fn run_test(test: TestDesc, monitor_ch: SharedChan<MonitorMsg>) {
-    if test.ignore {
-        monitor_ch.send((copy test, TrIgnored));
+pub fn run_test(test: TestDescAndFn, monitor_ch: SharedChan<MonitorMsg>) {
+    let TestDescAndFn {desc, testfn} = test;
+
+    if desc.ignore {
+        monitor_ch.send((desc, TrIgnored));
         return;
     }
 
-    do task::spawn |move test| {
-        let testfn = copy test.testfn;
+    let testfn_cell = ::cell::Cell(testfn);
+    do task::spawn {
         let mut result_future = None; // task::future_result(builder);
         task::task().unlinked().future_result(|+r| {
             result_future = Some(move r);
-        }).spawn(move testfn);
+        }).spawn(testfn_cell.take());
         let task_result = option::unwrap(move result_future).recv();
-        let test_result = calc_result(&test, task_result == task::Success);
-        monitor_ch.send((copy test, test_result));
+        let test_result = calc_result(&desc, task_result == task::Success);
+        monitor_ch.send((desc, test_result));
     };
 }
 
-fn calc_result(test: &TestDesc, task_succeeded: bool) -> TestResult {
+fn calc_result(desc: &TestDesc, task_succeeded: bool) -> TestResult {
     if task_succeeded {
-        if test.should_fail { TrFailed }
+        if desc.should_fail { TrFailed }
         else { TrOk }
     } else {
-        if test.should_fail { TrOk }
+        if desc.should_fail { TrOk }
         else { TrFailed }
     }
 }
 
 #[cfg(test)]
 mod tests {
-    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts, TestDesc};
+    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
+               TestDesc, TestDescAndFn};
     use test::{TestOpts, run_test};
 
     use core::either;
@@ -447,11 +453,13 @@ mod tests {
     #[test]
     pub fn do_not_run_ignored_tests() {
         fn f() { die!(); }
-        let desc = TestDesc {
-            name: ~"whatever",
+        let desc = TestDescAndFn {
+            desc: TestDesc {
+                name: ~"whatever",
+                ignore: true,
+                should_fail: false
+            },
             testfn: f,
-            ignore: true,
-            should_fail: false
         };
         let (p, ch) = stream();
         let ch = SharedChan(ch);
@@ -463,11 +471,13 @@ mod tests {
     #[test]
     pub fn ignored_tests_result_in_ignored() {
         fn f() { }
-        let desc = TestDesc {
-            name: ~"whatever",
+        let desc = TestDescAndFn {
+            desc: TestDesc {
+                name: ~"whatever",
+                ignore: true,
+                should_fail: false
+            },
             testfn: f,
-            ignore: true,
-            should_fail: false
         };
         let (p, ch) = stream();
         let ch = SharedChan(ch);
@@ -480,11 +490,13 @@ mod tests {
     #[ignore(cfg(windows))]
     pub fn test_should_fail() {
         fn f() { die!(); }
-        let desc = TestDesc {
-            name: ~"whatever",
+        let desc = TestDescAndFn {
+            desc: TestDesc {
+                name: ~"whatever",
+                ignore: false,
+                should_fail: true
+            },
             testfn: f,
-            ignore: false,
-            should_fail: true
         };
         let (p, ch) = stream();
         let ch = SharedChan(ch);
@@ -496,11 +508,13 @@ mod tests {
     #[test]
     pub fn test_should_fail_but_succeeds() {
         fn f() { }
-        let desc = TestDesc {
-            name: ~"whatever",
+        let desc = TestDescAndFn {
+            desc: TestDesc {
+                name: ~"whatever",
+                ignore: false,
+                should_fail: true
+            },
             testfn: f,
-            ignore: false,
-            should_fail: true
         };
         let (p, ch) = stream();
         let ch = SharedChan(ch);
@@ -531,6 +545,8 @@ mod tests {
 
     #[test]
     pub fn filter_for_ignored_option() {
+        fn dummy() {}
+
         // When we run ignored tests the test filter should filter out all the
         // unignored tests and flip the ignore flag on the rest to false
 
@@ -541,24 +557,28 @@ mod tests {
         };
 
         let tests = ~[
-            TestDesc {
-                name: ~"1",
-                testfn: fn~() { },
-                ignore: true,
-                should_fail: false,
+            TestDescAndFn {
+                desc: TestDesc {
+                    name: ~"1",
+                    ignore: true,
+                    should_fail: false,
+                },
+                testfn: dummy,
             },
-            TestDesc {
-                name: ~"2",
-                testfn: fn~() { },
-                ignore: false,
-                should_fail: false,
+            TestDescAndFn {
+                desc: TestDesc {
+                    name: ~"2",
+                    ignore: false,
+                    should_fail: false
+                },
+                testfn: dummy,
             },
         ];
         let filtered = filter_tests(&opts, tests);
 
-        assert (vec::len(filtered) == 1u);
-        assert (filtered[0].name == ~"1");
-        assert (filtered[0].ignore == false);
+        assert (vec::len(filtered) == 1);
+        assert (filtered[0].desc.name == ~"1");
+        assert (filtered[0].desc.ignore == false);
     }
 
     #[test]
@@ -578,12 +598,16 @@ mod tests {
              ~"test::sort_tests"];
         let tests =
         {
-            let testfn = fn~() { };
+            fn testfn() { }
             let mut tests = ~[];
             for vec::each(names) |name| {
-                let test = TestDesc {
-                    name: *name, testfn: copy testfn, ignore: false,
-                    should_fail: false};
+                let test = TestDescAndFn {
+                    desc: TestDesc {
+                        name: *name, ignore: false,
+                        should_fail: false
+                    },
+                    testfn: testfn,
+                };
                 tests.push(move test);
             }
             move tests
@@ -603,7 +627,7 @@ mod tests {
 
         for vec::each(pairs) |p| {
             match *p {
-                (ref a, ref b) => { assert (*a == b.name); }
+                (ref a, ref b) => { assert (*a == b.desc.name); }
             }
         }
     }