lib.rs 43.6 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"]
27
#![unstable(feature = "test")]
28
#![feature(staged_api)]
B
Brian Anderson 已提交
29
#![staged_api]
30 31 32 33
#![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",
34
       html_root_url = "http://doc.rust-lang.org/nightly/")]
35
#![allow(unknown_features)]
A
Alex Crichton 已提交
36
#![feature(asm, slicing_syntax)]
37
#![feature(box_syntax)]
H
Huon Wilson 已提交
38
#![allow(unknown_features)] #![feature(int_uint)]
39 40 41 42 43 44 45
#![feature(collections)]
#![feature(core)]
#![feature(io)]
#![feature(os)]
#![feature(path)]
#![feature(rustc_private)]
#![feature(std_misc)]
46
#![feature(hash)]
J
Jorge Aparicio 已提交
47 48
// NOTE(stage0): remove cfg_attr after a snapshot
#![cfg_attr(not(stage0), allow(unused_mut))]
L
Liigo Zhuang 已提交
49

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

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

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

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

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

94 95
pub mod stats;

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

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

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

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

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

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

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

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

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

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

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

217 218
unsafe impl Send for TestDesc {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

345 346
Test Attributes:

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

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

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

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

380
    let run_ignored = matches.opt_present("ignored");
381

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

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

389 390 391 392 393
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
        nocapture = os::getenv("RUST_TEST_NOCAPTURE").is_some();
    }

394 395 396 397 398 399 400 401 402 403
    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))),
    };

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

B
Brian Anderson 已提交
414
    Some(Ok(test_opts))
415 416
}

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

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

432 433
unsafe impl Send for TestResult {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

549 550
                try!(self.write_plain(format!(": {}",
                                              fmt_bench_samples(bs)).as_slice()));
551 552

                Ok(())
553
            }
A
Alex Crichton 已提交
554 555
        });
        self.write_plain("\n")
556
    }
557

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

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

A
Alex Crichton 已提交
594
        try!(self.write_plain("\nfailures:\n"));
595
        failures.sort();
D
Daniel Micay 已提交
596
        for name in failures.iter() {
597 598
            try!(self.write_plain(format!("    {}\n",
                                          name.as_slice()).as_slice()));
599
        }
A
Alex Crichton 已提交
600
        Ok(())
601 602
    }

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

A
Ahmed Charles 已提交
606 607
        let success = self.failed == 0u;
        if !success {
A
Alex Crichton 已提交
608
            try!(self.write_failures());
609
        }
610

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

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

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

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

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

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
701 702 703
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
704
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
705
    };
706

A
Alex Crichton 已提交
707 708 709
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
710
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
711
    };
712

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

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

733 734
    let apos = s.find_str("a").unwrap();
    let bpos = s.find_str("b").unwrap();
P
Patrick Walton 已提交
735
    assert!(apos < bpos);
736 737
}

738 739
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
A
Alex Crichton 已提交
740
        AutoColor => get_concurrency() == 1 && old_io::stdout().get_ref().isatty(),
741 742 743
        AlwaysColor => true,
        NeverColor => false,
    }
744
}
745

746
#[derive(Clone)]
B
Brian Anderson 已提交
747
enum TestEvent {
748
    TeFiltered(Vec<TestDesc> ),
749
    TeWait(TestDesc, NamePadding),
750
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
751 752
}

753
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
754

F
Flavio Percoco 已提交
755

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

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

A
Aaron Turon 已提交
768 769
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
770 771 772 773 774
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
                _ => false
            }
        });
775

B
Brian Anderson 已提交
776 777
    // 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 已提交
778
    let concurrency = get_concurrency();
779

780
    let mut remaining = filtered_tests;
781
    remaining.reverse();
782
    let mut pending = 0;
B
Brian Anderson 已提交
783

784
    let (tx, rx) = channel::<MonitorMsg>();
785

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

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

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

818
fn get_concurrency() -> uint {
819
    use std::rt;
820 821
    match os::getenv("RUST_TEST_TASKS") {
        Some(s) => {
A
Alex Crichton 已提交
822
            let opt_n: Option<uint> = s.parse().ok();
823 824
            match opt_n {
                Some(n) if n > 0 => n,
S
Steve Klabnik 已提交
825
                _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s)
826 827 828
            }
        }
        None => {
829
            rt::default_sched_threads()
830 831
        }
    }
832
}
833

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

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

868
    filtered
869
}
870

871 872
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
873
                test: TestDescAndFn,
874
                monitor_ch: Sender<MonitorMsg>) {
875

876 877
    let TestDescAndFn {desc, testfn} = test;

878
    if force_ignore || desc.ignore {
879
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
880
        return;
881 882
    }

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

A
Aaron Turon 已提交
903
            let result_guard = cfg.scoped(move || { testfn.invoke(()) });
A
Aaron Turon 已提交
904
            let stdout = reader.read_to_end().unwrap().into_iter().collect();
A
Aaron Turon 已提交
905
            let test_result = calc_result(&desc, result_guard.join());
906
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
907
        });
908 909 910
    }

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

939 940 941 942 943 944 945 946 947 948 949
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,
950
    }
951 952
}

953 954
impl MetricMap {

955
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
956
        MetricMap(BTreeMap::new())
957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976
    }

    /// 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
        };
977
        let MetricMap(ref mut map) = *self;
978
        map.insert(name.to_string(), m);
979 980
    }

981 982 983 984 985 986 987
    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(", ")
988 989 990 991 992 993
    }
}


// Benchmarking

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


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

1018
    pub fn ns_elapsed(&mut self) -> u64 {
1019
        self.dur.num_nanoseconds().unwrap() as u64
1020
    }
1021

1022 1023 1024 1025
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1026
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1027
        }
1028
    }
1029

J
Jorge Aparicio 已提交
1030
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1031 1032 1033
        self.iterations = n;
        f(self);
    }
1034

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

1041 1042 1043 1044 1045
        // 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 已提交
1046
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1047
        }
1048 1049 1050 1051 1052 1053 1054
        // 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; }

1055
        let mut total_run = Duration::nanoseconds(0);
1056
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1057
        loop {
1058 1059
            let mut summ = None;
            let mut summ5 = None;
1060

1061
            let loop_run = Duration::span(|| {
1062

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

1068 1069
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1070

1071 1072 1073 1074
                for p in samples.iter_mut() {
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1075

1076 1077 1078 1079 1080
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1081

1082
            // If we've run for 100ms and seem to have converged to a
1083
            // stable median.
1084
            if loop_run.num_milliseconds() > 100 &&
1085 1086 1087
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1088 1089
            }

1090
            total_run = total_run + loop_run;
1091
            // Longest we ever run for is 3s.
1092
            if total_run.num_seconds() > 3 {
1093
                return summ5;
1094
            }
1095

1096
            n *= 2;
1097 1098
        }
    }
1099 1100 1101
}

pub mod bench {
M
Michael Darakananda 已提交
1102
    use std::cmp;
1103
    use std::time::Duration;
1104
    use super::{Bencher, BenchSamples};
1105

J
Jorge Aparicio 已提交
1106
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1107
        let mut bs = Bencher {
1108
            iterations: 0,
1109
            dur: Duration::nanoseconds(0),
1110 1111 1112
            bytes: 0
        };

1113
        let ns_iter_summ = bs.auto_bench(f);
1114

M
Michael Darakananda 已提交
1115
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1116
        let iter_s = 1_000_000_000 / ns_iter;
1117 1118 1119
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1120
            ns_iter_summ: ns_iter_summ,
1121 1122 1123 1124 1125
            mb_s: mb_s as uint
        }
    }
}

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

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

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

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

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

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

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

    #[test]
1250
    pub fn filter_for_ignored_option() {
1251 1252 1253
        // 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

1254 1255 1256
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1257

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

1277
        assert_eq!(filtered.len(), 1);
1278
        assert_eq!(filtered[0].desc.name.to_string(),
1279
                   "1");
1280
        assert!(filtered[0].desc.ignore == false);
1281 1282 1283
    }

    #[test]
1284
    pub fn sort_tests() {
1285 1286
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1287 1288

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

1317
        let expected =
1318 1319 1320 1321 1322 1323 1324 1325 1326
            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());
1327

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

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