]> git.lizzy.rs Git - rust.git/blob - src/libtest/lib.rs
25377e3afa179178b870c1ed949e35643fe70809
[rust.git] / src / libtest / lib.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Support code for rustc's built in unit-test and micro-benchmarking
12 //! framework.
13 //!
14 //! Almost all user code will only be interested in `Bencher` and
15 //! `black_box`. All other interactions (such as writing tests and
16 //! benchmarks themselves) should be done via the `#[test]` and
17 //! `#[bench]` attributes.
18 //!
19 //! See the [Testing Guide](../guide-testing.html) for more details.
20
21 // Currently, not much of this is meant for users. It is intended to
22 // support the simplest interface possible for representing and
23 // running tests while providing a base that other test frameworks may
24 // build off of.
25
26 #![crate_name = "test"]
27 #![unstable(feature = "test")]
28 #![staged_api]
29 #![crate_type = "rlib"]
30 #![crate_type = "dylib"]
31 #![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
32        html_favicon_url = "http://www.rust-lang.org/favicon.ico",
33        html_root_url = "http://doc.rust-lang.org/nightly/")]
34
35 #![cfg_attr(not(stage0), allow(unused_mut))] // NOTE: remove after stage0 snap
36
37 #![feature(asm, slicing_syntax)]
38 #![feature(box_syntax)]
39 #![feature(collections)]
40 #![feature(core)]
41 #![feature(hash)]
42 #![feature(int_uint)]
43 #![feature(io)]
44 #![feature(os)]
45 #![feature(path)]
46 #![feature(rustc_private)]
47 #![feature(staged_api)]
48 #![feature(std_misc)]
49
50 extern crate getopts;
51 extern crate serialize;
52 extern crate "serialize" as rustc_serialize;
53 extern crate term;
54
55 pub use self::TestFn::*;
56 pub use self::ColorConfig::*;
57 pub use self::TestResult::*;
58 pub use self::TestName::*;
59 use self::TestEvent::*;
60 use self::NamePadding::*;
61 use self::OutputLocation::*;
62
63 use stats::Stats;
64 use getopts::{OptGroup, optflag, optopt};
65 use serialize::Encodable;
66 use term::Terminal;
67 use term::color::{Color, RED, YELLOW, GREEN, CYAN};
68
69 use std::any::Any;
70 use std::cmp;
71 use std::collections::BTreeMap;
72 use std::fmt;
73 use std::old_io::stdio::StdWriter;
74 use std::old_io::{File, ChanReader, ChanWriter};
75 use std::old_io;
76 use std::iter::repeat;
77 use std::num::{Float, Int};
78 use std::os;
79 use std::sync::mpsc::{channel, Sender};
80 use std::thread::{self, Thread};
81 use std::thunk::{Thunk, Invoke};
82 use std::time::Duration;
83
84 // to be used by rustc to compile tests in libtest
85 pub mod test {
86     pub use {Bencher, TestName, TestResult, TestDesc,
87              TestDescAndFn, TestOpts, TrFailed, TrIgnored, TrOk,
88              Metric, MetricMap,
89              StaticTestFn, StaticTestName, DynTestName, DynTestFn,
90              run_test, test_main, test_main_static, filter_tests,
91              parse_opts, StaticBenchFn, ShouldFail};
92 }
93
94 pub mod stats;
95
96 // The name of a test. By convention this follows the rules for rust
97 // paths; i.e. it should be a series of identifiers separated by double
98 // colons. This way if some test runner wants to arrange the tests
99 // hierarchically it may.
100
101 #[derive(Clone, PartialEq, Eq, Hash, Debug)]
102 pub enum TestName {
103     StaticTestName(&'static str),
104     DynTestName(String)
105 }
106 impl TestName {
107     fn as_slice<'a>(&'a self) -> &'a str {
108         match *self {
109             StaticTestName(s) => s,
110             DynTestName(ref s) => s.as_slice()
111         }
112     }
113 }
114 impl fmt::Display for TestName {
115     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116         fmt::Display::fmt(self.as_slice(), f)
117     }
118 }
119
120 #[derive(Clone, Copy)]
121 enum NamePadding {
122     PadNone,
123     PadOnLeft,
124     PadOnRight,
125 }
126
127 impl TestDesc {
128     fn padded_name(&self, column_count: uint, align: NamePadding) -> String {
129         let mut name = String::from_str(self.name.as_slice());
130         let fill = column_count.saturating_sub(name.len());
131         let mut pad = repeat(" ").take(fill).collect::<String>();
132         match align {
133             PadNone => name,
134             PadOnLeft => {
135                 pad.push_str(name.as_slice());
136                 pad
137             }
138             PadOnRight => {
139                 name.push_str(pad.as_slice());
140                 name
141             }
142         }
143     }
144 }
145
146 /// Represents a benchmark function.
147 pub trait TDynBenchFn {
148     fn run(&self, harness: &mut Bencher);
149 }
150
151 // A function that runs a test. If the function returns successfully,
152 // the test succeeds; if the function panics then the test fails. We
153 // may need to come up with a more clever definition of test in order
154 // to support isolation of tests into tasks.
155 pub enum TestFn {
156     StaticTestFn(fn()),
157     StaticBenchFn(fn(&mut Bencher)),
158     StaticMetricFn(fn(&mut MetricMap)),
159     DynTestFn(Thunk),
160     DynMetricFn(Box<for<'a> Invoke<&'a mut MetricMap>+'static>),
161     DynBenchFn(Box<TDynBenchFn+'static>)
162 }
163
164 impl TestFn {
165     fn padding(&self) -> NamePadding {
166         match self {
167             &StaticTestFn(..)   => PadNone,
168             &StaticBenchFn(..)  => PadOnRight,
169             &StaticMetricFn(..) => PadOnRight,
170             &DynTestFn(..)      => PadNone,
171             &DynMetricFn(..)    => PadOnRight,
172             &DynBenchFn(..)     => PadOnRight,
173         }
174     }
175 }
176
177 impl fmt::Debug for TestFn {
178     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
179         f.write_str(match *self {
180             StaticTestFn(..) => "StaticTestFn(..)",
181             StaticBenchFn(..) => "StaticBenchFn(..)",
182             StaticMetricFn(..) => "StaticMetricFn(..)",
183             DynTestFn(..) => "DynTestFn(..)",
184             DynMetricFn(..) => "DynMetricFn(..)",
185             DynBenchFn(..) => "DynBenchFn(..)"
186         })
187     }
188 }
189
190 /// Manager of the benchmarking runs.
191 ///
192 /// This is fed into functions marked with `#[bench]` to allow for
193 /// set-up & tear-down before running a piece of code repeatedly via a
194 /// call to `iter`.
195 #[derive(Copy)]
196 pub struct Bencher {
197     iterations: u64,
198     dur: Duration,
199     pub bytes: u64,
200 }
201
202 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
203 pub enum ShouldFail {
204     No,
205     Yes(Option<&'static str>)
206 }
207
208 // The definition of a single test. A test runner will run a list of
209 // these.
210 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
211 pub struct TestDesc {
212     pub name: TestName,
213     pub ignore: bool,
214     pub should_fail: ShouldFail,
215 }
216
217 unsafe impl Send for TestDesc {}
218
219 #[derive(Debug)]
220 pub struct TestDescAndFn {
221     pub desc: TestDesc,
222     pub testfn: TestFn,
223 }
224
225 #[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Debug, Copy)]
226 pub struct Metric {
227     value: f64,
228     noise: f64
229 }
230
231 impl Metric {
232     pub fn new(value: f64, noise: f64) -> Metric {
233         Metric {value: value, noise: noise}
234     }
235 }
236
237 #[derive(PartialEq)]
238 pub struct MetricMap(BTreeMap<String,Metric>);
239
240 impl Clone for MetricMap {
241     fn clone(&self) -> MetricMap {
242         let MetricMap(ref map) = *self;
243         MetricMap(map.clone())
244     }
245 }
246
247 // The default console test runner. It accepts the command line
248 // arguments and a vector of test_descs.
249 pub fn test_main(args: &[String], tests: Vec<TestDescAndFn> ) {
250     let opts =
251         match parse_opts(args) {
252             Some(Ok(o)) => o,
253             Some(Err(msg)) => panic!("{:?}", msg),
254             None => return
255         };
256     match run_tests_console(&opts, tests) {
257         Ok(true) => {}
258         Ok(false) => panic!("Some tests failed"),
259         Err(e) => panic!("io error when running tests: {:?}", e),
260     }
261 }
262
263 // A variant optimized for invocation with a static test vector.
264 // This will panic (intentionally) when fed any dynamic tests, because
265 // it is copying the static values out into a dynamic vector and cannot
266 // copy dynamic values. It is doing this because from this point on
267 // a ~[TestDescAndFn] is used in order to effect ownership-transfer
268 // semantics into parallel test runners, which in turn requires a ~[]
269 // rather than a &[].
270 pub fn test_main_static(args: &[String], tests: &[TestDescAndFn]) {
271     let owned_tests = tests.iter().map(|t| {
272         match t.testfn {
273             StaticTestFn(f) => TestDescAndFn { testfn: StaticTestFn(f), desc: t.desc.clone() },
274             StaticBenchFn(f) => TestDescAndFn { testfn: StaticBenchFn(f), desc: t.desc.clone() },
275             _ => panic!("non-static tests passed to test::test_main_static")
276         }
277     }).collect();
278     test_main(args, owned_tests)
279 }
280
281 #[derive(Copy)]
282 pub enum ColorConfig {
283     AutoColor,
284     AlwaysColor,
285     NeverColor,
286 }
287
288 pub struct TestOpts {
289     pub filter: Option<String>,
290     pub run_ignored: bool,
291     pub run_tests: bool,
292     pub run_benchmarks: bool,
293     pub logfile: Option<Path>,
294     pub nocapture: bool,
295     pub color: ColorConfig,
296 }
297
298 impl TestOpts {
299     #[cfg(test)]
300     fn new() -> TestOpts {
301         TestOpts {
302             filter: None,
303             run_ignored: false,
304             run_tests: false,
305             run_benchmarks: false,
306             logfile: None,
307             nocapture: false,
308             color: AutoColor,
309         }
310     }
311 }
312
313 /// Result of parsing the options.
314 pub type OptRes = Result<TestOpts, String>;
315
316 fn optgroups() -> Vec<getopts::OptGroup> {
317     vec!(getopts::optflag("", "ignored", "Run ignored tests"),
318       getopts::optflag("", "test", "Run tests and not benchmarks"),
319       getopts::optflag("", "bench", "Run benchmarks instead of tests"),
320       getopts::optflag("h", "help", "Display this message (longer with --help)"),
321       getopts::optopt("", "logfile", "Write logs to the specified file instead \
322                           of stdout", "PATH"),
323       getopts::optflag("", "nocapture", "don't capture stdout/stderr of each \
324                                          task, allow printing directly"),
325       getopts::optopt("", "color", "Configure coloring of output:
326             auto   = colorize if stdout is a tty and tests are run on serially (default);
327             always = always colorize output;
328             never  = never colorize output;", "auto|always|never"))
329 }
330
331 fn usage(binary: &str) {
332     let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
333     println!(r#"{usage}
334
335 The FILTER regex is tested against the name of all tests to run, and
336 only those tests that match are run.
337
338 By default, all tests are run in parallel. This can be altered with the
339 RUST_TEST_TASKS environment variable when running tests (set it to 1).
340
341 All tests have their standard output and standard error captured by default.
342 This can be overridden with the --nocapture flag or the RUST_TEST_NOCAPTURE=1
343 environment variable. Logging is not captured by default.
344
345 Test Attributes:
346
347     #[test]        - Indicates a function is a test to be run. This function
348                      takes no arguments.
349     #[bench]       - Indicates a function is a benchmark to be run. This
350                      function takes one argument (test::Bencher).
351     #[should_fail] - This function (also labeled with #[test]) will only pass if
352                      the code causes a failure (an assertion failure or panic!)
353                      A message may be provided, which the failure string must
354                      contain: #[should_fail(expected = "foo")].
355     #[ignore]      - When applied to a function which is already attributed as a
356                      test, then the test runner will ignore these tests during
357                      normal test runs. Running with --ignored will run these
358                      tests."#,
359              usage = getopts::usage(message.as_slice(),
360                                     optgroups().as_slice()));
361 }
362
363 // Parses command line arguments into test options
364 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
365     let args_ = args.tail();
366     let matches =
367         match getopts::getopts(args_.as_slice(), optgroups().as_slice()) {
368           Ok(m) => m,
369           Err(f) => return Some(Err(f.to_string()))
370         };
371
372     if matches.opt_present("h") { usage(args[0].as_slice()); return None; }
373
374     let filter = if matches.free.len() > 0 {
375         Some(matches.free[0].clone())
376     } else {
377         None
378     };
379
380     let run_ignored = matches.opt_present("ignored");
381
382     let logfile = matches.opt_str("logfile");
383     let logfile = logfile.map(|s| Path::new(s));
384
385     let run_benchmarks = matches.opt_present("bench");
386     let run_tests = ! run_benchmarks ||
387         matches.opt_present("test");
388
389     let mut nocapture = matches.opt_present("nocapture");
390     if !nocapture {
391         nocapture = os::getenv("RUST_TEST_NOCAPTURE").is_some();
392     }
393
394     let color = match matches.opt_str("color").as_ref().map(|s| s.as_slice()) {
395         Some("auto") | None => AutoColor,
396         Some("always") => AlwaysColor,
397         Some("never") => NeverColor,
398
399         Some(v) => return Some(Err(format!("argument for --color must be \
400                                             auto, always, or never (was {})",
401                                             v))),
402     };
403
404     let test_opts = TestOpts {
405         filter: filter,
406         run_ignored: run_ignored,
407         run_tests: run_tests,
408         run_benchmarks: run_benchmarks,
409         logfile: logfile,
410         nocapture: nocapture,
411         color: color,
412     };
413
414     Some(Ok(test_opts))
415 }
416
417 #[derive(Clone, PartialEq)]
418 pub struct BenchSamples {
419     ns_iter_summ: stats::Summary<f64>,
420     mb_s: uint,
421 }
422
423 #[derive(Clone, PartialEq)]
424 pub enum TestResult {
425     TrOk,
426     TrFailed,
427     TrIgnored,
428     TrMetrics(MetricMap),
429     TrBench(BenchSamples),
430 }
431
432 unsafe impl Send for TestResult {}
433
434 enum OutputLocation<T> {
435     Pretty(Box<term::Terminal<term::WriterWrapper> + Send>),
436     Raw(T),
437 }
438
439 struct ConsoleTestState<T> {
440     log_out: Option<File>,
441     out: OutputLocation<T>,
442     use_color: bool,
443     total: uint,
444     passed: uint,
445     failed: uint,
446     ignored: uint,
447     measured: uint,
448     metrics: MetricMap,
449     failures: Vec<(TestDesc, Vec<u8> )> ,
450     max_name_len: uint, // number of columns to fill when aligning names
451 }
452
453 impl<T: Writer> ConsoleTestState<T> {
454     pub fn new(opts: &TestOpts,
455                _: Option<T>) -> old_io::IoResult<ConsoleTestState<StdWriter>> {
456         let log_out = match opts.logfile {
457             Some(ref path) => Some(try!(File::create(path))),
458             None => None
459         };
460         let out = match term::stdout() {
461             None => Raw(old_io::stdio::stdout_raw()),
462             Some(t) => Pretty(t)
463         };
464
465         Ok(ConsoleTestState {
466             out: out,
467             log_out: log_out,
468             use_color: use_color(opts),
469             total: 0u,
470             passed: 0u,
471             failed: 0u,
472             ignored: 0u,
473             measured: 0u,
474             metrics: MetricMap::new(),
475             failures: Vec::new(),
476             max_name_len: 0u,
477         })
478     }
479
480     pub fn write_ok(&mut self) -> old_io::IoResult<()> {
481         self.write_pretty("ok", term::color::GREEN)
482     }
483
484     pub fn write_failed(&mut self) -> old_io::IoResult<()> {
485         self.write_pretty("FAILED", term::color::RED)
486     }
487
488     pub fn write_ignored(&mut self) -> old_io::IoResult<()> {
489         self.write_pretty("ignored", term::color::YELLOW)
490     }
491
492     pub fn write_metric(&mut self) -> old_io::IoResult<()> {
493         self.write_pretty("metric", term::color::CYAN)
494     }
495
496     pub fn write_bench(&mut self) -> old_io::IoResult<()> {
497         self.write_pretty("bench", term::color::CYAN)
498     }
499
500     pub fn write_pretty(&mut self,
501                         word: &str,
502                         color: term::color::Color) -> old_io::IoResult<()> {
503         match self.out {
504             Pretty(ref mut term) => {
505                 if self.use_color {
506                     try!(term.fg(color));
507                 }
508                 try!(term.write_all(word.as_bytes()));
509                 if self.use_color {
510                     try!(term.reset());
511                 }
512                 Ok(())
513             }
514             Raw(ref mut stdout) => stdout.write_all(word.as_bytes())
515         }
516     }
517
518     pub fn write_plain(&mut self, s: &str) -> old_io::IoResult<()> {
519         match self.out {
520             Pretty(ref mut term) => term.write_all(s.as_bytes()),
521             Raw(ref mut stdout) => stdout.write_all(s.as_bytes())
522         }
523     }
524
525     pub fn write_run_start(&mut self, len: uint) -> old_io::IoResult<()> {
526         self.total = len;
527         let noun = if len != 1 { "tests" } else { "test" };
528         self.write_plain(format!("\nrunning {} {}\n", len, noun).as_slice())
529     }
530
531     pub fn write_test_start(&mut self, test: &TestDesc,
532                             align: NamePadding) -> old_io::IoResult<()> {
533         let name = test.padded_name(self.max_name_len, align);
534         self.write_plain(format!("test {} ... ", name).as_slice())
535     }
536
537     pub fn write_result(&mut self, result: &TestResult) -> old_io::IoResult<()> {
538         try!(match *result {
539             TrOk => self.write_ok(),
540             TrFailed => self.write_failed(),
541             TrIgnored => self.write_ignored(),
542             TrMetrics(ref mm) => {
543                 try!(self.write_metric());
544                 self.write_plain(format!(": {}", mm.fmt_metrics()).as_slice())
545             }
546             TrBench(ref bs) => {
547                 try!(self.write_bench());
548
549                 try!(self.write_plain(format!(": {}",
550                                               fmt_bench_samples(bs)).as_slice()));
551
552                 Ok(())
553             }
554         });
555         self.write_plain("\n")
556     }
557
558     pub fn write_log(&mut self, test: &TestDesc,
559                      result: &TestResult) -> old_io::IoResult<()> {
560         match self.log_out {
561             None => Ok(()),
562             Some(ref mut o) => {
563                 let s = format!("{} {}\n", match *result {
564                         TrOk => "ok".to_string(),
565                         TrFailed => "failed".to_string(),
566                         TrIgnored => "ignored".to_string(),
567                         TrMetrics(ref mm) => mm.fmt_metrics(),
568                         TrBench(ref bs) => fmt_bench_samples(bs)
569                     }, test.name.as_slice());
570                 o.write_all(s.as_bytes())
571             }
572         }
573     }
574
575     pub fn write_failures(&mut self) -> old_io::IoResult<()> {
576         try!(self.write_plain("\nfailures:\n"));
577         let mut failures = Vec::new();
578         let mut fail_out = String::new();
579         for &(ref f, ref stdout) in &self.failures {
580             failures.push(f.name.to_string());
581             if stdout.len() > 0 {
582                 fail_out.push_str(format!("---- {} stdout ----\n\t",
583                                           f.name.as_slice()).as_slice());
584                 let output = String::from_utf8_lossy(stdout.as_slice());
585                 fail_out.push_str(output.as_slice());
586                 fail_out.push_str("\n");
587             }
588         }
589         if fail_out.len() > 0 {
590             try!(self.write_plain("\n"));
591             try!(self.write_plain(fail_out.as_slice()));
592         }
593
594         try!(self.write_plain("\nfailures:\n"));
595         failures.sort();
596         for name in &failures {
597             try!(self.write_plain(format!("    {}\n",
598                                           name.as_slice()).as_slice()));
599         }
600         Ok(())
601     }
602
603     pub fn write_run_finish(&mut self) -> old_io::IoResult<bool> {
604         assert!(self.passed + self.failed + self.ignored + self.measured == self.total);
605
606         let success = self.failed == 0u;
607         if !success {
608             try!(self.write_failures());
609         }
610
611         try!(self.write_plain("\ntest result: "));
612         if success {
613             // There's no parallelism at this point so it's safe to use color
614             try!(self.write_ok());
615         } else {
616             try!(self.write_failed());
617         }
618         let s = format!(". {} passed; {} failed; {} ignored; {} measured\n\n",
619                         self.passed, self.failed, self.ignored, self.measured);
620         try!(self.write_plain(s.as_slice()));
621         return Ok(success);
622     }
623 }
624
625 pub fn fmt_bench_samples(bs: &BenchSamples) -> String {
626     if bs.mb_s != 0 {
627         format!("{:>9} ns/iter (+/- {}) = {} MB/s",
628              bs.ns_iter_summ.median as uint,
629              (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint,
630              bs.mb_s)
631     } else {
632         format!("{:>9} ns/iter (+/- {})",
633              bs.ns_iter_summ.median as uint,
634              (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as uint)
635     }
636 }
637
638 // A simple console test runner
639 pub fn run_tests_console(opts: &TestOpts, tests: Vec<TestDescAndFn> ) -> old_io::IoResult<bool> {
640
641     fn callback<T: Writer>(event: &TestEvent,
642                            st: &mut ConsoleTestState<T>) -> old_io::IoResult<()> {
643         match (*event).clone() {
644             TeFiltered(ref filtered_tests) => st.write_run_start(filtered_tests.len()),
645             TeWait(ref test, padding) => st.write_test_start(test, padding),
646             TeResult(test, result, stdout) => {
647                 try!(st.write_log(&test, &result));
648                 try!(st.write_result(&result));
649                 match result {
650                     TrOk => st.passed += 1,
651                     TrIgnored => st.ignored += 1,
652                     TrMetrics(mm) => {
653                         let tname = test.name.as_slice();
654                         let MetricMap(mm) = mm;
655                         for (k,v) in &mm {
656                             st.metrics
657                               .insert_metric(format!("{}.{}",
658                                                      tname,
659                                                      k).as_slice(),
660                                              v.value,
661                                              v.noise);
662                         }
663                         st.measured += 1
664                     }
665                     TrBench(bs) => {
666                         st.metrics.insert_metric(test.name.as_slice(),
667                                                  bs.ns_iter_summ.median,
668                                                  bs.ns_iter_summ.max - bs.ns_iter_summ.min);
669                         st.measured += 1
670                     }
671                     TrFailed => {
672                         st.failed += 1;
673                         st.failures.push((test, stdout));
674                     }
675                 }
676                 Ok(())
677             }
678         }
679     }
680
681     let mut st = try!(ConsoleTestState::new(opts, None::<StdWriter>));
682     fn len_if_padded(t: &TestDescAndFn) -> uint {
683         match t.testfn.padding() {
684             PadNone => 0u,
685             PadOnLeft | PadOnRight => t.desc.name.as_slice().len(),
686         }
687     }
688     match tests.iter().max_by(|t|len_if_padded(*t)) {
689         Some(t) => {
690             let n = t.desc.name.as_slice();
691             st.max_name_len = n.len();
692         },
693         None => {}
694     }
695     try!(run_tests(opts, tests, |x| callback(&x, &mut st)));
696     return st.write_run_finish();
697 }
698
699 #[test]
700 fn should_sort_failures_before_printing_them() {
701     let test_a = TestDesc {
702         name: StaticTestName("a"),
703         ignore: false,
704         should_fail: ShouldFail::No
705     };
706
707     let test_b = TestDesc {
708         name: StaticTestName("b"),
709         ignore: false,
710         should_fail: ShouldFail::No
711     };
712
713     let mut st = ConsoleTestState {
714         log_out: None,
715         out: Raw(Vec::new()),
716         use_color: false,
717         total: 0u,
718         passed: 0u,
719         failed: 0u,
720         ignored: 0u,
721         measured: 0u,
722         max_name_len: 10u,
723         metrics: MetricMap::new(),
724         failures: vec!((test_b, Vec::new()), (test_a, Vec::new()))
725     };
726
727     st.write_failures().unwrap();
728     let s = match st.out {
729         Raw(ref m) => String::from_utf8_lossy(&m[]),
730         Pretty(_) => unreachable!()
731     };
732
733     let apos = s.find_str("a").unwrap();
734     let bpos = s.find_str("b").unwrap();
735     assert!(apos < bpos);
736 }
737
738 fn use_color(opts: &TestOpts) -> bool {
739     match opts.color {
740         AutoColor => get_concurrency() == 1 && old_io::stdout().get_ref().isatty(),
741         AlwaysColor => true,
742         NeverColor => false,
743     }
744 }
745
746 #[derive(Clone)]
747 enum TestEvent {
748     TeFiltered(Vec<TestDesc> ),
749     TeWait(TestDesc, NamePadding),
750     TeResult(TestDesc, TestResult, Vec<u8> ),
751 }
752
753 pub type MonitorMsg = (TestDesc, TestResult, Vec<u8> );
754
755
756 fn run_tests<F>(opts: &TestOpts,
757                 tests: Vec<TestDescAndFn> ,
758                 mut callback: F) -> old_io::IoResult<()> where
759     F: FnMut(TestEvent) -> old_io::IoResult<()>,
760 {
761     let filtered_tests = filter_tests(opts, tests);
762     let filtered_descs = filtered_tests.iter()
763                                        .map(|t| t.desc.clone())
764                                        .collect();
765
766     try!(callback(TeFiltered(filtered_descs)));
767
768     let (filtered_tests, filtered_benchs_and_metrics): (Vec<_>, _) =
769         filtered_tests.into_iter().partition(|e| {
770             match e.testfn {
771                 StaticTestFn(_) | DynTestFn(_) => true,
772                 _ => false
773             }
774         });
775
776     // It's tempting to just spawn all the tests at once, but since we have
777     // many tests that run in other processes we would be making a big mess.
778     let concurrency = get_concurrency();
779
780     let mut remaining = filtered_tests;
781     remaining.reverse();
782     let mut pending = 0;
783
784     let (tx, rx) = channel::<MonitorMsg>();
785
786     while pending > 0 || !remaining.is_empty() {
787         while pending < concurrency && !remaining.is_empty() {
788             let test = remaining.pop().unwrap();
789             if concurrency == 1 {
790                 // We are doing one test at a time so we can print the name
791                 // of the test before we run it. Useful for debugging tests
792                 // that hang forever.
793                 try!(callback(TeWait(test.desc.clone(), test.testfn.padding())));
794             }
795             run_test(opts, !opts.run_tests, test, tx.clone());
796             pending += 1;
797         }
798
799         let (desc, result, stdout) = rx.recv().unwrap();
800         if concurrency != 1 {
801             try!(callback(TeWait(desc.clone(), PadNone)));
802         }
803         try!(callback(TeResult(desc, result, stdout)));
804         pending -= 1;
805     }
806
807     // All benchmarks run at the end, in serial.
808     // (this includes metric fns)
809     for b in filtered_benchs_and_metrics {
810         try!(callback(TeWait(b.desc.clone(), b.testfn.padding())));
811         run_test(opts, !opts.run_benchmarks, b, tx.clone());
812         let (test, result, stdout) = rx.recv().unwrap();
813         try!(callback(TeResult(test, result, stdout)));
814     }
815     Ok(())
816 }
817
818 fn get_concurrency() -> uint {
819     use std::rt;
820     match os::getenv("RUST_TEST_TASKS") {
821         Some(s) => {
822             let opt_n: Option<uint> = s.parse().ok();
823             match opt_n {
824                 Some(n) if n > 0 => n,
825                 _ => panic!("RUST_TEST_TASKS is `{}`, should be a positive integer.", s)
826             }
827         }
828         None => {
829             rt::default_sched_threads()
830         }
831     }
832 }
833
834 pub fn filter_tests(opts: &TestOpts, tests: Vec<TestDescAndFn>) -> Vec<TestDescAndFn> {
835     let mut filtered = tests;
836
837     // Remove tests that don't match the test filter
838     filtered = match opts.filter {
839         None => filtered,
840         Some(ref filter) => {
841             filtered.into_iter().filter(|test| {
842                 test.desc.name.as_slice().contains(&filter[])
843             }).collect()
844         }
845     };
846
847     // Maybe pull out the ignored test and unignore them
848     filtered = if !opts.run_ignored {
849         filtered
850     } else {
851         fn filter(test: TestDescAndFn) -> Option<TestDescAndFn> {
852             if test.desc.ignore {
853                 let TestDescAndFn {desc, testfn} = test;
854                 Some(TestDescAndFn {
855                     desc: TestDesc {ignore: false, ..desc},
856                     testfn: testfn
857                 })
858             } else {
859                 None
860             }
861         };
862         filtered.into_iter().filter_map(|x| filter(x)).collect()
863     };
864
865     // Sort the tests alphabetically
866     filtered.sort_by(|t1, t2| t1.desc.name.as_slice().cmp(t2.desc.name.as_slice()));
867
868     filtered
869 }
870
871 pub fn run_test(opts: &TestOpts,
872                 force_ignore: bool,
873                 test: TestDescAndFn,
874                 monitor_ch: Sender<MonitorMsg>) {
875
876     let TestDescAndFn {desc, testfn} = test;
877
878     if force_ignore || desc.ignore {
879         monitor_ch.send((desc, TrIgnored, Vec::new())).unwrap();
880         return;
881     }
882
883     fn run_test_inner(desc: TestDesc,
884                       monitor_ch: Sender<MonitorMsg>,
885                       nocapture: bool,
886                       testfn: Thunk) {
887         Thread::spawn(move || {
888             let (tx, rx) = channel();
889             let mut reader = ChanReader::new(rx);
890             let stdout = ChanWriter::new(tx.clone());
891             let stderr = ChanWriter::new(tx);
892             let mut cfg = thread::Builder::new().name(match desc.name {
893                 DynTestName(ref name) => name.clone().to_string(),
894                 StaticTestName(name) => name.to_string(),
895             });
896             if nocapture {
897                 drop((stdout, stderr));
898             } else {
899                 cfg = cfg.stdout(box stdout as Box<Writer + Send>);
900                 cfg = cfg.stderr(box stderr as Box<Writer + Send>);
901             }
902
903             let result_guard = cfg.scoped(move || { testfn.invoke(()) });
904             let stdout = reader.read_to_end().unwrap().into_iter().collect();
905             let test_result = calc_result(&desc, result_guard.join());
906             monitor_ch.send((desc.clone(), test_result, stdout)).unwrap();
907         });
908     }
909
910     match testfn {
911         DynBenchFn(bencher) => {
912             let bs = ::bench::benchmark(|harness| bencher.run(harness));
913             monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
914             return;
915         }
916         StaticBenchFn(benchfn) => {
917             let bs = ::bench::benchmark(|harness| (benchfn.clone())(harness));
918             monitor_ch.send((desc, TrBench(bs), Vec::new())).unwrap();
919             return;
920         }
921         DynMetricFn(f) => {
922             let mut mm = MetricMap::new();
923             f.invoke(&mut mm);
924             monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
925             return;
926         }
927         StaticMetricFn(f) => {
928             let mut mm = MetricMap::new();
929             f(&mut mm);
930             monitor_ch.send((desc, TrMetrics(mm), Vec::new())).unwrap();
931             return;
932         }
933         DynTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture, f),
934         StaticTestFn(f) => run_test_inner(desc, monitor_ch, opts.nocapture,
935                                           Thunk::new(move|| f()))
936     }
937 }
938
939 fn calc_result(desc: &TestDesc, task_result: Result<(), Box<Any+Send>>) -> TestResult {
940     match (&desc.should_fail, task_result) {
941         (&ShouldFail::No, Ok(())) |
942         (&ShouldFail::Yes(None), Err(_)) => TrOk,
943         (&ShouldFail::Yes(Some(msg)), Err(ref err))
944             if err.downcast_ref::<String>()
945                 .map(|e| &**e)
946                 .or_else(|| err.downcast_ref::<&'static str>().map(|e| *e))
947                 .map(|e| e.contains(msg))
948                 .unwrap_or(false) => TrOk,
949         _ => TrFailed,
950     }
951 }
952
953 impl MetricMap {
954
955     pub fn new() -> MetricMap {
956         MetricMap(BTreeMap::new())
957     }
958
959     /// Insert a named `value` (+/- `noise`) metric into the map. The value
960     /// must be non-negative. The `noise` indicates the uncertainty of the
961     /// metric, which doubles as the "noise range" of acceptable
962     /// pairwise-regressions on this named value, when comparing from one
963     /// metric to the next using `compare_to_old`.
964     ///
965     /// If `noise` is positive, then it means this metric is of a value
966     /// you want to see grow smaller, so a change larger than `noise` in the
967     /// positive direction represents a regression.
968     ///
969     /// If `noise` is negative, then it means this metric is of a value
970     /// you want to see grow larger, so a change larger than `noise` in the
971     /// negative direction represents a regression.
972     pub fn insert_metric(&mut self, name: &str, value: f64, noise: f64) {
973         let m = Metric {
974             value: value,
975             noise: noise
976         };
977         let MetricMap(ref mut map) = *self;
978         map.insert(name.to_string(), m);
979     }
980
981     pub fn fmt_metrics(&self) -> String {
982         let MetricMap(ref mm) = *self;
983         let v : Vec<String> = mm.iter()
984             .map(|(k,v)| format!("{}: {} (+/- {})", *k,
985                                  v.value as f64, v.noise as f64))
986             .collect();
987         v.connect(", ")
988     }
989 }
990
991
992 // Benchmarking
993
994 /// A function that is opaque to the optimizer, to allow benchmarks to
995 /// pretend to use outputs to assist in avoiding dead-code
996 /// elimination.
997 ///
998 /// This function is a no-op, and does not even read from `dummy`.
999 pub fn black_box<T>(dummy: T) -> T {
1000     // we need to "use" the argument in some way LLVM can't
1001     // introspect.
1002     unsafe {asm!("" : : "r"(&dummy))}
1003     dummy
1004 }
1005
1006
1007 impl Bencher {
1008     /// Callback for benchmark functions to run in their body.
1009     pub fn iter<T, F>(&mut self, mut inner: F) where F: FnMut() -> T {
1010         self.dur = Duration::span(|| {
1011             let k = self.iterations;
1012             for _ in 0u64..k {
1013                 black_box(inner());
1014             }
1015         });
1016     }
1017
1018     pub fn ns_elapsed(&mut self) -> u64 {
1019         self.dur.num_nanoseconds().unwrap() as u64
1020     }
1021
1022     pub fn ns_per_iter(&mut self) -> u64 {
1023         if self.iterations == 0 {
1024             0
1025         } else {
1026             self.ns_elapsed() / cmp::max(self.iterations, 1)
1027         }
1028     }
1029
1030     pub fn bench_n<F>(&mut self, n: u64, f: F) where F: FnOnce(&mut Bencher) {
1031         self.iterations = n;
1032         f(self);
1033     }
1034
1035     // This is a more statistics-driven benchmark algorithm
1036     pub fn auto_bench<F>(&mut self, mut f: F) -> stats::Summary<f64> where F: FnMut(&mut Bencher) {
1037         // Initial bench run to get ballpark figure.
1038         let mut n = 1_u64;
1039         self.bench_n(n, |x| f(x));
1040
1041         // Try to estimate iter count for 1ms falling back to 1m
1042         // iterations if first run took < 1ns.
1043         if self.ns_per_iter() == 0 {
1044             n = 1_000_000;
1045         } else {
1046             n = 1_000_000 / cmp::max(self.ns_per_iter(), 1);
1047         }
1048         // if the first run took more than 1ms we don't want to just
1049         // be left doing 0 iterations on every loop. The unfortunate
1050         // side effect of not being able to do as many runs is
1051         // automatically handled by the statistical analysis below
1052         // (i.e. larger error bars).
1053         if n == 0 { n = 1; }
1054
1055         let mut total_run = Duration::nanoseconds(0);
1056         let samples : &mut [f64] = &mut [0.0_f64; 50];
1057         loop {
1058             let mut summ = None;
1059             let mut summ5 = None;
1060
1061             let loop_run = Duration::span(|| {
1062
1063                 for p in &mut *samples {
1064                     self.bench_n(n, |x| f(x));
1065                     *p = self.ns_per_iter() as f64;
1066                 };
1067
1068                 stats::winsorize(samples, 5.0);
1069                 summ = Some(stats::Summary::new(samples));
1070
1071                 for p in &mut *samples {
1072                     self.bench_n(5 * n, |x| f(x));
1073                     *p = self.ns_per_iter() as f64;
1074                 };
1075
1076                 stats::winsorize(samples, 5.0);
1077                 summ5 = Some(stats::Summary::new(samples));
1078             });
1079             let summ = summ.unwrap();
1080             let summ5 = summ5.unwrap();
1081
1082             // If we've run for 100ms and seem to have converged to a
1083             // stable median.
1084             if loop_run.num_milliseconds() > 100 &&
1085                 summ.median_abs_dev_pct < 1.0 &&
1086                 summ.median - summ5.median < summ5.median_abs_dev {
1087                 return summ5;
1088             }
1089
1090             total_run = total_run + loop_run;
1091             // Longest we ever run for is 3s.
1092             if total_run.num_seconds() > 3 {
1093                 return summ5;
1094             }
1095
1096             n *= 2;
1097         }
1098     }
1099 }
1100
1101 pub mod bench {
1102     use std::cmp;
1103     use std::time::Duration;
1104     use super::{Bencher, BenchSamples};
1105
1106     pub fn benchmark<F>(f: F) -> BenchSamples where F: FnMut(&mut Bencher) {
1107         let mut bs = Bencher {
1108             iterations: 0,
1109             dur: Duration::nanoseconds(0),
1110             bytes: 0
1111         };
1112
1113         let ns_iter_summ = bs.auto_bench(f);
1114
1115         let ns_iter = cmp::max(ns_iter_summ.median as u64, 1);
1116         let iter_s = 1_000_000_000 / ns_iter;
1117         let mb_s = (bs.bytes * iter_s) / 1_000_000;
1118
1119         BenchSamples {
1120             ns_iter_summ: ns_iter_summ,
1121             mb_s: mb_s as uint
1122         }
1123     }
1124 }
1125
1126 #[cfg(test)]
1127 mod tests {
1128     use test::{TrFailed, TrIgnored, TrOk, filter_tests, parse_opts,
1129                TestDesc, TestDescAndFn, TestOpts, run_test,
1130                MetricMap,
1131                StaticTestName, DynTestName, DynTestFn, ShouldFail};
1132     use std::thunk::Thunk;
1133     use std::sync::mpsc::channel;
1134
1135     #[test]
1136     pub fn do_not_run_ignored_tests() {
1137         fn f() { panic!(); }
1138         let desc = TestDescAndFn {
1139             desc: TestDesc {
1140                 name: StaticTestName("whatever"),
1141                 ignore: true,
1142                 should_fail: ShouldFail::No,
1143             },
1144             testfn: DynTestFn(Thunk::new(move|| f())),
1145         };
1146         let (tx, rx) = channel();
1147         run_test(&TestOpts::new(), false, desc, tx);
1148         let (_, res, _) = rx.recv().unwrap();
1149         assert!(res != TrOk);
1150     }
1151
1152     #[test]
1153     pub fn ignored_tests_result_in_ignored() {
1154         fn f() { }
1155         let desc = TestDescAndFn {
1156             desc: TestDesc {
1157                 name: StaticTestName("whatever"),
1158                 ignore: true,
1159                 should_fail: ShouldFail::No,
1160             },
1161             testfn: DynTestFn(Thunk::new(move|| f())),
1162         };
1163         let (tx, rx) = channel();
1164         run_test(&TestOpts::new(), false, desc, tx);
1165         let (_, res, _) = rx.recv().unwrap();
1166         assert!(res == TrIgnored);
1167     }
1168
1169     #[test]
1170     fn test_should_fail() {
1171         fn f() { panic!(); }
1172         let desc = TestDescAndFn {
1173             desc: TestDesc {
1174                 name: StaticTestName("whatever"),
1175                 ignore: false,
1176                 should_fail: ShouldFail::Yes(None)
1177             },
1178             testfn: DynTestFn(Thunk::new(move|| f())),
1179         };
1180         let (tx, rx) = channel();
1181         run_test(&TestOpts::new(), false, desc, tx);
1182         let (_, res, _) = rx.recv().unwrap();
1183         assert!(res == TrOk);
1184     }
1185
1186     #[test]
1187     fn test_should_fail_good_message() {
1188         fn f() { panic!("an error message"); }
1189         let desc = TestDescAndFn {
1190             desc: TestDesc {
1191                 name: StaticTestName("whatever"),
1192                 ignore: false,
1193                 should_fail: ShouldFail::Yes(Some("error message"))
1194             },
1195             testfn: DynTestFn(Thunk::new(move|| f())),
1196         };
1197         let (tx, rx) = channel();
1198         run_test(&TestOpts::new(), false, desc, tx);
1199         let (_, res, _) = rx.recv().unwrap();
1200         assert!(res == TrOk);
1201     }
1202
1203     #[test]
1204     fn test_should_fail_bad_message() {
1205         fn f() { panic!("an error message"); }
1206         let desc = TestDescAndFn {
1207             desc: TestDesc {
1208                 name: StaticTestName("whatever"),
1209                 ignore: false,
1210                 should_fail: ShouldFail::Yes(Some("foobar"))
1211             },
1212             testfn: DynTestFn(Thunk::new(move|| f())),
1213         };
1214         let (tx, rx) = channel();
1215         run_test(&TestOpts::new(), false, desc, tx);
1216         let (_, res, _) = rx.recv().unwrap();
1217         assert!(res == TrFailed);
1218     }
1219
1220     #[test]
1221     fn test_should_fail_but_succeeds() {
1222         fn f() { }
1223         let desc = TestDescAndFn {
1224             desc: TestDesc {
1225                 name: StaticTestName("whatever"),
1226                 ignore: false,
1227                 should_fail: ShouldFail::Yes(None)
1228             },
1229             testfn: DynTestFn(Thunk::new(move|| f())),
1230         };
1231         let (tx, rx) = channel();
1232         run_test(&TestOpts::new(), false, desc, tx);
1233         let (_, res, _) = rx.recv().unwrap();
1234         assert!(res == TrFailed);
1235     }
1236
1237     #[test]
1238     fn parse_ignored_flag() {
1239         let args = vec!("progname".to_string(),
1240                         "filter".to_string(),
1241                         "--ignored".to_string());
1242         let opts = match parse_opts(args.as_slice()) {
1243             Some(Ok(o)) => o,
1244             _ => panic!("Malformed arg in parse_ignored_flag")
1245         };
1246         assert!((opts.run_ignored));
1247     }
1248
1249     #[test]
1250     pub fn filter_for_ignored_option() {
1251         // When we run ignored tests the test filter should filter out all the
1252         // unignored tests and flip the ignore flag on the rest to false
1253
1254         let mut opts = TestOpts::new();
1255         opts.run_tests = true;
1256         opts.run_ignored = true;
1257
1258         let tests = vec!(
1259             TestDescAndFn {
1260                 desc: TestDesc {
1261                     name: StaticTestName("1"),
1262                     ignore: true,
1263                     should_fail: ShouldFail::No,
1264                 },
1265                 testfn: DynTestFn(Thunk::new(move|| {})),
1266             },
1267             TestDescAndFn {
1268                 desc: TestDesc {
1269                     name: StaticTestName("2"),
1270                     ignore: false,
1271                     should_fail: ShouldFail::No,
1272                 },
1273                 testfn: DynTestFn(Thunk::new(move|| {})),
1274             });
1275         let filtered = filter_tests(&opts, tests);
1276
1277         assert_eq!(filtered.len(), 1);
1278         assert_eq!(filtered[0].desc.name.to_string(),
1279                    "1");
1280         assert!(filtered[0].desc.ignore == false);
1281     }
1282
1283     #[test]
1284     pub fn sort_tests() {
1285         let mut opts = TestOpts::new();
1286         opts.run_tests = true;
1287
1288         let names =
1289             vec!("sha1::test".to_string(),
1290                  "int::test_to_str".to_string(),
1291                  "int::test_pow".to_string(),
1292                  "test::do_not_run_ignored_tests".to_string(),
1293                  "test::ignored_tests_result_in_ignored".to_string(),
1294                  "test::first_free_arg_should_be_a_filter".to_string(),
1295                  "test::parse_ignored_flag".to_string(),
1296                  "test::filter_for_ignored_option".to_string(),
1297                  "test::sort_tests".to_string());
1298         let tests =
1299         {
1300             fn testfn() { }
1301             let mut tests = Vec::new();
1302             for name in &names {
1303                 let test = TestDescAndFn {
1304                     desc: TestDesc {
1305                         name: DynTestName((*name).clone()),
1306                         ignore: false,
1307                         should_fail: ShouldFail::No,
1308                     },
1309                     testfn: DynTestFn(Thunk::new(testfn)),
1310                 };
1311                 tests.push(test);
1312             }
1313             tests
1314         };
1315         let filtered = filter_tests(&opts, tests);
1316
1317         let expected =
1318             vec!("int::test_pow".to_string(),
1319                  "int::test_to_str".to_string(),
1320                  "sha1::test".to_string(),
1321                  "test::do_not_run_ignored_tests".to_string(),
1322                  "test::filter_for_ignored_option".to_string(),
1323                  "test::first_free_arg_should_be_a_filter".to_string(),
1324                  "test::ignored_tests_result_in_ignored".to_string(),
1325                  "test::parse_ignored_flag".to_string(),
1326                  "test::sort_tests".to_string());
1327
1328         for (a, b) in expected.iter().zip(filtered.iter()) {
1329             assert!(*a == b.desc.name.to_string());
1330         }
1331     }
1332
1333     #[test]
1334     pub fn test_metricmap_compare() {
1335         let mut m1 = MetricMap::new();
1336         let mut m2 = MetricMap::new();
1337         m1.insert_metric("in-both-noise", 1000.0, 200.0);
1338         m2.insert_metric("in-both-noise", 1100.0, 200.0);
1339
1340         m1.insert_metric("in-first-noise", 1000.0, 2.0);
1341         m2.insert_metric("in-second-noise", 1000.0, 2.0);
1342
1343         m1.insert_metric("in-both-want-downwards-but-regressed", 1000.0, 10.0);
1344         m2.insert_metric("in-both-want-downwards-but-regressed", 2000.0, 10.0);
1345
1346         m1.insert_metric("in-both-want-downwards-and-improved", 2000.0, 10.0);
1347         m2.insert_metric("in-both-want-downwards-and-improved", 1000.0, 10.0);
1348
1349         m1.insert_metric("in-both-want-upwards-but-regressed", 2000.0, -10.0);
1350         m2.insert_metric("in-both-want-upwards-but-regressed", 1000.0, -10.0);
1351
1352         m1.insert_metric("in-both-want-upwards-and-improved", 1000.0, -10.0);
1353         m2.insert_metric("in-both-want-upwards-and-improved", 2000.0, -10.0);
1354     }
1355 }