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