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

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

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

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

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

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

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

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

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

85 86
pub mod stats;

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

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

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

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

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

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

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

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

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

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

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

208 209
unsafe impl Send for TestDesc {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

336 337
Test Attributes:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

423 424
unsafe impl Send for TestResult {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

F
Flavio Percoco 已提交
746

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

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

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

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

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

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

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

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

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

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

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

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

859
    filtered
860
}
861

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

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

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

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

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

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

930 931 932 933 934 935 936 937 938 939 940
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,
941
    }
942 943
}

944 945
impl MetricMap {

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

    /// 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
        };
968
        let MetricMap(ref mut map) = *self;
969
        map.insert(name.to_string(), m);
970
    }
971 972 973 974 975 976 977 978 979

    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(", ")
    }
980 981 982 983 984
}


// Benchmarking

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

1309
        let expected =
1310 1311 1312 1313 1314 1315 1316 1317 1318
            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());
1319

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

1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341 1342 1343 1344 1345 1346
    #[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);
    }
1347
}