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