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

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

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

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
#![feature(collections)]
#![feature(core)]
#![feature(rustc_private)]
A
Alex Crichton 已提交
42
#![feature(staged_api)]
43
#![feature(std_misc)]
44
#![feature(libc)]
45
#![feature(set_stdio)]
46 47
#![feature(duration)]
#![feature(duration_span)]
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::thunk::Thunk;
84
use std::time::Duration;
85

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

96 97
pub mod stats;

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

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

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

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

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

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

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

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

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

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

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

214 215
unsafe impl Send for TestDesc {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

343 344
Test Attributes:

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

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

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

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

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

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

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

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

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

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

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

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

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

429 430
unsafe impl Send for TestResult {}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

628
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
629
    if bs.mb_s != 0 {
A
Alex Crichton 已提交
630
        format!("{:>9} ns/iter (+/- {}) = {} MB/s",
631 632
             bs.ns_iter_summ.median as usize,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize,
633 634
             bs.mb_s)
    } else {
A
Alex Crichton 已提交
635
        format!("{:>9} ns/iter (+/- {})",
636 637
             bs.ns_iter_summ.median as usize,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize)
638
    }
639 640 641
}

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

644 645
    fn callback<T: Write>(event: &TestEvent,
                          st: &mut ConsoleTestState<T>) -> io::Result<()> {
646
        match (*event).clone() {
647
            TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
648
            TeWait(ref test, padding) => st.write_test_start(test, padding),
649
            TeResult(test, result, stdout) => {
650
                try!(st.write_log(&test, &result));
A
Alex Crichton 已提交
651
                try!(st.write_result(&result));
652 653 654
                match result {
                    TrOk => st.passed += 1,
                    TrIgnored => st.ignored += 1,
655
                    TrMetrics(mm) => {
656
                        let tname = test.name;
657
                        let MetricMap(mm) = mm;
658
                        for (k,v) in &mm {
659
                            st.metrics
660 661 662
                              .insert_metric(&format!("{}.{}",
                                                      tname,
                                                      k),
663 664
                                             v.value,
                                             v.noise);
665 666 667
                        }
                        st.measured += 1
                    }
668
                    TrBench(bs) => {
669
                        st.metrics.insert_metric(test.name.as_slice(),
670 671
                                                 bs.ns_iter_summ.median,
                                                 bs.ns_iter_summ.max - bs.ns_iter_summ.min);
672
                        st.measured += 1
673
                    }
674 675
                    TrFailed => {
                        st.failed += 1;
676
                        st.failures.push((test, stdout));
677 678
                    }
                }
A
Alex Crichton 已提交
679
                Ok(())
680 681
            }
        }
682
    }
683

684
    let mut st = try!(ConsoleTestState::new(opts, None::<io::Stdout>));
685
    fn len_if_padded(t: &TestDescAndFn) -> usize {
686
        match t.testfn.padding() {
A
Alfie John 已提交
687
            PadNone => 0,
E
Erick Tryzelaar 已提交
688
            PadOnRight => t.desc.name.as_slice().len(),
689 690 691 692
        }
    }
    match tests.iter().max_by(|t|len_if_padded(*t)) {
        Some(t) => {
693
            let n = t.desc.name.as_slice();
A
Aaron Turon 已提交
694
            st.max_name_len = n.len();
695
        },
696 697
        None => {}
    }
A
Alex Crichton 已提交
698
    try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
A
Ahmed Charles 已提交
699
    return st.write_run_finish();
700 701 702 703
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
704 705 706
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
707
        should_panic: ShouldPanic::No
A
Alex Crichton 已提交
708
    };
709

A
Alex Crichton 已提交
710 711 712
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
713
        should_panic: ShouldPanic::No
A
Alex Crichton 已提交
714
    };
715

L
Luqman Aden 已提交
716
    let mut st = ConsoleTestState {
A
Alex Crichton 已提交
717
        log_out: None,
D
Daniel Micay 已提交
718
        out: Raw(Vec::new()),
A
Alex Crichton 已提交
719
        use_color: false,
A
Alfie John 已提交
720 721 722 723 724 725
        total: 0,
        passed: 0,
        failed: 0,
        ignored: 0,
        measured: 0,
        max_name_len: 10,
A
Alex Crichton 已提交
726
        metrics: MetricMap::new(),
727
        failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
728
    };
729

730
    st.write_failures().unwrap();
L
Luqman Aden 已提交
731
    let s = match st.out {
732
        Raw(ref m) => String::from_utf8_lossy(&m[..]),
A
Alex Crichton 已提交
733
        Pretty(_) => unreachable!()
L
Luqman Aden 已提交
734
    };
A
Alex Crichton 已提交
735

736 737
    let apos = s.find("a").unwrap();
    let bpos = s.find("b").unwrap();
P
Patrick Walton 已提交
738
    assert!(apos < bpos);
739 740
}

741 742
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
743
        AutoColor => !opts.nocapture && stdout_isatty(),
744 745 746
        AlwaysColor => true,
        NeverColor => false,
    }
747
}
748

749 750 751 752 753 754
#[cfg(unix)]
fn stdout_isatty() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
fn stdout_isatty() -> bool {
755
    const STD_OUTPUT_HANDLE: libc::DWORD = -11i32 as libc::DWORD;
756 757 758 759 760 761 762 763 764 765 766 767
    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
    }
}

768
#[derive(Clone)]
B
Brian Anderson 已提交
769
enum TestEvent {
770
    TeFiltered(Vec<TestDesc> ),
771
    TeWait(TestDesc, NamePadding),
772
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
773 774
}

775
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
776

F
Flavio Percoco 已提交
777

J
Jorge Aparicio 已提交
778 779
fn run_tests<F>(opts: &TestOpts,
                tests: Vec<TestDescAndFn> ,
780 781
                mut callback: F) -> io::Result<()> where
    F: FnMut(TestEvent) -> io::Result<()>,
J
Jorge Aparicio 已提交
782
{
783 784 785 786 787
    let mut filtered_tests = filter_tests(opts, tests);
    if !opts.bench_benchmarks {
        filtered_tests = convert_benchmarks_to_tests(filtered_tests);
    }

788 789 790
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
791

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

A
Aaron Turon 已提交
794 795
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
796 797 798 799 800
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
                _ => false
            }
        });
801

B
Brian Anderson 已提交
802 803
    // 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 已提交
804
    let concurrency = get_concurrency();
805

806
    let mut remaining = filtered_tests;
807
    remaining.reverse();
808
    let mut pending = 0;
B
Brian Anderson 已提交
809

810
    let (tx, rx) = channel::<MonitorMsg>();
811

812 813
    while pending > 0 || !remaining.is_empty() {
        while pending < concurrency && !remaining.is_empty() {
814
            let test = remaining.pop().unwrap();
815
            if concurrency == 1 {
816 817 818
                // 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 已提交
819
                try!(callback(TeWait(test.desc.clone(), test.testfn.padding())));
820
            }
821
            run_test(opts, !opts.run_tests, test, tx.clone());
822
            pending += 1;
B
Brian Anderson 已提交
823 824
        }

825
        let (desc, result, stdout) = rx.recv().unwrap();
826
        if concurrency != 1 {
A
Alex Crichton 已提交
827
            try!(callback(TeWait(desc.clone(), PadNone)));
828
        }
A
Alex Crichton 已提交
829
        try!(callback(TeResult(desc, result, stdout)));
830
        pending -= 1;
B
Brian Anderson 已提交
831
    }
832

833 834 835 836 837 838 839 840 841
    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)));
        }
842
    }
A
Alex Crichton 已提交
843
    Ok(())
B
Brian Anderson 已提交
844 845
}

846
#[allow(deprecated)]
847
fn get_concurrency() -> usize {
848
    match env::var("RUST_TEST_THREADS") {
A
Alex Crichton 已提交
849
        Ok(s) => {
850
            let opt_n: Option<usize> = s.parse().ok();
851 852
            match opt_n {
                Some(n) if n > 0 => n,
853
                _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s)
854 855
            }
        }
A
Alex Crichton 已提交
856
        Err(..) => {
857 858 859
            if std::rt::util::limit_thread_creation_due_to_osx_and_valgrind() {
                1
            } else {
860 861
                extern { fn rust_get_num_cpus() -> libc::uintptr_t; }
                unsafe { rust_get_num_cpus() as usize }
862
            }
863 864
        }
    }
865
}
866

867 868 869 870 871 872
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 已提交
873 874
        Some(ref filter) => {
            filtered.into_iter().filter(|test| {
875
                test.desc.name.as_slice().contains(&filter[..])
A
Alex Crichton 已提交
876
            }).collect()
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900
        }
    };

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

901
    filtered
902
}
903

904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919
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()
}

920 921
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
922
                test: TestDescAndFn,
923
                monitor_ch: Sender<MonitorMsg>) {
924

925 926
    let TestDescAndFn {desc, testfn} = test;

927
    if force_ignore || desc.ignore {
928
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
929
        return;
930 931
    }

932
    fn run_test_inner(desc: TestDesc,
933
                      monitor_ch: Sender<MonitorMsg>,
934
                      nocapture: bool,
935
                      testfn: Thunk<'static>) {
936 937 938 939 940 941 942 943
        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 已提交
944
        thread::spawn(move || {
945 946 947
            let data = Arc::new(Mutex::new(Vec::new()));
            let data2 = data.clone();
            let cfg = thread::Builder::new().name(match desc.name {
R
Richo Healey 已提交
948 949
                DynTestName(ref name) => name.clone().to_string(),
                StaticTestName(name) => name.to_string(),
950
            });
951

952 953
            let result_guard = cfg.spawn(move || {
                if !nocapture {
954
                    io::set_print(box Sink(data2.clone()));
955 956
                    io::set_panic(box Sink(data2));
                }
957
                testfn()
958
            }).unwrap();
A
Aaron Turon 已提交
959
            let test_result = calc_result(&desc, result_guard.join());
960
            let stdout = data.lock().unwrap().to_vec();
961
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
962
        });
963 964 965
    }

    match testfn {
966
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
967
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
968
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
969 970 971
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
972
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
973
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
974 975
            return;
        }
976 977
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
N
Niko Matsakis 已提交
978
            f.call_box((&mut mm,));
979
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
980 981 982 983 984
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
985
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
986 987
            return;
        }
988 989
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
990
                                          Box::new(move|| f()))
991
    }
992 993
}

994
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
995 996 997 998
    match (&desc.should_panic, task_result) {
        (&ShouldPanic::No, Ok(())) |
        (&ShouldPanic::Yes(None), Err(_)) => TrOk,
        (&ShouldPanic::Yes(Some(msg)), Err(ref err))
999 1000 1001 1002 1003 1004
            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,
1005
    }
1006 1007
}

1008 1009
impl MetricMap {

1010
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
1011
        MetricMap(BTreeMap::new())
1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
    }

    /// 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
        };
1032
        let MetricMap(ref mut map) = *self;
1033
        map.insert(name.to_string(), m);
1034 1035
    }

1036 1037 1038 1039
    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 已提交
1040
                                 v.value, v.noise))
1041 1042
            .collect();
        v.connect(", ")
1043 1044 1045 1046 1047 1048
    }
}


// Benchmarking

H
Huon Wilson 已提交
1049
/// A function that is opaque to the optimizer, to allow benchmarks to
1050 1051 1052 1053
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1054
pub fn black_box<T>(dummy: T) -> T {
1055 1056 1057
    // we need to "use" the argument in some way LLVM can't
    // introspect.
    unsafe {asm!("" : : "r"(&dummy))}
1058
    dummy
1059 1060 1061
}


1062
impl Bencher {
1063
    /// Callback for benchmark functions to run in their body.
J
Jorge Aparicio 已提交
1064
    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1065 1066
        self.dur = Duration::span(|| {
            let k = self.iterations;
1067
            for _ in 0..k {
1068 1069 1070
                black_box(inner());
            }
        });
1071
    }
1072

1073
    pub fn ns_elapsed(&mut self) -> u64 {
1074
        self.dur.secs() * 1_000_000_000 + (self.dur.extra_nanos() as u64)
1075
    }
1076

1077 1078 1079 1080
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1081
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1082
        }
1083
    }
1084

J
Jorge Aparicio 已提交
1085
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1086 1087 1088
        self.iterations = n;
        f(self);
    }
1089

1090
    // This is a more statistics-driven benchmark algorithm
1091
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary where F: FnMut(&mut Bencher) {
1092
        // Initial bench run to get ballpark figure.
1093
        let mut n = 1;
1094
        self.bench_n(n, |x| f(x));
1095

1096 1097 1098 1099 1100
        // 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 已提交
1101
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1102
        }
1103 1104 1105 1106 1107 1108 1109
        // 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; }

1110
        let mut total_run = Duration::new(0, 0);
1111
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1112
        loop {
1113 1114
            let mut summ = None;
            let mut summ5 = None;
1115

1116
            let loop_run = Duration::span(|| {
1117

1118
                for p in &mut *samples {
1119 1120 1121
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1122

1123 1124
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1125

1126
                for p in &mut *samples {
1127 1128 1129
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1130

1131 1132 1133 1134 1135
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1136

1137
            // If we've run for 100ms and seem to have converged to a
1138
            // stable median.
1139
            if loop_run > Duration::from_millis(100) &&
1140 1141 1142
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1143 1144
            }

1145
            total_run = total_run + loop_run;
1146
            // Longest we ever run for is 3s.
1147
            if total_run > Duration::from_secs(3) {
1148
                return summ5;
1149
            }
1150

1151 1152 1153 1154 1155 1156 1157 1158
            // 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,
            };
1159 1160
        }
    }
1161 1162 1163
}

pub mod bench {
M
Michael Darakananda 已提交
1164
    use std::cmp;
1165
    use std::time::Duration;
1166
    use super::{Bencher, BenchSamples};
1167

J
Jorge Aparicio 已提交
1168
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1169
        let mut bs = Bencher {
1170
            iterations: 0,
1171
            dur: Duration::new(0, 0),
1172 1173 1174
            bytes: 0
        };

1175
        let ns_iter_summ = bs.auto_bench(f);
1176

M
Michael Darakananda 已提交
1177
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1178
        let iter_s = 1_000_000_000 / ns_iter;
1179 1180 1181
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1182
            ns_iter_summ: ns_iter_summ,
1183
            mb_s: mb_s as usize
1184 1185
        }
    }
1186 1187 1188 1189

    pub fn run_once<F>(f: F) where F: FnOnce(&mut Bencher) {
        let mut bs = Bencher {
            iterations: 0,
1190
            dur: Duration::new(0, 0),
1191 1192 1193 1194
            bytes: 0
        };
        bs.bench_n(1, f);
    }
1195 1196
}

1197 1198
#[cfg(test)]
mod tests {
1199
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1200
               TestDesc, TestDescAndFn, TestOpts, run_test,
J
Jorge Aparicio 已提交
1201
               MetricMap,
1202
               StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1203
    use std::thunk::Thunk;
1204
    use std::sync::mpsc::channel;
1205

1206
    #[test]
1207
    pub fn do_not_run_ignored_tests() {
S
Steve Klabnik 已提交
1208
        fn f() { panic!(); }
1209 1210
        let desc = TestDescAndFn {
            desc: TestDesc {
1211
                name: StaticTestName("whatever"),
1212
                ignore: true,
1213
                should_panic: ShouldPanic::No,
1214
            },
1215
            testfn: DynTestFn(Box::new(move|| f())),
1216
        };
1217
        let (tx, rx) = channel();
1218
        run_test(&TestOpts::new(), false, desc, tx);
1219
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1220
        assert!(res != TrOk);
1221 1222 1223
    }

    #[test]
1224
    pub fn ignored_tests_result_in_ignored() {
1225
        fn f() { }
1226 1227
        let desc = TestDescAndFn {
            desc: TestDesc {
1228
                name: StaticTestName("whatever"),
1229
                ignore: true,
1230
                should_panic: ShouldPanic::No,
1231
            },
1232
            testfn: DynTestFn(Box::new(move|| f())),
1233
        };
1234
        let (tx, rx) = channel();
1235
        run_test(&TestOpts::new(), false, desc, tx);
1236
        let (_, res, _) = rx.recv().unwrap();
1237
        assert!(res == TrIgnored);
1238 1239 1240
    }

    #[test]
1241
    fn test_should_panic() {
S
Steve Klabnik 已提交
1242
        fn f() { panic!(); }
1243 1244
        let desc = TestDescAndFn {
            desc: TestDesc {
1245
                name: StaticTestName("whatever"),
1246
                ignore: false,
1247
                should_panic: ShouldPanic::Yes(None)
1248
            },
1249
            testfn: DynTestFn(Box::new(move|| f())),
1250 1251 1252
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1253
        let (_, res, _) = rx.recv().unwrap();
1254 1255 1256 1257
        assert!(res == TrOk);
    }

    #[test]
1258
    fn test_should_panic_good_message() {
1259 1260 1261 1262 1263
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1264
                should_panic: ShouldPanic::Yes(Some("error message"))
1265
            },
1266
            testfn: DynTestFn(Box::new(move|| f())),
1267
        };
1268
        let (tx, rx) = channel();
1269
        run_test(&TestOpts::new(), false, desc, tx);
1270
        let (_, res, _) = rx.recv().unwrap();
1271
        assert!(res == TrOk);
1272 1273
    }

1274
    #[test]
1275
    fn test_should_panic_bad_message() {
1276 1277 1278 1279 1280
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1281
                should_panic: ShouldPanic::Yes(Some("foobar"))
1282
            },
1283
            testfn: DynTestFn(Box::new(move|| f())),
1284 1285 1286
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1287
        let (_, res, _) = rx.recv().unwrap();
1288 1289 1290
        assert!(res == TrFailed);
    }

1291
    #[test]
1292
    fn test_should_panic_but_succeeds() {
1293
        fn f() { }
1294 1295
        let desc = TestDescAndFn {
            desc: TestDesc {
1296
                name: StaticTestName("whatever"),
1297
                ignore: false,
1298
                should_panic: ShouldPanic::Yes(None)
1299
            },
1300
            testfn: DynTestFn(Box::new(move|| f())),
1301
        };
1302
        let (tx, rx) = channel();
1303
        run_test(&TestOpts::new(), false, desc, tx);
1304
        let (_, res, _) = rx.recv().unwrap();
1305
        assert!(res == TrFailed);
1306 1307 1308
    }

    #[test]
1309
    fn parse_ignored_flag() {
1310 1311 1312
        let args = vec!("progname".to_string(),
                        "filter".to_string(),
                        "--ignored".to_string());
1313
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1314
            Some(Ok(o)) => o,
S
Steve Klabnik 已提交
1315
            _ => panic!("Malformed arg in parse_ignored_flag")
B
Brian Anderson 已提交
1316
        };
P
Patrick Walton 已提交
1317
        assert!((opts.run_ignored));
1318 1319 1320
    }

    #[test]
1321
    pub fn filter_for_ignored_option() {
1322 1323 1324
        // 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

1325 1326 1327
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1328

1329
        let tests = vec!(
1330 1331
            TestDescAndFn {
                desc: TestDesc {
1332
                    name: StaticTestName("1"),
1333
                    ignore: true,
1334
                    should_panic: ShouldPanic::No,
1335
                },
1336
                testfn: DynTestFn(Box::new(move|| {})),
1337
            },
1338 1339
            TestDescAndFn {
                desc: TestDesc {
1340
                    name: StaticTestName("2"),
1341
                    ignore: false,
1342
                    should_panic: ShouldPanic::No,
1343
                },
1344
                testfn: DynTestFn(Box::new(move|| {})),
1345
            });
B
Brian Anderson 已提交
1346
        let filtered = filter_tests(&opts, tests);
1347

1348
        assert_eq!(filtered.len(), 1);
1349
        assert_eq!(filtered[0].desc.name.to_string(),
1350
                   "1");
1351
        assert!(filtered[0].desc.ignore == false);
1352 1353 1354
    }

    #[test]
1355
    pub fn sort_tests() {
1356 1357
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1358 1359

        let names =
1360
            vec!("sha1::test".to_string(),
1361 1362
                 "isize::test_to_str".to_string(),
                 "isize::test_pow".to_string(),
1363 1364 1365 1366 1367 1368
                 "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());
1369 1370
        let tests =
        {
1371
            fn testfn() { }
1372
            let mut tests = Vec::new();
1373
            for name in &names {
1374 1375
                let test = TestDescAndFn {
                    desc: TestDesc {
1376
                        name: DynTestName((*name).clone()),
1377
                        ignore: false,
1378
                        should_panic: ShouldPanic::No,
1379
                    },
1380
                    testfn: DynTestFn(Box::new(testfn)),
1381
                };
L
Luqman Aden 已提交
1382
                tests.push(test);
1383
            }
L
Luqman Aden 已提交
1384
            tests
1385
        };
B
Brian Anderson 已提交
1386
        let filtered = filter_tests(&opts, tests);
1387

1388
        let expected =
1389 1390
            vec!("isize::test_pow".to_string(),
                 "isize::test_to_str".to_string(),
1391 1392 1393 1394 1395 1396 1397
                 "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());
1398

1399
        for (a, b) in expected.iter().zip(filtered.iter()) {
1400
            assert!(*a == b.desc.name.to_string());
1401 1402
        }
    }
1403 1404 1405 1406 1407 1408 1409 1410 1411 1412 1413 1414 1415 1416 1417 1418 1419 1420 1421 1422 1423 1424 1425

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