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

A
Alex Crichton 已提交
26
#![crate_name = "test"]
27
#![unstable(feature = "test", issue = "27812")]
28 29
#![crate_type = "rlib"]
#![crate_type = "dylib"]
30
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
A
Alex Crichton 已提交
31
       html_favicon_url = "https://doc.rust-lang.org/favicon.ico",
K
Kevin Butler 已提交
32 33
       html_root_url = "https://doc.rust-lang.org/nightly/",
       test(attr(deny(warnings))))]
34
#![cfg_attr(not(stage0), deny(warnings))]
A
Alex Crichton 已提交
35

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

A
Alex Crichton 已提交
46 47
extern crate getopts;
extern crate term;
48
extern crate libc;
49
extern crate panic_unwind;
50

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

59
use std::boxed::FnBox;
60

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

76 77
const TEST_WARN_TIMEOUT_S: u64 = 60;

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

86 87
pub mod stats;

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

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

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

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

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

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

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

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

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

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

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

205 206 207 208 209 210
#[derive(Clone)]
pub struct TestPaths {
    pub file: PathBuf,         // e.g., compile-test/foo/bar/baz.rs
    pub base: PathBuf,         // e.g., compile-test, auxiliary
    pub relative_dir: PathBuf, // e.g., foo/bar
}
211

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

218
#[derive(Clone, PartialEq, Debug, Copy)]
219
pub struct Metric {
220
    value: f64,
N
Nick Cameron 已提交
221
    noise: f64,
222 223
}

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

233
#[derive(PartialEq)]
N
Nick Cameron 已提交
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.
N
Nick Cameron 已提交
245 246 247 248 249 250
pub fn test_main(args: &[String], tests: Vec<TestDescAndFn>) {
    let opts = match parse_opts(args) {
        Some(Ok(o)) => o,
        Some(Err(msg)) => panic!("{:?}", msg),
        None => return,
    };
A
Alex Crichton 已提交
251 252
    match run_tests_console(&opts, tests) {
        Ok(true) => {}
253
        Ok(false) => std::process::exit(101),
254
        Err(e) => panic!("io error when running tests: {:?}", e),
A
Alex Crichton 已提交
255
    }
256 257
}

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

289
#[derive(Copy, Clone)]
290 291 292 293 294 295
pub enum ColorConfig {
    AutoColor,
    AlwaysColor,
    NeverColor,
}

296
pub struct TestOpts {
A
Alex Crichton 已提交
297
    pub filter: Option<String>,
298 299
    pub run_ignored: bool,
    pub run_tests: bool,
300
    pub bench_benchmarks: bool,
A
Alex Crichton 已提交
301
    pub logfile: Option<PathBuf>,
302
    pub nocapture: bool,
303
    pub color: ColorConfig,
304
    pub quiet: bool,
305
    pub test_threads: Option<usize>,
306
    pub skip: Vec<String>,
307 308 309 310 311 312 313 314 315
}

impl TestOpts {
    #[cfg(test)]
    fn new() -> TestOpts {
        TestOpts {
            filter: None,
            run_ignored: false,
            run_tests: false,
316
            bench_benchmarks: false,
317 318
            logfile: None,
            nocapture: false,
319
            color: AutoColor,
320
            quiet: false,
321
            test_threads: None,
322
            skip: vec![],
323 324
        }
    }
325
}
326

327
/// Result of parsing the options.
328
pub type OptRes = Result<TestOpts, String>;
329

N
Nick Cameron 已提交
330
#[cfg_attr(rustfmt, rustfmt_skip)]
331
fn optgroups() -> Vec<getopts::OptGroup> {
N
Nick Cameron 已提交
332 333 334 335 336 337 338 339
    vec!(getopts::optflag("", "ignored", "Run ignored tests"),
      getopts::optflag("", "test", "Run tests and not benchmarks"),
      getopts::optflag("", "bench", "Run benchmarks instead of tests"),
      getopts::optflag("h", "help", "Display this message (longer with --help)"),
      getopts::optopt("", "logfile", "Write logs to the specified file instead \
                          of stdout", "PATH"),
      getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \
                                         task, allow printing directly"),
340 341
      getopts::optopt("", "test-threads", "Number of threads used for running tests \
                                           in parallel", "n_threads"),
342 343
      getopts::optmulti("", "skip", "Skip tests whose names contain FILTER (this flag can \
                                     be used multiple times)","FILTER"),
344
      getopts::optflag("q", "quiet", "Display one character per test instead of one line"),
N
Nick Cameron 已提交
345 346 347 348
      getopts::optopt("", "color", "Configure coloring of output:
            auto   = colorize if stdout is a tty and tests are run on serially (default);
            always = always colorize output;
            never  = never colorize output;", "auto|always|never"))
349 350
}

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

355 356
The FILTER string is tested against the name of all tests, and only those
tests whose names contain the filter are run.
357 358

By default, all tests are run in parallel. This can be altered with the
359 360
--test-threads flag or the RUST_TEST_THREADS environment variable when running
tests (set it to 1).
361

362
All tests have their standard output and standard error captured by default.
363 364
This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
environment variable to a value other than "0". Logging is not captured by default.
365

366 367
Test Attributes:

A
Alex Crichton 已提交
368
    #[test]        - Indicates a function is a test to be run. This function
369
                     takes no arguments.
A
Alex Crichton 已提交
370
    #[bench]       - Indicates a function is a benchmark to be run. This
371
                     function takes one argument (test::Bencher).
372 373
    #[should_panic] - This function (also labeled with #[test]) will only pass if
                     the code causes a panic (an assertion failure or panic!)
374
                     A message may be provided, which the failure string must
375
                     contain: #[should_panic(expected = "foo")].
A
Alex Crichton 已提交
376
    #[ignore]      - When applied to a function which is already attributed as a
377 378
                     test, then the test runner will ignore these tests during
                     normal test runs. Running with --ignored will run these
379
                     tests."#,
380
             usage = getopts::usage(&message, &optgroups()));
381 382
}

383
// Parses command line arguments into test options
384
pub fn parse_opts(args: &[String]) -> Option<OptRes> {
S
Simonas Kazlauskas 已提交
385
    let args_ = &args[1..];
N
Nick Cameron 已提交
386 387 388 389
    let matches = match getopts::getopts(args_, &optgroups()) {
        Ok(m) => m,
        Err(f) => return Some(Err(f.to_string())),
    };
M
Marijn Haverbeke 已提交
390

N
Nick Cameron 已提交
391 392 393 394
    if matches.opt_present("h") {
        usage(&args[0]);
        return None;
    }
395

396
    let filter = if !matches.free.is_empty() {
A
Alex Crichton 已提交
397
        Some(matches.free[0].clone())
398 399 400
    } else {
        None
    };
401

402
    let run_ignored = matches.opt_present("ignored");
403
    let quiet = matches.opt_present("quiet");
404

405
    let logfile = matches.opt_str("logfile");
A
Aaron Turon 已提交
406
    let logfile = logfile.map(|s| PathBuf::from(&s));
407

408
    let bench_benchmarks = matches.opt_present("bench");
N
Nick Cameron 已提交
409
    let run_tests = !bench_benchmarks || matches.opt_present("test");
410

411 412
    let mut nocapture = matches.opt_present("nocapture");
    if !nocapture {
413 414 415 416
        nocapture = match env::var("RUST_TEST_NOCAPTURE") {
            Ok(val) => &val != "0",
            Err(_) => false
        };
417 418
    }

419 420 421 422 423 424 425 426 427 428 429 430
    let test_threads = match matches.opt_str("test-threads") {
        Some(n_str) =>
            match n_str.parse::<usize>() {
                Ok(n) => Some(n),
                Err(e) =>
                    return Some(Err(format!("argument for --test-threads must be a number > 0 \
                                             (error: {})", e)))
            },
        None =>
            None,
    };

431
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
432 433 434 435
        Some("auto") | None => AutoColor,
        Some("always") => AlwaysColor,
        Some("never") => NeverColor,

N
Nick Cameron 已提交
436 437 438 439 440
        Some(v) => {
            return Some(Err(format!("argument for --color must be auto, always, or never (was \
                                     {})",
                                    v)))
        }
441 442
    };

443 444 445
    let test_opts = TestOpts {
        filter: filter,
        run_ignored: run_ignored,
446
        run_tests: run_tests,
447
        bench_benchmarks: bench_benchmarks,
448 449
        logfile: logfile,
        nocapture: nocapture,
450
        color: color,
451
        quiet: quiet,
452
        test_threads: test_threads,
453
        skip: matches.opt_strs("skip"),
454
    };
455

B
Brian Anderson 已提交
456
    Some(Ok(test_opts))
457 458
}

459
#[derive(Clone, PartialEq)]
460
pub struct BenchSamples {
461
    ns_iter_summ: stats::Summary,
462
    mb_s: usize,
463 464
}

465
#[derive(Clone, PartialEq)]
466 467 468 469 470
pub enum TestResult {
    TrOk,
    TrFailed,
    TrIgnored,
    TrMetrics(MetricMap),
471
    TrBench(BenchSamples),
472
}
473

474 475
unsafe impl Send for TestResult {}

A
Alex Crichton 已提交
476
enum OutputLocation<T> {
477
    Pretty(Box<term::StdoutTerminal>),
A
Alex Crichton 已提交
478 479 480
    Raw(T),
}

L
Luqman Aden 已提交
481 482
struct ConsoleTestState<T> {
    log_out: Option<File>,
A
Alex Crichton 已提交
483
    out: OutputLocation<T>,
484
    use_color: bool,
485
    quiet: bool,
486 487 488 489 490
    total: usize,
    passed: usize,
    failed: usize,
    ignored: usize,
    measured: usize,
491
    metrics: MetricMap,
N
Nick Cameron 已提交
492
    failures: Vec<(TestDesc, Vec<u8>)>,
493
    max_name_len: usize, // number of columns to fill when aligning names
494
}
495

496
impl<T: Write> ConsoleTestState<T> {
N
Nick Cameron 已提交
497
    pub fn new(opts: &TestOpts, _: Option<T>) -> io::Result<ConsoleTestState<io::Stdout>> {
498
        let log_out = match opts.logfile {
J
Jorge Aparicio 已提交
499
            Some(ref path) => Some(File::create(path)?),
N
Nick Cameron 已提交
500
            None => None,
501
        };
C
Corey Richardson 已提交
502
        let out = match term::stdout() {
503
            None => Raw(io::stdout()),
N
Nick Cameron 已提交
504
            Some(t) => Pretty(t),
505
        };
C
Corey Richardson 已提交
506

A
Alex Crichton 已提交
507
        Ok(ConsoleTestState {
508 509
            out: out,
            log_out: log_out,
510
            use_color: use_color(opts),
511
            quiet: opts.quiet,
A
Alfie John 已提交
512 513 514 515 516
            total: 0,
            passed: 0,
            failed: 0,
            ignored: 0,
            measured: 0,
517
            metrics: MetricMap::new(),
518
            failures: Vec::new(),
A
Alfie John 已提交
519
            max_name_len: 0,
A
Alex Crichton 已提交
520
        })
521 522
    }

523
    pub fn write_ok(&mut self) -> io::Result<()> {
524
        self.write_short_result("ok", ".", term::color::GREEN)
525
    }
526

527
    pub fn write_failed(&mut self) -> io::Result<()> {
528
        self.write_short_result("FAILED", "F", term::color::RED)
529 530
    }

531
    pub fn write_ignored(&mut self) -> io::Result<()> {
532
        self.write_short_result("ignored", "i", term::color::YELLOW)
533
    }
534

535
    pub fn write_metric(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
536
        self.write_pretty("metric", term::color::CYAN)
537 538
    }

539
    pub fn write_bench(&mut self) -> io::Result<()> {
A
Alex Crichton 已提交
540
        self.write_pretty("bench", term::color::CYAN)
541
    }
542

543 544 545 546 547
    pub fn write_short_result(&mut self, verbose: &str, quiet: &str, color: term::color::Color)
                              -> io::Result<()> {
        if self.quiet {
            self.write_pretty(quiet, color)
        } else {
J
Jorge Aparicio 已提交
548
            self.write_pretty(verbose, color)?;
549 550 551 552
            self.write_plain("\n")
        }
    }

N
Nick Cameron 已提交
553
    pub fn write_pretty(&mut self, word: &str, color: term::color::Color) -> io::Result<()> {
L
Luqman Aden 已提交
554
        match self.out {
A
Alex Crichton 已提交
555
            Pretty(ref mut term) => {
556
                if self.use_color {
J
Jorge Aparicio 已提交
557
                    term.fg(color)?;
558
                }
J
Jorge Aparicio 已提交
559
                term.write_all(word.as_bytes())?;
560
                if self.use_color {
J
Jorge Aparicio 已提交
561
                    term.reset()?;
562
                }
563 564 565
                term.flush()
            }
            Raw(ref mut stdout) => {
J
Jorge Aparicio 已提交
566
                stdout.write_all(word.as_bytes())?;
567
                stdout.flush()
568
            }
L
Luqman Aden 已提交
569 570 571
        }
    }

572
    pub fn write_plain(&mut self, s: &str) -> io::Result<()> {
L
Luqman Aden 已提交
573
        match self.out {
574
            Pretty(ref mut term) => {
J
Jorge Aparicio 已提交
575
                term.write_all(s.as_bytes())?;
576
                term.flush()
N
Nick Cameron 已提交
577
            }
578
            Raw(ref mut stdout) => {
J
Jorge Aparicio 已提交
579
                stdout.write_all(s.as_bytes())?;
580
                stdout.flush()
N
Nick Cameron 已提交
581
            }
582 583 584
        }
    }

585
    pub fn write_run_start(&mut self, len: usize) -> io::Result<()> {
586
        self.total = len;
N
Nick Cameron 已提交
587 588 589 590 591
        let noun = if len != 1 {
            "tests"
        } else {
            "test"
        };
592
        self.write_plain(&format!("\nrunning {} {}\n", len, noun))
593 594
    }

N
Nick Cameron 已提交
595
    pub fn write_test_start(&mut self, test: &TestDesc, align: NamePadding) -> io::Result<()> {
596 597 598 599 600 601
        if self.quiet && align != PadOnRight {
            Ok(())
        } else {
            let name = test.padded_name(self.max_name_len, align);
            self.write_plain(&format!("test {} ... ", name))
        }
M
Marijn Haverbeke 已提交
602
    }
603

604
    pub fn write_result(&mut self, result: &TestResult) -> io::Result<()> {
605
        match *result {
606 607 608
            TrOk => self.write_ok(),
            TrFailed => self.write_failed(),
            TrIgnored => self.write_ignored(),
609
            TrMetrics(ref mm) => {
J
Jorge Aparicio 已提交
610
                self.write_metric()?;
611
                self.write_plain(&format!(": {}\n", mm.fmt_metrics()))
612
            }
613
            TrBench(ref bs) => {
J
Jorge Aparicio 已提交
614
                self.write_bench()?;
615
                self.write_plain(&format!(": {}\n", fmt_bench_samples(bs)))
616
            }
617
        }
618
    }
619

620
    pub fn write_timeout(&mut self, desc: &TestDesc) -> io::Result<()> {
621 622 623
        self.write_plain(&format!("test {} has been running for over {} seconds\n",
                                  desc.name,
                                  TEST_WARN_TIMEOUT_S))
624 625
    }

N
Nick Cameron 已提交
626
    pub fn write_log(&mut self, test: &TestDesc, result: &TestResult) -> io::Result<()> {
627
        match self.log_out {
A
Alex Crichton 已提交
628
            None => Ok(()),
L
Luqman Aden 已提交
629
            Some(ref mut o) => {
N
Nick Cameron 已提交
630 631 632 633 634 635 636 637 638
                let s = format!("{} {}\n",
                                match *result {
                                    TrOk => "ok".to_owned(),
                                    TrFailed => "failed".to_owned(),
                                    TrIgnored => "ignored".to_owned(),
                                    TrMetrics(ref mm) => mm.fmt_metrics(),
                                    TrBench(ref bs) => fmt_bench_samples(bs),
                                },
                                test.name);
639
                o.write_all(s.as_bytes())
640 641
            }
        }
B
Brian Anderson 已提交
642 643
    }

644
    pub fn write_failures(&mut self) -> io::Result<()> {
J
Jorge Aparicio 已提交
645
        self.write_plain("\nfailures:\n")?;
646
        let mut failures = Vec::new();
647
        let mut fail_out = String::new();
648
        for &(ref f, ref stdout) in &self.failures {
649
            failures.push(f.name.to_string());
650
            if !stdout.is_empty() {
651 652 653
                fail_out.push_str(&format!("---- {} stdout ----\n\t", f.name));
                let output = String::from_utf8_lossy(stdout);
                fail_out.push_str(&output);
654 655 656
                fail_out.push_str("\n");
            }
        }
657
        if !fail_out.is_empty() {
J
Jorge Aparicio 已提交
658 659
            self.write_plain("\n")?;
            self.write_plain(&fail_out)?;
660
        }
661

J
Jorge Aparicio 已提交
662
        self.write_plain("\nfailures:\n")?;
663
        failures.sort();
664
        for name in &failures {
J
Jorge Aparicio 已提交
665
            self.write_plain(&format!("    {}\n", name))?;
666
        }
A
Alex Crichton 已提交
667
        Ok(())
668 669
    }

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

A
Alfie John 已提交
673
        let success = self.failed == 0;
A
Ahmed Charles 已提交
674
        if !success {
J
Jorge Aparicio 已提交
675
            self.write_failures()?;
676
        }
677

J
Jorge Aparicio 已提交
678
        self.write_plain("\ntest result: ")?;
679 680
        if success {
            // There's no parallelism at this point so it's safe to use color
J
Jorge Aparicio 已提交
681
            self.write_pretty("ok", term::color::GREEN)?;
682
        } else {
J
Jorge Aparicio 已提交
683
            self.write_pretty("FAILED", term::color::RED)?;
684
        }
L
Luqman Aden 已提交
685
        let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
N
Nick Cameron 已提交
686 687 688 689
                        self.passed,
                        self.failed,
                        self.ignored,
                        self.measured);
J
Jorge Aparicio 已提交
690
        self.write_plain(&s)?;
A
Alex Crichton 已提交
691
        return Ok(success);
692
    }
693 694
}

695 696 697 698
// 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();
699
    let mut trailing = false;
700 701
    for &pow in &[9, 6, 3, 0] {
        let base = 10_usize.pow(pow);
702 703
        if pow == 0 || trailing || n / base != 0 {
            if !trailing {
704 705 706 707 708 709 710
                output.write_fmt(format_args!("{}", n / base)).unwrap();
            } else {
                output.write_fmt(format_args!("{:03}", n / base)).unwrap();
            }
            if pow != 0 {
                output.push(sep);
            }
711
            trailing = true;
712 713 714 715 716 717 718
        }
        n %= base;
    }

    output
}

719
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
720 721 722 723 724 725 726
    use std::fmt::Write;
    let mut output = String::new();

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

    output.write_fmt(format_args!("{:>11} ns/iter (+/- {})",
N
Nick Cameron 已提交
727 728 729
                                  fmt_thousands_sep(median, ','),
                                  fmt_thousands_sep(deviation, ',')))
          .unwrap();
730
    if bs.mb_s != 0 {
731
        output.write_fmt(format_args!(" = {} MB/s", bs.mb_s)).unwrap();
732
    }
733
    output
734 735 736
}

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

N
Nick Cameron 已提交
739
    fn callback<T: Write>(event: &TestEvent, st: &mut ConsoleTestState<T>) -> io::Result<()> {
740
        match (*event).clone() {
741
            TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
742
            TeWait(ref test, padding) => st.write_test_start(test, padding),
743
            TeTimeout(ref test) => st.write_timeout(test),
744
            TeResult(test, result, stdout) => {
J
Jorge Aparicio 已提交
745 746
                st.write_log(&test, &result)?;
                st.write_result(&result)?;
747 748 749
                match result {
                    TrOk => st.passed += 1,
                    TrIgnored => st.ignored += 1,
750
                    TrMetrics(mm) => {
751
                        let tname = test.name;
752
                        let MetricMap(mm) = mm;
N
Nick Cameron 已提交
753
                        for (k, v) in &mm {
754
                            st.metrics
N
Nick Cameron 已提交
755
                              .insert_metric(&format!("{}.{}", tname, k), v.value, v.noise);
756 757 758
                        }
                        st.measured += 1
                    }
759
                    TrBench(bs) => {
760
                        st.metrics.insert_metric(test.name.as_slice(),
761 762
                                                 bs.ns_iter_summ.median,
                                                 bs.ns_iter_summ.max - bs.ns_iter_summ.min);
763
                        st.measured += 1
764
                    }
765 766
                    TrFailed => {
                        st.failed += 1;
767
                        st.failures.push((test, stdout));
768 769
                    }
                }
A
Alex Crichton 已提交
770
                Ok(())
771 772
            }
        }
773
    }
774

J
Jorge Aparicio 已提交
775
    let mut st = ConsoleTestState::new(opts, None::<io::Stdout>)?;
776
    fn len_if_padded(t: &TestDescAndFn) -> usize {
777
        match t.testfn.padding() {
A
Alfie John 已提交
778
            PadNone => 0,
E
Erick Tryzelaar 已提交
779
            PadOnRight => t.desc.name.as_slice().len(),
780 781
        }
    }
782 783 784
    if let Some(t) = tests.iter().max_by_key(|t| len_if_padded(*t)) {
        let n = t.desc.name.as_slice();
        st.max_name_len = n.len();
785
    }
J
Jorge Aparicio 已提交
786
    run_tests(opts, tests, |x| callback(&x, &mut st))?;
A
Ahmed Charles 已提交
787
    return st.write_run_finish();
788 789 790 791
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
792 793 794
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
N
Nick Cameron 已提交
795
        should_panic: ShouldPanic::No,
A
Alex Crichton 已提交
796
    };
797

A
Alex Crichton 已提交
798 799 800
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
N
Nick Cameron 已提交
801
        should_panic: ShouldPanic::No,
A
Alex Crichton 已提交
802
    };
803

L
Luqman Aden 已提交
804
    let mut st = ConsoleTestState {
A
Alex Crichton 已提交
805
        log_out: None,
D
Daniel Micay 已提交
806
        out: Raw(Vec::new()),
A
Alex Crichton 已提交
807
        use_color: false,
808
        quiet: false,
A
Alfie John 已提交
809 810 811 812 813 814
        total: 0,
        passed: 0,
        failed: 0,
        ignored: 0,
        measured: 0,
        max_name_len: 10,
A
Alex Crichton 已提交
815
        metrics: MetricMap::new(),
N
Nick Cameron 已提交
816
        failures: vec![(test_b, Vec::new()), (test_a, Vec::new())],
817
    };
818

819
    st.write_failures().unwrap();
L
Luqman Aden 已提交
820
    let s = match st.out {
821
        Raw(ref m) => String::from_utf8_lossy(&m[..]),
N
Nick Cameron 已提交
822
        Pretty(_) => unreachable!(),
L
Luqman Aden 已提交
823
    };
A
Alex Crichton 已提交
824

825 826
    let apos = s.find("a").unwrap();
    let bpos = s.find("b").unwrap();
P
Patrick Walton 已提交
827
    assert!(apos < bpos);
828 829
}

830 831
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
832
        AutoColor => !opts.nocapture && stdout_isatty(),
833 834 835
        AlwaysColor => true,
        NeverColor => false,
    }
836
}
837

838 839 840 841 842 843
#[cfg(unix)]
fn stdout_isatty() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
fn stdout_isatty() -> bool {
A
Alex Crichton 已提交
844 845 846 847 848
    type DWORD = u32;
    type BOOL = i32;
    type HANDLE = *mut u8;
    type LPDWORD = *mut u32;
    const STD_OUTPUT_HANDLE: DWORD = -11i32 as DWORD;
849
    extern "system" {
A
Alex Crichton 已提交
850 851
        fn GetStdHandle(which: DWORD) -> HANDLE;
        fn GetConsoleMode(hConsoleHandle: HANDLE, lpMode: LPDWORD) -> BOOL;
852 853 854 855 856 857 858 859
    }
    unsafe {
        let handle = GetStdHandle(STD_OUTPUT_HANDLE);
        let mut out = 0;
        GetConsoleMode(handle, &mut out) != 0
    }
}

860
#[derive(Clone)]
B
Brian Anderson 已提交
861
enum TestEvent {
N
Nick Cameron 已提交
862
    TeFiltered(Vec<TestDesc>),
863
    TeWait(TestDesc, NamePadding),
N
Nick Cameron 已提交
864
    TeResult(TestDesc, TestResult, Vec<u8>),
865
    TeTimeout(TestDesc),
B
Brian Anderson 已提交
866 867
}

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

F
Flavio Percoco 已提交
870

N
Nick Cameron 已提交
871 872
fn run_tests<F>(opts: &TestOpts, tests: Vec<TestDescAndFn>, mut callback: F) -> io::Result<()>
    where F: FnMut(TestEvent) -> io::Result<()>
J
Jorge Aparicio 已提交
873
{
874 875 876
    use std::collections::HashMap;
    use std::sync::mpsc::RecvTimeoutError;

877 878 879 880 881
    let mut filtered_tests = filter_tests(opts, tests);
    if !opts.bench_benchmarks {
        filtered_tests = convert_benchmarks_to_tests(filtered_tests);
    }

882 883 884
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
885

J
Jorge Aparicio 已提交
886
    callback(TeFiltered(filtered_descs))?;
B
Brian Anderson 已提交
887

A
Aaron Turon 已提交
888 889
    let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
        filtered_tests.into_iter().partition(|e| {
890 891
            match e.testfn {
                StaticTestFn(_) | DynTestFn(_) => true,
N
Nick Cameron 已提交
892
                _ => false,
893 894
            }
        });
895

896 897 898 899
    let concurrency = match opts.test_threads {
        Some(n) => n,
        None => get_concurrency(),
    };
900

901
    let mut remaining = filtered_tests;
902
    remaining.reverse();
903
    let mut pending = 0;
B
Brian Anderson 已提交
904

905
    let (tx, rx) = channel::<MonitorMsg>();
906

907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
    let mut running_tests: HashMap<TestDesc, Instant> = HashMap::new();

    fn get_timed_out_tests(running_tests: &mut HashMap<TestDesc, Instant>) -> Vec<TestDesc> {
        let now = Instant::now();
        let timed_out = running_tests.iter()
            .filter_map(|(desc, timeout)| if &now >= timeout { Some(desc.clone())} else { None })
            .collect();
        for test in &timed_out {
            running_tests.remove(test);
        }
        timed_out
    };

    fn calc_timeout(running_tests: &HashMap<TestDesc, Instant>) -> Option<Duration> {
        running_tests.values().min().map(|next_timeout| {
            let now = Instant::now();
            if *next_timeout >= now {
                *next_timeout - now
            } else {
                Duration::new(0, 0)
            }})
    };
929

930 931
    while pending > 0 || !remaining.is_empty() {
        while pending < concurrency && !remaining.is_empty() {
932
            let test = remaining.pop().unwrap();
933
            if concurrency == 1 {
934 935 936
                // 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.
J
Jorge Aparicio 已提交
937
                callback(TeWait(test.desc.clone(), test.testfn.padding()))?;
938
            }
939 940
            let timeout = Instant::now() + Duration::from_secs(TEST_WARN_TIMEOUT_S);
            running_tests.insert(test.desc.clone(), timeout);
941
            run_test(opts, !opts.run_tests, test, tx.clone());
942
            pending += 1;
B
Brian Anderson 已提交
943 944
        }

945
        let mut res;
946 947 948 949 950
        loop {
            if let Some(timeout) = calc_timeout(&running_tests) {
                res = rx.recv_timeout(timeout);
                for test in get_timed_out_tests(&mut running_tests) {
                    callback(TeTimeout(test))?;
951 952 953 954
                }
                if res != Err(RecvTimeoutError::Timeout) {
                    break;
                }
955 956 957
            } else {
                res = rx.recv().map_err(|_| RecvTimeoutError::Disconnected);
                break;
958 959 960 961 962 963
            }
        }

        let (desc, result, stdout) = res.unwrap();
        running_tests.remove(&desc);

964
        if concurrency != 1 {
J
Jorge Aparicio 已提交
965
            callback(TeWait(desc.clone(), PadNone))?;
966
        }
J
Jorge Aparicio 已提交
967
        callback(TeResult(desc, result, stdout))?;
968
        pending -= 1;
B
Brian Anderson 已提交
969
    }
970

971 972 973 974
    if opts.bench_benchmarks {
        // All benchmarks run at the end, in serial.
        // (this includes metric fns)
        for b in filtered_benchs_and_metrics {
J
Jorge Aparicio 已提交
975
            callback(TeWait(b.desc.clone(), b.testfn.padding()))?;
976 977
            run_test(opts, false, b, tx.clone());
            let (test, result, stdout) = rx.recv().unwrap();
J
Jorge Aparicio 已提交
978
            callback(TeResult(test, result, stdout))?;
979
        }
980
    }
A
Alex Crichton 已提交
981
    Ok(())
B
Brian Anderson 已提交
982 983
}

984
#[allow(deprecated)]
985
fn get_concurrency() -> usize {
A
Alex Crichton 已提交
986
    return match env::var("RUST_TEST_THREADS") {
A
Alex Crichton 已提交
987
        Ok(s) => {
988
            let opt_n: Option<usize> = s.parse().ok();
989 990
            match opt_n {
                Some(n) if n > 0 => n,
N
Nick Cameron 已提交
991 992 993 994
                _ => {
                    panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.",
                           s)
                }
995 996
            }
        }
997
        Err(..) => num_cpus(),
A
Alex Crichton 已提交
998 999 1000
    };

    #[cfg(windows)]
A
Alex Crichton 已提交
1001
    #[allow(bad_style)]
A
Alex Crichton 已提交
1002
    fn num_cpus() -> usize {
A
Alex Crichton 已提交
1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019
        #[repr(C)]
        struct SYSTEM_INFO {
            wProcessorArchitecture: u16,
            wReserved: u16,
            dwPageSize: u32,
            lpMinimumApplicationAddress: *mut u8,
            lpMaximumApplicationAddress: *mut u8,
            dwActiveProcessorMask: *mut u8,
            dwNumberOfProcessors: u32,
            dwProcessorType: u32,
            dwAllocationGranularity: u32,
            wProcessorLevel: u16,
            wProcessorRevision: u16,
        }
        extern "system" {
            fn GetSystemInfo(info: *mut SYSTEM_INFO) -> i32;
        }
A
Alex Crichton 已提交
1020 1021
        unsafe {
            let mut sysinfo = std::mem::zeroed();
A
Alex Crichton 已提交
1022
            GetSystemInfo(&mut sysinfo);
A
Alex Crichton 已提交
1023 1024 1025 1026
            sysinfo.dwNumberOfProcessors as usize
        }
    }

1027 1028 1029
    #[cfg(any(target_os = "linux",
              target_os = "macos",
              target_os = "ios",
N
Nikita Baksalyar 已提交
1030
              target_os = "android",
1031 1032
              target_os = "solaris",
              target_os = "emscripten"))]
A
Alex Crichton 已提交
1033
    fn num_cpus() -> usize {
N
Nick Cameron 已提交
1034
        unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as usize }
1035 1036 1037 1038 1039 1040 1041
    }

    #[cfg(any(target_os = "freebsd",
              target_os = "dragonfly",
              target_os = "bitrig",
              target_os = "netbsd"))]
    fn num_cpus() -> usize {
1042 1043
        use std::ptr;

1044
        let mut cpus: libc::c_uint = 0;
1045
        let mut cpus_size = std::mem::size_of_val(&cpus);
1046 1047

        unsafe {
D
Dave Huseby 已提交
1048
            cpus = libc::sysconf(libc::_SC_NPROCESSORS_ONLN) as libc::c_uint;
1049 1050
        }
        if cpus < 1 {
D
Dave Huseby 已提交
1051
            let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];
1052
            unsafe {
N
Nick Cameron 已提交
1053 1054
                libc::sysctl(mib.as_mut_ptr(),
                             2,
1055
                             &mut cpus as *mut _ as *mut _,
1056
                             &mut cpus_size as *mut _ as *mut _,
1057
                             ptr::null_mut(),
N
Nick Cameron 已提交
1058
                             0);
1059 1060 1061 1062 1063 1064
            }
            if cpus < 1 {
                cpus = 1;
            }
        }
        cpus as usize
1065
    }
1066 1067 1068

    #[cfg(target_os = "openbsd")]
    fn num_cpus() -> usize {
1069 1070
        use std::ptr;

1071 1072 1073 1074 1075
        let mut cpus: libc::c_uint = 0;
        let mut cpus_size = std::mem::size_of_val(&cpus);
        let mut mib = [libc::CTL_HW, libc::HW_NCPU, 0, 0];

        unsafe {
N
Nick Cameron 已提交
1076 1077
            libc::sysctl(mib.as_mut_ptr(),
                         2,
1078 1079
                         &mut cpus as *mut _ as *mut _,
                         &mut cpus_size as *mut _ as *mut _,
1080
                         ptr::null_mut(),
N
Nick Cameron 已提交
1081
                         0);
1082 1083 1084 1085 1086 1087
        }
        if cpus < 1 {
            cpus = 1;
        }
        cpus as usize
    }
1088 1089 1090

    #[cfg(target_os = "haiku")]
    fn num_cpus() -> usize {
1091
        // FIXME: implement
1092 1093
        1
    }
1094
}
1095

1096 1097 1098 1099 1100 1101
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 已提交
1102
        Some(ref filter) => {
N
Nick Cameron 已提交
1103 1104 1105
            filtered.into_iter()
                    .filter(|test| test.desc.name.as_slice().contains(&filter[..]))
                    .collect()
1106 1107 1108
        }
    };

1109 1110 1111 1112 1113
    // Skip tests that match any of the skip filters
    filtered = filtered.into_iter()
        .filter(|t| !opts.skip.iter().any(|sf| t.desc.name.as_slice().contains(&sf[..])))
        .collect();

1114 1115 1116 1117 1118 1119 1120 1121
    // Maybe pull out the ignored test and unignore them
    filtered = if !opts.run_ignored {
        filtered
    } else {
        fn filter(test: TestDescAndFn) -> Option<TestDescAndFn> {
            if test.desc.ignore {
                let TestDescAndFn {desc, testfn} = test;
                Some(TestDescAndFn {
N
Nick Cameron 已提交
1122 1123
                    desc: TestDesc { ignore: false, ..desc },
                    testfn: testfn,
1124 1125 1126 1127
                })
            } else {
                None
            }
F
Florian Hahn 已提交
1128
        }
1129
        filtered.into_iter().filter_map(filter).collect()
1130 1131 1132 1133 1134
    };

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

1135
    filtered
1136
}
1137

1138 1139
pub fn convert_benchmarks_to_tests(tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
    // convert benchmarks to tests, if we're not benchmarking them
N
Nick Cameron 已提交
1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156
    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()
1157 1158
}

1159 1160
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
1161
                test: TestDescAndFn,
1162
                monitor_ch: Sender<MonitorMsg>) {
1163

1164 1165
    let TestDescAndFn {desc, testfn} = test;

1166
    if force_ignore || desc.ignore {
1167
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
1168
        return;
1169 1170
    }

1171
    fn run_test_inner(desc: TestDesc,
1172
                      monitor_ch: Sender<MonitorMsg>,
1173
                      nocapture: bool,
1174
                      testfn: Box<FnBox() + Send>) {
1175 1176 1177 1178 1179
        struct Sink(Arc<Mutex<Vec<u8>>>);
        impl Write for Sink {
            fn write(&mut self, data: &[u8]) -> io::Result<usize> {
                Write::write(&mut *self.0.lock().unwrap(), data)
            }
N
Nick Cameron 已提交
1180 1181 1182
            fn flush(&mut self) -> io::Result<()> {
                Ok(())
            }
1183 1184
        }

1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202
        // If the platform is single-threaded we're just going to run
        // the test synchronously, regardless of the concurrency
        // level.
        let supports_threads = !cfg!(target_os = "emscripten");

        // Buffer for capturing standard I/O
        let data = Arc::new(Mutex::new(Vec::new()));
        let data2 = data.clone();

        if supports_threads {
            thread::spawn(move || {
                let cfg = thread::Builder::new().name(match desc.name {
                    DynTestName(ref name) => name.clone(),
                    StaticTestName(name) => name.to_owned(),
                });

                let result_guard = cfg.spawn(move || {
                    if !nocapture {
1203 1204
                        io::set_print(Some(box Sink(data2.clone())));
                        io::set_panic(Some(box Sink(data2)));
1205 1206 1207 1208 1209 1210 1211
                    }
                    testfn()
                })
                    .unwrap();
                let test_result = calc_result(&desc, result_guard.join());
                let stdout = data.lock().unwrap().to_vec();
                monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
1212
            });
1213 1214 1215
        } else {
            let oldio = if !nocapture {
                Some((
1216 1217
                    io::set_print(Some(box Sink(data2.clone()))),
                    io::set_panic(Some(box Sink(data2)))
1218 1219 1220 1221 1222 1223
                ))
            } else {
                None
            };

            use std::panic::{catch_unwind, AssertUnwindSafe};
1224

1225 1226 1227 1228 1229
            let result = catch_unwind(AssertUnwindSafe(|| {
                testfn()
            }));

            if let Some((printio, panicio)) = oldio {
1230 1231
                io::set_print(printio);
                io::set_panic(panicio);
1232 1233 1234
            };

            let test_result = calc_result(&desc, result);
1235
            let stdout = data.lock().unwrap().to_vec();
1236
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
1237
        }
1238 1239 1240
    }

    match testfn {
1241
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
1242
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
1243
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1244 1245 1246
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
1247
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
1248
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
1249 1250
            return;
        }
1251 1252
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
N
Niko Matsakis 已提交
1253
            f.call_box((&mut mm,));
1254
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1255 1256 1257 1258 1259
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
1260
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
1261 1262
            return;
        }
1263
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
N
Nick Cameron 已提交
1264
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, Box::new(f)),
1265
    }
1266 1267
}

N
Nick Cameron 已提交
1268
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any + Send>>) -> TestResult {
1269 1270
    match (&desc.should_panic, task_result) {
        (&ShouldPanic::No, Ok(())) |
1271 1272
        (&ShouldPanic::Yes, Err(_)) => TrOk,
        (&ShouldPanic::YesWithMessage(msg), Err(ref err))
1273
            if err.downcast_ref::<String>()
N
Nick Cameron 已提交
1274 1275 1276 1277
               .map(|e| &**e)
               .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
               .map(|e| e.contains(msg))
               .unwrap_or(false) => TrOk,
1278
        _ => TrFailed,
1279
    }
1280 1281
}

1282
impl MetricMap {
1283
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
1284
        MetricMap(BTreeMap::new())
1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302
    }

    /// Insert a named `value` (+/- `noise`) metric into the map. The value
    /// must be non-negative. The `noise` indicates the uncertainty of the
    /// metric, which doubles as the "noise range" of acceptable
    /// pairwise-regressions on this named value, when comparing from one
    /// metric to the next using `compare_to_old`.
    ///
    /// If `noise` is positive, then it means this metric is of a value
    /// you want to see grow smaller, so a change larger than `noise` in the
    /// positive direction represents a regression.
    ///
    /// If `noise` is negative, then it means this metric is of a value
    /// you want to see grow larger, so a change larger than `noise` in the
    /// negative direction represents a regression.
    pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) {
        let m = Metric {
            value: value,
N
Nick Cameron 已提交
1303
            noise: noise,
1304
        };
1305
        let MetricMap(ref mut map) = *self;
1306
        map.insert(name.to_owned(), m);
1307 1308
    }

1309 1310
    pub fn fmt_metrics(&self) -> String {
        let MetricMap(ref mm) = *self;
N
Nick Cameron 已提交
1311 1312 1313
        let v: Vec<String> = mm.iter()
                               .map(|(k, v)| format!("{}: {} (+/- {})", *k, v.value, v.noise))
                               .collect();
1314
        v.join(", ")
1315 1316 1317 1318 1319 1320
    }
}


// Benchmarking

H
Huon Wilson 已提交
1321
/// A function that is opaque to the optimizer, to allow benchmarks to
1322 1323 1324 1325
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1326
#[cfg(not(any(all(target_os = "nacl", target_arch = "le32"),
B
Brian Anderson 已提交
1327
              target_arch = "asmjs", target_arch = "wasm32")))]
1328
pub fn black_box<T>(dummy: T) -> T {
1329 1330
    // we need to "use" the argument in some way LLVM can't
    // introspect.
N
Nick Cameron 已提交
1331
    unsafe { asm!("" : : "r"(&dummy)) }
1332
    dummy
1333
}
1334
#[cfg(any(all(target_os = "nacl", target_arch = "le32"),
B
Brian Anderson 已提交
1335
          target_arch = "asmjs", target_arch = "wasm32"))]
1336
#[inline(never)]
N
Nick Cameron 已提交
1337 1338 1339
pub fn black_box<T>(dummy: T) -> T {
    dummy
}
1340 1341


1342
impl Bencher {
1343
    /// Callback for benchmark functions to run in their body.
N
Nick Cameron 已提交
1344 1345 1346
    pub fn iter<T, F>(&mut self, mut inner: F)
        where F: FnMut() -> T
    {
1347 1348 1349 1350 1351 1352
        let start = Instant::now();
        let k = self.iterations;
        for _ in 0..k {
            black_box(inner());
        }
        self.dur = start.elapsed();
1353
    }
1354

1355
    pub fn ns_elapsed(&mut self) -> u64 {
S
Steven Fackler 已提交
1356
        self.dur.as_secs() * 1_000_000_000 + (self.dur.subsec_nanos() as u64)
1357
    }
1358

1359 1360 1361 1362
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1363
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1364
        }
1365
    }
1366

N
Nick Cameron 已提交
1367 1368 1369
    pub fn bench_n<F>(&mut self, n: u64, f: F)
        where F: FnOnce(&mut Bencher)
    {
1370 1371 1372
        self.iterations = n;
        f(self);
    }
1373

1374
    // This is a more statistics-driven benchmark algorithm
N
Nick Cameron 已提交
1375 1376 1377
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary
        where F: FnMut(&mut Bencher)
    {
1378
        // Initial bench run to get ballpark figure.
1379
        let mut n = 1;
1380
        self.bench_n(n, |x| f(x));
1381

1382 1383 1384 1385 1386
        // 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 已提交
1387
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1388
        }
1389 1390 1391 1392 1393
        // if the first run took more than 1ms we don't want to just
        // be left doing 0 iterations on every loop. The unfortunate
        // side effect of not being able to do as many runs is
        // automatically handled by the statistical analysis below
        // (i.e. larger error bars).
N
Nick Cameron 已提交
1394 1395 1396
        if n == 0 {
            n = 1;
        }
1397

1398
        let mut total_run = Duration::new(0, 0);
N
Nick Cameron 已提交
1399
        let samples: &mut [f64] = &mut [0.0_f64; 50];
1400
        loop {
1401
            let loop_start = Instant::now();
1402

1403 1404 1405
            for p in &mut *samples {
                self.bench_n(n, |x| f(x));
                *p = self.ns_per_iter() as f64;
N
Nick Cameron 已提交
1406
            }
1407

1408 1409
            stats::winsorize(samples, 5.0);
            let summ = stats::Summary::new(samples);
1410

1411 1412 1413
            for p in &mut *samples {
                self.bench_n(5 * n, |x| f(x));
                *p = self.ns_per_iter() as f64;
N
Nick Cameron 已提交
1414
            }
1415

1416 1417 1418
            stats::winsorize(samples, 5.0);
            let summ5 = stats::Summary::new(samples);
            let loop_run = loop_start.elapsed();
1419

1420
            // If we've run for 100ms and seem to have converged to a
1421
            // stable median.
N
Nick Cameron 已提交
1422 1423
            if loop_run > Duration::from_millis(100) && summ.median_abs_dev_pct < 1.0 &&
               summ.median - summ5.median < summ5.median_abs_dev {
1424
                return summ5;
1425 1426
            }

1427
            total_run = total_run + loop_run;
1428
            // Longest we ever run for is 3s.
1429
            if total_run > Duration::from_secs(3) {
1430
                return summ5;
1431
            }
1432

1433 1434 1435 1436 1437 1438 1439 1440
            // 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,
            };
1441 1442
        }
    }
1443 1444 1445
}

pub mod bench {
M
Michael Darakananda 已提交
1446
    use std::cmp;
1447
    use std::time::Duration;
1448
    use super::{Bencher, BenchSamples};
1449

N
Nick Cameron 已提交
1450 1451 1452
    pub fn benchmark<F>(f: F) -> BenchSamples
        where F: FnMut(&mut Bencher)
    {
1453
        let mut bs = Bencher {
1454
            iterations: 0,
1455
            dur: Duration::new(0, 0),
N
Nick Cameron 已提交
1456
            bytes: 0,
1457 1458
        };

1459
        let ns_iter_summ = bs.auto_bench(f);
1460

M
Michael Darakananda 已提交
1461
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1462
        let mb_s = bs.bytes * 1000 / ns_iter;
1463 1464

        BenchSamples {
1465
            ns_iter_summ: ns_iter_summ,
N
Nick Cameron 已提交
1466
            mb_s: mb_s as usize,
1467 1468
        }
    }
1469

N
Nick Cameron 已提交
1470 1471 1472
    pub fn run_once<F>(f: F)
        where F: FnOnce(&mut Bencher)
    {
1473 1474
        let mut bs = Bencher {
            iterations: 0,
1475
            dur: Duration::new(0, 0),
N
Nick Cameron 已提交
1476
            bytes: 0,
1477 1478 1479
        };
        bs.bench_n(1, f);
    }
1480 1481
}

1482 1483
#[cfg(test)]
mod tests {
N
Nick Cameron 已提交
1484 1485
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts, TestDesc, TestDescAndFn,
               TestOpts, run_test, MetricMap, StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1486
    use std::sync::mpsc::channel;
1487

1488
    #[test]
1489
    pub fn do_not_run_ignored_tests() {
N
Nick Cameron 已提交
1490 1491 1492
        fn f() {
            panic!();
        }
1493 1494
        let desc = TestDescAndFn {
            desc: TestDesc {
1495
                name: StaticTestName("whatever"),
1496
                ignore: true,
1497
                should_panic: ShouldPanic::No,
1498
            },
N
Nick Cameron 已提交
1499
            testfn: DynTestFn(Box::new(move || f())),
1500
        };
1501
        let (tx, rx) = channel();
1502
        run_test(&TestOpts::new(), false, desc, tx);
1503
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1504
        assert!(res != TrOk);
1505 1506 1507
    }

    #[test]
1508
    pub fn ignored_tests_result_in_ignored() {
N
Nick Cameron 已提交
1509
        fn f() {}
1510 1511
        let desc = TestDescAndFn {
            desc: TestDesc {
1512
                name: StaticTestName("whatever"),
1513
                ignore: true,
1514
                should_panic: ShouldPanic::No,
1515
            },
N
Nick Cameron 已提交
1516
            testfn: DynTestFn(Box::new(move || f())),
1517
        };
1518
        let (tx, rx) = channel();
1519
        run_test(&TestOpts::new(), false, desc, tx);
1520
        let (_, res, _) = rx.recv().unwrap();
1521
        assert!(res == TrIgnored);
1522 1523 1524
    }

    #[test]
1525
    fn test_should_panic() {
N
Nick Cameron 已提交
1526 1527 1528
        fn f() {
            panic!();
        }
1529 1530
        let desc = TestDescAndFn {
            desc: TestDesc {
1531
                name: StaticTestName("whatever"),
1532
                ignore: false,
1533
                should_panic: ShouldPanic::Yes,
1534
            },
N
Nick Cameron 已提交
1535
            testfn: DynTestFn(Box::new(move || f())),
1536 1537 1538
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1539
        let (_, res, _) = rx.recv().unwrap();
1540 1541 1542 1543
        assert!(res == TrOk);
    }

    #[test]
1544
    fn test_should_panic_good_message() {
N
Nick Cameron 已提交
1545 1546 1547
        fn f() {
            panic!("an error message");
        }
1548 1549 1550 1551
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1552
                should_panic: ShouldPanic::YesWithMessage("error message"),
1553
            },
N
Nick Cameron 已提交
1554
            testfn: DynTestFn(Box::new(move || f())),
1555
        };
1556
        let (tx, rx) = channel();
1557
        run_test(&TestOpts::new(), false, desc, tx);
1558
        let (_, res, _) = rx.recv().unwrap();
1559
        assert!(res == TrOk);
1560 1561
    }

1562
    #[test]
1563
    fn test_should_panic_bad_message() {
N
Nick Cameron 已提交
1564 1565 1566
        fn f() {
            panic!("an error message");
        }
1567 1568 1569 1570
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1571
                should_panic: ShouldPanic::YesWithMessage("foobar"),
1572
            },
N
Nick Cameron 已提交
1573
            testfn: DynTestFn(Box::new(move || f())),
1574 1575 1576
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1577
        let (_, res, _) = rx.recv().unwrap();
1578 1579 1580
        assert!(res == TrFailed);
    }

1581
    #[test]
1582
    fn test_should_panic_but_succeeds() {
N
Nick Cameron 已提交
1583
        fn f() {}
1584 1585
        let desc = TestDescAndFn {
            desc: TestDesc {
1586
                name: StaticTestName("whatever"),
1587
                ignore: false,
1588
                should_panic: ShouldPanic::Yes,
1589
            },
N
Nick Cameron 已提交
1590
            testfn: DynTestFn(Box::new(move || f())),
1591
        };
1592
        let (tx, rx) = channel();
1593
        run_test(&TestOpts::new(), false, desc, tx);
1594
        let (_, res, _) = rx.recv().unwrap();
1595
        assert!(res == TrFailed);
1596 1597 1598
    }

    #[test]
1599
    fn parse_ignored_flag() {
N
Nick Cameron 已提交
1600
        let args = vec!["progname".to_string(), "filter".to_string(), "--ignored".to_string()];
1601
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1602
            Some(Ok(o)) => o,
N
Nick Cameron 已提交
1603
            _ => panic!("Malformed arg in parse_ignored_flag"),
B
Brian Anderson 已提交
1604
        };
P
Patrick Walton 已提交
1605
        assert!((opts.run_ignored));
1606 1607 1608
    }

    #[test]
1609
    pub fn filter_for_ignored_option() {
1610 1611 1612
        // 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

1613 1614 1615
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1616

N
Nick Cameron 已提交
1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632
        let tests = vec![TestDescAndFn {
                             desc: TestDesc {
                                 name: StaticTestName("1"),
                                 ignore: true,
                                 should_panic: ShouldPanic::No,
                             },
                             testfn: DynTestFn(Box::new(move || {})),
                         },
                         TestDescAndFn {
                             desc: TestDesc {
                                 name: StaticTestName("2"),
                                 ignore: false,
                                 should_panic: ShouldPanic::No,
                             },
                             testfn: DynTestFn(Box::new(move || {})),
                         }];
B
Brian Anderson 已提交
1633
        let filtered = filter_tests(&opts, tests);
1634

1635
        assert_eq!(filtered.len(), 1);
N
Nick Cameron 已提交
1636
        assert_eq!(filtered[0].desc.name.to_string(), "1");
1637
        assert!(!filtered[0].desc.ignore);
1638 1639 1640
    }

    #[test]
1641
    pub fn sort_tests() {
1642 1643
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1644

N
Nick Cameron 已提交
1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655
        let names = vec!["sha1::test".to_string(),
                         "isize::test_to_str".to_string(),
                         "isize::test_pow".to_string(),
                         "test::do_not_run_ignored_tests".to_string(),
                         "test::ignored_tests_result_in_ignored".to_string(),
                         "test::first_free_arg_should_be_a_filter".to_string(),
                         "test::parse_ignored_flag".to_string(),
                         "test::filter_for_ignored_option".to_string(),
                         "test::sort_tests".to_string()];
        let tests = {
            fn testfn() {}
1656
            let mut tests = Vec::new();
1657
            for name in &names {
1658 1659
                let test = TestDescAndFn {
                    desc: TestDesc {
1660
                        name: DynTestName((*name).clone()),
1661
                        ignore: false,
1662
                        should_panic: ShouldPanic::No,
1663
                    },
1664
                    testfn: DynTestFn(Box::new(testfn)),
1665
                };
L
Luqman Aden 已提交
1666
                tests.push(test);
1667
            }
L
Luqman Aden 已提交
1668
            tests
1669
        };
B
Brian Anderson 已提交
1670
        let filtered = filter_tests(&opts, tests);
1671

N
Nick Cameron 已提交
1672 1673 1674 1675 1676 1677 1678 1679 1680
        let expected = vec!["isize::test_pow".to_string(),
                            "isize::test_to_str".to_string(),
                            "sha1::test".to_string(),
                            "test::do_not_run_ignored_tests".to_string(),
                            "test::filter_for_ignored_option".to_string(),
                            "test::first_free_arg_should_be_a_filter".to_string(),
                            "test::ignored_tests_result_in_ignored".to_string(),
                            "test::parse_ignored_flag".to_string(),
                            "test::sort_tests".to_string()];
1681

1682
        for (a, b) in expected.iter().zip(filtered) {
1683
            assert!(*a == b.desc.name.to_string());
1684 1685
        }
    }
1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703 1704 1705 1706 1707 1708

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