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