lib.rs 43.0 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
//! `black_box`. All other interactions (such as writing tests and
//! benchmarks themselves) should be done via the `#[test]` and
//! `#[bench]` attributes.
//!
19
//! See the [Testing Chapter](../book/testing.html) of the book for more details.
20 21 22 23 24

// 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"]
27
#![unstable(feature = "test")]
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/")]
A
Alex Crichton 已提交
34

35
#![feature(asm)]
36
#![feature(box_syntax)]
37 38
#![feature(collections)]
#![feature(core)]
A
Alex Crichton 已提交
39
#![feature(env)]
A
Alex Crichton 已提交
40 41
#![feature(hash)]
#![feature(int_uint)]
42 43 44
#![feature(io)]
#![feature(path)]
#![feature(rustc_private)]
A
Alex Crichton 已提交
45
#![feature(staged_api)]
46
#![feature(std_misc)]
L
Liigo Zhuang 已提交
47

A
Alex Crichton 已提交
48
extern crate getopts;
L
Liigo Zhuang 已提交
49
extern crate serialize;
50
extern crate "serialize" as rustc_serialize;
A
Alex Crichton 已提交
51
extern crate term;
52

S
Steven Fackler 已提交
53 54 55 56 57 58 59 60
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::*;

61
use stats::Stats;
L
Liigo Zhuang 已提交
62
use getopts::{OptGroup, optflag, optopt};
A
Ahmed Charles 已提交
63
use serialize::Encodable;
L
Liigo Zhuang 已提交
64 65
use term::Terminal;
use term::color::{Color, RED, YELLOW, GREEN, CYAN};
66

67
use std::any::Any;
M
Michael Darakananda 已提交
68
use std::cmp;
69
use std::collections::BTreeMap;
70
use std::fmt;
A
Alex Crichton 已提交
71 72 73
use std::old_io::stdio::StdWriter;
use std::old_io::{File, ChanReader, ChanWriter};
use std::old_io;
A
Alex Crichton 已提交
74
use std::iter::repeat;
75
use std::num::{Float, Int};
A
Alex Crichton 已提交
76
use std::env;
77
use std::sync::mpsc::{channel, Sender};
78
use std::thread::{self, Thread};
79
use std::thunk::{Thunk, Invoke};
80
use std::time::Duration;
81

L
Liigo Zhuang 已提交
82 83
// to be used by rustc to compile tests in libtest
pub mod test {
84
    pub use {Bencher, TestName, TestResult, TestDesc,
L
Liigo Zhuang 已提交
85
             TestDescAndFn, TestOpts, TrFailed, TrIgnored, TrOk,
86
             Metric, MetricMap,
L
Liigo Zhuang 已提交
87 88
             StaticTestFn, StaticTestName, DynTestName, DynTestFn,
             run_test, test_main, test_main_static, filter_tests,
89
             parse_opts, StaticBenchFn, ShouldFail};
L
Liigo Zhuang 已提交
90 91
}

92 93
pub mod stats;

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

J
Jorge Aparicio 已提交
99
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
100
pub enum TestName {
101
    StaticTestName(&'static str),
102
    DynTestName(String)
103
}
104 105
impl TestName {
    fn as_slice<'a>(&'a self) -> &'a str {
106
        match *self {
107
            StaticTestName(s) => s,
108
            DynTestName(ref s) => s
109 110 111
        }
    }
}
112
impl fmt::Display for TestName {
113
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
114
        fmt::Display::fmt(self.as_slice(), f)
115 116
    }
}
117

118
#[derive(Clone, Copy)]
N
Niko Matsakis 已提交
119 120 121 122 123 124
enum NamePadding {
    PadNone,
    PadOnLeft,
    PadOnRight,
}

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

144 145
/// Represents a benchmark function.
pub trait TDynBenchFn {
146
    fn run(&self, harness: &mut Bencher);
147 148
}

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

162 163 164
impl TestFn {
    fn padding(&self) -> NamePadding {
        match self {
A
Alex Crichton 已提交
165 166 167 168 169 170
            &StaticTestFn(..)   => PadNone,
            &StaticBenchFn(..)  => PadOnRight,
            &StaticMetricFn(..) => PadOnRight,
            &DynTestFn(..)      => PadNone,
            &DynMetricFn(..)    => PadOnRight,
            &DynBenchFn(..)     => PadOnRight,
171 172 173 174
        }
    }
}

175
impl fmt::Debug for TestFn {
176
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177
        f.write_str(match *self {
178 179 180 181 182 183
            StaticTestFn(..) => "StaticTestFn(..)",
            StaticBenchFn(..) => "StaticBenchFn(..)",
            StaticMetricFn(..) => "StaticMetricFn(..)",
            DynTestFn(..) => "DynTestFn(..)",
            DynMetricFn(..) => "DynMetricFn(..)",
            DynBenchFn(..) => "DynBenchFn(..)"
184
        })
185 186 187
    }
}

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

J
Jorge Aparicio 已提交
200
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
201 202 203 204 205
pub enum ShouldFail {
    No,
    Yes(Option<&'static str>)
}

206 207
// The definition of a single test. A test runner will run a list of
// these.
J
Jorge Aparicio 已提交
208
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
209
pub struct TestDesc {
210 211
    pub name: TestName,
    pub ignore: bool,
212
    pub should_fail: ShouldFail,
213
}
214

215 216
unsafe impl Send for TestDesc {}

J
Jorge Aparicio 已提交
217
#[derive(Debug)]
218
pub struct TestDescAndFn {
219 220
    pub desc: TestDesc,
    pub testfn: TestFn,
221 222
}

J
Jorge Aparicio 已提交
223
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Copy)]
224
pub struct Metric {
225 226
    value: f64,
    noise: f64
227 228
}

L
Liigo Zhuang 已提交
229 230 231 232 233 234
impl Metric {
    pub fn new(value: f64, noise: f64) -> Metric {
        Metric {value: value, noise: noise}
    }
}

235
#[derive(PartialEq)]
A
Alexis Beingessner 已提交
236
pub struct MetricMap(BTreeMap<String,Metric>);
237

238
impl Clone for MetricMap {
239
    fn clone(&self) -> MetricMap {
240 241
        let MetricMap(ref map) = *self;
        MetricMap(map.clone())
242 243 244
    }
}

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

261
// A variant optimized for invocation with a static test vector.
S
Steve Klabnik 已提交
262
// This will panic (intentionally) when fed any dynamic tests, because
263 264 265 266 267
// 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 &[].
268
pub fn test_main_static(args: env::Args, tests: &[TestDescAndFn]) {
A
Alex Crichton 已提交
269
    let args = args.collect::<Vec<_>>();
270
    let owned_tests = tests.iter().map(|t| {
271
        match t.testfn {
272 273
            StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: t.desc.clone() },
            StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: t.desc.clone() },
S
Steve Klabnik 已提交
274
            _ => panic!("non-static tests passed to test::test_main_static")
275
        }
276
    }).collect();
277
    test_main(&args, owned_tests)
278 279
}

280
#[derive(Copy)]
281 282 283 284 285 286
pub enum ColorConfig {
    AutoColor,
    AlwaysColor,
    NeverColor,
}

287
pub struct TestOpts {
A
Alex Crichton 已提交
288
    pub filter: Option<String>,
289 290 291
    pub run_ignored: bool,
    pub run_tests: bool,
    pub run_benchmarks: bool,
292 293
    pub logfile: Option<Path>,
    pub nocapture: bool,
294
    pub color: ColorConfig,
295 296 297 298 299 300 301 302 303 304 305 306
}

impl TestOpts {
    #[cfg(test)]
    fn new() -> TestOpts {
        TestOpts {
            filter: None,
            run_ignored: false,
            run_tests: false,
            run_benchmarks: false,
            logfile: None,
            nocapture: false,
307
            color: AutoColor,
308 309
        }
    }
310
}
311

312
/// Result of parsing the options.
313
pub type OptRes = Result<TestOpts, String>;
314

315 316
fn optgroups() -> Vec<getopts::OptGroup> {
    vec!(getopts::optflag("", "ignored", "Run ignored tests"),
317 318 319 320
      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 \
321
                          of stdout", "PATH"),
322
      getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \
323 324 325 326
                                         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;
327
            never  = never colorize output;", "auto|always|never"))
328 329
}

330
fn usage(binary: &str) {
A
Alex Crichton 已提交
331
    let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
332
    println!(r#"{usage}
333 334

The FILTER regex is tested against the name of all tests to run, and
335
only those tests that match are run.
336 337

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

340 341 342 343
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.

344 345
Test Attributes:

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

361
// Parses command line arguments into test options
362
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
363
    let args_ = args.tail();
364
    let matches =
365
        match getopts::getopts(args_, &optgroups()) {
L
Luqman Aden 已提交
366
          Ok(m) => m,
367
          Err(f) => return Some(Err(f.to_string()))
M
Marijn Haverbeke 已提交
368 369
        };

370
    if matches.opt_present("h") { usage(&args[0]); return None; }
371

372
    let filter = if matches.free.len() > 0 {
A
Alex Crichton 已提交
373
        Some(matches.free[0].clone())
374 375 376
    } else {
        None
    };
377

378
    let run_ignored = matches.opt_present("ignored");
379

380
    let logfile = matches.opt_str("logfile");
381
    let logfile = logfile.map(|s| Path::new(s));
382

383
    let run_benchmarks = matches.opt_present("bench");
384
    let run_tests = ! run_benchmarks ||
385
        matches.opt_present("test");
386

387 388
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
389
        nocapture = env::var("RUST_TEST_NOCAPTURE").is_ok();
390 391
    }

392
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
393 394 395 396 397 398 399 400 401
        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))),
    };

402 403 404
    let test_opts = TestOpts {
        filter: filter,
        run_ignored: run_ignored,
405 406
        run_tests: run_tests,
        run_benchmarks: run_benchmarks,
407 408
        logfile: logfile,
        nocapture: nocapture,
409
        color: color,
410
    };
411

B
Brian Anderson 已提交
412
    Some(Ok(test_opts))
413 414
}

415
#[derive(Clone, PartialEq)]
416
pub struct BenchSamples {
417
    ns_iter_summ: stats::Summary<f64>,
418
    mb_s: uint,
419 420
}

421
#[derive(Clone, PartialEq)]
422 423 424 425 426
pub enum TestResult {
    TrOk,
    TrFailed,
    TrIgnored,
    TrMetrics(MetricMap),
427
    TrBench(BenchSamples),
428
}
429

430 431
unsafe impl Send for TestResult {}

A
Alex Crichton 已提交
432
enum OutputLocation<T> {
433
    Pretty(Box<term::Terminal<term::WriterWrapper> + Send>),
A
Alex Crichton 已提交
434 435 436
    Raw(T),
}

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

L
Luqman Aden 已提交
451
impl<T: Writer> ConsoleTestState<T> {
A
Alex Crichton 已提交
452
    pub fn new(opts: &TestOpts,
A
Alex Crichton 已提交
453
               _: Option<T>) -> old_io::IoResult<ConsoleTestState<StdWriter>> {
454
        let log_out = match opts.logfile {
A
Alex Crichton 已提交
455
            Some(ref path) => Some(try!(File::create(path))),
456 457
            None => None
        };
C
Corey Richardson 已提交
458
        let out = match term::stdout() {
A
Alex Crichton 已提交
459
            None => Raw(old_io::stdio::stdout_raw()),
C
Corey Richardson 已提交
460
            Some(t) => Pretty(t)
461
        };
C
Corey Richardson 已提交
462

A
Alex Crichton 已提交
463
        Ok(ConsoleTestState {
464 465
            out: out,
            log_out: log_out,
466
            use_color: use_color(opts),
A
Alfie John 已提交
467 468 469 470 471
            total: 0,
            passed: 0,
            failed: 0,
            ignored: 0,
            measured: 0,
472
            metrics: MetricMap::new(),
473
            failures: Vec::new(),
A
Alfie John 已提交
474
            max_name_len: 0,
A
Alex Crichton 已提交
475
        })
476 477
    }

A
Alex Crichton 已提交
478
    pub fn write_ok(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
479
        self.write_pretty("ok", term::color::GREEN)
480
    }
481

A
Alex Crichton 已提交
482
    pub fn write_failed(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
483
        self.write_pretty("FAILED", term::color::RED)
484 485
    }

A
Alex Crichton 已提交
486
    pub fn write_ignored(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
487
        self.write_pretty("ignored", term::color::YELLOW)
488
    }
489

A
Alex Crichton 已提交
490
    pub fn write_metric(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
491
        self.write_pretty("metric", term::color::CYAN)
492 493
    }

A
Alex Crichton 已提交
494
    pub fn write_bench(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
495
        self.write_pretty("bench", term::color::CYAN)
496
    }
497

L
Luqman Aden 已提交
498
    pub fn write_pretty(&mut self,
499
                        word: &str,
A
Alex Crichton 已提交
500
                        color: term::color::Color) -> old_io::IoResult<()> {
L
Luqman Aden 已提交
501
        match self.out {
A
Alex Crichton 已提交
502
            Pretty(ref mut term) => {
503
                if self.use_color {
A
Alex Crichton 已提交
504
                    try!(term.fg(color));
505
                }
506
                try!(term.write_all(word.as_bytes()));
507
                if self.use_color {
A
Alex Crichton 已提交
508
                    try!(term.reset());
509
                }
A
Alex Crichton 已提交
510
                Ok(())
511
            }
512
            Raw(ref mut stdout) => stdout.write_all(word.as_bytes())
L
Luqman Aden 已提交
513 514 515
        }
    }

A
Alex Crichton 已提交
516
    pub fn write_plain(&mut self, s: &str) -> old_io::IoResult<()> {
L
Luqman Aden 已提交
517
        match self.out {
518 519
            Pretty(ref mut term) => term.write_all(s.as_bytes()),
            Raw(ref mut stdout) => stdout.write_all(s.as_bytes())
520 521 522
        }
    }

A
Alex Crichton 已提交
523
    pub fn write_run_start(&mut self, len: uint) -> old_io::IoResult<()> {
524
        self.total = len;
525
        let noun = if len != 1 { "tests" } else { "test" };
526
        self.write_plain(&format!("\nrunning {} {}\n", len, noun))
527 528
    }

A
Alex Crichton 已提交
529
    pub fn write_test_start(&mut self, test: &TestDesc,
A
Alex Crichton 已提交
530
                            align: NamePadding) -> old_io::IoResult<()> {
531
        let name = test.padded_name(self.max_name_len, align);
532
        self.write_plain(&format!("test {} ... ", name))
M
Marijn Haverbeke 已提交
533
    }
534

A
Alex Crichton 已提交
535
    pub fn write_result(&mut self, result: &TestResult) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
536
        try!(match *result {
537 538 539
            TrOk => self.write_ok(),
            TrFailed => self.write_failed(),
            TrIgnored => self.write_ignored(),
540
            TrMetrics(ref mm) => {
A
Alex Crichton 已提交
541
                try!(self.write_metric());
542
                self.write_plain(&format!(": {}", mm.fmt_metrics()))
543
            }
544
            TrBench(ref bs) => {
A
Alex Crichton 已提交
545
                try!(self.write_bench());
546

547
                try!(self.write_plain(&format!(": {}", fmt_bench_samples(bs))));
548 549

                Ok(())
550
            }
A
Alex Crichton 已提交
551 552
        });
        self.write_plain("\n")
553
    }
554

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

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

A
Alex Crichton 已提交
590
        try!(self.write_plain("\nfailures:\n"));
591
        failures.sort();
592
        for name in &failures {
593
            try!(self.write_plain(&format!("    {}\n", name)));
594
        }
A
Alex Crichton 已提交
595
        Ok(())
596 597
    }

A
Alex Crichton 已提交
598
    pub fn write_run_finish(&mut self) -> old_io::IoResult<bool> {
599
        assert!(self.passed + self.failed + self.ignored + self.measured == self.total);
600

A
Alfie John 已提交
601
        let success = self.failed == 0;
A
Ahmed Charles 已提交
602
        if !success {
A
Alex Crichton 已提交
603
            try!(self.write_failures());
604
        }
605

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

620
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
621
    if bs.mb_s != 0 {
A
Alex Crichton 已提交
622
        format!("{:>9} ns/iter (+/- {}) = {} MB/s",
623 624 625 626
             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 已提交
627
        format!("{:>9} ns/iter (+/- {})",
628 629
             bs.ns_iter_summ.median as uint,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
630
    }
631 632 633
}

// A simple console test runner
A
Alex Crichton 已提交
634
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> old_io::IoResult<bool> {
635

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

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

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
696 697 698
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
699
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
700
    };
701

A
Alex Crichton 已提交
702 703 704
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
705
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
706
    };
707

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

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

728 729
    let apos = s.find_str("a").unwrap();
    let bpos = s.find_str("b").unwrap();
P
Patrick Walton 已提交
730
    assert!(apos < bpos);
731 732
}

733 734
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
A
Alex Crichton 已提交
735
        AutoColor => get_concurrency() == 1 && old_io::stdout().get_ref().isatty(),
736 737 738
        AlwaysColor => true,
        NeverColor => false,
    }
739
}
740

741
#[derive(Clone)]
B
Brian Anderson 已提交
742
enum TestEvent {
743
    TeFiltered(Vec<TestDesc> ),
744
    TeWait(TestDesc, NamePadding),
745
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
746 747
}

748
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
749

F
Flavio Percoco 已提交
750

J
Jorge Aparicio 已提交
751 752
fn run_tests<F>(opts: &TestOpts,
                tests: Vec<TestDescAndFn> ,
A
Alex Crichton 已提交
753 754
                mut callback: F) -> old_io::IoResult<()> where
    F: FnMut(TestEvent) -> old_io::IoResult<()>,
J
Jorge Aparicio 已提交
755
{
T
Tim Chevalier 已提交
756
    let filtered_tests = filter_tests(opts, tests);
757 758 759
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
760

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

A
Aaron Turon 已提交
763 764
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
765 766 767 768 769
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
                _ => false
            }
        });
770

B
Brian Anderson 已提交
771 772
    // 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 已提交
773
    let concurrency = get_concurrency();
774

775
    let mut remaining = filtered_tests;
776
    remaining.reverse();
777
    let mut pending = 0;
B
Brian Anderson 已提交
778

779
    let (tx, rx) = channel::<MonitorMsg>();
780

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

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

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

813
fn get_concurrency() -> uint {
814
    use std::rt;
815
    match env::var("RUST_TEST_TASKS") {
A
Alex Crichton 已提交
816
        Ok(s) => {
A
Alex Crichton 已提交
817
            let opt_n: Option<uint> = s.parse().ok();
818 819
            match opt_n {
                Some(n) if n > 0 => n,
S
Steve Klabnik 已提交
820
                _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s)
821 822
            }
        }
A
Alex Crichton 已提交
823
        Err(..) => {
824
            rt::default_sched_threads()
825 826
        }
    }
827
}
828

829 830 831 832 833 834
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 已提交
835 836 837 838
        Some(ref filter) => {
            filtered.into_iter().filter(|test| {
                test.desc.name.as_slice().contains(&filter[])
            }).collect()
839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862
        }
    };

    // 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()));

863
    filtered
864
}
865

866 867
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
868
                test: TestDescAndFn,
869
                monitor_ch: Sender<MonitorMsg>) {
870

871 872
    let TestDescAndFn {desc, testfn} = test;

873
    if force_ignore || desc.ignore {
874
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
875
        return;
876 877
    }

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

A
Aaron Turon 已提交
898
            let result_guard = cfg.scoped(move || { testfn.invoke(()) });
A
Aaron Turon 已提交
899
            let stdout = reader.read_to_end().unwrap().into_iter().collect();
A
Aaron Turon 已提交
900
            let test_result = calc_result(&desc, result_guard.join());
901
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
902
        });
903 904 905
    }

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

934 935 936 937 938 939 940 941 942 943 944
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,
945
    }
946 947
}

948 949
impl MetricMap {

950
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
951
        MetricMap(BTreeMap::new())
952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
    }

    /// 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
        };
972
        let MetricMap(ref mut map) = *self;
973
        map.insert(name.to_string(), m);
974 975
    }

976 977 978 979 980 981 982
    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(", ")
983 984 985 986 987 988
    }
}


// Benchmarking

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


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

1013
    pub fn ns_elapsed(&mut self) -> u64 {
1014
        self.dur.num_nanoseconds().unwrap() as u64
1015
    }
1016

1017 1018 1019 1020
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1021
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1022
        }
1023
    }
1024

J
Jorge Aparicio 已提交
1025
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1026 1027 1028
        self.iterations = n;
        f(self);
    }
1029

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

1036 1037 1038 1039 1040
        // 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 已提交
1041
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1042
        }
1043 1044 1045 1046 1047 1048 1049
        // 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; }

1050
        let mut total_run = Duration::nanoseconds(0);
1051
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1052
        loop {
1053 1054
            let mut summ = None;
            let mut summ5 = None;
1055

1056
            let loop_run = Duration::span(|| {
1057

1058
                for p in &mut *samples {
1059 1060 1061
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1062

1063 1064
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1065

1066
                for p in &mut *samples {
1067 1068 1069
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1070

1071 1072 1073 1074 1075
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1076

1077
            // If we've run for 100ms and seem to have converged to a
1078
            // stable median.
1079
            if loop_run.num_milliseconds() > 100 &&
1080 1081 1082
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1083 1084
            }

1085
            total_run = total_run + loop_run;
1086
            // Longest we ever run for is 3s.
1087
            if total_run.num_seconds() > 3 {
1088
                return summ5;
1089
            }
1090

1091
            n *= 2;
1092 1093
        }
    }
1094 1095 1096
}

pub mod bench {
M
Michael Darakananda 已提交
1097
    use std::cmp;
1098
    use std::time::Duration;
1099
    use super::{Bencher, BenchSamples};
1100

J
Jorge Aparicio 已提交
1101
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1102
        let mut bs = Bencher {
1103
            iterations: 0,
1104
            dur: Duration::nanoseconds(0),
1105 1106 1107
            bytes: 0
        };

1108
        let ns_iter_summ = bs.auto_bench(f);
1109

M
Michael Darakananda 已提交
1110
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1111
        let iter_s = 1_000_000_000 / ns_iter;
1112 1113 1114
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1115
            ns_iter_summ: ns_iter_summ,
1116 1117 1118 1119 1120
            mb_s: mb_s as uint
        }
    }
}

1121 1122
#[cfg(test)]
mod tests {
1123
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1124
               TestDesc, TestDescAndFn, TestOpts, run_test,
J
Jorge Aparicio 已提交
1125
               MetricMap,
1126
               StaticTestName, DynTestName, DynTestFn, ShouldFail};
1127
    use std::thunk::Thunk;
1128
    use std::sync::mpsc::channel;
1129

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

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

    #[test]
1165
    fn test_should_fail() {
S
Steve Klabnik 已提交
1166
        fn f() { panic!(); }
1167 1168
        let desc = TestDescAndFn {
            desc: TestDesc {
1169
                name: StaticTestName("whatever"),
1170
                ignore: false,
1171 1172
                should_fail: ShouldFail::Yes(None)
            },
1173
            testfn: DynTestFn(Thunk::new(move|| f())),
1174 1175 1176
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1177
        let (_, res, _) = rx.recv().unwrap();
1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188
        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"))
1189
            },
1190
            testfn: DynTestFn(Thunk::new(move|| f())),
1191
        };
1192
        let (tx, rx) = channel();
1193
        run_test(&TestOpts::new(), false, desc, tx);
1194
        let (_, res, _) = rx.recv().unwrap();
1195
        assert!(res == TrOk);
1196 1197
    }

1198 1199 1200 1201 1202 1203 1204 1205 1206
    #[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"))
            },
1207
            testfn: DynTestFn(Thunk::new(move|| f())),
1208 1209 1210
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1211
        let (_, res, _) = rx.recv().unwrap();
1212 1213 1214
        assert!(res == TrFailed);
    }

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

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

    #[test]
1245
    pub fn filter_for_ignored_option() {
1246 1247 1248
        // 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

1249 1250 1251
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1252

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

1272
        assert_eq!(filtered.len(), 1);
1273
        assert_eq!(filtered[0].desc.name.to_string(),
1274
                   "1");
1275
        assert!(filtered[0].desc.ignore == false);
1276 1277 1278
    }

    #[test]
1279
    pub fn sort_tests() {
1280 1281
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1282 1283

        let names =
1284 1285 1286 1287 1288 1289 1290 1291 1292
            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());
1293 1294
        let tests =
        {
1295
            fn testfn() { }
1296
            let mut tests = Vec::new();
1297
            for name in &names {
1298 1299
                let test = TestDescAndFn {
                    desc: TestDesc {
1300
                        name: DynTestName((*name).clone()),
1301
                        ignore: false,
1302
                        should_fail: ShouldFail::No,
1303
                    },
1304
                    testfn: DynTestFn(Thunk::new(testfn)),
1305
                };
L
Luqman Aden 已提交
1306
                tests.push(test);
1307
            }
L
Luqman Aden 已提交
1308
            tests
1309
        };
B
Brian Anderson 已提交
1310
        let filtered = filter_tests(&opts, tests);
1311

1312
        let expected =
1313 1314 1315 1316 1317 1318 1319 1320 1321
            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());
1322

1323
        for (a, b) in expected.iter().zip(filtered.iter()) {
1324
            assert!(*a == b.desc.name.to_string());
1325 1326
        }
    }
1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346 1347 1348 1349

    #[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);
    }
1350
}