]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
auto merge of #12855 : alexcrichton/rust/shutdown, r=brson
[rust.git] / src / compiletest / compiletest.rs
1 // Copyright 2012 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 #[crate_type = "bin"];
12
13 #[allow(non_camel_case_types)];
14 #[deny(warnings)];
15
16 extern crate test;
17 extern crate getopts;
18
19 use std::os;
20 use std::io;
21 use std::io::fs;
22 use getopts::{optopt, optflag, reqopt};
23 use common::config;
24 use common::mode_run_pass;
25 use common::mode_run_fail;
26 use common::mode_compile_fail;
27 use common::mode_pretty;
28 use common::mode_debug_info;
29 use common::mode_codegen;
30 use common::mode;
31 use util::logv;
32
33 pub mod procsrv;
34 pub mod util;
35 pub mod header;
36 pub mod runtest;
37 pub mod common;
38 pub mod errors;
39
40 pub fn main() {
41     let args = os::args();
42     let config = parse_config(args);
43     log_config(&config);
44     run_tests(&config);
45 }
46
47 pub fn parse_config(args: ~[~str]) -> config {
48
49     let groups : ~[getopts::OptGroup] =
50         ~[reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
51           reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
52           reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
53           optopt("", "clang-path", "path to  executable for codegen tests", "PATH"),
54           optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
55           reqopt("", "src-base", "directory to scan for test files", "PATH"),
56           reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
57           reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
58           reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
59           reqopt("", "mode", "which sort of compile tests to run",
60                  "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
61           optflag("", "ignored", "run tests marked as ignored"),
62           optopt("", "runtool", "supervisor program to run tests under \
63                                  (eg. emulator, valgrind)", "PROGRAM"),
64           optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
65           optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
66           optflag("", "verbose", "run tests verbosely, showing all output"),
67           optopt("", "logfile", "file to log test execution to", "FILE"),
68           optopt("", "save-metrics", "file to save metrics to", "FILE"),
69           optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
70           optopt("", "ratchet-noise-percent",
71                  "percent change in metrics to consider noise", "N"),
72           optflag("", "jit", "run tests under the JIT"),
73           optopt("", "target", "the target to build for", "TARGET"),
74           optopt("", "host", "the host to build for", "HOST"),
75           optopt("", "adb-path", "path to the android debugger", "PATH"),
76           optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
77           optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
78           optflag("h", "help", "show this message"),
79          ];
80
81     assert!(!args.is_empty());
82     let argv0 = args[0].clone();
83     let args_ = args.tail();
84     if args[1] == ~"-h" || args[1] == ~"--help" {
85         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
86         println!("{}", getopts::usage(message, groups));
87         println!("");
88         fail!()
89     }
90
91     let matches =
92         &match getopts::getopts(args_, groups) {
93           Ok(m) => m,
94           Err(f) => fail!("{}", f.to_err_msg())
95         };
96
97     if matches.opt_present("h") || matches.opt_present("help") {
98         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
99         println!("{}", getopts::usage(message, groups));
100         println!("");
101         fail!()
102     }
103
104     fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
105         Path::new(m.opt_str(nm).unwrap())
106     }
107
108     config {
109         compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
110         run_lib_path: matches.opt_str("run-lib-path").unwrap(),
111         rustc_path: opt_path(matches, "rustc-path"),
112         clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
113         llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
114         src_base: opt_path(matches, "src-base"),
115         build_base: opt_path(matches, "build-base"),
116         aux_base: opt_path(matches, "aux-base"),
117         stage_id: matches.opt_str("stage-id").unwrap(),
118         mode: str_mode(matches.opt_str("mode").unwrap()),
119         run_ignored: matches.opt_present("ignored"),
120         filter:
121             if !matches.free.is_empty() {
122                  Some(matches.free[0].clone())
123             } else {
124                 None
125             },
126         logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
127         save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
128         ratchet_metrics:
129             matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
130         ratchet_noise_percent:
131             matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::<f64>(s)),
132         runtool: matches.opt_str("runtool"),
133         host_rustcflags: matches.opt_str("host-rustcflags"),
134         target_rustcflags: matches.opt_str("target-rustcflags"),
135         jit: matches.opt_present("jit"),
136         target: opt_str2(matches.opt_str("target")).to_str(),
137         host: opt_str2(matches.opt_str("host")).to_str(),
138         adb_path: opt_str2(matches.opt_str("adb-path")).to_str(),
139         adb_test_dir:
140             opt_str2(matches.opt_str("adb-test-dir")).to_str(),
141         adb_device_status:
142             "arm-linux-androideabi" == opt_str2(matches.opt_str("target")) &&
143             "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
144             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
145         test_shard: test::opt_shard(matches.opt_str("test-shard")),
146         verbose: matches.opt_present("verbose")
147     }
148 }
149
150 pub fn log_config(config: &config) {
151     let c = config;
152     logv(c, format!("configuration:"));
153     logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
154     logv(c, format!("run_lib_path: {}", config.run_lib_path));
155     logv(c, format!("rustc_path: {}", config.rustc_path.display()));
156     logv(c, format!("src_base: {}", config.src_base.display()));
157     logv(c, format!("build_base: {}", config.build_base.display()));
158     logv(c, format!("stage_id: {}", config.stage_id));
159     logv(c, format!("mode: {}", mode_str(config.mode)));
160     logv(c, format!("run_ignored: {}", config.run_ignored));
161     logv(c, format!("filter: {}", opt_str(&config.filter)));
162     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
163     logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
164     logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
165     logv(c, format!("jit: {}", config.jit));
166     logv(c, format!("target: {}", config.target));
167     logv(c, format!("host: {}", config.host));
168     logv(c, format!("adb_path: {}", config.adb_path));
169     logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
170     logv(c, format!("adb_device_status: {}", config.adb_device_status));
171     match config.test_shard {
172         None => logv(c, ~"test_shard: (all)"),
173         Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
174     }
175     logv(c, format!("verbose: {}", config.verbose));
176     logv(c, format!("\n"));
177 }
178
179 pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
180     match *maybestr {
181         None => "(none)",
182         Some(ref s) => {
183             let s: &'a str = *s;
184             s
185         }
186     }
187 }
188
189 pub fn opt_str2(maybestr: Option<~str>) -> ~str {
190     match maybestr { None => ~"(none)", Some(s) => { s } }
191 }
192
193 pub fn str_mode(s: ~str) -> mode {
194     match s.as_slice() {
195       "compile-fail" => mode_compile_fail,
196       "run-fail" => mode_run_fail,
197       "run-pass" => mode_run_pass,
198       "pretty" => mode_pretty,
199       "debug-info" => mode_debug_info,
200       "codegen" => mode_codegen,
201       _ => fail!("invalid mode")
202     }
203 }
204
205 pub fn mode_str(mode: mode) -> ~str {
206     match mode {
207       mode_compile_fail => ~"compile-fail",
208       mode_run_fail => ~"run-fail",
209       mode_run_pass => ~"run-pass",
210       mode_pretty => ~"pretty",
211       mode_debug_info => ~"debug-info",
212       mode_codegen => ~"codegen",
213     }
214 }
215
216 pub fn run_tests(config: &config) {
217     if config.target == ~"arm-linux-androideabi" {
218         match config.mode{
219             mode_debug_info => {
220                 println!("arm-linux-androideabi debug-info \
221                          test uses tcp 5039 port. please reserve it");
222             }
223             _ =>{}
224         }
225
226         //arm-linux-androideabi debug-info test uses remote debugger
227         //so, we test 1 task at once.
228         // also trying to isolate problems with adb_run_wrapper.sh ilooping
229         os::setenv("RUST_TEST_TASKS","1");
230     }
231
232     let opts = test_opts(config);
233     let tests = make_tests(config);
234     // sadly osx needs some file descriptor limits raised for running tests in
235     // parallel (especially when we have lots and lots of child processes).
236     // For context, see #8904
237     io::test::raise_fd_limit();
238     let res = test::run_tests_console(&opts, tests);
239     match res {
240         Ok(true) => {}
241         Ok(false) => fail!("Some tests failed"),
242         Err(e) => {
243             println!("I/O failure during tests: {}", e);
244         }
245     }
246 }
247
248 pub fn test_opts(config: &config) -> test::TestOpts {
249     test::TestOpts {
250         filter: config.filter.clone(),
251         run_ignored: config.run_ignored,
252         logfile: config.logfile.clone(),
253         run_tests: true,
254         run_benchmarks: true,
255         ratchet_metrics: config.ratchet_metrics.clone(),
256         ratchet_noise_percent: config.ratchet_noise_percent.clone(),
257         save_metrics: config.save_metrics.clone(),
258         test_shard: config.test_shard.clone()
259     }
260 }
261
262 pub fn make_tests(config: &config) -> ~[test::TestDescAndFn] {
263     debug!("making tests from {}",
264            config.src_base.display());
265     let mut tests = ~[];
266     let dirs = fs::readdir(&config.src_base).unwrap();
267     for file in dirs.iter() {
268         let file = file.clone();
269         debug!("inspecting file {}", file.display());
270         if is_test(config, &file) {
271             let t = make_test(config, &file, || {
272                 match config.mode {
273                     mode_codegen => make_metrics_test_closure(config, &file),
274                     _ => make_test_closure(config, &file)
275                 }
276             });
277             tests.push(t)
278         }
279     }
280     tests
281 }
282
283 pub fn is_test(config: &config, testfile: &Path) -> bool {
284     // Pretty-printer does not work with .rc files yet
285     let valid_extensions =
286         match config.mode {
287           mode_pretty => ~[~".rs"],
288           _ => ~[~".rc", ~".rs"]
289         };
290     let invalid_prefixes = ~[~".", ~"#", ~"~"];
291     let name = testfile.filename_str().unwrap();
292
293     let mut valid = false;
294
295     for ext in valid_extensions.iter() {
296         if name.ends_with(*ext) { valid = true; }
297     }
298
299     for pre in invalid_prefixes.iter() {
300         if name.starts_with(*pre) { valid = false; }
301     }
302
303     return valid;
304 }
305
306 pub fn make_test(config: &config, testfile: &Path, f: || -> test::TestFn)
307                  -> test::TestDescAndFn {
308     test::TestDescAndFn {
309         desc: test::TestDesc {
310             name: make_test_name(config, testfile),
311             ignore: header::is_test_ignored(config, testfile),
312             should_fail: false
313         },
314         testfn: f(),
315     }
316 }
317
318 pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName {
319
320     // Try to elide redundant long paths
321     fn shorten(path: &Path) -> ~str {
322         let filename = path.filename_str();
323         let p = path.dir_path();
324         let dir = p.filename_str();
325         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
326     }
327
328     test::DynTestName(format!("[{}] {}",
329                               mode_str(config.mode),
330                               shorten(testfile)))
331 }
332
333 pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn {
334     let config = (*config).clone();
335     // FIXME (#9639): This needs to handle non-utf8 paths
336     let testfile = testfile.as_str().unwrap().to_owned();
337     test::DynTestFn(proc() { runtest::run(config, testfile) })
338 }
339
340 pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn {
341     let config = (*config).clone();
342     // FIXME (#9639): This needs to handle non-utf8 paths
343     let testfile = testfile.as_str().unwrap().to_owned();
344     test::DynMetricFn(proc(mm) {
345         runtest::run_metrics(config, testfile, mm)
346     })
347 }