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