lib.rs 43.2 KB
Newer Older
L
Liigo Zhuang 已提交
1
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 3 4 5 6 7 8 9 10
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

11 12 13
//! Support code for rustc's built in unit-test and micro-benchmarking
//! framework.
//!
14
//! Almost all user code will only be interested in `Bencher` and
15 16 17 18 19 20 21 22 23 24
//! `black_box`. All other interactions (such as writing tests and
//! benchmarks themselves) should be done via the `#[test]` and
//! `#[bench]` attributes.
//!
//! See the [Testing Guide](../guide-testing.html) for more details.

// Currently, not much of this is meant for users. It is intended to
// support the simplest interface possible for representing and
// running tests while providing a base that other test frameworks may
// build off of.
25

A
Alex Crichton 已提交
26
#![crate_name = "test"]
B
Brian Anderson 已提交
27
#![unstable]
B
Brian Anderson 已提交
28
#![staged_api]
29 30 31 32
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
33
       html_root_url = "http://doc.rust-lang.org/nightly/")]
34
#![allow(unknown_features)]
A
Alex Crichton 已提交
35
#![feature(asm, slicing_syntax)]
36
#![feature(box_syntax)]
H
Huon Wilson 已提交
37
#![allow(unknown_features)] #![feature(int_uint)]
38
#![allow(unstable)]
L
Liigo Zhuang 已提交
39

A
Alex Crichton 已提交
40
extern crate getopts;
L
Liigo Zhuang 已提交
41
extern crate serialize;
42
extern crate "serialize" as rustc_serialize;
A
Alex Crichton 已提交
43
extern crate term;
44

S
Steven Fackler 已提交
45 46 47 48 49 50 51 52
pub use self::TestFn::*;
pub use self::ColorConfig::*;
pub use self::TestResult::*;
pub use self::TestName::*;
use self::TestEvent::*;
use self::NamePadding::*;
use self::OutputLocation::*;

53
use stats::Stats;
L
Liigo Zhuang 已提交
54
use getopts::{OptGroup, optflag, optopt};
A
Ahmed Charles 已提交
55
use serialize::Encodable;
L
Liigo Zhuang 已提交
56 57
use term::Terminal;
use term::color::{Color, RED, YELLOW, GREEN, CYAN};
58

59
use std::any::Any;
M
Michael Darakananda 已提交
60
use std::cmp;
61
use std::collections::BTreeMap;
62
use std::fmt;
L
Luqman Aden 已提交
63
use std::io::stdio::StdWriter;
64
use std::io::{File, ChanReader, ChanWriter};
65
use std::io;
A
Alex Crichton 已提交
66
use std::iter::repeat;
67
use std::num::{Float, Int};
68
use std::os;
A
Alex Crichton 已提交
69
use std::str::FromStr;
70
use std::sync::mpsc::{channel, Sender};
71
use std::thread::{self, Thread};
72
use std::thunk::{Thunk, Invoke};
73
use std::time::Duration;
74

L
Liigo Zhuang 已提交
75 76
// to be used by rustc to compile tests in libtest
pub mod test {
77
    pub use {Bencher, TestName, TestResult, TestDesc,
L
Liigo Zhuang 已提交
78
             TestDescAndFn, TestOpts, TrFailed, TrIgnored, TrOk,
79
             Metric, MetricMap,
L
Liigo Zhuang 已提交
80 81
             StaticTestFn, StaticTestName, DynTestName, DynTestFn,
             run_test, test_main, test_main_static, filter_tests,
82
             parse_opts, StaticBenchFn, ShouldFail};
L
Liigo Zhuang 已提交
83 84
}

85 86
pub mod stats;

87
// The name of a test. By convention this follows the rules for rust
S
Sean Moon 已提交
88
// paths; i.e. it should be a series of identifiers separated by double
89
// colons. This way if some test runner wants to arrange the tests
90
// hierarchically it may.
91

A
Alex Crichton 已提交
92
#[derive(Clone, PartialEq, Eq, Hash, Show)]
93
pub enum TestName {
94
    StaticTestName(&'static str),
95
    DynTestName(String)
96
}
97 98
impl TestName {
    fn as_slice<'a>(&'a self) -> &'a str {
99
        match *self {
100 101
            StaticTestName(s) => s,
            DynTestName(ref s) => s.as_slice()
102 103 104
        }
    }
}
105
impl fmt::Display for TestName {
106
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
107
        fmt::Display::fmt(self.as_slice(), f)
108 109
    }
}
110

111
#[derive(Clone, Copy)]
N
Niko Matsakis 已提交
112 113 114 115 116 117
enum NamePadding {
    PadNone,
    PadOnLeft,
    PadOnRight,
}

118
impl TestDesc {
119 120
    fn padded_name(&self, column_count: uint, align: NamePadding) -> String {
        let mut name = String::from_str(self.name.as_slice());
121
        let fill = column_count.saturating_sub(name.len());
A
Alex Crichton 已提交
122
        let mut pad = repeat(" ").take(fill).collect::<String>();
123
        match align {
124
            PadNone => name,
125 126
            PadOnLeft => {
                pad.push_str(name.as_slice());
127
                pad
128 129 130
            }
            PadOnRight => {
                name.push_str(pad.as_slice());
131
                name
132
            }
133 134 135 136
        }
    }
}

137 138
/// Represents a benchmark function.
pub trait TDynBenchFn {
139
    fn run(&self, harness: &mut Bencher);
140 141
}

142
// A function that runs a test. If the function returns successfully,
S
Steve Klabnik 已提交
143
// the test succeeds; if the function panics then the test fails. We
144 145
// may need to come up with a more clever definition of test in order
// to support isolation of tests into tasks.
146
pub enum TestFn {
147
    StaticTestFn(fn()),
148
    StaticBenchFn(fn(&mut Bencher)),
149 150 151
    StaticMetricFn(fn(&mut MetricMap)),
    DynTestFn(Thunk),
    DynMetricFn(Box<for<'a> Invoke<&'a mut MetricMap>+'static>),
152
    DynBenchFn(Box<TDynBenchFn+'static>)
153 154
}

155 156 157
impl TestFn {
    fn padding(&self) -> NamePadding {
        match self {
A
Alex Crichton 已提交
158 159 160 161 162 163
            &StaticTestFn(..)   => PadNone,
            &StaticBenchFn(..)  => PadOnRight,
            &StaticMetricFn(..) => PadOnRight,
            &DynTestFn(..)      => PadNone,
            &DynMetricFn(..)    => PadOnRight,
            &DynBenchFn(..)     => PadOnRight,
164 165 166 167
        }
    }
}

168
impl fmt::Debug for TestFn {
169
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
170
        f.write_str(match *self {
171 172 173 174 175 176
            StaticTestFn(..) => "StaticTestFn(..)",
            StaticBenchFn(..) => "StaticBenchFn(..)",
            StaticMetricFn(..) => "StaticMetricFn(..)",
            DynTestFn(..) => "DynTestFn(..)",
            DynMetricFn(..) => "DynMetricFn(..)",
            DynBenchFn(..) => "DynBenchFn(..)"
177
        })
178 179 180
    }
}

181 182
/// Manager of the benchmarking runs.
///
T
Tshepang Lekhonkhobe 已提交
183
/// This is fed into functions marked with `#[bench]` to allow for
184 185
/// set-up & tear-down before running a piece of code repeatedly via a
/// call to `iter`.
186
#[derive(Copy)]
187
pub struct Bencher {
188
    iterations: u64,
189
    dur: Duration,
190
    pub bytes: u64,
191
}
192

193
#[derive(Copy, Clone, Show, PartialEq, Eq, Hash)]
194 195 196 197 198
pub enum ShouldFail {
    No,
    Yes(Option<&'static str>)
}

199 200
// The definition of a single test. A test runner will run a list of
// these.
201
#[derive(Clone, Show, PartialEq, Eq, Hash)]
202
pub struct TestDesc {
203 204
    pub name: TestName,
    pub ignore: bool,
205
    pub should_fail: ShouldFail,
206
}
207

208 209
unsafe impl Send for TestDesc {}

210
#[derive(Show)]
211
pub struct TestDescAndFn {
212 213
    pub desc: TestDesc,
    pub testfn: TestFn,
214 215
}

216
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Show, Copy)]
217
pub struct Metric {
218 219
    value: f64,
    noise: f64
220 221
}

L
Liigo Zhuang 已提交
222 223 224 225 226 227
impl Metric {
    pub fn new(value: f64, noise: f64) -> Metric {
        Metric {value: value, noise: noise}
    }
}

228
#[derive(PartialEq)]
A
Alexis Beingessner 已提交
229
pub struct MetricMap(BTreeMap<String,Metric>);
230

231
impl Clone for MetricMap {
232
    fn clone(&self) -> MetricMap {
233 234
        let MetricMap(ref map) = *self;
        MetricMap(map.clone())
235 236 237
    }
}

238
// The default console test runner. It accepts the command line
239
// arguments and a vector of test_descs.
240
pub fn test_main(args: &[String], tests: Vec<TestDescAndFn> ) {
M
Marijn Haverbeke 已提交
241
    let opts =
242
        match parse_opts(args) {
B
Brian Anderson 已提交
243
            Some(Ok(o)) => o,
244
            Some(Err(msg)) => panic!("{:?}", msg),
B
Brian Anderson 已提交
245
            None => return
M
Marijn Haverbeke 已提交
246
        };
A
Alex Crichton 已提交
247 248
    match run_tests_console(&opts, tests) {
        Ok(true) => {}
S
Steve Klabnik 已提交
249
        Ok(false) => panic!("Some tests failed"),
250
        Err(e) => panic!("io error when running tests: {:?}", e),
A
Alex Crichton 已提交
251
    }
252 253
}

254
// A variant optimized for invocation with a static test vector.
S
Steve Klabnik 已提交
255
// This will panic (intentionally) when fed any dynamic tests, because
256 257 258 259 260
// it is copying the static values out into a dynamic vector and cannot
// copy dynamic values. It is doing this because from this point on
// a ~[TestDescAndFn] is used in order to effect ownership-transfer
// semantics into parallel test runners, which in turn requires a ~[]
// rather than a &[].
261
pub fn test_main_static(args: &[String], tests: &[TestDescAndFn]) {
262
    let owned_tests = tests.iter().map(|t| {
263
        match t.testfn {
264 265
            StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: t.desc.clone() },
            StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: t.desc.clone() },
S
Steve Klabnik 已提交
266
            _ => panic!("non-static tests passed to test::test_main_static")
267
        }
268
    }).collect();
269 270 271
    test_main(args, owned_tests)
}

272
#[derive(Copy)]
273 274 275 276 277 278
pub enum ColorConfig {
    AutoColor,
    AlwaysColor,
    NeverColor,
}

279
pub struct TestOpts {
A
Alex Crichton 已提交
280
    pub filter: Option<String>,
281 282 283
    pub run_ignored: bool,
    pub run_tests: bool,
    pub run_benchmarks: bool,
284 285
    pub logfile: Option<Path>,
    pub nocapture: bool,
286
    pub color: ColorConfig,
287 288 289 290 291 292 293 294 295 296 297 298
}

impl TestOpts {
    #[cfg(test)]
    fn new() -> TestOpts {
        TestOpts {
            filter: None,
            run_ignored: false,
            run_tests: false,
            run_benchmarks: false,
            logfile: None,
            nocapture: false,
299
            color: AutoColor,
300 301
        }
    }
302
}
303

304
/// Result of parsing the options.
305
pub type OptRes = Result<TestOpts, String>;
306

307 308
fn optgroups() -> Vec<getopts::OptGroup> {
    vec!(getopts::optflag("", "ignored", "Run ignored tests"),
309 310 311 312
      getopts::optflag("", "test", "Run tests and not benchmarks"),
      getopts::optflag("", "bench", "Run benchmarks instead of tests"),
      getopts::optflag("h", "help", "Display this message (longer with --help)"),
      getopts::optopt("", "logfile", "Write logs to the specified file instead \
313
                          of stdout", "PATH"),
314
      getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \
315 316 317 318
                                         task, allow printing directly"),
      getopts::optopt("", "color", "Configure coloring of output:
            auto   = colorize if stdout is a tty and tests are run on serially (default);
            always = always colorize output;
319
            never  = never colorize output;", "auto|always|never"))
320 321
}

322
fn usage(binary: &str) {
A
Alex Crichton 已提交
323
    let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
324
    println!(r#"{usage}
325 326

The FILTER regex is tested against the name of all tests to run, and
327
only those tests that match are run.
328 329

By default, all tests are run in parallel. This can be altered with the
330
RUST_TEST_TASKS environment variable when running tests (set it to 1).
331

332 333 334 335
All tests have their standard output and standard error captured by default.
This can be overridden with the --nocapture flag or the RUST_TEST_NOCAPTURE=1
environment variable. Logging is not captured by default.

336 337
Test Attributes:

A
Alex Crichton 已提交
338
    #[test]        - Indicates a function is a test to be run. This function
339
                     takes no arguments.
A
Alex Crichton 已提交
340
    #[bench]       - Indicates a function is a benchmark to be run. This
341
                     function takes one argument (test::Bencher).
A
Alex Crichton 已提交
342
    #[should_fail] - This function (also labeled with #[test]) will only pass if
S
Steve Klabnik 已提交
343
                     the code causes a failure (an assertion failure or panic!)
344
                     A message may be provided, which the failure string must
S
Steven Fackler 已提交
345
                     contain: #[should_fail(expected = "foo")].
A
Alex Crichton 已提交
346
    #[ignore]      - When applied to a function which is already attributed as a
347 348
                     test, then the test runner will ignore these tests during
                     normal test runs. Running with --ignored will run these
349
                     tests."#,
350 351
             usage = getopts::usage(message.as_slice(),
                                    optgroups().as_slice()));
352 353
}

354
// Parses command line arguments into test options
355
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
356
    let args_ = args.tail();
357
    let matches =
358
        match getopts::getopts(args_.as_slice(), optgroups().as_slice()) {
L
Luqman Aden 已提交
359
          Ok(m) => m,
360
          Err(f) => return Some(Err(f.to_string()))
M
Marijn Haverbeke 已提交
361 362
        };

363
    if matches.opt_present("h") { usage(args[0].as_slice()); return None; }
364

365
    let filter = if matches.free.len() > 0 {
A
Alex Crichton 已提交
366
        Some(matches.free[0].clone())
367 368 369
    } else {
        None
    };
370

371
    let run_ignored = matches.opt_present("ignored");
372

373
    let logfile = matches.opt_str("logfile");
374
    let logfile = logfile.map(|s| Path::new(s));
375

376
    let run_benchmarks = matches.opt_present("bench");
377
    let run_tests = ! run_benchmarks ||
378
        matches.opt_present("test");
379

380 381 382 383 384
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
        nocapture = os::getenv("RUST_TEST_NOCAPTURE").is_some();
    }

385 386 387 388 389 390 391 392 393 394
    let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
        Some("auto") | None => AutoColor,
        Some("always") => AlwaysColor,
        Some("never") => NeverColor,

        Some(v) => return Some(Err(format!("argument for --color must be \
                                            auto, always, or never (was {})",
                                            v))),
    };

395 396 397
    let test_opts = TestOpts {
        filter: filter,
        run_ignored: run_ignored,
398 399
        run_tests: run_tests,
        run_benchmarks: run_benchmarks,
400 401
        logfile: logfile,
        nocapture: nocapture,
402
        color: color,
403
    };
404

B
Brian Anderson 已提交
405
    Some(Ok(test_opts))
406 407
}

408
#[derive(Clone, PartialEq)]
409
pub struct BenchSamples {
410
    ns_iter_summ: stats::Summary<f64>,
411
    mb_s: uint,
412 413
}

414
#[derive(Clone, PartialEq)]
415 416 417 418 419
pub enum TestResult {
    TrOk,
    TrFailed,
    TrIgnored,
    TrMetrics(MetricMap),
420
    TrBench(BenchSamples),
421
}
422

423 424
unsafe impl Send for TestResult {}

A
Alex Crichton 已提交
425
enum OutputLocation<T> {
426
    Pretty(Box<term::Terminal<term::WriterWrapper> + Send>),
A
Alex Crichton 已提交
427 428 429
    Raw(T),
}

L
Luqman Aden 已提交
430 431
struct ConsoleTestState<T> {
    log_out: Option<File>,
A
Alex Crichton 已提交
432
    out: OutputLocation<T>,
433
    use_color: bool,
434 435 436 437
    total: uint,
    passed: uint,
    failed: uint,
    ignored: uint,
438
    measured: uint,
439
    metrics: MetricMap,
440
    failures: Vec<(TestDesc, Vec<u8> )> ,
441
    max_name_len: uint, // number of columns to fill when aligning names
442
}
443

L
Luqman Aden 已提交
444
impl<T: Writer> ConsoleTestState<T> {
A
Alex Crichton 已提交
445 446
    pub fn new(opts: &TestOpts,
               _: Option<T>) -> io::IoResult<ConsoleTestState<StdWriter>> {
447
        let log_out = match opts.logfile {
A
Alex Crichton 已提交
448
            Some(ref path) => Some(try!(File::create(path))),
449 450
            None => None
        };
C
Corey Richardson 已提交
451 452 453
        let out = match term::stdout() {
            None => Raw(io::stdio::stdout_raw()),
            Some(t) => Pretty(t)
454
        };
C
Corey Richardson 已提交
455

A
Alex Crichton 已提交
456
        Ok(ConsoleTestState {
457 458
            out: out,
            log_out: log_out,
459
            use_color: use_color(opts),
460 461 462 463
            total: 0u,
            passed: 0u,
            failed: 0u,
            ignored: 0u,
464
            measured: 0u,
465
            metrics: MetricMap::new(),
466
            failures: Vec::new(),
467
            max_name_len: 0u,
A
Alex Crichton 已提交
468
        })
469 470
    }

A
Alex Crichton 已提交
471 472
    pub fn write_ok(&mut self) -> io::IoResult<()> {
        self.write_pretty("ok", term::color::GREEN)
473
    }
474

A
Alex Crichton 已提交
475 476
    pub fn write_failed(&mut self) -> io::IoResult<()> {
        self.write_pretty("FAILED", term::color::RED)
477 478
    }

A
Alex Crichton 已提交
479 480
    pub fn write_ignored(&mut self) -> io::IoResult<()> {
        self.write_pretty("ignored", term::color::YELLOW)
481
    }
482

A
Alex Crichton 已提交
483 484
    pub fn write_metric(&mut self) -> io::IoResult<()> {
        self.write_pretty("metric", term::color::CYAN)
485 486
    }

A
Alex Crichton 已提交
487 488
    pub fn write_bench(&mut self) -> io::IoResult<()> {
        self.write_pretty("bench", term::color::CYAN)
489
    }
490

L
Luqman Aden 已提交
491
    pub fn write_pretty(&mut self,
492
                        word: &str,
A
Alex Crichton 已提交
493
                        color: term::color::Color) -> io::IoResult<()> {
L
Luqman Aden 已提交
494
        match self.out {
A
Alex Crichton 已提交
495
            Pretty(ref mut term) => {
496
                if self.use_color {
A
Alex Crichton 已提交
497
                    try!(term.fg(color));
498
                }
A
Alex Crichton 已提交
499
                try!(term.write(word.as_bytes()));
500
                if self.use_color {
A
Alex Crichton 已提交
501
                    try!(term.reset());
502
                }
A
Alex Crichton 已提交
503
                Ok(())
504
            }
A
Alex Crichton 已提交
505
            Raw(ref mut stdout) => stdout.write(word.as_bytes())
L
Luqman Aden 已提交
506 507 508
        }
    }

A
Alex Crichton 已提交
509
    pub fn write_plain(&mut self, s: &str) -> io::IoResult<()> {
L
Luqman Aden 已提交
510
        match self.out {
A
Alex Crichton 已提交
511 512
            Pretty(ref mut term) => term.write(s.as_bytes()),
            Raw(ref mut stdout) => stdout.write(s.as_bytes())
513 514 515
        }
    }

A
Alex Crichton 已提交
516
    pub fn write_run_start(&mut self, len: uint) -> io::IoResult<()> {
517
        self.total = len;
518
        let noun = if len != 1 { "tests" } else { "test" };
519
        self.write_plain(format!("\nrunning {} {}\n", len, noun).as_slice())
520 521
    }

A
Alex Crichton 已提交
522 523
    pub fn write_test_start(&mut self, test: &TestDesc,
                            align: NamePadding) -> io::IoResult<()> {
524
        let name = test.padded_name(self.max_name_len, align);
525
        self.write_plain(format!("test {} ... ", name).as_slice())
M
Marijn Haverbeke 已提交
526
    }
527

A
Alex Crichton 已提交
528
    pub fn write_result(&mut self, result: &TestResult) -> io::IoResult<()> {
A
Alex Crichton 已提交
529
        try!(match *result {
530 531 532
            TrOk => self.write_ok(),
            TrFailed => self.write_failed(),
            TrIgnored => self.write_ignored(),
533
            TrMetrics(ref mm) => {
A
Alex Crichton 已提交
534
                try!(self.write_metric());
535
                self.write_plain(format!(": {}", mm.fmt_metrics()).as_slice())
536
            }
537
            TrBench(ref bs) => {
A
Alex Crichton 已提交
538
                try!(self.write_bench());
539

540 541
                try!(self.write_plain(format!(": {}",
                                              fmt_bench_samples(bs)).as_slice()));
542 543

                Ok(())
544
            }
A
Alex Crichton 已提交
545 546
        });
        self.write_plain("\n")
547
    }
548

A
Alex Crichton 已提交
549 550
    pub fn write_log(&mut self, test: &TestDesc,
                     result: &TestResult) -> io::IoResult<()> {
551
        match self.log_out {
A
Alex Crichton 已提交
552
            None => Ok(()),
L
Luqman Aden 已提交
553
            Some(ref mut o) => {
D
Derek Guenther 已提交
554
                let s = format!("{} {}\n", match *result {
555 556 557
                        TrOk => "ok".to_string(),
                        TrFailed => "failed".to_string(),
                        TrIgnored => "ignored".to_string(),
558
                        TrMetrics(ref mm) => mm.fmt_metrics(),
L
Luqman Aden 已提交
559
                        TrBench(ref bs) => fmt_bench_samples(bs)
560
                    }, test.name.as_slice());
A
Alex Crichton 已提交
561
                o.write(s.as_bytes())
562 563
            }
        }
B
Brian Anderson 已提交
564 565
    }

A
Alex Crichton 已提交
566
    pub fn write_failures(&mut self) -> io::IoResult<()> {
A
Alex Crichton 已提交
567
        try!(self.write_plain("\nfailures:\n"));
568
        let mut failures = Vec::new();
569
        let mut fail_out = String::new();
570
        for &(ref f, ref stdout) in self.failures.iter() {
571
            failures.push(f.name.to_string());
572 573
            if stdout.len() > 0 {
                fail_out.push_str(format!("---- {} stdout ----\n\t",
574
                                          f.name.as_slice()).as_slice());
575
                let output = String::from_utf8_lossy(stdout.as_slice());
576
                fail_out.push_str(output.as_slice());
577 578 579 580
                fail_out.push_str("\n");
            }
        }
        if fail_out.len() > 0 {
A
Alex Crichton 已提交
581
            try!(self.write_plain("\n"));
582
            try!(self.write_plain(fail_out.as_slice()));
583
        }
584

A
Alex Crichton 已提交
585
        try!(self.write_plain("\nfailures:\n"));
586
        failures.sort();
D
Daniel Micay 已提交
587
        for name in failures.iter() {
588 589
            try!(self.write_plain(format!("    {}\n",
                                          name.as_slice()).as_slice()));
590
        }
A
Alex Crichton 已提交
591
        Ok(())
592 593
    }

A
Ahmed Charles 已提交
594
    pub fn write_run_finish(&mut self) -> io::IoResult<bool> {
595
        assert!(self.passed + self.failed + self.ignored + self.measured == self.total);
596

A
Ahmed Charles 已提交
597 598
        let success = self.failed == 0u;
        if !success {
A
Alex Crichton 已提交
599
            try!(self.write_failures());
600
        }
601

A
Alex Crichton 已提交
602
        try!(self.write_plain("\ntest result: "));
603 604
        if success {
            // There's no parallelism at this point so it's safe to use color
A
Alex Crichton 已提交
605
            try!(self.write_ok());
606
        } else {
A
Alex Crichton 已提交
607
            try!(self.write_failed());
608
        }
L
Luqman Aden 已提交
609 610
        let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
                        self.passed, self.failed, self.ignored, self.measured);
611
        try!(self.write_plain(s.as_slice()));
A
Alex Crichton 已提交
612
        return Ok(success);
613
    }
614 615
}

616
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
617
    if bs.mb_s != 0 {
A
Alex Crichton 已提交
618
        format!("{:>9} ns/iter (+/- {}) = {} MB/s",
619 620 621 622
             bs.ns_iter_summ.median as uint,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint,
             bs.mb_s)
    } else {
A
Alex Crichton 已提交
623
        format!("{:>9} ns/iter (+/- {})",
624 625
             bs.ns_iter_summ.median as uint,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
626
    }
627 628 629
}

// A simple console test runner
630 631 632
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> io::IoResult<bool> {

    fn callback<T: Writer>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::IoResult<()> {
633
        match (*event).clone() {
634
            TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
635
            TeWait(ref test, padding) => st.write_test_start(test, padding),
636
            TeResult(test, result, stdout) => {
A
Alex Crichton 已提交
637 638
                try!(st.write_log(&test, &result));
                try!(st.write_result(&result));
639 640 641
                match result {
                    TrOk => st.passed += 1,
                    TrIgnored => st.ignored += 1,
642
                    TrMetrics(mm) => {
643
                        let tname = test.name.as_slice();
644
                        let MetricMap(mm) = mm;
D
Daniel Micay 已提交
645
                        for (k,v) in mm.iter() {
646
                            st.metrics
647 648 649
                              .insert_metric(format!("{}.{}",
                                                     tname,
                                                     k).as_slice(),
650 651
                                             v.value,
                                             v.noise);
652 653 654
                        }
                        st.measured += 1
                    }
655
                    TrBench(bs) => {
656
                        st.metrics.insert_metric(test.name.as_slice(),
657 658
                                                 bs.ns_iter_summ.median,
                                                 bs.ns_iter_summ.max - bs.ns_iter_summ.min);
659
                        st.measured += 1
660
                    }
661 662
                    TrFailed => {
                        st.failed += 1;
663
                        st.failures.push((test, stdout));
664 665
                    }
                }
A
Alex Crichton 已提交
666
                Ok(())
667 668
            }
        }
669
    }
670

A
Alex Crichton 已提交
671
    let mut st = try!(ConsoleTestState::new(opts, None::<StdWriter>));
672 673 674
    fn len_if_padded(t: &TestDescAndFn) -> uint {
        match t.testfn.padding() {
            PadNone => 0u,
675
            PadOnLeft | PadOnRight => t.desc.name.as_slice().len(),
676 677 678 679
        }
    }
    match tests.iter().max_by(|t|len_if_padded(*t)) {
        Some(t) => {
680
            let n = t.desc.name.as_slice();
681 682
            st.max_name_len = n.len();
        },
683 684
        None => {}
    }
A
Alex Crichton 已提交
685
    try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
A
Ahmed Charles 已提交
686
    return st.write_run_finish();
687 688 689 690
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
691 692 693
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
694
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
695
    };
696

A
Alex Crichton 已提交
697 698 699
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
700
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
701
    };
702

L
Luqman Aden 已提交
703
    let mut st = ConsoleTestState {
A
Alex Crichton 已提交
704
        log_out: None,
D
Daniel Micay 已提交
705
        out: Raw(Vec::new()),
A
Alex Crichton 已提交
706 707 708 709 710 711 712 713
        use_color: false,
        total: 0u,
        passed: 0u,
        failed: 0u,
        ignored: 0u,
        measured: 0u,
        max_name_len: 10u,
        metrics: MetricMap::new(),
714
        failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
715
    };
716

717
    st.write_failures().unwrap();
L
Luqman Aden 已提交
718
    let s = match st.out {
J
Jorge Aparicio 已提交
719
        Raw(ref m) => String::from_utf8_lossy(&m[]),
A
Alex Crichton 已提交
720
        Pretty(_) => unreachable!()
L
Luqman Aden 已提交
721
    };
A
Alex Crichton 已提交
722

723 724
    let apos = s.find_str("a").unwrap();
    let bpos = s.find_str("b").unwrap();
P
Patrick Walton 已提交
725
    assert!(apos < bpos);
726 727
}

728 729 730 731 732 733
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
        AutoColor => get_concurrency() == 1 && io::stdout().get_ref().isatty(),
        AlwaysColor => true,
        NeverColor => false,
    }
734
}
735

736
#[derive(Clone)]
B
Brian Anderson 已提交
737
enum TestEvent {
738
    TeFiltered(Vec<TestDesc> ),
739
    TeWait(TestDesc, NamePadding),
740
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
741 742
}

743
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
744

F
Flavio Percoco 已提交
745

J
Jorge Aparicio 已提交
746 747 748 749 750
fn run_tests<F>(opts: &TestOpts,
                tests: Vec<TestDescAndFn> ,
                mut callback: F) -> io::IoResult<()> where
    F: FnMut(TestEvent) -> io::IoResult<()>,
{
T
Tim Chevalier 已提交
751
    let filtered_tests = filter_tests(opts, tests);
752 753 754
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
755

A
Alex Crichton 已提交
756
    try!(callback(TeFiltered(filtered_descs)));
B
Brian Anderson 已提交
757

A
Aaron Turon 已提交
758 759
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
760 761 762 763 764
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
                _ => false
            }
        });
765

B
Brian Anderson 已提交
766 767
    // 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.
B
Brian Anderson 已提交
768
    let concurrency = get_concurrency();
769

770
    let mut remaining = filtered_tests;
771
    remaining.reverse();
772
    let mut pending = 0;
B
Brian Anderson 已提交
773

774
    let (tx, rx) = channel::<MonitorMsg>();
775

776 777
    while pending > 0 || !remaining.is_empty() {
        while pending < concurrency && !remaining.is_empty() {
778
            let test = remaining.pop().unwrap();
779
            if concurrency == 1 {
780 781 782
                // 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.
A
Alex Crichton 已提交
783
                try!(callback(TeWait(test.desc.clone(), test.testfn.padding())));
784
            }
785
            run_test(opts, !opts.run_tests, test, tx.clone());
786
            pending += 1;
B
Brian Anderson 已提交
787 788
        }

789
        let (desc, result, stdout) = rx.recv().unwrap();
790
        if concurrency != 1 {
A
Alex Crichton 已提交
791
            try!(callback(TeWait(desc.clone(), PadNone)));
792
        }
A
Alex Crichton 已提交
793
        try!(callback(TeResult(desc, result, stdout)));
794
        pending -= 1;
B
Brian Anderson 已提交
795
    }
796 797

    // All benchmarks run at the end, in serial.
798
    // (this includes metric fns)
A
Aaron Turon 已提交
799
    for b in filtered_benchs_and_metrics.into_iter() {
A
Alex Crichton 已提交
800
        try!(callback(TeWait(b.desc.clone(), b.testfn.padding())));
801
        run_test(opts, !opts.run_benchmarks, b, tx.clone());
802
        let (test, result, stdout) = rx.recv().unwrap();
A
Alex Crichton 已提交
803
        try!(callback(TeResult(test, result, stdout)));
804
    }
A
Alex Crichton 已提交
805
    Ok(())
B
Brian Anderson 已提交
806 807
}

808
fn get_concurrency() -> uint {
809
    use std::rt;
810 811
    match os::getenv("RUST_TEST_TASKS") {
        Some(s) => {
812
            let opt_n: Option<uint> = FromStr::from_str(s.as_slice());
813 814
            match opt_n {
                Some(n) if n > 0 => n,
S
Steve Klabnik 已提交
815
                _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s)
816 817 818
            }
        }
        None => {
819
            rt::default_sched_threads()
820 821
        }
    }
822
}
823

824 825 826 827 828 829
pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
    let mut filtered = tests;

    // Remove tests that don't match the test filter
    filtered = match opts.filter {
        None => filtered,
A
Alex Crichton 已提交
830 831 832 833
        Some(ref filter) => {
            filtered.into_iter().filter(|test| {
                test.desc.name.as_slice().contains(&filter[])
            }).collect()
834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857
        }
    };

    // Maybe pull out the ignored test and unignore them
    filtered = if !opts.run_ignored {
        filtered
    } else {
        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
            }
        };
        filtered.into_iter().filter_map(|x| filter(x)).collect()
    };

    // Sort the tests alphabetically
    filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice()));

858
    filtered
859
}
860

861 862
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
863
                test: TestDescAndFn,
864
                monitor_ch: Sender<MonitorMsg>) {
865

866 867
    let TestDescAndFn {desc, testfn} = test;

868
    if force_ignore || desc.ignore {
869
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
870
        return;
871 872
    }

873
    fn run_test_inner(desc: TestDesc,
874
                      monitor_ch: Sender<MonitorMsg>,
875
                      nocapture: bool,
876
                      testfn: Thunk) {
A
Aaron Turon 已提交
877
        Thread::spawn(move || {
878 879 880 881
            let (tx, rx) = channel();
            let mut reader = ChanReader::new(rx);
            let stdout = ChanWriter::new(tx.clone());
            let stderr = ChanWriter::new(tx);
882
            let mut cfg = thread::Builder::new().name(match desc.name {
R
Richo Healey 已提交
883 884
                DynTestName(ref name) => name.clone().to_string(),
                StaticTestName(name) => name.to_string(),
885
            });
886 887 888
            if nocapture {
                drop((stdout, stderr));
            } else {
A
Aaron Turon 已提交
889 890
                cfg = cfg.stdout(box stdout as Box<Writer + Send>);
                cfg = cfg.stderr(box stderr as Box<Writer + Send>);
891
            }
892

A
Aaron Turon 已提交
893
            let result_guard = cfg.scoped(move || { testfn.invoke(()) });
A
Aaron Turon 已提交
894
            let stdout = reader.read_to_end().unwrap().into_iter().collect();
A
Aaron Turon 已提交
895
            let test_result = calc_result(&desc, result_guard.join());
896
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
897
        });
898 899 900
    }

    match testfn {
901
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
902
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
903
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
904 905 906
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
907
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
908
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
909 910
            return;
        }
911 912
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
913
            f.invoke(&mut mm);
914
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
915 916 917 918 919
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
920
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
921 922
            return;
        }
923 924
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
925
                                          Thunk::new(move|| f()))
926
    }
927 928
}

929 930 931 932 933 934 935 936 937 938 939
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
    match (&desc.should_fail, task_result) {
        (&ShouldFail::No, Ok(())) |
        (&ShouldFail::Yes(None), Err(_)) => TrOk,
        (&ShouldFail::Yes(Some(msg)), Err(ref err))
            if err.downcast_ref::<String>()
                .map(|e| &**e)
                .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
                .map(|e| e.contains(msg))
                .unwrap_or(false) => TrOk,
        _ => TrFailed,
940
    }
941 942
}

943 944
impl MetricMap {

945
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
946
        MetricMap(BTreeMap::new())
947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966
    }

    /// Insert a named `value` (+/- `noise`) metric into the map. The value
    /// must be non-negative. The `noise` indicates the uncertainty of the
    /// metric, which doubles as the "noise range" of acceptable
    /// pairwise-regressions on this named value, when comparing from one
    /// metric to the next using `compare_to_old`.
    ///
    /// If `noise` is positive, then it means this metric is of a value
    /// you want to see grow smaller, so a change larger than `noise` in the
    /// positive direction represents a regression.
    ///
    /// If `noise` is negative, then it means this metric is of a value
    /// you want to see grow larger, so a change larger than `noise` in the
    /// negative direction represents a regression.
    pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) {
        let m = Metric {
            value: value,
            noise: noise
        };
967
        let MetricMap(ref mut map) = *self;
968
        map.insert(name.to_string(), m);
969
    }
970 971 972 973 974 975 976 977 978

    pub fn fmt_metrics(&self) -> String {
        let MetricMap(ref mm) = *self;
        let v : Vec<String> = mm.iter()
            .map(|(k,v)| format!("{}: {} (+/- {})", *k,
                                 v.value as f64, v.noise as f64))
            .collect();
        v.connect(", ")
    }
979 980 981 982 983
}


// Benchmarking

H
Huon Wilson 已提交
984
/// A function that is opaque to the optimizer, to allow benchmarks to
985 986 987 988
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
989
pub fn black_box<T>(dummy: T) -> T {
990 991 992
    // we need to "use" the argument in some way LLVM can't
    // introspect.
    unsafe {asm!("" : : "r"(&dummy))}
993
    dummy
994 995 996
}


997
impl Bencher {
998
    /// Callback for benchmark functions to run in their body.
J
Jorge Aparicio 已提交
999
    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1000 1001 1002 1003 1004 1005
        self.dur = Duration::span(|| {
            let k = self.iterations;
            for _ in range(0u64, k) {
                black_box(inner());
            }
        });
1006
    }
1007

1008
    pub fn ns_elapsed(&mut self) -> u64 {
1009
        self.dur.num_nanoseconds().unwrap() as u64
1010
    }
1011

1012 1013 1014 1015
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1016
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1017
        }
1018
    }
1019

J
Jorge Aparicio 已提交
1020
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1021 1022 1023
        self.iterations = n;
        f(self);
    }
1024

1025
    // This is a more statistics-driven benchmark algorithm
J
Jorge Aparicio 已提交
1026
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary<f64> where F: FnMut(&mut Bencher) {
1027 1028
        // Initial bench run to get ballpark figure.
        let mut n = 1_u64;
1029
        self.bench_n(n, |x| f(x));
1030

1031 1032 1033 1034 1035
        // Try to estimate iter count for 1ms falling back to 1m
        // iterations if first run took < 1ns.
        if self.ns_per_iter() == 0 {
            n = 1_000_000;
        } else {
M
Michael Darakananda 已提交
1036
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1037
        }
1038 1039 1040 1041 1042 1043 1044
        // if the first run took more than 1ms we don't want to just
        // be left doing 0 iterations on every loop. The unfortunate
        // side effect of not being able to do as many runs is
        // automatically handled by the statistical analysis below
        // (i.e. larger error bars).
        if n == 0 { n = 1; }

1045
        let mut total_run = Duration::nanoseconds(0);
1046
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1047
        loop {
1048 1049
            let mut summ = None;
            let mut summ5 = None;
1050

1051
            let loop_run = Duration::span(|| {
1052

1053 1054 1055 1056
                for p in samples.iter_mut() {
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1057

1058 1059
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1060

1061 1062 1063 1064
                for p in samples.iter_mut() {
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1065

1066 1067 1068 1069 1070
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1071

1072
            // If we've run for 100ms and seem to have converged to a
1073
            // stable median.
1074
            if loop_run.num_milliseconds() > 100 &&
1075 1076 1077
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1078 1079
            }

1080
            total_run = total_run + loop_run;
1081
            // Longest we ever run for is 3s.
1082
            if total_run.num_seconds() > 3 {
1083
                return summ5;
1084
            }
1085

1086
            n *= 2;
1087 1088
        }
    }
1089 1090 1091
}

pub mod bench {
M
Michael Darakananda 已提交
1092
    use std::cmp;
1093
    use std::time::Duration;
1094
    use super::{Bencher, BenchSamples};
1095

J
Jorge Aparicio 已提交
1096
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1097
        let mut bs = Bencher {
1098
            iterations: 0,
1099
            dur: Duration::nanoseconds(0),
1100 1101 1102
            bytes: 0
        };

1103
        let ns_iter_summ = bs.auto_bench(f);
1104

M
Michael Darakananda 已提交
1105
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1106
        let iter_s = 1_000_000_000 / ns_iter;
1107 1108 1109
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1110
            ns_iter_summ: ns_iter_summ,
1111 1112 1113 1114 1115
            mb_s: mb_s as uint
        }
    }
}

1116 1117
#[cfg(test)]
mod tests {
1118
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1119
               TestDesc, TestDescAndFn, TestOpts, run_test,
1120
               Metric, MetricMap,
1121
               StaticTestName, DynTestName, DynTestFn, ShouldFail};
1122
    use std::io::TempDir;
1123
    use std::thunk::Thunk;
1124
    use std::sync::mpsc::channel;
1125

1126
    #[test]
1127
    pub fn do_not_run_ignored_tests() {
S
Steve Klabnik 已提交
1128
        fn f() { panic!(); }
1129 1130
        let desc = TestDescAndFn {
            desc: TestDesc {
1131
                name: StaticTestName("whatever"),
1132
                ignore: true,
1133
                should_fail: ShouldFail::No,
1134
            },
1135
            testfn: DynTestFn(Thunk::new(move|| f())),
1136
        };
1137
        let (tx, rx) = channel();
1138
        run_test(&TestOpts::new(), false, desc, tx);
1139
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1140
        assert!(res != TrOk);
1141 1142 1143
    }

    #[test]
1144
    pub fn ignored_tests_result_in_ignored() {
1145
        fn f() { }
1146 1147
        let desc = TestDescAndFn {
            desc: TestDesc {
1148
                name: StaticTestName("whatever"),
1149
                ignore: true,
1150
                should_fail: ShouldFail::No,
1151
            },
1152
            testfn: DynTestFn(Thunk::new(move|| f())),
1153
        };
1154
        let (tx, rx) = channel();
1155
        run_test(&TestOpts::new(), false, desc, tx);
1156
        let (_, res, _) = rx.recv().unwrap();
1157
        assert!(res == TrIgnored);
1158 1159 1160
    }

    #[test]
1161
    fn test_should_fail() {
S
Steve Klabnik 已提交
1162
        fn f() { panic!(); }
1163 1164
        let desc = TestDescAndFn {
            desc: TestDesc {
1165
                name: StaticTestName("whatever"),
1166
                ignore: false,
1167 1168
                should_fail: ShouldFail::Yes(None)
            },
1169
            testfn: DynTestFn(Thunk::new(move|| f())),
1170 1171 1172
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1173
        let (_, res, _) = rx.recv().unwrap();
1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
        assert!(res == TrOk);
    }

    #[test]
    fn test_should_fail_good_message() {
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
                should_fail: ShouldFail::Yes(Some("error message"))
1185
            },
1186
            testfn: DynTestFn(Thunk::new(move|| f())),
1187
        };
1188
        let (tx, rx) = channel();
1189
        run_test(&TestOpts::new(), false, desc, tx);
1190
        let (_, res, _) = rx.recv().unwrap();
1191
        assert!(res == TrOk);
1192 1193
    }

1194 1195 1196 1197 1198 1199 1200 1201 1202
    #[test]
    fn test_should_fail_bad_message() {
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
                should_fail: ShouldFail::Yes(Some("foobar"))
            },
1203
            testfn: DynTestFn(Thunk::new(move|| f())),
1204 1205 1206
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1207
        let (_, res, _) = rx.recv().unwrap();
1208 1209 1210
        assert!(res == TrFailed);
    }

1211
    #[test]
1212
    fn test_should_fail_but_succeeds() {
1213
        fn f() { }
1214 1215
        let desc = TestDescAndFn {
            desc: TestDesc {
1216
                name: StaticTestName("whatever"),
1217
                ignore: false,
1218
                should_fail: ShouldFail::Yes(None)
1219
            },
1220
            testfn: DynTestFn(Thunk::new(move|| f())),
1221
        };
1222
        let (tx, rx) = channel();
1223
        run_test(&TestOpts::new(), false, desc, tx);
1224
        let (_, res, _) = rx.recv().unwrap();
1225
        assert!(res == TrFailed);
1226 1227 1228
    }

    #[test]
1229
    fn parse_ignored_flag() {
1230 1231 1232
        let args = vec!("progname".to_string(),
                        "filter".to_string(),
                        "--ignored".to_string());
1233
        let opts = match parse_opts(args.as_slice()) {
B
Brian Anderson 已提交
1234
            Some(Ok(o)) => o,
S
Steve Klabnik 已提交
1235
            _ => panic!("Malformed arg in parse_ignored_flag")
B
Brian Anderson 已提交
1236
        };
P
Patrick Walton 已提交
1237
        assert!((opts.run_ignored));
1238 1239 1240
    }

    #[test]
1241
    pub fn filter_for_ignored_option() {
1242 1243 1244
        // 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

1245 1246 1247
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1248

1249
        let tests = vec!(
1250 1251
            TestDescAndFn {
                desc: TestDesc {
1252
                    name: StaticTestName("1"),
1253
                    ignore: true,
1254
                    should_fail: ShouldFail::No,
1255
                },
1256
                testfn: DynTestFn(Thunk::new(move|| {})),
1257
            },
1258 1259
            TestDescAndFn {
                desc: TestDesc {
1260
                    name: StaticTestName("2"),
1261
                    ignore: false,
1262
                    should_fail: ShouldFail::No,
1263
                },
1264
                testfn: DynTestFn(Thunk::new(move|| {})),
1265
            });
B
Brian Anderson 已提交
1266
        let filtered = filter_tests(&opts, tests);
1267

1268
        assert_eq!(filtered.len(), 1);
1269
        assert_eq!(filtered[0].desc.name.to_string(),
1270
                   "1");
1271
        assert!(filtered[0].desc.ignore == false);
1272 1273 1274
    }

    #[test]
1275
    pub fn sort_tests() {
1276 1277
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1278 1279

        let names =
1280 1281 1282 1283 1284 1285 1286 1287 1288
            vec!("sha1::test".to_string(),
                 "int::test_to_str".to_string(),
                 "int::test_pow".to_string(),
                 "test::do_not_run_ignored_tests".to_string(),
                 "test::ignored_tests_result_in_ignored".to_string(),
                 "test::first_free_arg_should_be_a_filter".to_string(),
                 "test::parse_ignored_flag".to_string(),
                 "test::filter_for_ignored_option".to_string(),
                 "test::sort_tests".to_string());
1289 1290
        let tests =
        {
1291
            fn testfn() { }
1292
            let mut tests = Vec::new();
D
Daniel Micay 已提交
1293
            for name in names.iter() {
1294 1295
                let test = TestDescAndFn {
                    desc: TestDesc {
1296
                        name: DynTestName((*name).clone()),
1297
                        ignore: false,
1298
                        should_fail: ShouldFail::No,
1299
                    },
1300
                    testfn: DynTestFn(Thunk::new(testfn)),
1301
                };
L
Luqman Aden 已提交
1302
                tests.push(test);
1303
            }
L
Luqman Aden 已提交
1304
            tests
1305
        };
B
Brian Anderson 已提交
1306
        let filtered = filter_tests(&opts, tests);
1307

1308
        let expected =
1309 1310 1311 1312 1313 1314 1315 1316 1317
            vec!("int::test_pow".to_string(),
                 "int::test_to_str".to_string(),
                 "sha1::test".to_string(),
                 "test::do_not_run_ignored_tests".to_string(),
                 "test::filter_for_ignored_option".to_string(),
                 "test::first_free_arg_should_be_a_filter".to_string(),
                 "test::ignored_tests_result_in_ignored".to_string(),
                 "test::parse_ignored_flag".to_string(),
                 "test::sort_tests".to_string());
1318

1319
        for (a, b) in expected.iter().zip(filtered.iter()) {
1320
            assert!(*a == b.desc.name.to_string());
1321 1322 1323
        }
    }

1324 1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345
    #[test]
    pub fn test_metricmap_compare() {
        let mut m1 = MetricMap::new();
        let mut m2 = MetricMap::new();
        m1.insert_metric("in-both-noise", 1000.0, 200.0);
        m2.insert_metric("in-both-noise", 1100.0, 200.0);

        m1.insert_metric("in-first-noise", 1000.0, 2.0);
        m2.insert_metric("in-second-noise", 1000.0, 2.0);

        m1.insert_metric("in-both-want-downwards-but-regressed", 1000.0, 10.0);
        m2.insert_metric("in-both-want-downwards-but-regressed", 2000.0, 10.0);

        m1.insert_metric("in-both-want-downwards-and-improved", 2000.0, 10.0);
        m2.insert_metric("in-both-want-downwards-and-improved", 1000.0, 10.0);

        m1.insert_metric("in-both-want-upwards-but-regressed", 2000.0, -10.0);
        m2.insert_metric("in-both-want-upwards-but-regressed", 1000.0, -10.0);

        m1.insert_metric("in-both-want-upwards-and-improved", 1000.0, -10.0);
        m2.insert_metric("in-both-want-upwards-and-improved", 2000.0, -10.0);
    }
1346
}