lib.rs 44.6 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 34
#![crate_type = "rlib"]
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
       html_favicon_url = "http://www.rust-lang.org/favicon.ico",
35
       html_root_url = "http://doc.rust-lang.org/nightly/")]
A
Alex Crichton 已提交
36

37
#![feature(asm)]
38
#![feature(box_syntax)]
39 40
#![feature(collections)]
#![feature(core)]
A
Alex Crichton 已提交
41
#![feature(int_uint)]
42
#![feature(old_io)]
43
#![feature(rustc_private)]
A
Alex Crichton 已提交
44
#![feature(staged_api)]
45
#![feature(std_misc)]
A
Alex Crichton 已提交
46
#![feature(io)]
47 48
#![feature(libc)]
#![feature(set_panic)]
L
Liigo Zhuang 已提交
49

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

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

64
use stats::Stats;
L
Liigo Zhuang 已提交
65
use getopts::{OptGroup, optflag, optopt};
A
Ahmed Charles 已提交
66
use serialize::Encodable;
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::num::{Float, Int};
A
Alex Crichton 已提交
80
use std::path::{PathBuf};
81
use std::sync::mpsc::{channel, Sender};
82
use std::sync::{Arc, Mutex};
A
Alex Crichton 已提交
83
use std::thread;
84
use std::thunk::{Thunk, Invoke};
85
use std::time::Duration;
86

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

97 98
pub mod stats;

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

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

123
#[derive(Clone, Copy)]
N
Niko Matsakis 已提交
124 125
enum NamePadding {
    PadNone,
S
Seo Sanghyeon 已提交
126
    #[allow(dead_code)]
N
Niko Matsakis 已提交
127 128 129 130
    PadOnLeft,
    PadOnRight,
}

131
impl TestDesc {
132 133
    fn padded_name(&self, column_count: uint, align: NamePadding) -> String {
        let mut name = String::from_str(self.name.as_slice());
134
        let fill = column_count.saturating_sub(name.len());
A
Alex Crichton 已提交
135
        let mut pad = repeat(" ").take(fill).collect::<String>();
136
        match align {
137
            PadNone => name,
138
            PadOnLeft => {
139
                pad.push_str(&name);
140
                pad
141 142
            }
            PadOnRight => {
143
                name.push_str(&pad);
144
                name
145
            }
146 147 148 149
        }
    }
}

150 151
/// Represents a benchmark function.
pub trait TDynBenchFn {
152
    fn run(&self, harness: &mut Bencher);
153 154
}

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

168 169 170
impl TestFn {
    fn padding(&self) -> NamePadding {
        match self {
A
Alex Crichton 已提交
171 172 173 174 175 176
            &StaticTestFn(..)   => PadNone,
            &StaticBenchFn(..)  => PadOnRight,
            &StaticMetricFn(..) => PadOnRight,
            &DynTestFn(..)      => PadNone,
            &DynMetricFn(..)    => PadOnRight,
            &DynBenchFn(..)     => PadOnRight,
177 178 179 180
        }
    }
}

181
impl fmt::Debug for TestFn {
182
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
183
        f.write_str(match *self {
184 185 186 187 188 189
            StaticTestFn(..) => "StaticTestFn(..)",
            StaticBenchFn(..) => "StaticBenchFn(..)",
            StaticMetricFn(..) => "StaticMetricFn(..)",
            DynTestFn(..) => "DynTestFn(..)",
            DynMetricFn(..) => "DynMetricFn(..)",
            DynBenchFn(..) => "DynBenchFn(..)"
190
        })
191 192 193
    }
}

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

J
Jorge Aparicio 已提交
206
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
207
pub enum ShouldPanic {
208 209 210 211
    No,
    Yes(Option<&'static str>)
}

212 213
// The definition of a single test. A test runner will run a list of
// these.
J
Jorge Aparicio 已提交
214
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
215
pub struct TestDesc {
216 217
    pub name: TestName,
    pub ignore: bool,
218
    pub should_panic: ShouldPanic,
219
}
220

221 222
unsafe impl Send for TestDesc {}

J
Jorge Aparicio 已提交
223
#[derive(Debug)]
224
pub struct TestDescAndFn {
225 226
    pub desc: TestDesc,
    pub testfn: TestFn,
227 228
}

J
Jorge Aparicio 已提交
229
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Copy)]
230
pub struct Metric {
231 232
    value: f64,
    noise: f64
233 234
}

L
Liigo Zhuang 已提交
235 236 237 238 239 240
impl Metric {
    pub fn new(value: f64, noise: f64) -> Metric {
        Metric {value: value, noise: noise}
    }
}

241
#[derive(PartialEq)]
A
Alexis Beingessner 已提交
242
pub struct MetricMap(BTreeMap<String,Metric>);
243

244
impl Clone for MetricMap {
245
    fn clone(&self) -> MetricMap {
246 247
        let MetricMap(ref map) = *self;
        MetricMap(map.clone())
248 249 250
    }
}

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

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

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

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

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

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

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

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

The FILTER regex is tested against the name of all tests to run, and
341
only those tests that match are run.
342 343

By default, all tests are run in parallel. This can be altered with the
344
RUST_TEST_TASKS environment variable when running tests (set it to 1).
345

346 347 348 349
All tests have their standard output and standard error captured by default.
This can be overridden with the --nocapture flag or the RUST_TEST_NOCAPTURE=1
environment variable. Logging is not captured by default.

350 351
Test Attributes:

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

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

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

378
    let filter = if matches.free.len() > 0 {
A
Alex Crichton 已提交
379
        Some(matches.free[0].clone())
380 381 382
    } else {
        None
    };
383

384
    let run_ignored = matches.opt_present("ignored");
385

386
    let logfile = matches.opt_str("logfile");
A
Alex Crichton 已提交
387
    let logfile = logfile.map(|s| PathBuf::new(&s));
388

389
    let run_benchmarks = matches.opt_present("bench");
390
    let run_tests = ! run_benchmarks ||
391
        matches.opt_present("test");
392

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

398
    let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
399 400 401 402 403 404 405 406 407
        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))),
    };

408 409 410
    let test_opts = TestOpts {
        filter: filter,
        run_ignored: run_ignored,
411 412
        run_tests: run_tests,
        run_benchmarks: run_benchmarks,
413 414
        logfile: logfile,
        nocapture: nocapture,
415
        color: color,
416
    };
417

B
Brian Anderson 已提交
418
    Some(Ok(test_opts))
419 420
}

421
#[derive(Clone, PartialEq)]
422
pub struct BenchSamples {
423
    ns_iter_summ: stats::Summary<f64>,
424
    mb_s: uint,
425 426
}

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

436 437
unsafe impl Send for TestResult {}

A
Alex Crichton 已提交
438
enum OutputLocation<T> {
439
    Pretty(Box<term::Terminal<term::WriterWrapper> + Send>),
A
Alex Crichton 已提交
440 441 442
    Raw(T),
}

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

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

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

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

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

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

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

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

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

525
    pub fn write_plain(&mut self, s: &str) -> io::Result<()> {
L
Luqman Aden 已提交
526
        match self.out {
527 528 529 530 531 532 533 534
            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()
            },
535 536 537
        }
    }

538
    pub fn write_run_start(&mut self, len: uint) -> io::Result<()> {
539
        self.total = len;
540
        let noun = if len != 1 { "tests" } else { "test" };
541
        self.write_plain(&format!("\nrunning {} {}\n", len, noun))
542 543
    }

A
Alex Crichton 已提交
544
    pub fn write_test_start(&mut self, test: &TestDesc,
545
                            align: NamePadding) -> io::Result<()> {
546
        let name = test.padded_name(self.max_name_len, align);
547
        self.write_plain(&format!("test {} ... ", name))
M
Marijn Haverbeke 已提交
548
    }
549

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

562
                try!(self.write_plain(&format!(": {}", fmt_bench_samples(bs))));
563 564

                Ok(())
565
            }
A
Alex Crichton 已提交
566 567
        });
        self.write_plain("\n")
568
    }
569

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

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

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

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

A
Alfie John 已提交
616
        let success = self.failed == 0;
A
Ahmed Charles 已提交
617
        if !success {
A
Alex Crichton 已提交
618
            try!(self.write_failures());
619
        }
620

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

635
pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
636
    if bs.mb_s != 0 {
A
Alex Crichton 已提交
637
        format!("{:>9} ns/iter (+/- {}) = {} MB/s",
638 639 640 641
             bs.ns_iter_summ.median as uint,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint,
             bs.mb_s)
    } else {
A
Alex Crichton 已提交
642
        format!("{:>9} ns/iter (+/- {})",
643 644
             bs.ns_iter_summ.median as uint,
             (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
645
    }
646 647 648
}

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

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

691
    let mut st = try!(ConsoleTestState::new(opts, None::<io::Stdout>));
692 693
    fn len_if_padded(t: &TestDescAndFn) -> uint {
        match t.testfn.padding() {
A
Alfie John 已提交
694
            PadNone => 0,
695
            PadOnLeft | PadOnRight => t.desc.name.as_slice().len(),
696 697 698 699
        }
    }
    match tests.iter().max_by(|t|len_if_padded(*t)) {
        Some(t) => {
700
            let n = t.desc.name.as_slice();
701
            st.max_name_len = n.as_slice().len();
702
        },
703 704
        None => {}
    }
A
Alex Crichton 已提交
705
    try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
A
Ahmed Charles 已提交
706
    return st.write_run_finish();
707 708 709 710
}

#[test]
fn should_sort_failures_before_printing_them() {
A
Alex Crichton 已提交
711 712 713
    let test_a = TestDesc {
        name: StaticTestName("a"),
        ignore: false,
714
        should_panic: ShouldPanic::No
A
Alex Crichton 已提交
715
    };
716

A
Alex Crichton 已提交
717 718 719
    let test_b = TestDesc {
        name: StaticTestName("b"),
        ignore: false,
720
        should_panic: ShouldPanic::No
A
Alex Crichton 已提交
721
    };
722

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

737
    st.write_failures().unwrap();
L
Luqman Aden 已提交
738
    let s = match st.out {
739
        Raw(ref m) => String::from_utf8_lossy(&m[..]),
A
Alex Crichton 已提交
740
        Pretty(_) => unreachable!()
L
Luqman Aden 已提交
741
    };
A
Alex Crichton 已提交
742

743 744
    let apos = s.find("a").unwrap();
    let bpos = s.find("b").unwrap();
P
Patrick Walton 已提交
745
    assert!(apos < bpos);
746 747
}

748 749
fn use_color(opts: &TestOpts) -> bool {
    match opts.color {
750
        AutoColor => get_concurrency() == 1 && stdout_isatty(),
751 752 753
        AlwaysColor => true,
        NeverColor => false,
    }
754
}
755

756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774
#[cfg(unix)]
fn stdout_isatty() -> bool {
    unsafe { libc::isatty(libc::STDOUT_FILENO) != 0 }
}
#[cfg(windows)]
fn stdout_isatty() -> bool {
    const STD_OUTPUT_HANDLE: libc::DWORD = -11;
    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
    }
}

775
#[derive(Clone)]
B
Brian Anderson 已提交
776
enum TestEvent {
777
    TeFiltered(Vec<TestDesc> ),
778
    TeWait(TestDesc, NamePadding),
779
    TeResult(TestDesc, TestResult, Vec<u8> ),
B
Brian Anderson 已提交
780 781
}

782
pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
783

F
Flavio Percoco 已提交
784

J
Jorge Aparicio 已提交
785 786
fn run_tests<F>(opts: &TestOpts,
                tests: Vec<TestDescAndFn> ,
787 788
                mut callback: F) -> io::Result<()> where
    F: FnMut(TestEvent) -> io::Result<()>,
J
Jorge Aparicio 已提交
789
{
T
Tim Chevalier 已提交
790
    let filtered_tests = filter_tests(opts, tests);
791 792 793
    let filtered_descs = filtered_tests.iter()
                                       .map(|t| t.desc.clone())
                                       .collect();
T
Tim Chevalier 已提交
794

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

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

B
Brian Anderson 已提交
805 806
    // 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 已提交
807
    let concurrency = get_concurrency();
808

809
    let mut remaining = filtered_tests;
810
    remaining.reverse();
811
    let mut pending = 0;
B
Brian Anderson 已提交
812

813
    let (tx, rx) = channel::<MonitorMsg>();
814

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

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

    // All benchmarks run at the end, in serial.
837
    // (this includes metric fns)
838
    for b in filtered_benchs_and_metrics {
A
Alex Crichton 已提交
839
        try!(callback(TeWait(b.desc.clone(), b.testfn.padding())));
840
        run_test(opts, !opts.run_benchmarks, b, tx.clone());
841
        let (test, result, stdout) = rx.recv().unwrap();
A
Alex Crichton 已提交
842
        try!(callback(TeResult(test, result, stdout)));
843
    }
A
Alex Crichton 已提交
844
    Ok(())
B
Brian Anderson 已提交
845 846
}

847
fn get_concurrency() -> uint {
848
    use std::rt;
849
    match env::var("RUST_TEST_TASKS") {
A
Alex Crichton 已提交
850
        Ok(s) => {
A
Alex Crichton 已提交
851
            let opt_n: Option<uint> = s.parse().ok();
852 853
            match opt_n {
                Some(n) if n > 0 => n,
S
Steve Klabnik 已提交
854
                _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s)
855 856
            }
        }
A
Alex Crichton 已提交
857
        Err(..) => {
858
            rt::default_sched_threads()
859 860
        }
    }
861
}
862

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

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

897
    filtered
898
}
899

900 901
pub fn run_test(opts: &TestOpts,
                force_ignore: bool,
902
                test: TestDescAndFn,
903
                monitor_ch: Sender<MonitorMsg>) {
904

905 906
    let TestDescAndFn {desc, testfn} = test;

907
    if force_ignore || desc.ignore {
908
        monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
B
Brian Anderson 已提交
909
        return;
910 911
    }

912
    #[allow(deprecated)] // set_stdout
913
    fn run_test_inner(desc: TestDesc,
914
                      monitor_ch: Sender<MonitorMsg>,
915
                      nocapture: bool,
916
                      testfn: Thunk<'static>) {
917 918 919 920 921 922 923 924 925 926 927 928 929
        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(()) }
        }
        impl Writer for Sink {
            fn write_all(&mut self, data: &[u8]) -> std::old_io::IoResult<()> {
                Writer::write_all(&mut *self.0.lock().unwrap(), data)
            }
        }

A
Aaron Turon 已提交
930
        thread::spawn(move || {
931 932 933
            let data = Arc::new(Mutex::new(Vec::new()));
            let data2 = data.clone();
            let cfg = thread::Builder::new().name(match desc.name {
R
Richo Healey 已提交
934 935
                DynTestName(ref name) => name.clone().to_string(),
                StaticTestName(name) => name.to_string(),
936
            });
937

938 939 940 941 942 943 944
            let result_guard = cfg.spawn(move || {
                if !nocapture {
                    std::old_io::stdio::set_stdout(box Sink(data2.clone()));
                    io::set_panic(box Sink(data2));
                }
                testfn.invoke(())
            }).unwrap();
A
Aaron Turon 已提交
945
            let test_result = calc_result(&desc, result_guard.join());
946
            let stdout = data.lock().unwrap().to_vec();
947
            monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
A
Aaron Turon 已提交
948
        });
949 950 951
    }

    match testfn {
952
        DynBenchFn(bencher) => {
L
Liigo Zhuang 已提交
953
            let bs = ::bench::benchmark(|harness| bencher.run(harness));
954
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
955 956 957
            return;
        }
        StaticBenchFn(benchfn) => {
N
Niko Matsakis 已提交
958
            let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
959
            monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
960 961
            return;
        }
962 963
        DynMetricFn(f) => {
            let mut mm = MetricMap::new();
964
            f.invoke(&mut mm);
965
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
966 967 968 969 970
            return;
        }
        StaticMetricFn(f) => {
            let mut mm = MetricMap::new();
            f(&mut mm);
971
            monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
972 973
            return;
        }
974 975
        DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
        StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
976
                                          Thunk::new(move|| f()))
977
    }
978 979
}

980
fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
981 982 983 984
    match (&desc.should_panic, task_result) {
        (&ShouldPanic::No, Ok(())) |
        (&ShouldPanic::Yes(None), Err(_)) => TrOk,
        (&ShouldPanic::Yes(Some(msg)), Err(ref err))
985 986 987 988 989 990
            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,
991
    }
992 993
}

994 995
impl MetricMap {

996
    pub fn new() -> MetricMap {
A
Alexis Beingessner 已提交
997
        MetricMap(BTreeMap::new())
998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017
    }

    /// 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
        };
1018
        let MetricMap(ref mut map) = *self;
1019
        map.insert(name.to_string(), m);
1020 1021
    }

1022 1023 1024 1025 1026 1027 1028
    pub fn fmt_metrics(&self) -> String {
        let MetricMap(ref mm) = *self;
        let v : Vec<String> = mm.iter()
            .map(|(k,v)| format!("{}: {} (+/- {})", *k,
                                 v.value as f64, v.noise as f64))
            .collect();
        v.connect(", ")
1029 1030 1031 1032 1033 1034
    }
}


// Benchmarking

H
Huon Wilson 已提交
1035
/// A function that is opaque to the optimizer, to allow benchmarks to
1036 1037 1038 1039
/// pretend to use outputs to assist in avoiding dead-code
/// elimination.
///
/// This function is a no-op, and does not even read from `dummy`.
1040
pub fn black_box<T>(dummy: T) -> T {
1041 1042 1043
    // we need to "use" the argument in some way LLVM can't
    // introspect.
    unsafe {asm!("" : : "r"(&dummy))}
1044
    dummy
1045 1046 1047
}


1048
impl Bencher {
1049
    /// Callback for benchmark functions to run in their body.
J
Jorge Aparicio 已提交
1050
    pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1051 1052
        self.dur = Duration::span(|| {
            let k = self.iterations;
1053
            for _ in 0..k {
1054 1055 1056
                black_box(inner());
            }
        });
1057
    }
1058

1059
    pub fn ns_elapsed(&mut self) -> u64 {
1060
        self.dur.num_nanoseconds().unwrap() as u64
1061
    }
1062

1063 1064 1065 1066
    pub fn ns_per_iter(&mut self) -> u64 {
        if self.iterations == 0 {
            0
        } else {
M
Michael Darakananda 已提交
1067
            self.ns_elapsed() / cmp::max(self.iterations, 1)
1068
        }
1069
    }
1070

J
Jorge Aparicio 已提交
1071
    pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1072 1073 1074
        self.iterations = n;
        f(self);
    }
1075

1076
    // This is a more statistics-driven benchmark algorithm
J
Jorge Aparicio 已提交
1077
    pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary<f64> where F: FnMut(&mut Bencher) {
1078
        // Initial bench run to get ballpark figure.
1079
        let mut n = 1;
1080
        self.bench_n(n, |x| f(x));
1081

1082 1083 1084 1085 1086
        // 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 已提交
1087
            n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1088
        }
1089 1090 1091 1092 1093 1094 1095
        // 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; }

1096
        let mut total_run = Duration::nanoseconds(0);
1097
        let samples : &mut [f64] = &mut [0.0_f64; 50];
1098
        loop {
1099 1100
            let mut summ = None;
            let mut summ5 = None;
1101

1102
            let loop_run = Duration::span(|| {
1103

1104
                for p in &mut *samples {
1105 1106 1107
                    self.bench_n(n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1108

1109 1110
                stats::winsorize(samples, 5.0);
                summ = Some(stats::Summary::new(samples));
1111

1112
                for p in &mut *samples {
1113 1114 1115
                    self.bench_n(5 * n, |x| f(x));
                    *p = self.ns_per_iter() as f64;
                };
1116

1117 1118 1119 1120 1121
                stats::winsorize(samples, 5.0);
                summ5 = Some(stats::Summary::new(samples));
            });
            let summ = summ.unwrap();
            let summ5 = summ5.unwrap();
1122

1123
            // If we've run for 100ms and seem to have converged to a
1124
            // stable median.
1125
            if loop_run.num_milliseconds() > 100 &&
1126 1127 1128
                summ.median_abs_dev_pct < 1.0 &&
                summ.median - summ5.median < summ5.median_abs_dev {
                return summ5;
1129 1130
            }

1131
            total_run = total_run + loop_run;
1132
            // Longest we ever run for is 3s.
1133
            if total_run.num_seconds() > 3 {
1134
                return summ5;
1135
            }
1136

1137 1138 1139 1140 1141 1142 1143 1144
            // 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,
            };
1145 1146
        }
    }
1147 1148 1149
}

pub mod bench {
M
Michael Darakananda 已提交
1150
    use std::cmp;
1151
    use std::time::Duration;
1152
    use super::{Bencher, BenchSamples};
1153

J
Jorge Aparicio 已提交
1154
    pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1155
        let mut bs = Bencher {
1156
            iterations: 0,
1157
            dur: Duration::nanoseconds(0),
1158 1159 1160
            bytes: 0
        };

1161
        let ns_iter_summ = bs.auto_bench(f);
1162

M
Michael Darakananda 已提交
1163
        let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1164
        let iter_s = 1_000_000_000 / ns_iter;
1165 1166 1167
        let mb_s = (bs.bytes * iter_s) / 1_000_000;

        BenchSamples {
1168
            ns_iter_summ: ns_iter_summ,
1169 1170 1171 1172 1173
            mb_s: mb_s as uint
        }
    }
}

1174 1175
#[cfg(test)]
mod tests {
1176
    use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
L
Liigo Zhuang 已提交
1177
               TestDesc, TestDescAndFn, TestOpts, run_test,
J
Jorge Aparicio 已提交
1178
               MetricMap,
1179
               StaticTestName, DynTestName, DynTestFn, ShouldPanic};
1180
    use std::thunk::Thunk;
1181
    use std::sync::mpsc::channel;
1182

1183
    #[test]
1184
    pub fn do_not_run_ignored_tests() {
S
Steve Klabnik 已提交
1185
        fn f() { panic!(); }
1186 1187
        let desc = TestDescAndFn {
            desc: TestDesc {
1188
                name: StaticTestName("whatever"),
1189
                ignore: true,
1190
                should_panic: ShouldPanic::No,
1191
            },
1192
            testfn: DynTestFn(Thunk::new(move|| f())),
1193
        };
1194
        let (tx, rx) = channel();
1195
        run_test(&TestOpts::new(), false, desc, tx);
1196
        let (_, res, _) = rx.recv().unwrap();
P
Patrick Walton 已提交
1197
        assert!(res != TrOk);
1198 1199 1200
    }

    #[test]
1201
    pub fn ignored_tests_result_in_ignored() {
1202
        fn f() { }
1203 1204
        let desc = TestDescAndFn {
            desc: TestDesc {
1205
                name: StaticTestName("whatever"),
1206
                ignore: true,
1207
                should_panic: ShouldPanic::No,
1208
            },
1209
            testfn: DynTestFn(Thunk::new(move|| f())),
1210
        };
1211
        let (tx, rx) = channel();
1212
        run_test(&TestOpts::new(), false, desc, tx);
1213
        let (_, res, _) = rx.recv().unwrap();
1214
        assert!(res == TrIgnored);
1215 1216 1217
    }

    #[test]
1218
    fn test_should_panic() {
S
Steve Klabnik 已提交
1219
        fn f() { panic!(); }
1220 1221
        let desc = TestDescAndFn {
            desc: TestDesc {
1222
                name: StaticTestName("whatever"),
1223
                ignore: false,
1224
                should_panic: ShouldPanic::Yes(None)
1225
            },
1226
            testfn: DynTestFn(Thunk::new(move|| f())),
1227 1228 1229
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1230
        let (_, res, _) = rx.recv().unwrap();
1231 1232 1233 1234
        assert!(res == TrOk);
    }

    #[test]
1235
    fn test_should_panic_good_message() {
1236 1237 1238 1239 1240
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1241
                should_panic: ShouldPanic::Yes(Some("error message"))
1242
            },
1243
            testfn: DynTestFn(Thunk::new(move|| f())),
1244
        };
1245
        let (tx, rx) = channel();
1246
        run_test(&TestOpts::new(), false, desc, tx);
1247
        let (_, res, _) = rx.recv().unwrap();
1248
        assert!(res == TrOk);
1249 1250
    }

1251
    #[test]
1252
    fn test_should_panic_bad_message() {
1253 1254 1255 1256 1257
        fn f() { panic!("an error message"); }
        let desc = TestDescAndFn {
            desc: TestDesc {
                name: StaticTestName("whatever"),
                ignore: false,
1258
                should_panic: ShouldPanic::Yes(Some("foobar"))
1259
            },
1260
            testfn: DynTestFn(Thunk::new(move|| f())),
1261 1262 1263
        };
        let (tx, rx) = channel();
        run_test(&TestOpts::new(), false, desc, tx);
1264
        let (_, res, _) = rx.recv().unwrap();
1265 1266 1267
        assert!(res == TrFailed);
    }

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

    #[test]
1286
    fn parse_ignored_flag() {
1287 1288 1289
        let args = vec!("progname".to_string(),
                        "filter".to_string(),
                        "--ignored".to_string());
1290
        let opts = match parse_opts(&args) {
B
Brian Anderson 已提交
1291
            Some(Ok(o)) => o,
S
Steve Klabnik 已提交
1292
            _ => panic!("Malformed arg in parse_ignored_flag")
B
Brian Anderson 已提交
1293
        };
P
Patrick Walton 已提交
1294
        assert!((opts.run_ignored));
1295 1296 1297
    }

    #[test]
1298
    pub fn filter_for_ignored_option() {
1299 1300 1301
        // 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

1302 1303 1304
        let mut opts = TestOpts::new();
        opts.run_tests = true;
        opts.run_ignored = true;
1305

1306
        let tests = vec!(
1307 1308
            TestDescAndFn {
                desc: TestDesc {
1309
                    name: StaticTestName("1"),
1310
                    ignore: true,
1311
                    should_panic: ShouldPanic::No,
1312
                },
1313
                testfn: DynTestFn(Thunk::new(move|| {})),
1314
            },
1315 1316
            TestDescAndFn {
                desc: TestDesc {
1317
                    name: StaticTestName("2"),
1318
                    ignore: false,
1319
                    should_panic: ShouldPanic::No,
1320
                },
1321
                testfn: DynTestFn(Thunk::new(move|| {})),
1322
            });
B
Brian Anderson 已提交
1323
        let filtered = filter_tests(&opts, tests);
1324

1325
        assert_eq!(filtered.len(), 1);
1326
        assert_eq!(filtered[0].desc.name.to_string(),
1327
                   "1");
1328
        assert!(filtered[0].desc.ignore == false);
1329 1330 1331
    }

    #[test]
1332
    pub fn sort_tests() {
1333 1334
        let mut opts = TestOpts::new();
        opts.run_tests = true;
1335 1336

        let names =
1337 1338 1339 1340 1341 1342 1343 1344 1345
            vec!("sha1::test".to_string(),
                 "int::test_to_str".to_string(),
                 "int::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());
1346 1347
        let tests =
        {
1348
            fn testfn() { }
1349
            let mut tests = Vec::new();
1350
            for name in &names {
1351 1352
                let test = TestDescAndFn {
                    desc: TestDesc {
1353
                        name: DynTestName((*name).clone()),
1354
                        ignore: false,
1355
                        should_panic: ShouldPanic::No,
1356
                    },
1357
                    testfn: DynTestFn(Thunk::new(testfn)),
1358
                };
L
Luqman Aden 已提交
1359
                tests.push(test);
1360
            }
L
Luqman Aden 已提交
1361
            tests
1362
        };
B
Brian Anderson 已提交
1363
        let filtered = filter_tests(&opts, tests);
1364

1365
        let expected =
1366 1367 1368 1369 1370 1371 1372 1373 1374
            vec!("int::test_pow".to_string(),
                 "int::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());
1375

1376
        for (a, b) in expected.iter().zip(filtered.iter()) {
1377
            assert!(*a == b.desc.name.to_string());
1378 1379
        }
    }
1380 1381 1382 1383 1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396 1397 1398 1399 1400 1401 1402

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