]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
Kill RacyCell in favor of marking SyncSender explicitly Send.
[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           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("", "gdb-version", "the version of GDB used", "VERSION STRING"),
88           optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
89           optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
90           optopt("", "adb-path", "path to the android debugger", "PATH"),
91           optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
92           optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
93           optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
94           optflag("h", "help", "show this message"));
95
96     assert!(!args.is_empty());
97     let argv0 = args[0].clone();
98     let args_ = args.tail();
99     if args[1].as_slice() == "-h" || args[1].as_slice() == "--help" {
100         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
101         println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
102         println!("");
103         panic!()
104     }
105
106     let matches =
107         &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
108           Ok(m) => m,
109           Err(f) => panic!("{:?}", f)
110         };
111
112     if matches.opt_present("h") || matches.opt_present("help") {
113         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
114         println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
115         println!("");
116         panic!()
117     }
118
119     fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
120         Path::new(m.opt_str(nm).unwrap())
121     }
122
123     let filter = if !matches.free.is_empty() {
124         let s = matches.free[0].as_slice();
125         match regex::Regex::new(s) {
126             Ok(re) => Some(re),
127             Err(e) => {
128                 println!("failed to parse filter /{}/: {:?}", s, e);
129                 panic!()
130             }
131         }
132     } else {
133         None
134     };
135
136     Config {
137         compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
138         run_lib_path: matches.opt_str("run-lib-path").unwrap(),
139         rustc_path: opt_path(matches, "rustc-path"),
140         clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
141         valgrind_path: matches.opt_str("valgrind-path"),
142         force_valgrind: matches.opt_present("force-valgrind"),
143         llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
144         src_base: opt_path(matches, "src-base"),
145         build_base: opt_path(matches, "build-base"),
146         aux_base: opt_path(matches, "aux-base"),
147         stage_id: matches.opt_str("stage-id").unwrap(),
148         mode: FromStr::from_str(matches.opt_str("mode")
149                                        .unwrap()
150                                        .as_slice()).expect("invalid mode"),
151         run_ignored: matches.opt_present("ignored"),
152         filter: filter,
153         cfail_regex: Regex::new(errors::EXPECTED_PATTERN).unwrap(),
154         logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
155         save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
156         ratchet_metrics:
157             matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
158         ratchet_noise_percent:
159             matches.opt_str("ratchet-noise-percent")
160                    .and_then(|s| s.as_slice().parse::<f64>()),
161         runtool: matches.opt_str("runtool"),
162         host_rustcflags: matches.opt_str("host-rustcflags"),
163         target_rustcflags: matches.opt_str("target-rustcflags"),
164         jit: matches.opt_present("jit"),
165         target: opt_str2(matches.opt_str("target")),
166         host: opt_str2(matches.opt_str("host")),
167         gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
168         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
169         android_cross_path: opt_path(matches, "android-cross-path"),
170         adb_path: opt_str2(matches.opt_str("adb-path")),
171         adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
172         adb_device_status:
173             "arm-linux-androideabi" ==
174                 opt_str2(matches.opt_str("target")).as_slice() &&
175             "(none)" !=
176                 opt_str2(matches.opt_str("adb-test-dir")).as_slice() &&
177             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
178         lldb_python_dir: matches.opt_str("lldb-python-dir"),
179         test_shard: test::opt_shard(matches.opt_str("test-shard")),
180         verbose: matches.opt_present("verbose"),
181     }
182 }
183
184 pub fn log_config(config: &Config) {
185     let c = config;
186     logv(c, format!("configuration:"));
187     logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
188     logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
189     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
190     logv(c, format!("src_base: {:?}", config.src_base.display()));
191     logv(c, format!("build_base: {:?}", config.build_base.display()));
192     logv(c, format!("stage_id: {}", config.stage_id));
193     logv(c, format!("mode: {}", config.mode));
194     logv(c, format!("run_ignored: {}", config.run_ignored));
195     logv(c, format!("filter: {}",
196                     opt_str(&config.filter
197                                    .as_ref()
198                                    .map(|re| re.to_string()))));
199     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
200     logv(c, format!("host-rustcflags: {}",
201                     opt_str(&config.host_rustcflags)));
202     logv(c, format!("target-rustcflags: {}",
203                     opt_str(&config.target_rustcflags)));
204     logv(c, format!("jit: {}", config.jit));
205     logv(c, format!("target: {}", config.target));
206     logv(c, format!("host: {}", config.host));
207     logv(c, format!("android-cross-path: {:?}",
208                     config.android_cross_path.display()));
209     logv(c, format!("adb_path: {:?}", config.adb_path));
210     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
211     logv(c, format!("adb_device_status: {}",
212                     config.adb_device_status));
213     match config.test_shard {
214         None => logv(c, "test_shard: (all)".to_string()),
215         Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
216     }
217     logv(c, format!("verbose: {}", config.verbose));
218     logv(c, format!("\n"));
219 }
220
221 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
222     match *maybestr {
223         None => "(none)",
224         Some(ref s) => s.as_slice(),
225     }
226 }
227
228 pub fn opt_str2(maybestr: Option<String>) -> String {
229     match maybestr {
230         None => "(none)".to_string(),
231         Some(s) => s,
232     }
233 }
234
235 pub fn run_tests(config: &Config) {
236     if config.target.as_slice() == "arm-linux-androideabi" {
237         match config.mode {
238             DebugInfoGdb => {
239                 println!("arm-linux-androideabi debug-info \
240                          test uses tcp 5039 port. please reserve it");
241             }
242             _ =>{}
243         }
244
245         //arm-linux-androideabi debug-info test uses remote debugger
246         //so, we test 1 task at once.
247         // also trying to isolate problems with adb_run_wrapper.sh ilooping
248         os::setenv("RUST_TEST_TASKS","1");
249     }
250
251     match config.mode {
252         DebugInfoLldb => {
253             // Some older versions of LLDB seem to have problems with multiple
254             // instances running in parallel, so only run one test task at a
255             // time.
256             os::setenv("RUST_TEST_TASKS", "1");
257         }
258         _ => { /* proceed */ }
259     }
260
261     let opts = test_opts(config);
262     let tests = make_tests(config);
263     // sadly osx needs some file descriptor limits raised for running tests in
264     // parallel (especially when we have lots and lots of child processes).
265     // For context, see #8904
266     io::test::raise_fd_limit();
267     let res = test::run_tests_console(&opts, tests.into_iter().collect());
268     match res {
269         Ok(true) => {}
270         Ok(false) => panic!("Some tests failed"),
271         Err(e) => {
272             println!("I/O failure during tests: {:?}", e);
273         }
274     }
275 }
276
277 pub fn test_opts(config: &Config) -> test::TestOpts {
278     test::TestOpts {
279         filter: match config.filter {
280             None => None,
281             Some(ref filter) => Some(filter.clone()),
282         },
283         run_ignored: config.run_ignored,
284         logfile: config.logfile.clone(),
285         run_tests: true,
286         run_benchmarks: true,
287         ratchet_metrics: config.ratchet_metrics.clone(),
288         ratchet_noise_percent: config.ratchet_noise_percent.clone(),
289         save_metrics: config.save_metrics.clone(),
290         test_shard: config.test_shard.clone(),
291         nocapture: false,
292         color: test::AutoColor,
293         show_boxplot: false,
294         boxplot_width: 50,
295         show_all_stats: false,
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<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
348     F: FnOnce() -> test::TestFn,
349 {
350     test::TestDescAndFn {
351         desc: test::TestDesc {
352             name: make_test_name(config, testfile),
353             ignore: header::is_test_ignored(config, testfile),
354             should_fail: test::ShouldFail::No,
355         },
356         testfn: f(),
357     }
358 }
359
360 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
361
362     // Try to elide redundant long paths
363     fn shorten(path: &Path) -> String {
364         let filename = path.filename_str();
365         let p = path.dir_path();
366         let dir = p.filename_str();
367         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
368     }
369
370     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
371 }
372
373 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
374     let config = (*config).clone();
375     // FIXME (#9639): This needs to handle non-utf8 paths
376     let testfile = testfile.as_str().unwrap().to_string();
377     test::DynTestFn(Thunk::new(move || {
378         runtest::run(config, testfile)
379     }))
380 }
381
382 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
383     let config = (*config).clone();
384     // FIXME (#9639): This needs to handle non-utf8 paths
385     let testfile = testfile.as_str().unwrap().to_string();
386     test::DynMetricFn(box move |: mm: &mut test::MetricMap| {
387         runtest::run_metrics(config, testfile, mm)
388     })
389 }
390
391 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
392     match full_version_line {
393         Some(ref full_version_line)
394           if full_version_line.as_slice().trim().len() > 0 => {
395             let full_version_line = full_version_line.as_slice().trim();
396
397             let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
398
399             match re.captures(full_version_line) {
400                 Some(captures) => {
401                     Some(captures.at(2).unwrap_or("").to_string())
402                 }
403                 None => {
404                     println!("Could not extract GDB version from line '{}'",
405                              full_version_line);
406                     None
407                 }
408             }
409         },
410         _ => None
411     }
412 }
413
414 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
415     // Extract the major LLDB version from the given version string.
416     // LLDB version strings are different for Apple and non-Apple platforms.
417     // At the moment, this function only supports the Apple variant, which looks
418     // like this:
419     //
420     // LLDB-179.5 (older versions)
421     // lldb-300.2.51 (new versions)
422     //
423     // We are only interested in the major version number, so this function
424     // will return `Some("179")` and `Some("300")` respectively.
425
426     match full_version_line {
427         Some(ref full_version_line)
428           if full_version_line.as_slice().trim().len() > 0 => {
429             let full_version_line = full_version_line.as_slice().trim();
430
431             let re = Regex::new(r"[Ll][Ll][Dd][Bb]-([0-9]+)").unwrap();
432
433             match re.captures(full_version_line) {
434                 Some(captures) => {
435                     Some(captures.at(1).unwrap_or("").to_string())
436                 }
437                 None => {
438                     println!("Could not extract LLDB version from line '{}'",
439                              full_version_line);
440                     None
441                 }
442             }
443         },
444         _ => None
445     }
446 }