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

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

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

A
Alex Crichton 已提交
26
#![crate_name = "test"]
27
#![unstable(feature = "test")]
B
Brian Anderson 已提交
28
#![staged_api]
29 30 31 32
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
33
       html_root_url = "http://doc.rust-lang.org/nightly/")]
A
Alex Crichton 已提交
34

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

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

S
Steven Fackler 已提交
53 54 55 56 57 58 59 60
pub use self::TestFn::*;
pub use self::ColorConfig::*;
pub use self::TestResult::*;
pub use self::TestName::*;
use self::TestEvent::*;
use self::NamePadding::*;
use self::OutputLocation::*;

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

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

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

95 96
pub mod stats;

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

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

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

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

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

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

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

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

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

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

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

218 219
unsafe impl Send for TestDesc {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

343 344 345 346
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.

347 348
Test Attributes:

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

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

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

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

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

383
    let logfile = matches.opt_str("logfile");
A
Alex Crichton 已提交
384
    let logfile = logfile.map(|s| PathBuf::new(&s));
385

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

390 391
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
392
        nocapture = env::var("RUST_TEST_NOCAPTURE").is_ok();
393 394
    }

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

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

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

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

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

433 434
unsafe impl Send for TestResult {}

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

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

A
Alex Crichton 已提交
454 455 456 457 458 459 460 461
fn new2old(new: io::Error) -> old_io::IoError {
    old_io::IoError {
        kind: old_io::OtherIoError,
        desc: "other error",
        detail: Some(new.to_string()),
    }
}

L
Luqman Aden 已提交
462
impl<T: Writer> ConsoleTestState<T> {
A
Alex Crichton 已提交
463
    pub fn new(opts: &TestOpts,
A
Alex Crichton 已提交
464
               _: Option<T>) -> old_io::IoResult<ConsoleTestState<StdWriter>> {
465
        let log_out = match opts.logfile {
A
Alex Crichton 已提交
466
            Some(ref path) => Some(try!(File::create(path).map_err(new2old))),
467 468
            None => None
        };
C
Corey Richardson 已提交
469
        let out = match term::stdout() {
A
Alex Crichton 已提交
470
            None => Raw(old_io::stdio::stdout_raw()),
C
Corey Richardson 已提交
471
            Some(t) => Pretty(t)
472
        };
C
Corey Richardson 已提交
473

A
Alex Crichton 已提交
474
        Ok(ConsoleTestState {
475 476
            out: out,
            log_out: log_out,
477
            use_color: use_color(opts),
A
Alfie John 已提交
478 479 480 481 482
            total: 0,
            passed: 0,
            failed: 0,
            ignored: 0,
            measured: 0,
483
            metrics: MetricMap::new(),
484
            failures: Vec::new(),
A
Alfie John 已提交
485
            max_name_len: 0,
A
Alex Crichton 已提交
486
        })
487 488
    }

A
Alex Crichton 已提交
489
    pub fn write_ok(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
490
        self.write_pretty("ok", term::color::GREEN)
491
    }
492

A
Alex Crichton 已提交
493
    pub fn write_failed(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
494
        self.write_pretty("FAILED", term::color::RED)
495 496
    }

A
Alex Crichton 已提交
497
    pub fn write_ignored(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
498
        self.write_pretty("ignored", term::color::YELLOW)
499
    }
500

A
Alex Crichton 已提交
501
    pub fn write_metric(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
502
        self.write_pretty("metric", term::color::CYAN)
503 504
    }

A
Alex Crichton 已提交
505
    pub fn write_bench(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
506
        self.write_pretty("bench", term::color::CYAN)
507
    }
508

L
Luqman Aden 已提交
509
    pub fn write_pretty(&mut self,
510
                        word: &str,
A
Alex Crichton 已提交
511
                        color: term::color::Color) -> old_io::IoResult<()> {
L
Luqman Aden 已提交
512
        match self.out {
A
Alex Crichton 已提交
513
            Pretty(ref mut term) => {
514
                if self.use_color {
A
Alex Crichton 已提交
515
                    try!(term.fg(color));
516
                }
517
                try!(term.write_all(word.as_bytes()));
518
                if self.use_color {
A
Alex Crichton 已提交
519
                    try!(term.reset());
520
                }
521 522 523 524 525
                term.flush()
            }
            Raw(ref mut stdout) => {
                try!(stdout.write_all(word.as_bytes()));
                stdout.flush()
526
            }
L
Luqman Aden 已提交
527 528 529
        }
    }

A
Alex Crichton 已提交
530
    pub fn write_plain(&mut self, s: &str) -> old_io::IoResult<()> {
L
Luqman Aden 已提交
531
        match self.out {
532 533 534 535 536 537 538 539
            Pretty(ref mut term) => {
                try!(term.write_all(s.as_bytes()));
                term.flush()
            },
            Raw(ref mut stdout) => {
                try!(stdout.write_all(s.as_bytes()));
                stdout.flush()
            },
540 541 542
        }
    }

A
Alex Crichton 已提交
543
    pub fn write_run_start(&mut self, len: uint) -> old_io::IoResult<()> {
544
        self.total = len;
545
        let noun = if len != 1 { "tests" } else { "test" };
546
        self.write_plain(&format!("\nrunning {} {}\n", len, noun))
547 548
    }

A
Alex Crichton 已提交
549
    pub fn write_test_start(&mut self, test: &TestDesc,
A
Alex Crichton 已提交
550
                            align: NamePadding) -> old_io::IoResult<()> {
551
        let name = test.padded_name(self.max_name_len, align);
552
        self.write_plain(&format!("test {} ... ", name))
M
Marijn Haverbeke 已提交
553
    }
554

A
Alex Crichton 已提交
555
    pub fn write_result(&mut self, result: &TestResult) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
556
        try!(match *result {
557 558 559
            TrOk => self.write_ok(),
            TrFailed => self.write_failed(),
            TrIgnored => self.write_ignored(),
560
            TrMetrics(ref mm) => {
A
Alex Crichton 已提交
561
                try!(self.write_metric());
562
                self.write_plain(&format!(": {}", mm.fmt_metrics()))
563
            }
564
            TrBench(ref bs) => {
A
Alex Crichton 已提交
565
                try!(self.write_bench());
566

567
                try!(self.write_plain(&format!(": {}", fmt_bench_samples(bs))));
568 569

                Ok(())
570
            }
A
Alex Crichton 已提交
571 572
        });
        self.write_plain("\n")
573
    }
574

A
Alex Crichton 已提交
575
    pub fn write_log(&mut self, test: &TestDesc,
A
Alex Crichton 已提交
576
                     result: &TestResult) -> io::Result<()> {
577
        match self.log_out {
A
Alex Crichton 已提交
578
            None => Ok(()),
L
Luqman Aden 已提交
579
            Some(ref mut o) => {
D
Derek Guenther 已提交
580
                let s = format!("{} {}\n", match *result {
581 582 583
                        TrOk => "ok".to_string(),
                        TrFailed => "failed".to_string(),
                        TrIgnored => "ignored".to_string(),
584
                        TrMetrics(ref mm) => mm.fmt_metrics(),
L
Luqman Aden 已提交
585
                        TrBench(ref bs) => fmt_bench_samples(bs)
586
                    }, test.name);
587
                o.write_all(s.as_bytes())
588 589
            }
        }
B
Brian Anderson 已提交
590 591
    }

A
Alex Crichton 已提交
592
    pub fn write_failures(&mut self) -> old_io::IoResult<()> {
A
Alex Crichton 已提交
593
        try!(self.write_plain("\nfailures:\n"));
594
        let mut failures = Vec::new();
595
        let mut fail_out = String::new();
596
        for &(ref f, ref stdout) in &self.failures {
597
            failures.push(f.name.to_string());
598
            if stdout.len() > 0 {
599 600 601
                fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
                let output = String::from_utf8_lossy(stdout);
                fail_out.push_str(&output);
602 603 604 605
                fail_out.push_str("\n");
            }
        }
        if fail_out.len() > 0 {
A
Alex Crichton 已提交
606
            try!(self.write_plain("\n"));
607
            try!(self.write_plain(&fail_out));
608
        }
609

A
Alex Crichton 已提交
610
        try!(self.write_plain("\nfailures:\n"));
611
        failures.sort();
612
        for name in &failures {
613
            try!(self.write_plain(&format!("    {}\n", name)));
614
        }
A
Alex Crichton 已提交
615
        Ok(())
616 617
    }

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

A
Alfie John 已提交
621
        let success = self.failed == 0;
A
Ahmed Charles 已提交
622
        if !success {
A
Alex Crichton 已提交
623
            try!(self.write_failures());
624
        }
625

A
Alex Crichton 已提交
626
        try!(self.write_plain("\ntest result: "));
627 628
        if success {
            // There's no parallelism at this point so it's safe to use color
A
Alex Crichton 已提交
629
            try!(self.write_ok());
630
        } else {
A
Alex Crichton 已提交
631
            try!(self.write_failed());
632
        }
L
Luqman Aden 已提交
633 634
        let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
                        self.passed, self.failed, self.ignored, self.measured);
635
        try!(self.write_plain(&s));
A
Alex Crichton 已提交
636
        return Ok(success);
637
    }
638 639
}

640
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
641
    if bs.mb_s != 0 {
A
Alex Crichton 已提交
642
        format!("{:>9} ns/iter (+/- {}) = {} MB/s",
643 644 645 646
             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 已提交
647
        format!("{:>9} ns/iter (+/- {})",
648 649
             bs.ns_iter_summ.median as uint,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
650
    }
651 652 653
}

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

A
Alex Crichton 已提交
656 657
    fn callback<T: Writer>(event: &TestEvent,
                           st: &mut ConsoleTestState<T>) -> old_io::IoResult<()> {
658
        match (*event).clone() {
659
            TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
660
            TeWait(ref test, padding) => st.write_test_start(test, padding),
661
            TeResult(test, result, stdout) => {
A
Alex Crichton 已提交
662
                try!(st.write_log(&test, &result).map_err(new2old));
A
Alex Crichton 已提交
663
                try!(st.write_result(&result));
664 665 666
                match result {
                    TrOk => st.passed += 1,
                    TrIgnored => st.ignored += 1,
667
                    TrMetrics(mm) => {
668
                        let tname = test.name;
669
                        let MetricMap(mm) = mm;
670
                        for (k,v) in &mm {
671
                            st.metrics
672 673 674
                              .insert_metric(&format!("{}.{}",
                                                      tname,
                                                      k),
675 676
                                             v.value,
                                             v.noise);
677 678 679
                        }
                        st.measured += 1
                    }
680
                    TrBench(bs) => {
681
                        st.metrics.insert_metric(test.name.as_slice(),
682 683
                                                 bs.ns_iter_summ.median,
                                                 bs.ns_iter_summ.max - bs.ns_iter_summ.min);
684
                        st.measured += 1
685
                    }
686 687
                    TrFailed => {
                        st.failed += 1;
688
                        st.failures.push((test, stdout));
689 690
                    }
                }
A
Alex Crichton 已提交
691
                Ok(())
692 693
            }
        }
694
    }
695

A
Alex Crichton 已提交
696
    let mut st = try!(ConsoleTestState::new(opts, None::<StdWriter>));
697 698
    fn len_if_padded(t: &TestDescAndFn) -> uint {
        match t.testfn.padding() {
A
Alfie John 已提交
699
            PadNone => 0,
700
            PadOnLeft | PadOnRight => t.desc.name.as_slice().len(),
701 702 703 704
        }
    }
    match tests.iter().max_by(|t|len_if_padded(*t)) {
        Some(t) => {
705
            let n = t.desc.name.as_slice();
706
            st.max_name_len = n.as_slice().len();
707
        },
708 709
        None => {}
    }
A
Alex Crichton 已提交
710
    try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
A
Ahmed Charles 已提交
711
    return st.write_run_finish();
712 713 714 715
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
716 717 718
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
719
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
720
    };
721

A
Alex Crichton 已提交
722 723 724
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
725
        should_fail: ShouldFail::No
A
Alex Crichton 已提交
726
    };
727

L
Luqman Aden 已提交
728
    let mut st = ConsoleTestState {
A
Alex Crichton 已提交
729
        log_out: None,
D
Daniel Micay 已提交
730
        out: Raw(Vec::new()),
A
Alex Crichton 已提交
731
        use_color: false,
A
Alfie John 已提交
732 733 734 735 736 737
        total: 0,
        passed: 0,
        failed: 0,
        ignored: 0,
        measured: 0,
        max_name_len: 10,
A
Alex Crichton 已提交
738
        metrics: MetricMap::new(),
739
        failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
740
    };
741

742
    st.write_failures().unwrap();
L
Luqman Aden 已提交
743
    let s = match st.out {
744
        Raw(ref m) => String::from_utf8_lossy(&m[..]),
A
Alex Crichton 已提交
745
        Pretty(_) => unreachable!()
L
Luqman Aden 已提交
746
    };
A
Alex Crichton 已提交
747

748 749
    let apos = s.find("a").unwrap();
    let bpos = s.find("b").unwrap();
P
Patrick Walton 已提交
750
    assert!(apos < bpos);
751 752
}

753 754
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
A
Alex Crichton 已提交
755
        AutoColor => get_concurrency() == 1 && old_io::stdout().get_ref().isatty(),
756 757 758
        AlwaysColor => true,
        NeverColor => false,
    }
759
}
760

761
#[derive(Clone)]
B
Brian Anderson 已提交
762
enum TestEvent {
763
    TeFiltered(Vec<TestDesc> ),
764
    TeWait(TestDesc, NamePadding),
765
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
766 767
}

768
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
769

F
Flavio Percoco 已提交
770

J
Jorge Aparicio 已提交
771 772
fn run_tests<F>(opts: &TestOpts,
                tests: Vec<TestDescAndFn> ,
A
Alex Crichton 已提交
773 774
                mut callback: F) -> old_io::IoResult<()> where
    F: FnMut(TestEvent) -> old_io::IoResult<()>,
J
Jorge Aparicio 已提交
775
{
T
Tim Chevalier 已提交
776
    let filtered_tests = filter_tests(opts, tests);
777 778 779
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
780

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

A
Aaron Turon 已提交
783 784
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
785 786 787 788 789
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
                _ => false
            }
        });
790

B
Brian Anderson 已提交
791 792
    // 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 已提交
793
    let concurrency = get_concurrency();
794

795
    let mut remaining = filtered_tests;
796
    remaining.reverse();
797
    let mut pending = 0;
B
Brian Anderson 已提交
798

799
    let (tx, rx) = channel::<MonitorMsg>();
800

801 802
    while pending > 0 || !remaining.is_empty() {
        while pending < concurrency && !remaining.is_empty() {
803
            let test = remaining.pop().unwrap();
804
            if concurrency == 1 {
805 806 807
                // 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 已提交
808
                try!(callback(TeWait(test.desc.clone(), test.testfn.padding())));
809
            }
810
            run_test(opts, !opts.run_tests, test, tx.clone());
811
            pending += 1;
B
Brian Anderson 已提交
812 813
        }

814
        let (desc, result, stdout) = rx.recv().unwrap();
815
        if concurrency != 1 {
A
Alex Crichton 已提交
816
            try!(callback(TeWait(desc.clone(), PadNone)));
817
        }
A
Alex Crichton 已提交
818
        try!(callback(TeResult(desc, result, stdout)));
819
        pending -= 1;
B
Brian Anderson 已提交
820
    }
821 822

    // All benchmarks run at the end, in serial.
823
    // (this includes metric fns)
824
    for b in filtered_benchs_and_metrics {
A
Alex Crichton 已提交
825
        try!(callback(TeWait(b.desc.clone(), b.testfn.padding())));
826
        run_test(opts, !opts.run_benchmarks, b, tx.clone());
827
        let (test, result, stdout) = rx.recv().unwrap();
A
Alex Crichton 已提交
828
        try!(callback(TeResult(test, result, stdout)));
829
    }
A
Alex Crichton 已提交
830
    Ok(())
B
Brian Anderson 已提交
831 832
}

833
fn get_concurrency() -> uint {
834
    use std::rt;
835
    match env::var("RUST_TEST_TASKS") {
A
Alex Crichton 已提交
836
        Ok(s) => {
A
Alex Crichton 已提交
837
            let opt_n: Option<uint> = s.parse().ok();
838 839
            match opt_n {
                Some(n) if n > 0 => n,
S
Steve Klabnik 已提交
840
                _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s)
841 842
            }
        }
A
Alex Crichton 已提交
843
        Err(..) => {
844
            rt::default_sched_threads()
845 846
        }
    }
847
}
848

849 850 851 852 853 854
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 已提交
855 856
        Some(ref filter) => {
            filtered.into_iter().filter(|test| {
857
                test.desc.name.as_slice().contains(&filter[..])
A
Alex Crichton 已提交
858
            }).collect()
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
        }
    };

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

883
    filtered
884
}
885

886 887
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
888
                test: TestDescAndFn,
889
                monitor_ch: Sender<MonitorMsg>) {
890

891 892
    let TestDescAndFn {desc, testfn} = test;

893
    if force_ignore || desc.ignore {
894
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
895
        return;
896 897
    }

898
    fn run_test_inner(desc: TestDesc,
899
                      monitor_ch: Sender<MonitorMsg>,
900
                      nocapture: bool,
901
                      testfn: Thunk<'static>) {
A
Aaron Turon 已提交
902
        thread::spawn(move || {
903 904 905 906
            let (tx, rx) = channel();
            let mut reader = ChanReader::new(rx);
            let stdout = ChanWriter::new(tx.clone());
            let stderr = ChanWriter::new(tx);
907
            let mut cfg = thread::Builder::new().name(match desc.name {
R
Richo Healey 已提交
908 909
                DynTestName(ref name) => name.clone().to_string(),
                StaticTestName(name) => name.to_string(),
910
            });
911 912 913
            if nocapture {
                drop((stdout, stderr));
            } else {
A
Aaron Turon 已提交
914 915
                cfg = cfg.stdout(box stdout as Box<Writer + Send>);
                cfg = cfg.stderr(box stderr as Box<Writer + Send>);
916
            }
917

A
Alex Crichton 已提交
918
            let result_guard = cfg.spawn(move || { testfn.invoke(()) }).unwrap();
A
Aaron Turon 已提交
919
            let stdout = reader.read_to_end().unwrap().into_iter().collect();
A
Aaron Turon 已提交
920
            let test_result = calc_result(&desc, result_guard.join());
921
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
922
        });
923 924 925
    }

    match testfn {
926
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
927
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
928
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
929 930 931
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
932
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
933
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
934 935
            return;
        }
936 937
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
938
            f.invoke(&mut mm);
939
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
940 941 942 943 944
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
945
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
946 947
            return;
        }
948 949
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
950
                                          Thunk::new(move|| f()))
951
    }
952 953
}

954 955 956 957 958 959 960 961 962 963 964
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,
965
    }
966 967
}

968 969
impl MetricMap {

970
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
971
        MetricMap(BTreeMap::new())
972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991
    }

    /// 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
        };
992
        let MetricMap(ref mut map) = *self;
993
        map.insert(name.to_string(), m);
994 995
    }

996 997 998 999 1000 1001 1002
    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(", ")
1003 1004 1005 1006 1007 1008
    }
}


// Benchmarking

H
Huon Wilson 已提交
1009
/// A function that is opaque to the optimizer, to allow benchmarks to
1010 1011 1012 1013
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1014
pub fn black_box<T>(dummy: T) -> T {
1015 1016 1017
    // we need to "use" the argument in some way LLVM can't
    // introspect.
    unsafe {asm!("" : : "r"(&dummy))}
1018
    dummy
1019 1020 1021
}


1022
impl Bencher {
1023
    /// Callback for benchmark functions to run in their body.
J
Jorge Aparicio 已提交
1024
    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1025 1026
        self.dur = Duration::span(|| {
            let k = self.iterations;
1027
            for _ in 0..k {
1028 1029 1030
                black_box(inner());
            }
        });
1031
    }
1032

1033
    pub fn ns_elapsed(&mut self) -> u64 {
1034
        self.dur.num_nanoseconds().unwrap() as u64
1035
    }
1036

1037 1038 1039 1040
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1041
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1042
        }
1043
    }
1044

J
Jorge Aparicio 已提交
1045
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1046 1047 1048
        self.iterations = n;
        f(self);
    }
1049

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

1056 1057 1058 1059 1060
        // 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 已提交
1061
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1062
        }
1063 1064 1065 1066 1067 1068 1069
        // 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; }

1070
        let mut total_run = Duration::nanoseconds(0);
1071
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1072
        loop {
1073 1074
            let mut summ = None;
            let mut summ5 = None;
1075

1076
            let loop_run = Duration::span(|| {
1077

1078
                for p in &mut *samples {
1079 1080 1081
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1082

1083 1084
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1085

1086
                for p in &mut *samples {
1087 1088 1089
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1090

1091 1092 1093 1094 1095
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1096

1097
            // If we've run for 100ms and seem to have converged to a
1098
            // stable median.
1099
            if loop_run.num_milliseconds() > 100 &&
1100 1101 1102
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1103 1104
            }

1105
            total_run = total_run + loop_run;
1106
            // Longest we ever run for is 3s.
1107
            if total_run.num_seconds() > 3 {
1108
                return summ5;
1109
            }
1110

1111
            n *= 2;
1112 1113
        }
    }
1114 1115 1116
}

pub mod bench {
M
Michael Darakananda 已提交
1117
    use std::cmp;
1118
    use std::time::Duration;
1119
    use super::{Bencher, BenchSamples};
1120

J
Jorge Aparicio 已提交
1121
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1122
        let mut bs = Bencher {
1123
            iterations: 0,
1124
            dur: Duration::nanoseconds(0),
1125 1126 1127
            bytes: 0
        };

1128
        let ns_iter_summ = bs.auto_bench(f);
1129

M
Michael Darakananda 已提交
1130
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1131
        let iter_s = 1_000_000_000 / ns_iter;
1132 1133 1134
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1135
            ns_iter_summ: ns_iter_summ,
1136 1137 1138 1139 1140
            mb_s: mb_s as uint
        }
    }
}

1141 1142
#[cfg(test)]
mod tests {
1143
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1144
               TestDesc, TestDescAndFn, TestOpts, run_test,
J
Jorge Aparicio 已提交
1145
               MetricMap,
1146
               StaticTestName, DynTestName, DynTestFn, ShouldFail};
1147
    use std::thunk::Thunk;
1148
    use std::sync::mpsc::channel;
1149

1150
    #[test]
1151
    pub fn do_not_run_ignored_tests() {
S
Steve Klabnik 已提交
1152
        fn f() { panic!(); }
1153 1154
        let desc = TestDescAndFn {
            desc: TestDesc {
1155
                name: StaticTestName("whatever"),
1156
                ignore: true,
1157
                should_fail: ShouldFail::No,
1158
            },
1159
            testfn: DynTestFn(Thunk::new(move|| f())),
1160
        };
1161
        let (tx, rx) = channel();
1162
        run_test(&TestOpts::new(), false, desc, tx);
1163
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1164
        assert!(res != TrOk);
1165 1166 1167
    }

    #[test]
1168
    pub fn ignored_tests_result_in_ignored() {
1169
        fn f() { }
1170 1171
        let desc = TestDescAndFn {
            desc: TestDesc {
1172
                name: StaticTestName("whatever"),
1173
                ignore: true,
1174
                should_fail: ShouldFail::No,
1175
            },
1176
            testfn: DynTestFn(Thunk::new(move|| f())),
1177
        };
1178
        let (tx, rx) = channel();
1179
        run_test(&TestOpts::new(), false, desc, tx);
1180
        let (_, res, _) = rx.recv().unwrap();
1181
        assert!(res == TrIgnored);
1182 1183 1184
    }

    #[test]
1185
    fn test_should_fail() {
S
Steve Klabnik 已提交
1186
        fn f() { panic!(); }
1187 1188
        let desc = TestDescAndFn {
            desc: TestDesc {
1189
                name: StaticTestName("whatever"),
1190
                ignore: false,
1191 1192
                should_fail: ShouldFail::Yes(None)
            },
1193
            testfn: DynTestFn(Thunk::new(move|| f())),
1194 1195 1196
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1197
        let (_, res, _) = rx.recv().unwrap();
1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208
        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"))
1209
            },
1210
            testfn: DynTestFn(Thunk::new(move|| f())),
1211
        };
1212
        let (tx, rx) = channel();
1213
        run_test(&TestOpts::new(), false, desc, tx);
1214
        let (_, res, _) = rx.recv().unwrap();
1215
        assert!(res == TrOk);
1216 1217
    }

1218 1219 1220 1221 1222 1223 1224 1225 1226
    #[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"))
            },
1227
            testfn: DynTestFn(Thunk::new(move|| f())),
1228 1229 1230
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1231
        let (_, res, _) = rx.recv().unwrap();
1232 1233 1234
        assert!(res == TrFailed);
    }

1235
    #[test]
1236
    fn test_should_fail_but_succeeds() {
1237
        fn f() { }
1238 1239
        let desc = TestDescAndFn {
            desc: TestDesc {
1240
                name: StaticTestName("whatever"),
1241
                ignore: false,
1242
                should_fail: ShouldFail::Yes(None)
1243
            },
1244
            testfn: DynTestFn(Thunk::new(move|| f())),
1245
        };
1246
        let (tx, rx) = channel();
1247
        run_test(&TestOpts::new(), false, desc, tx);
1248
        let (_, res, _) = rx.recv().unwrap();
1249
        assert!(res == TrFailed);
1250 1251 1252
    }

    #[test]
1253
    fn parse_ignored_flag() {
1254 1255 1256
        let args = vec!("progname".to_string(),
                        "filter".to_string(),
                        "--ignored".to_string());
1257
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1258
            Some(Ok(o)) => o,
S
Steve Klabnik 已提交
1259
            _ => panic!("Malformed arg in parse_ignored_flag")
B
Brian Anderson 已提交
1260
        };
P
Patrick Walton 已提交
1261
        assert!((opts.run_ignored));
1262 1263 1264
    }

    #[test]
1265
    pub fn filter_for_ignored_option() {
1266 1267 1268
        // 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

1269 1270 1271
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1272

1273
        let tests = vec!(
1274 1275
            TestDescAndFn {
                desc: TestDesc {
1276
                    name: StaticTestName("1"),
1277
                    ignore: true,
1278
                    should_fail: ShouldFail::No,
1279
                },
1280
                testfn: DynTestFn(Thunk::new(move|| {})),
1281
            },
1282 1283
            TestDescAndFn {
                desc: TestDesc {
1284
                    name: StaticTestName("2"),
1285
                    ignore: false,
1286
                    should_fail: ShouldFail::No,
1287
                },
1288
                testfn: DynTestFn(Thunk::new(move|| {})),
1289
            });
B
Brian Anderson 已提交
1290
        let filtered = filter_tests(&opts, tests);
1291

1292
        assert_eq!(filtered.len(), 1);
1293
        assert_eq!(filtered[0].desc.name.to_string(),
1294
                   "1");
1295
        assert!(filtered[0].desc.ignore == false);
1296 1297 1298
    }

    #[test]
1299
    pub fn sort_tests() {
1300 1301
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1302 1303

        let names =
1304 1305 1306 1307 1308 1309 1310 1311 1312
            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());
1313 1314
        let tests =
        {
1315
            fn testfn() { }
1316
            let mut tests = Vec::new();
1317
            for name in &names {
1318 1319
                let test = TestDescAndFn {
                    desc: TestDesc {
1320
                        name: DynTestName((*name).clone()),
1321
                        ignore: false,
1322
                        should_fail: ShouldFail::No,
1323
                    },
1324
                    testfn: DynTestFn(Thunk::new(testfn)),
1325
                };
L
Luqman Aden 已提交
1326
                tests.push(test);
1327
            }
L
Luqman Aden 已提交
1328
            tests
1329
        };
B
Brian Anderson 已提交
1330
        let filtered = filter_tests(&opts, tests);
1331

1332
        let expected =
1333 1334 1335 1336 1337 1338 1339 1340 1341
            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());
1342

1343
        for (a, b) in expected.iter().zip(filtered.iter()) {
1344
            assert!(*a == b.desc.name.to_string());
1345 1346
        }
    }
1347 1348 1349 1350 1351 1352 1353 1354 1355 1356 1357 1358 1359 1360 1361 1362 1363 1364 1365 1366 1367 1368 1369

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