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 #![allow(unknown_features)]
13 #![feature(slicing_syntax, unboxed_closures)]
14 #![feature(box_syntax)]
30 use std::str::FromStr;
31 use std::thunk::Thunk;
32 use getopts::{optopt, optflag, reqopt};
34 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
46 let args = os::args();
47 let config = parse_config(args);
49 if config.valgrind_path.is_none() && config.force_valgrind {
50 panic!("Can't find Valgrind to run Valgrind tests");
57 pub fn parse_config(args: Vec<String> ) -> Config {
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"));
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()));
107 &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
109 Err(f) => panic!("{:?}", f)
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()));
119 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
120 Path::new(m.opt_str(nm).unwrap())
123 let filter = if !matches.free.is_empty() {
124 let s = matches.free[0].as_slice();
125 match regex::Regex::new(s) {
128 println!("failed to parse filter /{}/: {:?}", s, e);
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")
150 .as_slice()).expect("invalid mode"),
151 run_ignored: matches.opt_present("ignored"),
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)),
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")),
173 "arm-linux-androideabi" ==
174 opt_str2(matches.opt_str("target")).as_slice() &&
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"),
184 pub fn log_config(config: &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
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))
217 logv(c, format!("verbose: {}", config.verbose));
218 logv(c, format!("\n"));
221 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
224 Some(ref s) => s.as_slice(),
228 pub fn opt_str2(maybestr: Option<String>) -> String {
230 None => "(none)".to_string(),
235 pub fn run_tests(config: &Config) {
236 if config.target.as_slice() == "arm-linux-androideabi" {
239 println!("arm-linux-androideabi debug-info \
240 test uses tcp 5039 port. please reserve it");
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");
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
256 os::setenv("RUST_TEST_TASKS", "1");
258 _ => { /* proceed */ }
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());
270 Ok(false) => panic!("Some tests failed"),
272 println!("I/O failure during tests: {:?}", e);
277 pub fn test_opts(config: &Config) -> test::TestOpts {
279 filter: match config.filter {
281 Some(ref filter) => Some(filter.clone()),
283 run_ignored: config.run_ignored,
284 logfile: config.logfile.clone(),
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(),
292 color: test::AutoColor,
295 show_all_stats: false,
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, || {
310 Codegen => make_metrics_test_closure(config, &file),
311 _ => make_test_closure(config, &file)
320 pub fn is_test(config: &Config, testfile: &Path) -> bool {
321 // Pretty-printer does not work with .rc files yet
322 let valid_extensions =
324 Pretty => vec!(".rs".to_string()),
325 _ => vec!(".rc".to_string(), ".rs".to_string())
327 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
328 let name = testfile.filename_str().unwrap();
330 let mut valid = false;
332 for ext in valid_extensions.iter() {
333 if name.ends_with(ext.as_slice()) {
338 for pre in invalid_prefixes.iter() {
339 if name.starts_with(pre.as_slice()) {
347 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
348 F: FnOnce() -> test::TestFn,
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,
360 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
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(""))
370 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
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)
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)
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();
397 let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
399 match re.captures(full_version_line) {
401 Some(captures.at(2).unwrap_or("").to_string())
404 println!("Could not extract GDB version from line '{}'",
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
420 // LLDB-179.5 (older versions)
421 // lldb-300.2.51 (new versions)
423 // We are only interested in the major version number, so this function
424 // will return `Some("179")` and `Some("300")` respectively.
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();
431 let re = Regex::new(r"[Ll][Ll][Dd][Bb]-([0-9]+)").unwrap();
433 match re.captures(full_version_line) {
435 Some(captures.at(1).unwrap_or("").to_string())
438 println!("Could not extract LLDB version from line '{}'",