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