diff options
Diffstat (limited to 'src/libstd/test.rs')
| -rw-r--r-- | src/libstd/test.rs | 272 |
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); } } } } |
