lib.rs 49.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
//! `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", issue = "27812")]
28 29
#![crate_type = "rlib"]
#![crate_type = "dylib"]
30
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
A
Alex Crichton 已提交
31
       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
K
Kevin Butler 已提交
32 33
       html_root_url = "https://doc.rust-lang.org/nightly/",
       test(attr(deny(warnings))))]
34
#![cfg_attr(not(stage0), deny(warnings))]
A
Alex Crichton 已提交
35

36
#![feature(asm)]
37
#![feature(box_syntax)]
38 39
#![feature(fnbox)]
#![feature(libc)]
40
#![feature(rustc_private)]
41
#![feature(set_stdio)]
A
Alex Crichton 已提交
42
#![feature(staged_api)]
43
#![feature(time2)]
L
Liigo Zhuang 已提交
44

A
Alex Crichton 已提交
45
extern crate getopts;
L
Liigo Zhuang 已提交
46
extern crate serialize;
A
Alex Crichton 已提交
47
extern crate serialize as rustc_serialize;
A
Alex Crichton 已提交
48
extern crate term;
49
extern crate libc;
50

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

59
use stats::Stats;
A
Ahmed Charles 已提交
60
use serialize::Encodable;
61
use std::boxed::FnBox;
L
Liigo Zhuang 已提交
62
use term::Terminal;
63

64
use std::any::Any;
M
Michael Darakananda 已提交
65
use std::cmp;
66
use std::collections::BTreeMap;
A
Alex Crichton 已提交
67
use std::env;
68
use std::fmt;
A
Alex Crichton 已提交
69
use std::fs::File;
70 71
use std::io::prelude::*;
use std::io;
A
Alex Crichton 已提交
72
use std::iter::repeat;
73
use std::path::PathBuf;
74
use std::sync::mpsc::{channel, Sender};
75
use std::sync::{Arc, Mutex};
A
Alex Crichton 已提交
76
use std::thread;
77
use std::time::{Instant, Duration};
78

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

87 88
pub mod stats;

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

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

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

119
impl TestDesc {
120
    fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
121
        let mut name = String::from(self.name.as_slice());
122
        let fill = column_count.saturating_sub(name.len());
E
Erick Tryzelaar 已提交
123
        let pad = repeat(" ").take(fill).collect::<String>();
124
        match align {
125
            PadNone => name,
126
            PadOnRight => {
127
                name.push_str(&pad);
128
                name
129
            }
130 131 132 133
        }
    }
}

134
/// Represents a benchmark function.
135
pub trait TDynBenchFn: Send {
136
    fn run(&self, harness: &mut Bencher);
137 138
}

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

152 153
impl TestFn {
    fn padding(&self) -> NamePadding {
154
        match *self {
N
Nick Cameron 已提交
155 156
            StaticTestFn(..) => PadNone,
            StaticBenchFn(..) => PadOnRight,
157
            StaticMetricFn(..) => PadOnRight,
N
Nick Cameron 已提交
158 159 160
            DynTestFn(..) => PadNone,
            DynMetricFn(..) => PadOnRight,
            DynBenchFn(..) => PadOnRight,
161 162 163 164
        }
    }
}

165
impl fmt::Debug for TestFn {
166
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
167
        f.write_str(match *self {
168 169 170 171 172
            StaticTestFn(..) => "StaticTestFn(..)",
            StaticBenchFn(..) => "StaticBenchFn(..)",
            StaticMetricFn(..) => "StaticMetricFn(..)",
            DynTestFn(..) => "DynTestFn(..)",
            DynMetricFn(..) => "DynMetricFn(..)",
N
Nick Cameron 已提交
173
            DynBenchFn(..) => "DynBenchFn(..)",
174
        })
175 176 177
    }
}

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

J
Jorge Aparicio 已提交
190
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
191
pub enum ShouldPanic {
192
    No,
193
    Yes,
N
Nick Cameron 已提交
194
    YesWithMessage(&'static str),
195 196
}

197 198
// The definition of a single test. A test runner will run a list of
// these.
J
Jorge Aparicio 已提交
199
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
200
pub struct TestDesc {
201 202
    pub name: TestName,
    pub ignore: bool,
203
    pub should_panic: ShouldPanic,
204
}
205

206 207
unsafe impl Send for TestDesc {}

J
Jorge Aparicio 已提交
208
#[derive(Debug)]
209
pub struct TestDescAndFn {
210 211
    pub desc: TestDesc,
    pub testfn: TestFn,
212 213
}

J
Jorge Aparicio 已提交
214
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Copy)]
215
pub struct Metric {
216
    value: f64,
N
Nick Cameron 已提交
217
    noise: f64,
218 219
}

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

229
#[derive(PartialEq)]
N
Nick Cameron 已提交
230
pub struct MetricMap(BTreeMap<String, Metric>);
231

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

239
// The default console test runner. It accepts the command line
240
// arguments and a vector of test_descs.
N
Nick Cameron 已提交
241 242 243 244 245 246
pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>) {
    let opts = match parse_opts(args) {
        Some(Ok(o)) => o,
        Some(Err(msg)) => panic!("{:?}", msg),
        None => return,
    };
A
Alex Crichton 已提交
247 248
    match run_tests_console(&opts, tests) {
        Ok(true) => {}
249
        Ok(false) => std::process::exit(101),
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
// 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
258 259
// a Vec<TestDescAndFn> is used in order to effect ownership-transfer
// semantics into parallel test runners, which in turn requires a Vec<>
260
// rather than a &[].
261 262
pub fn test_main_static(tests: &[TestDescAndFn]) {
    let args = env::args().collect::<Vec<_>>();
N
Nick Cameron 已提交
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    let owned_tests = tests.iter()
                           .map(|t| {
                               match t.testfn {
                                   StaticTestFn(f) => {
                                       TestDescAndFn {
                                           testfn: StaticTestFn(f),
                                           desc: t.desc.clone(),
                                       }
                                   }
                                   StaticBenchFn(f) => {
                                       TestDescAndFn {
                                           testfn: StaticBenchFn(f),
                                           desc: t.desc.clone(),
                                       }
                                   }
                                   _ => panic!("non-static tests passed to test::test_main_static"),
                               }
                           })
                           .collect();
282
    test_main(&args, owned_tests)
283 284
}

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

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

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

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

N
Nick Cameron 已提交
320
#[cfg_attr(rustfmt, rustfmt_skip)]
321
fn optgroups() -> Vec<getopts::OptGroup> {
N
Nick Cameron 已提交
322 323 324 325 326 327 328 329 330 331 332 333
    vec!(getopts::optflag("", "ignored", "Run ignored tests"),
      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 \
                          of stdout", "PATH"),
      getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \
                                         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;
            never  = never colorize output;", "auto|always|never"))
334 335
}

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

340 341
The FILTER string is tested against the name of all tests, and only those
tests whose names contain the filter are run.
342 343

By default, all tests are run in parallel. This can be altered with the
A
Andrew Paseltiner 已提交
344
RUST_TEST_THREADS environment variable when running tests (set it to 1).
345

346 347 348 349
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.

350 351
Test Attributes:

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

367
// Parses command line arguments into test options
368
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
S
Simonas Kazlauskas 已提交
369
    let args_ = &args[1..];
N
Nick Cameron 已提交
370 371 372 373
    let matches = match getopts::getopts(args_, &optgroups()) {
        Ok(m) => m,
        Err(f) => return Some(Err(f.to_string())),
    };
M
Marijn Haverbeke 已提交
374

N
Nick Cameron 已提交
375 376 377 378
    if matches.opt_present("h") {
        usage(&args[0]);
        return None;
    }
379

380
    let filter = if !matches.free.is_empty() {
A
Alex Crichton 已提交
381
        Some(matches.free[0].clone())
382 383 384
    } else {
        None
    };
385

386
    let run_ignored = matches.opt_present("ignored");
387

388
    let logfile = matches.opt_str("logfile");
A
Aaron Turon 已提交
389
    let logfile = logfile.map(|s| PathBuf::from(&s));
390

391
    let bench_benchmarks = matches.opt_present("bench");
N
Nick Cameron 已提交
392
    let run_tests = !bench_benchmarks || matches.opt_present("test");
393

394 395
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
396
        nocapture = env::var("RUST_TEST_NOCAPTURE").is_ok();
397 398
    }

399
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
400 401 402 403
        Some("auto") | None => AutoColor,
        Some("always") => AlwaysColor,
        Some("never") => NeverColor,

N
Nick Cameron 已提交
404 405 406 407 408
        Some(v) => {
            return Some(Err(format!("argument for --color must be auto, always, or never (was \
                                     {})",
                                    v)))
        }
409 410
    };

411 412 413
    let test_opts = TestOpts {
        filter: filter,
        run_ignored: run_ignored,
414
        run_tests: run_tests,
415
        bench_benchmarks: bench_benchmarks,
416 417
        logfile: logfile,
        nocapture: nocapture,
418
        color: color,
419
    };
420

B
Brian Anderson 已提交
421
    Some(Ok(test_opts))
422 423
}

424
#[derive(Clone, PartialEq)]
425
pub struct BenchSamples {
426
    ns_iter_summ: stats::Summary,
427
    mb_s: usize,
428 429
}

430
#[derive(Clone, PartialEq)]
431 432 433 434 435
pub enum TestResult {
    TrOk,
    TrFailed,
    TrIgnored,
    TrMetrics(MetricMap),
436
    TrBench(BenchSamples),
437
}
438

439 440
unsafe impl Send for TestResult {}

A
Alex Crichton 已提交
441
enum OutputLocation<T> {
442
    Pretty(Box<term::StdoutTerminal>),
A
Alex Crichton 已提交
443 444 445
    Raw(T),
}

L
Luqman Aden 已提交
446 447
struct ConsoleTestState<T> {
    log_out: Option<File>,
A
Alex Crichton 已提交
448
    out: OutputLocation<T>,
449
    use_color: bool,
450 451 452 453 454
    total: usize,
    passed: usize,
    failed: usize,
    ignored: usize,
    measured: usize,
455
    metrics: MetricMap,
N
Nick Cameron 已提交
456
    failures: Vec<(TestDesc, Vec<u8>)>,
457
    max_name_len: usize, // number of columns to fill when aligning names
458
}
459

460
impl<T: Write> ConsoleTestState<T> {
N
Nick Cameron 已提交
461
    pub fn new(opts: &TestOpts, _: Option<T>) -> io::Result<ConsoleTestState<io::Stdout>> {
462
        let log_out = match opts.logfile {
463
            Some(ref path) => Some(try!(File::create(path))),
N
Nick Cameron 已提交
464
            None => None,
465
        };
C
Corey Richardson 已提交
466
        let out = match term::stdout() {
467
            None => Raw(io::stdout()),
N
Nick Cameron 已提交
468
            Some(t) => Pretty(t),
469
        };
C
Corey Richardson 已提交
470

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

486
    pub fn write_ok(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
487
        self.write_pretty("ok", term::color::GREEN)
488
    }
489

490
    pub fn write_failed(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
491
        self.write_pretty("FAILED", term::color::RED)
492 493
    }

494
    pub fn write_ignored(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
495
        self.write_pretty("ignored", term::color::YELLOW)
496
    }
497

498
    pub fn write_metric(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
499
        self.write_pretty("metric", term::color::CYAN)
500 501
    }

502
    pub fn write_bench(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
503
        self.write_pretty("bench", term::color::CYAN)
504
    }
505

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

525
    pub fn write_plain(&mut self, s: &str) -> io::Result<()> {
L
Luqman Aden 已提交
526
        match self.out {
527 528 529
            Pretty(ref mut term) => {
                try!(term.write_all(s.as_bytes()));
                term.flush()
N
Nick Cameron 已提交
530
            }
531 532 533
            Raw(ref mut stdout) => {
                try!(stdout.write_all(s.as_bytes()));
                stdout.flush()
N
Nick Cameron 已提交
534
            }
535 536 537
        }
    }

538
    pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
539
        self.total = len;
N
Nick Cameron 已提交
540 541 542 543 544
        let noun = if len != 1 {
            "tests"
        } else {
            "test"
        };
545
        self.write_plain(&format!("\nrunning {} {}\n", len, noun))
546 547
    }

N
Nick Cameron 已提交
548
    pub fn write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()> {
549
        let name = test.padded_name(self.max_name_len, align);
550
        self.write_plain(&format!("test {} ... ", name))
M
Marijn Haverbeke 已提交
551
    }
552

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

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

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

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

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

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

617
    pub fn write_run_finish(&mut self) -> io::Result<bool> {
618
        assert!(self.passed + self.failed + self.ignored + self.measured == self.total);
619

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

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

642 643 644 645
// Format a number with thousands separators
fn fmt_thousands_sep(mut n: usize, sep: char) -> String {
    use std::fmt::Write;
    let mut output = String::new();
646
    let mut trailing = false;
647 648
    for &pow in &[9, 6, 3, 0] {
        let base = 10_usize.pow(pow);
649 650
        if pow == 0 || trailing || n / base != 0 {
            if !trailing {
651 652 653 654 655 656 657
                output.write_fmt(format_args!("{}", n / base)).unwrap();
            } else {
                output.write_fmt(format_args!("{:03}", n / base)).unwrap();
            }
            if pow != 0 {
                output.push(sep);
            }
658
            trailing = true;
659 660 661 662 663 664 665
        }
        n %= base;
    }

    output
}

666
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
667 668 669 670 671 672 673
    use std::fmt::Write;
    let mut output = String::new();

    let median = bs.ns_iter_summ.median as usize;
    let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;

    output.write_fmt(format_args!("{:>11} ns/iter (+/- {})",
N
Nick Cameron 已提交
674 675 676
                                  fmt_thousands_sep(median, ','),
                                  fmt_thousands_sep(deviation, ',')))
          .unwrap();
677
    if bs.mb_s != 0 {
678
        output.write_fmt(format_args!(" = {} MB/s", bs.mb_s)).unwrap();
679
    }
680
    output
681 682 683
}

// A simple console test runner
N
Nick Cameron 已提交
684
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> io::Result<bool> {
685

N
Nick Cameron 已提交
686
    fn callback<T: Write>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::Result<()> {
687
        match (*event).clone() {
688
            TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
689
            TeWait(ref test, padding) => st.write_test_start(test, padding),
690
            TeResult(test, result, stdout) => {
691
                try!(st.write_log(&test, &result));
A
Alex Crichton 已提交
692
                try!(st.write_result(&result));
693 694 695
                match result {
                    TrOk => st.passed += 1,
                    TrIgnored => st.ignored += 1,
696
                    TrMetrics(mm) => {
697
                        let tname = test.name;
698
                        let MetricMap(mm) = mm;
N
Nick Cameron 已提交
699
                        for (k, v) in &mm {
700
                            st.metrics
N
Nick Cameron 已提交
701
                              .insert_metric(&format!("{}.{}", tname, k), v.value, v.noise);
702 703 704
                        }
                        st.measured += 1
                    }
705
                    TrBench(bs) => {
706
                        st.metrics.insert_metric(test.name.as_slice(),
707 708
                                                 bs.ns_iter_summ.median,
                                                 bs.ns_iter_summ.max - bs.ns_iter_summ.min);
709
                        st.measured += 1
710
                    }
711 712
                    TrFailed => {
                        st.failed += 1;
713
                        st.failures.push((test, stdout));
714 715
                    }
                }
A
Alex Crichton 已提交
716
                Ok(())
717 718
            }
        }
719
    }
720

721
    let mut st = try!(ConsoleTestState::new(opts, None::<io::Stdout>));
722
    fn len_if_padded(t: &TestDescAndFn) -> usize {
723
        match t.testfn.padding() {
A
Alfie John 已提交
724
            PadNone => 0,
E
Erick Tryzelaar 已提交
725
            PadOnRight => t.desc.name.as_slice().len(),
726 727
        }
    }
N
Nick Cameron 已提交
728
    match tests.iter().max_by_key(|t| len_if_padded(*t)) {
729
        Some(t) => {
730
            let n = t.desc.name.as_slice();
A
Aaron Turon 已提交
731
            st.max_name_len = n.len();
N
Nick Cameron 已提交
732
        }
733 734
        None => {}
    }
A
Alex Crichton 已提交
735
    try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
A
Ahmed Charles 已提交
736
    return st.write_run_finish();
737 738 739 740
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
741 742 743
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
N
Nick Cameron 已提交
744
        should_panic: ShouldPanic::No,
A
Alex Crichton 已提交
745
    };
746

A
Alex Crichton 已提交
747 748 749
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
N
Nick Cameron 已提交
750
        should_panic: ShouldPanic::No,
A
Alex Crichton 已提交
751
    };
752

L
Luqman Aden 已提交
753
    let mut st = ConsoleTestState {
A
Alex Crichton 已提交
754
        log_out: None,
D
Daniel Micay 已提交
755
        out: Raw(Vec::new()),
A
Alex Crichton 已提交
756
        use_color: false,
A
Alfie John 已提交
757 758 759 760 761 762
        total: 0,
        passed: 0,
        failed: 0,
        ignored: 0,
        measured: 0,
        max_name_len: 10,
A
Alex Crichton 已提交
763
        metrics: MetricMap::new(),
N
Nick Cameron 已提交
764
        failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
765
    };
766

767
    st.write_failures().unwrap();
L
Luqman Aden 已提交
768
    let s = match st.out {
769
        Raw(ref m) => String::from_utf8_lossy(&m[..]),
N
Nick Cameron 已提交
770
        Pretty(_) => unreachable!(),
L
Luqman Aden 已提交
771
    };
A
Alex Crichton 已提交
772

773 774
    let apos = s.find("a").unwrap();
    let bpos = s.find("b").unwrap();
P
Patrick Walton 已提交
775
    assert!(apos < bpos);
776 777
}

778 779
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
780
        AutoColor => !opts.nocapture && stdout_isatty(),
781 782 783
        AlwaysColor => true,
        NeverColor => false,
    }
784
}
785

786 787 788 789 790 791
#[cfg(unix)]
fn stdout_isatty() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
fn stdout_isatty() -> bool {
A
Alex Crichton 已提交
792 793 794 795 796
    type DWORD = u32;
    type BOOL = i32;
    type HANDLE = *mut u8;
    type LPDWORD = *mut u32;
    const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
797
    extern "system" {
A
Alex Crichton 已提交
798 799
        fn GetStdHandle(which: DWORD) -> HANDLE;
        fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
800 801 802 803 804 805 806 807
    }
    unsafe {
        let handle = GetStdHandle(STD_OUTPUT_HANDLE);
        let mut out = 0;
        GetConsoleMode(handle, &mut out) != 0
    }
}

808
#[derive(Clone)]
B
Brian Anderson 已提交
809
enum TestEvent {
N
Nick Cameron 已提交
810
    TeFiltered(Vec<TestDesc>),
811
    TeWait(TestDesc, NamePadding),
N
Nick Cameron 已提交
812
    TeResult(TestDesc, TestResult, Vec<u8>),
B
Brian Anderson 已提交
813 814
}

N
Nick Cameron 已提交
815
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8>);
816

F
Flavio Percoco 已提交
817

N
Nick Cameron 已提交
818 819
fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
    where F: FnMut(TestEvent) -> io::Result<()>
J
Jorge Aparicio 已提交
820
{
821 822 823 824 825
    let mut filtered_tests = filter_tests(opts, tests);
    if !opts.bench_benchmarks {
        filtered_tests = convert_benchmarks_to_tests(filtered_tests);
    }

826 827 828
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
829

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

A
Aaron Turon 已提交
832 833
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
834 835
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
N
Nick Cameron 已提交
836
                _ => false,
837 838
            }
        });
839

B
Brian Anderson 已提交
840 841
    // 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 已提交
842
    let concurrency = get_concurrency();
843

844
    let mut remaining = filtered_tests;
845
    remaining.reverse();
846
    let mut pending = 0;
B
Brian Anderson 已提交
847

848
    let (tx, rx) = channel::<MonitorMsg>();
849

850 851
    while pending > 0 || !remaining.is_empty() {
        while pending < concurrency && !remaining.is_empty() {
852
            let test = remaining.pop().unwrap();
853
            if concurrency == 1 {
854 855 856
                // 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 已提交
857
                try!(callback(TeWait(test.desc.clone(), test.testfn.padding())));
858
            }
859
            run_test(opts, !opts.run_tests, test, tx.clone());
860
            pending += 1;
B
Brian Anderson 已提交
861 862
        }

863
        let (desc, result, stdout) = rx.recv().unwrap();
864
        if concurrency != 1 {
A
Alex Crichton 已提交
865
            try!(callback(TeWait(desc.clone(), PadNone)));
866
        }
A
Alex Crichton 已提交
867
        try!(callback(TeResult(desc, result, stdout)));
868
        pending -= 1;
B
Brian Anderson 已提交
869
    }
870

871 872 873 874 875 876 877 878 879
    if opts.bench_benchmarks {
        // All benchmarks run at the end, in serial.
        // (this includes metric fns)
        for b in filtered_benchs_and_metrics {
            try!(callback(TeWait(b.desc.clone(), b.testfn.padding())));
            run_test(opts, false, b, tx.clone());
            let (test, result, stdout) = rx.recv().unwrap();
            try!(callback(TeResult(test, result, stdout)));
        }
880
    }
A
Alex Crichton 已提交
881
    Ok(())
B
Brian Anderson 已提交
882 883
}

884
#[allow(deprecated)]
885
fn get_concurrency() -> usize {
A
Alex Crichton 已提交
886
    return match env::var("RUST_TEST_THREADS") {
A
Alex Crichton 已提交
887
        Ok(s) => {
888
            let opt_n: Option<usize> = s.parse().ok();
889 890
            match opt_n {
                Some(n) if n > 0 => n,
N
Nick Cameron 已提交
891 892 893 894
                _ => {
                    panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.",
                           s)
                }
895 896
            }
        }
897
        Err(..) => num_cpus(),
A
Alex Crichton 已提交
898 899 900
    };

    #[cfg(windows)]
A
Alex Crichton 已提交
901
    #[allow(bad_style)]
A
Alex Crichton 已提交
902
    fn num_cpus() -> usize {
A
Alex Crichton 已提交
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
        #[repr(C)]
        struct SYSTEM_INFO {
            wProcessorArchitecture: u16,
            wReserved: u16,
            dwPageSize: u32,
            lpMinimumApplicationAddress: *mut u8,
            lpMaximumApplicationAddress: *mut u8,
            dwActiveProcessorMask: *mut u8,
            dwNumberOfProcessors: u32,
            dwProcessorType: u32,
            dwAllocationGranularity: u32,
            wProcessorLevel: u16,
            wProcessorRevision: u16,
        }
        extern "system" {
            fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
        }
A
Alex Crichton 已提交
920 921
        unsafe {
            let mut sysinfo = std::mem::zeroed();
A
Alex Crichton 已提交
922
            GetSystemInfo(&mut sysinfo);
A
Alex Crichton 已提交
923 924 925 926
            sysinfo.dwNumberOfProcessors as usize
        }
    }

927 928 929
    #[cfg(any(target_os = "linux",
              target_os = "macos",
              target_os = "ios",
N
Nikita Baksalyar 已提交
930
              target_os = "android",
931 932
              target_os = "solaris",
              target_os = "emscripten"))]
A
Alex Crichton 已提交
933
    fn num_cpus() -> usize {
N
Nick Cameron 已提交
934
        unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
935 936 937 938 939 940 941 942
    }

    #[cfg(any(target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "bitrig",
              target_os = "netbsd"))]
    fn num_cpus() -> usize {
        let mut cpus: libc::c_uint = 0;
943
        let mut cpus_size = std::mem::size_of_val(&cpus);
944 945

        unsafe {
D
Dave Huseby 已提交
946
            cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
947 948
        }
        if cpus < 1 {
D
Dave Huseby 已提交
949
            let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
950
            unsafe {
N
Nick Cameron 已提交
951 952
                libc::sysctl(mib.as_mut_ptr(),
                             2,
953
                             &mut cpus as *mut _ as *mut _,
954
                             &mut cpus_size as *mut _ as *mut _,
N
Nick Cameron 已提交
955 956
                             0 as *mut _,
                             0);
957 958 959 960 961 962
            }
            if cpus < 1 {
                cpus = 1;
            }
        }
        cpus as usize
963
    }
964 965 966 967 968 969 970 971

    #[cfg(target_os = "openbsd")]
    fn num_cpus() -> usize {
        let mut cpus: libc::c_uint = 0;
        let mut cpus_size = std::mem::size_of_val(&cpus);
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

        unsafe {
N
Nick Cameron 已提交
972 973
            libc::sysctl(mib.as_mut_ptr(),
                         2,
974 975
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
N
Nick Cameron 已提交
976 977
                         0 as *mut _,
                         0);
978 979 980 981 982 983
        }
        if cpus < 1 {
            cpus = 1;
        }
        cpus as usize
    }
984
}
985

986 987 988 989 990 991
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 已提交
992
        Some(ref filter) => {
N
Nick Cameron 已提交
993 994 995
            filtered.into_iter()
                    .filter(|test| test.desc.name.as_slice().contains(&filter[..]))
                    .collect()
996 997 998 999 1000 1001 1002 1003 1004 1005 1006
        }
    };

    // 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 {
N
Nick Cameron 已提交
1007 1008
                    desc: TestDesc { ignore: false, ..desc },
                    testfn: testfn,
1009 1010 1011 1012
                })
            } else {
                None
            }
F
Florian Hahn 已提交
1013
        }
1014
        filtered.into_iter().filter_map(filter).collect()
1015 1016 1017 1018 1019
    };

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

1020
    filtered
1021
}
1022

1023 1024
pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
    // convert benchmarks to tests, if we're not benchmarking them
N
Nick Cameron 已提交
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
    tests.into_iter()
         .map(|x| {
             let testfn = match x.testfn {
                 DynBenchFn(bench) => {
                     DynTestFn(Box::new(move || bench::run_once(|b| bench.run(b))))
                 }
                 StaticBenchFn(benchfn) => {
                     DynTestFn(Box::new(move || bench::run_once(|b| benchfn(b))))
                 }
                 f => f,
             };
             TestDescAndFn {
                 desc: x.desc,
                 testfn: testfn,
             }
         })
         .collect()
1042 1043
}

1044 1045
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
1046
                test: TestDescAndFn,
1047
                monitor_ch: Sender<MonitorMsg>) {
1048

1049 1050
    let TestDescAndFn {desc, testfn} = test;

1051
    if force_ignore || desc.ignore {
1052
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
1053
        return;
1054 1055
    }

1056
    fn run_test_inner(desc: TestDesc,
1057
                      monitor_ch: Sender<MonitorMsg>,
1058
                      nocapture: bool,
1059
                      testfn: Box<FnBox() + Send>) {
1060 1061 1062 1063 1064
        struct Sink(Arc<Mutex<Vec<u8>>>);
        impl Write for Sink {
            fn write(&mut self, data: &[u8]) -> io::Result<usize> {
                Write::write(&mut *self.0.lock().unwrap(), data)
            }
N
Nick Cameron 已提交
1065 1066 1067
            fn flush(&mut self) -> io::Result<()> {
                Ok(())
            }
1068 1069
        }

A
Aaron Turon 已提交
1070
        thread::spawn(move || {
1071 1072 1073
            let data = Arc::new(Mutex::new(Vec::new()));
            let data2 = data.clone();
            let cfg = thread::Builder::new().name(match desc.name {
1074 1075
                DynTestName(ref name) => name.clone(),
                StaticTestName(name) => name.to_owned(),
1076
            });
1077

1078
            let result_guard = cfg.spawn(move || {
N
Nick Cameron 已提交
1079 1080 1081 1082 1083 1084 1085
                                      if !nocapture {
                                          io::set_print(box Sink(data2.clone()));
                                          io::set_panic(box Sink(data2));
                                      }
                                      testfn()
                                  })
                                  .unwrap();
A
Aaron Turon 已提交
1086
            let test_result = calc_result(&desc, result_guard.join());
1087
            let stdout = data.lock().unwrap().to_vec();
1088
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
1089
        });
1090 1091 1092
    }

    match testfn {
1093
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
1094
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
1095
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1096 1097 1098
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
1099
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
1100
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1101 1102
            return;
        }
1103 1104
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
N
Niko Matsakis 已提交
1105
            f.call_box((&mut mm,));
1106
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1107 1108 1109 1110 1111
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
1112
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1113 1114
            return;
        }
1115
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
N
Nick Cameron 已提交
1116
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(f)),
1117
    }
1118 1119
}

N
Nick Cameron 已提交
1120
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any + Send>>) -> TestResult {
1121 1122
    match (&desc.should_panic, task_result) {
        (&ShouldPanic::No, Ok(())) |
1123 1124
        (&ShouldPanic::Yes, Err(_)) => TrOk,
        (&ShouldPanic::YesWithMessage(msg), Err(ref err))
1125
            if err.downcast_ref::<String>()
N
Nick Cameron 已提交
1126 1127 1128 1129
               .map(|e| &**e)
               .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
               .map(|e| e.contains(msg))
               .unwrap_or(false) => TrOk,
1130
        _ => TrFailed,
1131
    }
1132 1133
}

1134
impl MetricMap {
1135
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
1136
        MetricMap(BTreeMap::new())
1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154
    }

    /// 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,
N
Nick Cameron 已提交
1155
            noise: noise,
1156
        };
1157
        let MetricMap(ref mut map) = *self;
1158
        map.insert(name.to_owned(), m);
1159 1160
    }

1161 1162
    pub fn fmt_metrics(&self) -> String {
        let MetricMap(ref mm) = *self;
N
Nick Cameron 已提交
1163 1164 1165
        let v: Vec<String> = mm.iter()
                               .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise))
                               .collect();
1166
        v.join(", ")
1167 1168 1169 1170 1171 1172
    }
}


// Benchmarking

H
Huon Wilson 已提交
1173
/// A function that is opaque to the optimizer, to allow benchmarks to
1174 1175 1176 1177
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1178 1179
#[cfg(not(any(all(target_os = "nacl", target_arch = "le32"),
              target_arch = "asmjs")))]
1180
pub fn black_box<T>(dummy: T) -> T {
1181 1182
    // we need to "use" the argument in some way LLVM can't
    // introspect.
N
Nick Cameron 已提交
1183
    unsafe { asm!("" : : "r"(&dummy)) }
1184
    dummy
1185
}
1186 1187
#[cfg(any(all(target_os = "nacl", target_arch = "le32"),
          target_arch = "asmjs"))]
1188
#[inline(never)]
N
Nick Cameron 已提交
1189 1190 1191
pub fn black_box<T>(dummy: T) -> T {
    dummy
}
1192 1193


1194
impl Bencher {
1195
    /// Callback for benchmark functions to run in their body.
N
Nick Cameron 已提交
1196 1197 1198
    pub fn iter<T, F>(&mut self, mut inner: F)
        where F: FnMut() -> T
    {
1199 1200 1201 1202 1203 1204
        let start = Instant::now();
        let k = self.iterations;
        for _ in 0..k {
            black_box(inner());
        }
        self.dur = start.elapsed();
1205
    }
1206

1207
    pub fn ns_elapsed(&mut self) -> u64 {
S
Steven Fackler 已提交
1208
        self.dur.as_secs() * 1_000_000_000 + (self.dur.subsec_nanos() as u64)
1209
    }
1210

1211 1212 1213 1214
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1215
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1216
        }
1217
    }
1218

N
Nick Cameron 已提交
1219 1220 1221
    pub fn bench_n<F>(&mut self, n: u64, f: F)
        where F: FnOnce(&mut Bencher)
    {
1222 1223 1224
        self.iterations = n;
        f(self);
    }
1225

1226
    // This is a more statistics-driven benchmark algorithm
N
Nick Cameron 已提交
1227 1228 1229
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary
        where F: FnMut(&mut Bencher)
    {
1230
        // Initial bench run to get ballpark figure.
1231
        let mut n = 1;
1232
        self.bench_n(n, |x| f(x));
1233

1234 1235 1236 1237 1238
        // 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 已提交
1239
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1240
        }
1241 1242 1243 1244 1245
        // 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).
N
Nick Cameron 已提交
1246 1247 1248
        if n == 0 {
            n = 1;
        }
1249

1250
        let mut total_run = Duration::new(0, 0);
N
Nick Cameron 已提交
1251
        let samples: &mut [f64] = &mut [0.0_f64; 50];
1252
        loop {
1253
            let loop_start = Instant::now();
1254

1255 1256 1257
            for p in &mut *samples {
                self.bench_n(n, |x| f(x));
                *p = self.ns_per_iter() as f64;
N
Nick Cameron 已提交
1258
            }
1259

1260 1261
            stats::winsorize(samples, 5.0);
            let summ = stats::Summary::new(samples);
1262

1263 1264 1265
            for p in &mut *samples {
                self.bench_n(5 * n, |x| f(x));
                *p = self.ns_per_iter() as f64;
N
Nick Cameron 已提交
1266
            }
1267

1268 1269 1270
            stats::winsorize(samples, 5.0);
            let summ5 = stats::Summary::new(samples);
            let loop_run = loop_start.elapsed();
1271

1272
            // If we've run for 100ms and seem to have converged to a
1273
            // stable median.
N
Nick Cameron 已提交
1274 1275
            if loop_run > Duration::from_millis(100) && summ.median_abs_dev_pct < 1.0 &&
               summ.median - summ5.median < summ5.median_abs_dev {
1276
                return summ5;
1277 1278
            }

1279
            total_run = total_run + loop_run;
1280
            // Longest we ever run for is 3s.
1281
            if total_run > Duration::from_secs(3) {
1282
                return summ5;
1283
            }
1284

1285 1286 1287 1288 1289 1290 1291 1292
            // If we overflow here just return the results so far. We check a
            // multiplier of 10 because we're about to multiply by 2 and the
            // next iteration of the loop will also multiply by 5 (to calculate
            // the summ5 result)
            n = match n.checked_mul(10) {
                Some(_) => n * 2,
                None => return summ5,
            };
1293 1294
        }
    }
1295 1296 1297
}

pub mod bench {
M
Michael Darakananda 已提交
1298
    use std::cmp;
1299
    use std::time::Duration;
1300
    use super::{Bencher, BenchSamples};
1301

N
Nick Cameron 已提交
1302 1303 1304
    pub fn benchmark<F>(f: F) -> BenchSamples
        where F: FnMut(&mut Bencher)
    {
1305
        let mut bs = Bencher {
1306
            iterations: 0,
1307
            dur: Duration::new(0, 0),
N
Nick Cameron 已提交
1308
            bytes: 0,
1309 1310
        };

1311
        let ns_iter_summ = bs.auto_bench(f);
1312

M
Michael Darakananda 已提交
1313
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1314
        let mb_s = bs.bytes * 1000 / ns_iter;
1315 1316

        BenchSamples {
1317
            ns_iter_summ: ns_iter_summ,
N
Nick Cameron 已提交
1318
            mb_s: mb_s as usize,
1319 1320
        }
    }
1321

N
Nick Cameron 已提交
1322 1323 1324
    pub fn run_once<F>(f: F)
        where F: FnOnce(&mut Bencher)
    {
1325 1326
        let mut bs = Bencher {
            iterations: 0,
1327
            dur: Duration::new(0, 0),
N
Nick Cameron 已提交
1328
            bytes: 0,
1329 1330 1331
        };
        bs.bench_n(1, f);
    }
1332 1333
}

1334 1335
#[cfg(test)]
mod tests {
N
Nick Cameron 已提交
1336 1337
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts, TestDesc, TestDescAndFn,
               TestOpts, run_test, MetricMap, StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1338
    use std::sync::mpsc::channel;
1339

1340
    #[test]
1341
    pub fn do_not_run_ignored_tests() {
N
Nick Cameron 已提交
1342 1343 1344
        fn f() {
            panic!();
        }
1345 1346
        let desc = TestDescAndFn {
            desc: TestDesc {
1347
                name: StaticTestName("whatever"),
1348
                ignore: true,
1349
                should_panic: ShouldPanic::No,
1350
            },
N
Nick Cameron 已提交
1351
            testfn: DynTestFn(Box::new(move || f())),
1352
        };
1353
        let (tx, rx) = channel();
1354
        run_test(&TestOpts::new(), false, desc, tx);
1355
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1356
        assert!(res != TrOk);
1357 1358 1359
    }

    #[test]
1360
    pub fn ignored_tests_result_in_ignored() {
N
Nick Cameron 已提交
1361
        fn f() {}
1362 1363
        let desc = TestDescAndFn {
            desc: TestDesc {
1364
                name: StaticTestName("whatever"),
1365
                ignore: true,
1366
                should_panic: ShouldPanic::No,
1367
            },
N
Nick Cameron 已提交
1368
            testfn: DynTestFn(Box::new(move || f())),
1369
        };
1370
        let (tx, rx) = channel();
1371
        run_test(&TestOpts::new(), false, desc, tx);
1372
        let (_, res, _) = rx.recv().unwrap();
1373
        assert!(res == TrIgnored);
1374 1375 1376
    }

    #[test]
1377
    fn test_should_panic() {
N
Nick Cameron 已提交
1378 1379 1380
        fn f() {
            panic!();
        }
1381 1382
        let desc = TestDescAndFn {
            desc: TestDesc {
1383
                name: StaticTestName("whatever"),
1384
                ignore: false,
1385
                should_panic: ShouldPanic::Yes,
1386
            },
N
Nick Cameron 已提交
1387
            testfn: DynTestFn(Box::new(move || f())),
1388 1389 1390
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1391
        let (_, res, _) = rx.recv().unwrap();
1392 1393 1394 1395
        assert!(res == TrOk);
    }

    #[test]
1396
    fn test_should_panic_good_message() {
N
Nick Cameron 已提交
1397 1398 1399
        fn f() {
            panic!("an error message");
        }
1400 1401 1402 1403
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1404
                should_panic: ShouldPanic::YesWithMessage("error message"),
1405
            },
N
Nick Cameron 已提交
1406
            testfn: DynTestFn(Box::new(move || f())),
1407
        };
1408
        let (tx, rx) = channel();
1409
        run_test(&TestOpts::new(), false, desc, tx);
1410
        let (_, res, _) = rx.recv().unwrap();
1411
        assert!(res == TrOk);
1412 1413
    }

1414
    #[test]
1415
    fn test_should_panic_bad_message() {
N
Nick Cameron 已提交
1416 1417 1418
        fn f() {
            panic!("an error message");
        }
1419 1420 1421 1422
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1423
                should_panic: ShouldPanic::YesWithMessage("foobar"),
1424
            },
N
Nick Cameron 已提交
1425
            testfn: DynTestFn(Box::new(move || f())),
1426 1427 1428
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1429
        let (_, res, _) = rx.recv().unwrap();
1430 1431 1432
        assert!(res == TrFailed);
    }

1433
    #[test]
1434
    fn test_should_panic_but_succeeds() {
N
Nick Cameron 已提交
1435
        fn f() {}
1436 1437
        let desc = TestDescAndFn {
            desc: TestDesc {
1438
                name: StaticTestName("whatever"),
1439
                ignore: false,
1440
                should_panic: ShouldPanic::Yes,
1441
            },
N
Nick Cameron 已提交
1442
            testfn: DynTestFn(Box::new(move || f())),
1443
        };
1444
        let (tx, rx) = channel();
1445
        run_test(&TestOpts::new(), false, desc, tx);
1446
        let (_, res, _) = rx.recv().unwrap();
1447
        assert!(res == TrFailed);
1448 1449 1450
    }

    #[test]
1451
    fn parse_ignored_flag() {
N
Nick Cameron 已提交
1452
        let args = vec!["progname".to_string(), "filter".to_string(), "--ignored".to_string()];
1453
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1454
            Some(Ok(o)) => o,
N
Nick Cameron 已提交
1455
            _ => panic!("Malformed arg in parse_ignored_flag"),
B
Brian Anderson 已提交
1456
        };
P
Patrick Walton 已提交
1457
        assert!((opts.run_ignored));
1458 1459 1460
    }

    #[test]
1461
    pub fn filter_for_ignored_option() {
1462 1463 1464
        // 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

1465 1466 1467
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1468

N
Nick Cameron 已提交
1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484
        let tests = vec![TestDescAndFn {
                             desc: TestDesc {
                                 name: StaticTestName("1"),
                                 ignore: true,
                                 should_panic: ShouldPanic::No,
                             },
                             testfn: DynTestFn(Box::new(move || {})),
                         },
                         TestDescAndFn {
                             desc: TestDesc {
                                 name: StaticTestName("2"),
                                 ignore: false,
                                 should_panic: ShouldPanic::No,
                             },
                             testfn: DynTestFn(Box::new(move || {})),
                         }];
B
Brian Anderson 已提交
1485
        let filtered = filter_tests(&opts, tests);
1486

1487
        assert_eq!(filtered.len(), 1);
N
Nick Cameron 已提交
1488
        assert_eq!(filtered[0].desc.name.to_string(), "1");
1489
        assert!(filtered[0].desc.ignore == false);
1490 1491 1492
    }

    #[test]
1493
    pub fn sort_tests() {
1494 1495
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1496

N
Nick Cameron 已提交
1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507
        let names = vec!["sha1::test".to_string(),
                         "isize::test_to_str".to_string(),
                         "isize::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()];
        let tests = {
            fn testfn() {}
1508
            let mut tests = Vec::new();
1509
            for name in &names {
1510 1511
                let test = TestDescAndFn {
                    desc: TestDesc {
1512
                        name: DynTestName((*name).clone()),
1513
                        ignore: false,
1514
                        should_panic: ShouldPanic::No,
1515
                    },
1516
                    testfn: DynTestFn(Box::new(testfn)),
1517
                };
L
Luqman Aden 已提交
1518
                tests.push(test);
1519
            }
L
Luqman Aden 已提交
1520
            tests
1521
        };
B
Brian Anderson 已提交
1522
        let filtered = filter_tests(&opts, tests);
1523

N
Nick Cameron 已提交
1524 1525 1526 1527 1528 1529 1530 1531 1532
        let expected = vec!["isize::test_pow".to_string(),
                            "isize::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()];
1533

1534
        for (a, b) in expected.iter().zip(filtered) {
1535
            assert!(*a == b.desc.name.to_string());
1536 1537
        }
    }
1538 1539 1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559 1560

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