lib.rs 46.3 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
#![feature(duration)]
#![feature(duration_span)]
41 42 43
#![feature(fnbox)]
#![feature(iter_cmp)]
#![feature(libc)]
44
#![feature(rt)]
45
#![feature(rustc_private)]
46
#![feature(set_stdio)]
A
Alex Crichton 已提交
47
#![feature(staged_api)]
L
Liigo Zhuang 已提交
48

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

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

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

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

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

95 96
pub mod stats;

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

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

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

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

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

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

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

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

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

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

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

213 214
unsafe impl Send for TestDesc {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

338 339 340 341
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.

342 343
Test Attributes:

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

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

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

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

376
    let run_ignored = matches.opt_present("ignored");
377

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

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

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

390
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
391 392 393 394 395 396 397 398 399
        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))),
    };

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

B
Brian Anderson 已提交
410
    Some(Ok(test_opts))
411 412
}

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

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

428 429
unsafe impl Send for TestResult {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    output
}

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

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

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

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

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

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

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

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

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

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

775 776 777 778 779 780
#[cfg(unix)]
fn stdout_isatty() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
fn stdout_isatty() -> bool {
781
    const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
782 783 784 785 786 787 788 789 790 791 792 793
    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
    }
}

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

801
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
802

F
Flavio Percoco 已提交
803

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

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

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

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

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

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

836
    let (tx, rx) = channel::<MonitorMsg>();
837

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

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

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

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

    #[cfg(windows)]
    fn num_cpus() -> usize {
        unsafe {
            let mut sysinfo = std::mem::zeroed();
            libc::GetSystemInfo(&mut sysinfo);
            sysinfo.dwNumberOfProcessors as usize
        }
    }

    #[cfg(unix)]
    fn num_cpus() -> usize {
        extern { fn rust_get_num_cpus() -> libc::uintptr_t; }
        unsafe { rust_get_num_cpus() as usize }
904
    }
905
}
906

907 908 909 910 911 912
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 已提交
913 914
        Some(ref filter) => {
            filtered.into_iter().filter(|test| {
915
                test.desc.name.as_slice().contains(&filter[..])
A
Alex Crichton 已提交
916
            }).collect()
917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940
        }
    };

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

941
    filtered
942
}
943

944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
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()
}

960 961
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
962
                test: TestDescAndFn,
963
                monitor_ch: Sender<MonitorMsg>) {
964

965 966
    let TestDescAndFn {desc, testfn} = test;

967
    if force_ignore || desc.ignore {
968
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
969
        return;
970 971
    }

972
    fn run_test_inner(desc: TestDesc,
973
                      monitor_ch: Sender<MonitorMsg>,
974
                      nocapture: bool,
975
                      testfn: Box<FnBox() + Send>) {
976 977 978 979 980 981 982 983
        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 已提交
984
        thread::spawn(move || {
985 986 987
            let data = Arc::new(Mutex::new(Vec::new()));
            let data2 = data.clone();
            let cfg = thread::Builder::new().name(match desc.name {
R
Richo Healey 已提交
988 989
                DynTestName(ref name) => name.clone().to_string(),
                StaticTestName(name) => name.to_string(),
990
            });
991

992 993
            let result_guard = cfg.spawn(move || {
                if !nocapture {
994
                    io::set_print(box Sink(data2.clone()));
995 996
                    io::set_panic(box Sink(data2));
                }
997
                testfn()
998
            }).unwrap();
A
Aaron Turon 已提交
999
            let test_result = calc_result(&desc, result_guard.join());
1000
            let stdout = data.lock().unwrap().to_vec();
1001
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
1002
        });
1003 1004 1005
    }

    match testfn {
1006
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
1007
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
1008
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1009 1010 1011
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
1012
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
1013
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1014 1015
            return;
        }
1016 1017
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
N
Niko Matsakis 已提交
1018
            f.call_box((&mut mm,));
1019
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1020 1021 1022 1023 1024
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
1025
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1026 1027
            return;
        }
1028 1029
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
1030
                                          Box::new(move|| f()))
1031
    }
1032 1033
}

1034
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
1035 1036 1037 1038
    match (&desc.should_panic, task_result) {
        (&ShouldPanic::No, Ok(())) |
        (&ShouldPanic::Yes(None), Err(_)) => TrOk,
        (&ShouldPanic::Yes(Some(msg)), Err(ref err))
1039 1040 1041 1042 1043 1044
            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,
1045
    }
1046 1047
}

1048 1049
impl MetricMap {

1050
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
1051
        MetricMap(BTreeMap::new())
1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071
    }

    /// 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
        };
1072
        let MetricMap(ref mut map) = *self;
1073
        map.insert(name.to_string(), m);
1074 1075
    }

1076 1077 1078 1079
    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 已提交
1080
                                 v.value, v.noise))
1081
            .collect();
1082
        v.join(", ")
1083 1084 1085 1086 1087 1088
    }
}


// Benchmarking

H
Huon Wilson 已提交
1089
/// A function that is opaque to the optimizer, to allow benchmarks to
1090 1091 1092 1093
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1094
pub fn black_box<T>(dummy: T) -> T {
1095 1096 1097
    // we need to "use" the argument in some way LLVM can't
    // introspect.
    unsafe {asm!("" : : "r"(&dummy))}
1098
    dummy
1099 1100 1101
}


1102
impl Bencher {
1103
    /// Callback for benchmark functions to run in their body.
J
Jorge Aparicio 已提交
1104
    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1105 1106
        self.dur = Duration::span(|| {
            let k = self.iterations;
1107
            for _ in 0..k {
1108 1109 1110
                black_box(inner());
            }
        });
1111
    }
1112

1113
    pub fn ns_elapsed(&mut self) -> u64 {
1114
        self.dur.secs() * 1_000_000_000 + (self.dur.extra_nanos() as u64)
1115
    }
1116

1117 1118 1119 1120
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1121
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1122
        }
1123
    }
1124

J
Jorge Aparicio 已提交
1125
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1126 1127 1128
        self.iterations = n;
        f(self);
    }
1129

1130
    // This is a more statistics-driven benchmark algorithm
1131
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary where F: FnMut(&mut Bencher) {
1132
        // Initial bench run to get ballpark figure.
1133
        let mut n = 1;
1134
        self.bench_n(n, |x| f(x));
1135

1136 1137 1138 1139 1140
        // 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 已提交
1141
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1142
        }
1143 1144 1145 1146 1147 1148 1149
        // 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; }

1150
        let mut total_run = Duration::new(0, 0);
1151
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1152
        loop {
1153 1154
            let mut summ = None;
            let mut summ5 = None;
1155

1156
            let loop_run = Duration::span(|| {
1157

1158
                for p in &mut *samples {
1159 1160 1161
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1162

1163 1164
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1165

1166
                for p in &mut *samples {
1167 1168 1169
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1170

1171 1172 1173 1174 1175
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1176

1177
            // If we've run for 100ms and seem to have converged to a
1178
            // stable median.
1179
            if loop_run > Duration::from_millis(100) &&
1180 1181 1182
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1183 1184
            }

1185
            total_run = total_run + loop_run;
1186
            // Longest we ever run for is 3s.
1187
            if total_run > Duration::from_secs(3) {
1188
                return summ5;
1189
            }
1190

1191 1192 1193 1194 1195 1196 1197 1198
            // 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,
            };
1199 1200
        }
    }
1201 1202 1203
}

pub mod bench {
M
Michael Darakananda 已提交
1204
    use std::cmp;
1205
    use std::time::Duration;
1206
    use super::{Bencher, BenchSamples};
1207

J
Jorge Aparicio 已提交
1208
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1209
        let mut bs = Bencher {
1210
            iterations: 0,
1211
            dur: Duration::new(0, 0),
1212 1213 1214
            bytes: 0
        };

1215
        let ns_iter_summ = bs.auto_bench(f);
1216

M
Michael Darakananda 已提交
1217
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1218
        let iter_s = 1_000_000_000 / ns_iter;
1219 1220 1221
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1222
            ns_iter_summ: ns_iter_summ,
1223
            mb_s: mb_s as usize
1224 1225
        }
    }
1226 1227 1228 1229

    pub fn run_once<F>(f: F) where F: FnOnce(&mut Bencher) {
        let mut bs = Bencher {
            iterations: 0,
1230
            dur: Duration::new(0, 0),
1231 1232 1233 1234
            bytes: 0
        };
        bs.bench_n(1, f);
    }
1235 1236
}

1237 1238
#[cfg(test)]
mod tests {
1239
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1240
               TestDesc, TestDescAndFn, TestOpts, run_test,
J
Jorge Aparicio 已提交
1241
               MetricMap,
1242
               StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1243
    use std::sync::mpsc::channel;
1244

1245
    #[test]
1246
    pub fn do_not_run_ignored_tests() {
S
Steve Klabnik 已提交
1247
        fn f() { panic!(); }
1248 1249
        let desc = TestDescAndFn {
            desc: TestDesc {
1250
                name: StaticTestName("whatever"),
1251
                ignore: true,
1252
                should_panic: ShouldPanic::No,
1253
            },
1254
            testfn: DynTestFn(Box::new(move|| f())),
1255
        };
1256
        let (tx, rx) = channel();
1257
        run_test(&TestOpts::new(), false, desc, tx);
1258
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1259
        assert!(res != TrOk);
1260 1261 1262
    }

    #[test]
1263
    pub fn ignored_tests_result_in_ignored() {
1264
        fn f() { }
1265 1266
        let desc = TestDescAndFn {
            desc: TestDesc {
1267
                name: StaticTestName("whatever"),
1268
                ignore: true,
1269
                should_panic: ShouldPanic::No,
1270
            },
1271
            testfn: DynTestFn(Box::new(move|| f())),
1272
        };
1273
        let (tx, rx) = channel();
1274
        run_test(&TestOpts::new(), false, desc, tx);
1275
        let (_, res, _) = rx.recv().unwrap();
1276
        assert!(res == TrIgnored);
1277 1278 1279
    }

    #[test]
1280
    fn test_should_panic() {
S
Steve Klabnik 已提交
1281
        fn f() { panic!(); }
1282 1283
        let desc = TestDescAndFn {
            desc: TestDesc {
1284
                name: StaticTestName("whatever"),
1285
                ignore: false,
1286
                should_panic: ShouldPanic::Yes(None)
1287
            },
1288
            testfn: DynTestFn(Box::new(move|| f())),
1289 1290 1291
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1292
        let (_, res, _) = rx.recv().unwrap();
1293 1294 1295 1296
        assert!(res == TrOk);
    }

    #[test]
1297
    fn test_should_panic_good_message() {
1298 1299 1300 1301 1302
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1303
                should_panic: ShouldPanic::Yes(Some("error message"))
1304
            },
1305
            testfn: DynTestFn(Box::new(move|| f())),
1306
        };
1307
        let (tx, rx) = channel();
1308
        run_test(&TestOpts::new(), false, desc, tx);
1309
        let (_, res, _) = rx.recv().unwrap();
1310
        assert!(res == TrOk);
1311 1312
    }

1313
    #[test]
1314
    fn test_should_panic_bad_message() {
1315 1316 1317 1318 1319
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1320
                should_panic: ShouldPanic::Yes(Some("foobar"))
1321
            },
1322
            testfn: DynTestFn(Box::new(move|| f())),
1323 1324 1325
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1326
        let (_, res, _) = rx.recv().unwrap();
1327 1328 1329
        assert!(res == TrFailed);
    }

1330
    #[test]
1331
    fn test_should_panic_but_succeeds() {
1332
        fn f() { }
1333 1334
        let desc = TestDescAndFn {
            desc: TestDesc {
1335
                name: StaticTestName("whatever"),
1336
                ignore: false,
1337
                should_panic: ShouldPanic::Yes(None)
1338
            },
1339
            testfn: DynTestFn(Box::new(move|| f())),
1340
        };
1341
        let (tx, rx) = channel();
1342
        run_test(&TestOpts::new(), false, desc, tx);
1343
        let (_, res, _) = rx.recv().unwrap();
1344
        assert!(res == TrFailed);
1345 1346 1347
    }

    #[test]
1348
    fn parse_ignored_flag() {
1349 1350 1351
        let args = vec!("progname".to_string(),
                        "filter".to_string(),
                        "--ignored".to_string());
1352
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1353
            Some(Ok(o)) => o,
S
Steve Klabnik 已提交
1354
            _ => panic!("Malformed arg in parse_ignored_flag")
B
Brian Anderson 已提交
1355
        };
P
Patrick Walton 已提交
1356
        assert!((opts.run_ignored));
1357 1358 1359
    }

    #[test]
1360
    pub fn filter_for_ignored_option() {
1361 1362 1363
        // 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

1364 1365 1366
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1367

1368
        let tests = vec!(
1369 1370
            TestDescAndFn {
                desc: TestDesc {
1371
                    name: StaticTestName("1"),
1372
                    ignore: true,
1373
                    should_panic: ShouldPanic::No,
1374
                },
1375
                testfn: DynTestFn(Box::new(move|| {})),
1376
            },
1377 1378
            TestDescAndFn {
                desc: TestDesc {
1379
                    name: StaticTestName("2"),
1380
                    ignore: false,
1381
                    should_panic: ShouldPanic::No,
1382
                },
1383
                testfn: DynTestFn(Box::new(move|| {})),
1384
            });
B
Brian Anderson 已提交
1385
        let filtered = filter_tests(&opts, tests);
1386

1387
        assert_eq!(filtered.len(), 1);
1388
        assert_eq!(filtered[0].desc.name.to_string(),
1389
                   "1");
1390
        assert!(filtered[0].desc.ignore == false);
1391 1392 1393
    }

    #[test]
1394
    pub fn sort_tests() {
1395 1396
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1397 1398

        let names =
1399
            vec!("sha1::test".to_string(),
1400 1401
                 "isize::test_to_str".to_string(),
                 "isize::test_pow".to_string(),
1402 1403 1404 1405 1406 1407
                 "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());
1408 1409
        let tests =
        {
1410
            fn testfn() { }
1411
            let mut tests = Vec::new();
1412
            for name in &names {
1413 1414
                let test = TestDescAndFn {
                    desc: TestDesc {
1415
                        name: DynTestName((*name).clone()),
1416
                        ignore: false,
1417
                        should_panic: ShouldPanic::No,
1418
                    },
1419
                    testfn: DynTestFn(Box::new(testfn)),
1420
                };
L
Luqman Aden 已提交
1421
                tests.push(test);
1422
            }
L
Luqman Aden 已提交
1423
            tests
1424
        };
B
Brian Anderson 已提交
1425
        let filtered = filter_tests(&opts, tests);
1426

1427
        let expected =
1428 1429
            vec!("isize::test_pow".to_string(),
                 "isize::test_to_str".to_string(),
1430 1431 1432 1433 1434 1435 1436
                 "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());
1437

1438
        for (a, b) in expected.iter().zip(filtered) {
1439
            assert!(*a == b.desc.name.to_string());
1440 1441
        }
    }
1442 1443 1444 1445 1446 1447 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464

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