lib.rs 46.1 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

26 27
// Do not remove on snapshot creation. Needed for bootstrap. (Issue #22364)
#![cfg_attr(stage0, feature(custom_attribute))]
A
Alex Crichton 已提交
28
#![crate_name = "test"]
29
#![unstable(feature = "test")]
B
Brian Anderson 已提交
30
#![staged_api]
31 32 33
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
A
Alex Crichton 已提交
34
       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
35
       html_root_url = "http://doc.rust-lang.org/nightly/")]
A
Alex Crichton 已提交
36

37
#![feature(asm)]
38
#![feature(box_syntax)]
39 40 41 42 43
#![feature(duration)]
#![feature(duration_span)]
#![feature(fnbox)]
#![feature(iter_cmp)]
#![feature(libc)]
44
#![feature(rustc_private)]
45
#![feature(set_stdio)]
46
#![feature(slice_extras)]
A
Alex Crichton 已提交
47
#![feature(staged_api)]
48
#![feature(std_misc)]
L
Liigo Zhuang 已提交
49

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

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

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

71
use std::any::Any;
M
Michael Darakananda 已提交
72
use std::cmp;
73
use std::collections::BTreeMap;
A
Alex Crichton 已提交
74
use std::env;
75
use std::fmt;
A
Alex Crichton 已提交
76
use std::fs::File;
77 78
use std::io::prelude::*;
use std::io;
A
Alex Crichton 已提交
79
use std::iter::repeat;
80
use std::path::PathBuf;
81
use std::sync::mpsc::{channel, Sender};
82
use std::sync::{Arc, Mutex};
A
Alex Crichton 已提交
83
use std::thread;
84
use std::thunk::Thunk;
85
use std::time::Duration;
86

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

97 98
pub mod stats;

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

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

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

129
impl TestDesc {
130
    fn padded_name(&self, column_count: usize, align: NamePadding) -> String {
131
        let mut name = String::from(self.name.as_slice());
132
        let fill = column_count.saturating_sub(name.len());
E
Erick Tryzelaar 已提交
133
        let pad = repeat(" ").take(fill).collect::<String>();
134
        match align {
135
            PadNone => name,
136
            PadOnRight => {
137
                name.push_str(&pad);
138
                name
139
            }
140 141 142 143
        }
    }
}

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

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

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

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

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

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

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

215 216
unsafe impl Send for TestDesc {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

340 341 342 343
All tests have their standard output and standard error captured by default.
This can be overridden with the --nocapture flag or the RUST_TEST_NOCAPTURE=1
environment variable. Logging is not captured by default.

344 345
Test Attributes:

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

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

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

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

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

380
    let logfile = matches.opt_str("logfile");
A
Aaron Turon 已提交
381
    let logfile = logfile.map(|s| PathBuf::from(&s));
382

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

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

392
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
393 394 395 396 397 398 399 400 401
        Some("auto") | None => AutoColor,
        Some("always") => AlwaysColor,
        Some("never") => NeverColor,

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

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

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

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

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

430 431
unsafe impl Send for TestResult {}

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

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

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

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

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

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

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

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

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

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

519
    pub fn write_plain(&mut self, s: &str) -> io::Result<()> {
L
Luqman Aden 已提交
520
        match self.out {
521 522 523 524 525 526 527 528
            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()
            },
529 530 531
        }
    }

532
    pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
533
        self.total = len;
534
        let noun = if len != 1 { "tests" } else { "test" };
535
        self.write_plain(&format!("\nrunning {} {}\n", len, noun))
536 537
    }

A
Alex Crichton 已提交
538
    pub fn write_test_start(&mut self, test: &TestDesc,
539
                            align: NamePadding) -> io::Result<()> {
540
        let name = test.padded_name(self.max_name_len, align);
541
        self.write_plain(&format!("test {} ... ", name))
M
Marijn Haverbeke 已提交
542
    }
543

544
    pub fn write_result(&mut self, result: &TestResult) -> io::Result<()> {
A
Alex Crichton 已提交
545
        try!(match *result {
546 547 548
            TrOk => self.write_ok(),
            TrFailed => self.write_failed(),
            TrIgnored => self.write_ignored(),
549
            TrMetrics(ref mm) => {
A
Alex Crichton 已提交
550
                try!(self.write_metric());
551
                self.write_plain(&format!(": {}", mm.fmt_metrics()))
552
            }
553
            TrBench(ref bs) => {
A
Alex Crichton 已提交
554
                try!(self.write_bench());
555

556
                try!(self.write_plain(&format!(": {}", fmt_bench_samples(bs))));
557 558

                Ok(())
559
            }
A
Alex Crichton 已提交
560 561
        });
        self.write_plain("\n")
562
    }
563

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

581
    pub fn write_failures(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
582
        try!(self.write_plain("\nfailures:\n"));
583
        let mut failures = Vec::new();
584
        let mut fail_out = String::new();
585
        for &(ref f, ref stdout) in &self.failures {
586
            failures.push(f.name.to_string());
587
            if !stdout.is_empty() {
588 589 590
                fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
                let output = String::from_utf8_lossy(stdout);
                fail_out.push_str(&output);
591 592 593
                fail_out.push_str("\n");
            }
        }
594
        if !fail_out.is_empty() {
A
Alex Crichton 已提交
595
            try!(self.write_plain("\n"));
596
            try!(self.write_plain(&fail_out));
597
        }
598

A
Alex Crichton 已提交
599
        try!(self.write_plain("\nfailures:\n"));
600
        failures.sort();
601
        for name in &failures {
602
            try!(self.write_plain(&format!("    {}\n", name)));
603
        }
A
Alex Crichton 已提交
604
        Ok(())
605 606
    }

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

A
Alfie John 已提交
610
        let success = self.failed == 0;
A
Ahmed Charles 已提交
611
        if !success {
A
Alex Crichton 已提交
612
            try!(self.write_failures());
613
        }
614

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

629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
// 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();
    let mut first = true;
    for &pow in &[9, 6, 3, 0] {
        let base = 10_usize.pow(pow);
        if pow == 0 || n / base != 0 {
            if first {
                output.write_fmt(format_args!("{}", n / base)).unwrap();
            } else {
                output.write_fmt(format_args!("{:03}", n / base)).unwrap();
            }
            if pow != 0 {
                output.push(sep);
            }
            first = false;
        }
        n %= base;
    }

    output
}

653
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
654 655 656 657 658 659 660 661 662
    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 (+/- {})",
                     fmt_thousands_sep(median, ','),
                     fmt_thousands_sep(deviation, ','))).unwrap();
663
    if bs.mb_s != 0 {
664
        output.write_fmt(format_args!(" = {} MB/s", bs.mb_s)).unwrap();
665
    }
666
    output
667 668 669
}

// A simple console test runner
670
pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> io::Result<bool> {
671

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

712
    let mut st = try!(ConsoleTestState::new(opts, None::<io::Stdout>));
713
    fn len_if_padded(t: &TestDescAndFn) -> usize {
714
        match t.testfn.padding() {
A
Alfie John 已提交
715
            PadNone => 0,
E
Erick Tryzelaar 已提交
716
            PadOnRight => t.desc.name.as_slice().len(),
717 718 719 720
        }
    }
    match tests.iter().max_by(|t|len_if_padded(*t)) {
        Some(t) => {
721
            let n = t.desc.name.as_slice();
A
Aaron Turon 已提交
722
            st.max_name_len = n.len();
723
        },
724 725
        None => {}
    }
A
Alex Crichton 已提交
726
    try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
A
Ahmed Charles 已提交
727
    return st.write_run_finish();
728 729 730 731
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
732 733 734
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
735
        should_panic: ShouldPanic::No
A
Alex Crichton 已提交
736
    };
737

A
Alex Crichton 已提交
738 739 740
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
741
        should_panic: ShouldPanic::No
A
Alex Crichton 已提交
742
    };
743

L
Luqman Aden 已提交
744
    let mut st = ConsoleTestState {
A
Alex Crichton 已提交
745
        log_out: None,
D
Daniel Micay 已提交
746
        out: Raw(Vec::new()),
A
Alex Crichton 已提交
747
        use_color: false,
A
Alfie John 已提交
748 749 750 751 752 753
        total: 0,
        passed: 0,
        failed: 0,
        ignored: 0,
        measured: 0,
        max_name_len: 10,
A
Alex Crichton 已提交
754
        metrics: MetricMap::new(),
755
        failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
756
    };
757

758
    st.write_failures().unwrap();
L
Luqman Aden 已提交
759
    let s = match st.out {
760
        Raw(ref m) => String::from_utf8_lossy(&m[..]),
A
Alex Crichton 已提交
761
        Pretty(_) => unreachable!()
L
Luqman Aden 已提交
762
    };
A
Alex Crichton 已提交
763

764 765
    let apos = s.find("a").unwrap();
    let bpos = s.find("b").unwrap();
P
Patrick Walton 已提交
766
    assert!(apos < bpos);
767 768
}

769 770
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
771
        AutoColor => !opts.nocapture && stdout_isatty(),
772 773 774
        AlwaysColor => true,
        NeverColor => false,
    }
775
}
776

777 778 779 780 781 782
#[cfg(unix)]
fn stdout_isatty() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
fn stdout_isatty() -> bool {
783
    const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
784 785 786 787 788 789 790 791 792 793 794 795
    extern "system" {
        fn GetStdHandle(which: libc::DWORD) -> libc::HANDLE;
        fn GetConsoleMode(hConsoleHandle: libc::HANDLE,
                          lpMode: libc::LPDWORD) -> libc::BOOL;
    }
    unsafe {
        let handle = GetStdHandle(STD_OUTPUT_HANDLE);
        let mut out = 0;
        GetConsoleMode(handle, &mut out) != 0
    }
}

796
#[derive(Clone)]
B
Brian Anderson 已提交
797
enum TestEvent {
798
    TeFiltered(Vec<TestDesc> ),
799
    TeWait(TestDesc, NamePadding),
800
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
801 802
}

803
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
804

F
Flavio Percoco 已提交
805

J
Jorge Aparicio 已提交
806 807
fn run_tests<F>(opts: &TestOpts,
                tests: Vec<TestDescAndFn> ,
808 809
                mut callback: F) -> io::Result<()> where
    F: FnMut(TestEvent) -> io::Result<()>,
J
Jorge Aparicio 已提交
810
{
811 812 813 814 815
    let mut filtered_tests = filter_tests(opts, tests);
    if !opts.bench_benchmarks {
        filtered_tests = convert_benchmarks_to_tests(filtered_tests);
    }

816 817 818
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
819

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

A
Aaron Turon 已提交
822 823
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
824 825 826 827 828
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
                _ => false
            }
        });
829

B
Brian Anderson 已提交
830 831
    // 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 已提交
832
    let concurrency = get_concurrency();
833

834
    let mut remaining = filtered_tests;
835
    remaining.reverse();
836
    let mut pending = 0;
B
Brian Anderson 已提交
837

838
    let (tx, rx) = channel::<MonitorMsg>();
839

840 841
    while pending > 0 || !remaining.is_empty() {
        while pending < concurrency && !remaining.is_empty() {
842
            let test = remaining.pop().unwrap();
843
            if concurrency == 1 {
844 845 846
                // 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 已提交
847
                try!(callback(TeWait(test.desc.clone(), test.testfn.padding())));
848
            }
849
            run_test(opts, !opts.run_tests, test, tx.clone());
850
            pending += 1;
B
Brian Anderson 已提交
851 852
        }

853
        let (desc, result, stdout) = rx.recv().unwrap();
854
        if concurrency != 1 {
A
Alex Crichton 已提交
855
            try!(callback(TeWait(desc.clone(), PadNone)));
856
        }
A
Alex Crichton 已提交
857
        try!(callback(TeResult(desc, result, stdout)));
858
        pending -= 1;
B
Brian Anderson 已提交
859
    }
860

861 862 863 864 865 866 867 868 869
    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)));
        }
870
    }
A
Alex Crichton 已提交
871
    Ok(())
B
Brian Anderson 已提交
872 873
}

874
#[allow(deprecated)]
875
fn get_concurrency() -> usize {
876
    match env::var("RUST_TEST_THREADS") {
A
Alex Crichton 已提交
877
        Ok(s) => {
878
            let opt_n: Option<usize> = s.parse().ok();
879 880
            match opt_n {
                Some(n) if n > 0 => n,
881
                _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s)
882 883
            }
        }
A
Alex Crichton 已提交
884
        Err(..) => {
885 886 887
            if std::rt::util::limit_thread_creation_due_to_osx_and_valgrind() {
                1
            } else {
888 889
                extern { fn rust_get_num_cpus() -> libc::uintptr_t; }
                unsafe { rust_get_num_cpus() as usize }
890
            }
891 892
        }
    }
893
}
894

895 896 897 898 899 900
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 已提交
901 902
        Some(ref filter) => {
            filtered.into_iter().filter(|test| {
903
                test.desc.name.as_slice().contains(&filter[..])
A
Alex Crichton 已提交
904
            }).collect()
905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
        }
    };

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

929
    filtered
930
}
931

932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947
pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
    // convert benchmarks to tests, if we're not benchmarking them
    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()
}

948 949
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
950
                test: TestDescAndFn,
951
                monitor_ch: Sender<MonitorMsg>) {
952

953 954
    let TestDescAndFn {desc, testfn} = test;

955
    if force_ignore || desc.ignore {
956
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
957
        return;
958 959
    }

960
    fn run_test_inner(desc: TestDesc,
961
                      monitor_ch: Sender<MonitorMsg>,
962
                      nocapture: bool,
963
                      testfn: Thunk<'static>) {
964 965 966 967 968 969 970 971
        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)
            }
            fn flush(&mut self) -> io::Result<()> { Ok(()) }
        }

A
Aaron Turon 已提交
972
        thread::spawn(move || {
973 974 975
            let data = Arc::new(Mutex::new(Vec::new()));
            let data2 = data.clone();
            let cfg = thread::Builder::new().name(match desc.name {
R
Richo Healey 已提交
976 977
                DynTestName(ref name) => name.clone().to_string(),
                StaticTestName(name) => name.to_string(),
978
            });
979

980 981
            let result_guard = cfg.spawn(move || {
                if !nocapture {
982
                    io::set_print(box Sink(data2.clone()));
983 984
                    io::set_panic(box Sink(data2));
                }
985
                testfn()
986
            }).unwrap();
A
Aaron Turon 已提交
987
            let test_result = calc_result(&desc, result_guard.join());
988
            let stdout = data.lock().unwrap().to_vec();
989
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
990
        });
991 992 993
    }

    match testfn {
994
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
995
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
996
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
997 998 999
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
1000
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
1001
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1002 1003
            return;
        }
1004 1005
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
N
Niko Matsakis 已提交
1006
            f.call_box((&mut mm,));
1007
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1008 1009 1010 1011 1012
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
1013
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1014 1015
            return;
        }
1016 1017
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
1018
                                          Box::new(move|| f()))
1019
    }
1020 1021
}

1022
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
1023 1024 1025 1026
    match (&desc.should_panic, task_result) {
        (&ShouldPanic::No, Ok(())) |
        (&ShouldPanic::Yes(None), Err(_)) => TrOk,
        (&ShouldPanic::Yes(Some(msg)), Err(ref err))
1027 1028 1029 1030 1031 1032
            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,
1033
    }
1034 1035
}

1036 1037
impl MetricMap {

1038
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
1039
        MetricMap(BTreeMap::new())
1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059
    }

    /// 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
        };
1060
        let MetricMap(ref mut map) = *self;
1061
        map.insert(name.to_string(), m);
1062 1063
    }

1064 1065 1066 1067
    pub fn fmt_metrics(&self) -> String {
        let MetricMap(ref mm) = *self;
        let v : Vec<String> = mm.iter()
            .map(|(k,v)| format!("{}: {} (+/- {})", *k,
N
Nick Cameron 已提交
1068
                                 v.value, v.noise))
1069 1070
            .collect();
        v.connect(", ")
1071 1072 1073 1074 1075 1076
    }
}


// Benchmarking

H
Huon Wilson 已提交
1077
/// A function that is opaque to the optimizer, to allow benchmarks to
1078 1079 1080 1081
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1082
pub fn black_box<T>(dummy: T) -> T {
1083 1084 1085
    // we need to "use" the argument in some way LLVM can't
    // introspect.
    unsafe {asm!("" : : "r"(&dummy))}
1086
    dummy
1087 1088 1089
}


1090
impl Bencher {
1091
    /// Callback for benchmark functions to run in their body.
J
Jorge Aparicio 已提交
1092
    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1093 1094
        self.dur = Duration::span(|| {
            let k = self.iterations;
1095
            for _ in 0..k {
1096 1097 1098
                black_box(inner());
            }
        });
1099
    }
1100

1101
    pub fn ns_elapsed(&mut self) -> u64 {
1102
        self.dur.secs() * 1_000_000_000 + (self.dur.extra_nanos() as u64)
1103
    }
1104

1105 1106 1107 1108
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1109
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1110
        }
1111
    }
1112

J
Jorge Aparicio 已提交
1113
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1114 1115 1116
        self.iterations = n;
        f(self);
    }
1117

1118
    // This is a more statistics-driven benchmark algorithm
1119
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary where F: FnMut(&mut Bencher) {
1120
        // Initial bench run to get ballpark figure.
1121
        let mut n = 1;
1122
        self.bench_n(n, |x| f(x));
1123

1124 1125 1126 1127 1128
        // 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 已提交
1129
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1130
        }
1131 1132 1133 1134 1135 1136 1137
        // 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; }

1138
        let mut total_run = Duration::new(0, 0);
1139
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1140
        loop {
1141 1142
            let mut summ = None;
            let mut summ5 = None;
1143

1144
            let loop_run = Duration::span(|| {
1145

1146
                for p in &mut *samples {
1147 1148 1149
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1150

1151 1152
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1153

1154
                for p in &mut *samples {
1155 1156 1157
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1158

1159 1160 1161 1162 1163
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1164

1165
            // If we've run for 100ms and seem to have converged to a
1166
            // stable median.
1167
            if loop_run > Duration::from_millis(100) &&
1168 1169 1170
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1171 1172
            }

1173
            total_run = total_run + loop_run;
1174
            // Longest we ever run for is 3s.
1175
            if total_run > Duration::from_secs(3) {
1176
                return summ5;
1177
            }
1178

1179 1180 1181 1182 1183 1184 1185 1186
            // 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,
            };
1187 1188
        }
    }
1189 1190 1191
}

pub mod bench {
M
Michael Darakananda 已提交
1192
    use std::cmp;
1193
    use std::time::Duration;
1194
    use super::{Bencher, BenchSamples};
1195

J
Jorge Aparicio 已提交
1196
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1197
        let mut bs = Bencher {
1198
            iterations: 0,
1199
            dur: Duration::new(0, 0),
1200 1201 1202
            bytes: 0
        };

1203
        let ns_iter_summ = bs.auto_bench(f);
1204

M
Michael Darakananda 已提交
1205
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1206
        let iter_s = 1_000_000_000 / ns_iter;
1207 1208 1209
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1210
            ns_iter_summ: ns_iter_summ,
1211
            mb_s: mb_s as usize
1212 1213
        }
    }
1214 1215 1216 1217

    pub fn run_once<F>(f: F) where F: FnOnce(&mut Bencher) {
        let mut bs = Bencher {
            iterations: 0,
1218
            dur: Duration::new(0, 0),
1219 1220 1221 1222
            bytes: 0
        };
        bs.bench_n(1, f);
    }
1223 1224
}

1225 1226
#[cfg(test)]
mod tests {
1227
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1228
               TestDesc, TestDescAndFn, TestOpts, run_test,
J
Jorge Aparicio 已提交
1229
               MetricMap,
1230
               StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1231
    use std::thunk::Thunk;
1232
    use std::sync::mpsc::channel;
1233

1234
    #[test]
1235
    pub fn do_not_run_ignored_tests() {
S
Steve Klabnik 已提交
1236
        fn f() { panic!(); }
1237 1238
        let desc = TestDescAndFn {
            desc: TestDesc {
1239
                name: StaticTestName("whatever"),
1240
                ignore: true,
1241
                should_panic: ShouldPanic::No,
1242
            },
1243
            testfn: DynTestFn(Box::new(move|| f())),
1244
        };
1245
        let (tx, rx) = channel();
1246
        run_test(&TestOpts::new(), false, desc, tx);
1247
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1248
        assert!(res != TrOk);
1249 1250 1251
    }

    #[test]
1252
    pub fn ignored_tests_result_in_ignored() {
1253
        fn f() { }
1254 1255
        let desc = TestDescAndFn {
            desc: TestDesc {
1256
                name: StaticTestName("whatever"),
1257
                ignore: true,
1258
                should_panic: ShouldPanic::No,
1259
            },
1260
            testfn: DynTestFn(Box::new(move|| f())),
1261
        };
1262
        let (tx, rx) = channel();
1263
        run_test(&TestOpts::new(), false, desc, tx);
1264
        let (_, res, _) = rx.recv().unwrap();
1265
        assert!(res == TrIgnored);
1266 1267 1268
    }

    #[test]
1269
    fn test_should_panic() {
S
Steve Klabnik 已提交
1270
        fn f() { panic!(); }
1271 1272
        let desc = TestDescAndFn {
            desc: TestDesc {
1273
                name: StaticTestName("whatever"),
1274
                ignore: false,
1275
                should_panic: ShouldPanic::Yes(None)
1276
            },
1277
            testfn: DynTestFn(Box::new(move|| f())),
1278 1279 1280
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1281
        let (_, res, _) = rx.recv().unwrap();
1282 1283 1284 1285
        assert!(res == TrOk);
    }

    #[test]
1286
    fn test_should_panic_good_message() {
1287 1288 1289 1290 1291
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1292
                should_panic: ShouldPanic::Yes(Some("error message"))
1293
            },
1294
            testfn: DynTestFn(Box::new(move|| f())),
1295
        };
1296
        let (tx, rx) = channel();
1297
        run_test(&TestOpts::new(), false, desc, tx);
1298
        let (_, res, _) = rx.recv().unwrap();
1299
        assert!(res == TrOk);
1300 1301
    }

1302
    #[test]
1303
    fn test_should_panic_bad_message() {
1304 1305 1306 1307 1308
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1309
                should_panic: ShouldPanic::Yes(Some("foobar"))
1310
            },
1311
            testfn: DynTestFn(Box::new(move|| f())),
1312 1313 1314
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1315
        let (_, res, _) = rx.recv().unwrap();
1316 1317 1318
        assert!(res == TrFailed);
    }

1319
    #[test]
1320
    fn test_should_panic_but_succeeds() {
1321
        fn f() { }
1322 1323
        let desc = TestDescAndFn {
            desc: TestDesc {
1324
                name: StaticTestName("whatever"),
1325
                ignore: false,
1326
                should_panic: ShouldPanic::Yes(None)
1327
            },
1328
            testfn: DynTestFn(Box::new(move|| f())),
1329
        };
1330
        let (tx, rx) = channel();
1331
        run_test(&TestOpts::new(), false, desc, tx);
1332
        let (_, res, _) = rx.recv().unwrap();
1333
        assert!(res == TrFailed);
1334 1335 1336
    }

    #[test]
1337
    fn parse_ignored_flag() {
1338 1339 1340
        let args = vec!("progname".to_string(),
                        "filter".to_string(),
                        "--ignored".to_string());
1341
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1342
            Some(Ok(o)) => o,
S
Steve Klabnik 已提交
1343
            _ => panic!("Malformed arg in parse_ignored_flag")
B
Brian Anderson 已提交
1344
        };
P
Patrick Walton 已提交
1345
        assert!((opts.run_ignored));
1346 1347 1348
    }

    #[test]
1349
    pub fn filter_for_ignored_option() {
1350 1351 1352
        // 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

1353 1354 1355
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1356

1357
        let tests = vec!(
1358 1359
            TestDescAndFn {
                desc: TestDesc {
1360
                    name: StaticTestName("1"),
1361
                    ignore: true,
1362
                    should_panic: ShouldPanic::No,
1363
                },
1364
                testfn: DynTestFn(Box::new(move|| {})),
1365
            },
1366 1367
            TestDescAndFn {
                desc: TestDesc {
1368
                    name: StaticTestName("2"),
1369
                    ignore: false,
1370
                    should_panic: ShouldPanic::No,
1371
                },
1372
                testfn: DynTestFn(Box::new(move|| {})),
1373
            });
B
Brian Anderson 已提交
1374
        let filtered = filter_tests(&opts, tests);
1375

1376
        assert_eq!(filtered.len(), 1);
1377
        assert_eq!(filtered[0].desc.name.to_string(),
1378
                   "1");
1379
        assert!(filtered[0].desc.ignore == false);
1380 1381 1382
    }

    #[test]
1383
    pub fn sort_tests() {
1384 1385
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1386 1387

        let names =
1388
            vec!("sha1::test".to_string(),
1389 1390
                 "isize::test_to_str".to_string(),
                 "isize::test_pow".to_string(),
1391 1392 1393 1394 1395 1396
                 "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());
1397 1398
        let tests =
        {
1399
            fn testfn() { }
1400
            let mut tests = Vec::new();
1401
            for name in &names {
1402 1403
                let test = TestDescAndFn {
                    desc: TestDesc {
1404
                        name: DynTestName((*name).clone()),
1405
                        ignore: false,
1406
                        should_panic: ShouldPanic::No,
1407
                    },
1408
                    testfn: DynTestFn(Box::new(testfn)),
1409
                };
L
Luqman Aden 已提交
1410
                tests.push(test);
1411
            }
L
Luqman Aden 已提交
1412
            tests
1413
        };
B
Brian Anderson 已提交
1414
        let filtered = filter_tests(&opts, tests);
1415

1416
        let expected =
1417 1418
            vec!("isize::test_pow".to_string(),
                 "isize::test_to_str".to_string(),
1419 1420 1421 1422 1423 1424 1425
                 "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());
1426

1427
        for (a, b) in expected.iter().zip(filtered) {
1428
            assert!(*a == b.desc.name.to_string());
1429 1430
        }
    }
1431 1432 1433 1434 1435 1436 1437 1438 1439 1440 1441 1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453

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