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