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