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 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"));
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()));
102 &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
104 Err(f) => panic!("{:?}", f)
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()));
114 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
115 Path::new(m.opt_str(nm).unwrap())
118 let filter = if !matches.free.is_empty() {
119 let s = matches.free[0].as_slice();
120 match regex::Regex::new(s) {
123 println!("failed to parse filter /{}/: {:?}", s, e);
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")
145 .as_slice()).expect("invalid mode"),
146 run_ignored: matches.opt_present("ignored"),
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")),
162 "arm-linux-androideabi" ==
163 opt_str2(matches.opt_str("target")).as_slice() &&
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"),
172 pub fn log_config(config: &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
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"));
205 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
208 Some(ref s) => s.as_slice(),
212 pub fn opt_str2(maybestr: Option<String>) -> String {
214 None => "(none)".to_string(),
219 pub fn run_tests(config: &Config) {
220 if config.target.as_slice() == "arm-linux-androideabi" {
223 println!("arm-linux-androideabi debug-info \
224 test uses tcp 5039 port. please reserve it");
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");
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
240 os::setenv("RUST_TEST_TASKS", "1");
242 _ => { /* proceed */ }
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());
254 Ok(false) => panic!("Some tests failed"),
256 println!("I/O failure during tests: {:?}", e);
261 pub fn test_opts(config: &Config) -> test::TestOpts {
263 filter: match config.filter {
265 Some(ref filter) => Some(filter.clone()),
267 run_ignored: config.run_ignored,
268 logfile: config.logfile.clone(),
270 run_benchmarks: true,
272 color: test::AutoColor,
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, || {
287 Codegen => make_metrics_test_closure(config, &file),
288 _ => make_test_closure(config, &file)
297 pub fn is_test(config: &Config, testfile: &Path) -> bool {
298 // Pretty-printer does not work with .rc files yet
299 let valid_extensions =
301 Pretty => vec!(".rs".to_string()),
302 _ => vec!(".rc".to_string(), ".rs".to_string())
304 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
305 let name = testfile.filename_str().unwrap();
307 let mut valid = false;
309 for ext in valid_extensions.iter() {
310 if name.ends_with(ext.as_slice()) {
315 for pre in invalid_prefixes.iter() {
316 if name.starts_with(pre.as_slice()) {
324 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
325 F: FnOnce() -> test::TestFn,
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,
337 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
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(""))
347 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
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)
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)
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();
374 let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
376 match re.captures(full_version_line) {
378 Some(captures.at(2).unwrap_or("").to_string())
381 println!("Could not extract GDB version from line '{}'",
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
397 // LLDB-179.5 (older versions)
398 // lldb-300.2.51 (new versions)
400 // We are only interested in the major version number, so this function
401 // will return `Some("179")` and `Some("300")` respectively.
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();
408 let re = Regex::new(r"[Ll][Ll][Dd][Bb]-([0-9]+)").unwrap();
410 match re.captures(full_version_line) {
412 Some(captures.at(1).unwrap_or("").to_string())
415 println!("Could not extract LLDB version from line '{}'",