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.
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.
11 #![crate_type = "bin"]
12 #![feature(phase, slicing_syntax, globs, unboxed_closures)]
20 #[phase(plugin, link)]
32 use std::str::FromStr;
33 use std::thunk::Thunk;
34 use getopts::{optopt, optflag, reqopt};
36 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
48 let args = os::args();
49 let config = parse_config(args);
51 if config.valgrind_path.is_none() && config.force_valgrind {
52 panic!("Can't find Valgrind to run Valgrind tests");
59 pub fn parse_config(args: Vec<String> ) -> Config {
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"));
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()));
109 &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
111 Err(f) => panic!("{}", f)
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()));
121 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
122 Path::new(m.opt_str(nm).unwrap())
125 let filter = if !matches.free.is_empty() {
126 let s = matches.free[0].as_slice();
127 match regex::Regex::new(s) {
130 println!("failed to parse filter /{}/: {}", s, e);
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")
152 .as_slice()).expect("invalid mode"),
153 run_ignored: matches.opt_present("ignored"),
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)),
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")),
175 "arm-linux-androideabi" ==
176 opt_str2(matches.opt_str("target")).as_slice() &&
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"),
186 pub fn log_config(config: &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
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))
219 logv(c, format!("verbose: {}", config.verbose));
220 logv(c, format!("\n"));
223 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
226 Some(ref s) => s.as_slice(),
230 pub fn opt_str2(maybestr: Option<String>) -> String {
232 None => "(none)".to_string(),
237 pub fn run_tests(config: &Config) {
238 if config.target.as_slice() == "arm-linux-androideabi" {
241 println!("arm-linux-androideabi debug-info \
242 test uses tcp 5039 port. please reserve it");
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");
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
258 os::setenv("RUST_TEST_TASKS", "1");
260 _ => { /* proceed */ }
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());
272 Ok(false) => panic!("Some tests failed"),
274 println!("I/O failure during tests: {}", e);
279 pub fn test_opts(config: &Config) -> test::TestOpts {
281 filter: match config.filter {
283 Some(ref filter) => Some(filter.clone()),
285 run_ignored: config.run_ignored,
286 logfile: config.logfile.clone(),
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(),
294 color: test::AutoColor,
297 show_all_stats: false,
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, || {
312 Codegen => make_metrics_test_closure(config, &file),
313 _ => make_test_closure(config, &file)
322 pub fn is_test(config: &Config, testfile: &Path) -> bool {
323 // Pretty-printer does not work with .rc files yet
324 let valid_extensions =
326 Pretty => vec!(".rs".to_string()),
327 _ => vec!(".rc".to_string(), ".rs".to_string())
329 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
330 let name = testfile.filename_str().unwrap();
332 let mut valid = false;
334 for ext in valid_extensions.iter() {
335 if name.ends_with(ext.as_slice()) {
340 for pre in invalid_prefixes.iter() {
341 if name.starts_with(pre.as_slice()) {
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,
361 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
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(""))
371 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
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)
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)
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();
398 let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
400 match re.captures(full_version_line) {
402 Some(captures.at(2).unwrap_or("").to_string())
405 println!("Could not extract GDB version from line '{}'",
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
421 // LLDB-179.5 (older versions)
422 // lldb-300.2.51 (new versions)
424 // We are only interested in the major version number, so this function
425 // will return `Some("179")` and `Some("300")` respectively.
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();
432 let re = Regex::new(r"[Ll][Ll][Dd][Bb]-([0-9]+)").unwrap();
434 match re.captures(full_version_line) {
436 Some(captures.at(1).unwrap_or("").to_string())
439 println!("Could not extract LLDB version from line '{}'",