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"]
13 #![feature(box_syntax)]
14 #![feature(collections)]
18 #![feature(rustc_private)]
19 #![feature(unboxed_closures)]
35 use std::path::{Path, PathBuf};
36 use std::thunk::Thunk;
37 use getopts::{optopt, optflag, reqopt};
39 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
50 let config = parse_config(env::args().collect());
52 if config.valgrind_path.is_none() && config.force_valgrind {
53 panic!("Can't find Valgrind to run Valgrind tests");
60 pub fn parse_config(args: Vec<String> ) -> Config {
62 let groups : Vec<getopts::OptGroup> =
63 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
64 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
65 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
66 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
67 optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
68 optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
69 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
70 reqopt("", "src-base", "directory to scan for test files", "PATH"),
71 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
72 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
73 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
74 reqopt("", "mode", "which sort of compile tests to run",
75 "(compile-fail|parse-fail|run-fail|run-pass|run-pass-valgrind|pretty|debug-info)"),
76 optflag("", "ignored", "run tests marked as ignored"),
77 optopt("", "runtool", "supervisor program to run tests under \
78 (eg. emulator, valgrind)", "PROGRAM"),
79 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
80 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
81 optflag("", "verbose", "run tests verbosely, showing all output"),
82 optopt("", "logfile", "file to log test execution to", "FILE"),
83 optflag("", "jit", "run tests under the JIT"),
84 optopt("", "target", "the target to build for", "TARGET"),
85 optopt("", "host", "the host to build for", "HOST"),
86 optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
87 optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
88 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
89 optopt("", "adb-path", "path to the android debugger", "PATH"),
90 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
91 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
92 optflag("h", "help", "show this message"));
94 assert!(!args.is_empty());
95 let argv0 = args[0].clone();
96 let args_ = args.tail();
97 if args[1] == "-h" || args[1] == "--help" {
98 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
99 println!("{}", getopts::usage(&message, &groups));
105 &match getopts::getopts(args_, &groups) {
107 Err(f) => panic!("{:?}", f)
110 if matches.opt_present("h") || matches.opt_present("help") {
111 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
112 println!("{}", getopts::usage(&message, &groups));
117 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
118 match m.opt_str(nm) {
119 Some(s) => PathBuf::new(&s),
120 None => panic!("no option (=path) found for {}", nm),
124 let filter = if !matches.free.is_empty() {
125 Some(matches.free[0].clone())
131 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
132 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
133 rustc_path: opt_path(matches, "rustc-path"),
134 clang_path: matches.opt_str("clang-path").map(|s| PathBuf::new(&s)),
135 valgrind_path: matches.opt_str("valgrind-path"),
136 force_valgrind: matches.opt_present("force-valgrind"),
137 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::new(&s)),
138 src_base: opt_path(matches, "src-base"),
139 build_base: opt_path(matches, "build-base"),
140 aux_base: opt_path(matches, "aux-base"),
141 stage_id: matches.opt_str("stage-id").unwrap(),
142 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
143 run_ignored: matches.opt_present("ignored"),
145 logfile: matches.opt_str("logfile").map(|s| PathBuf::new(&s)),
146 runtool: matches.opt_str("runtool"),
147 host_rustcflags: matches.opt_str("host-rustcflags"),
148 target_rustcflags: matches.opt_str("target-rustcflags"),
149 jit: matches.opt_present("jit"),
150 target: opt_str2(matches.opt_str("target")),
151 host: opt_str2(matches.opt_str("host")),
152 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
153 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
154 android_cross_path: opt_path(matches, "android-cross-path"),
155 adb_path: opt_str2(matches.opt_str("adb-path")),
156 adb_test_dir: format!("{}/{}",
157 opt_str2(matches.opt_str("adb-test-dir")),
158 opt_str2(matches.opt_str("target"))),
160 opt_str2(matches.opt_str("target")).contains("android") &&
161 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
162 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
163 lldb_python_dir: matches.opt_str("lldb-python-dir"),
164 verbose: matches.opt_present("verbose"),
168 pub fn log_config(config: &Config) {
170 logv(c, format!("configuration:"));
171 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
172 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
173 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
174 logv(c, format!("src_base: {:?}", config.src_base.display()));
175 logv(c, format!("build_base: {:?}", config.build_base.display()));
176 logv(c, format!("stage_id: {}", config.stage_id));
177 logv(c, format!("mode: {}", config.mode));
178 logv(c, format!("run_ignored: {}", config.run_ignored));
179 logv(c, format!("filter: {}",
180 opt_str(&config.filter
182 .map(|re| re.to_string()))));
183 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
184 logv(c, format!("host-rustcflags: {}",
185 opt_str(&config.host_rustcflags)));
186 logv(c, format!("target-rustcflags: {}",
187 opt_str(&config.target_rustcflags)));
188 logv(c, format!("jit: {}", config.jit));
189 logv(c, format!("target: {}", config.target));
190 logv(c, format!("host: {}", config.host));
191 logv(c, format!("android-cross-path: {:?}",
192 config.android_cross_path.display()));
193 logv(c, format!("adb_path: {:?}", config.adb_path));
194 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
195 logv(c, format!("adb_device_status: {}",
196 config.adb_device_status));
197 logv(c, format!("verbose: {}", config.verbose));
198 logv(c, format!("\n"));
201 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
208 pub fn opt_str2(maybestr: Option<String>) -> String {
210 None => "(none)".to_string(),
215 pub fn run_tests(config: &Config) {
216 if config.target.contains("android") {
219 println!("{} debug-info test uses tcp 5039 port.\
220 please reserve it", config.target);
225 // android debug-info test uses remote debugger
226 // so, we test 1 task at once.
227 // also trying to isolate problems with adb_run_wrapper.sh ilooping
228 env::set_var("RUST_TEST_THREADS","1");
233 // Some older versions of LLDB seem to have problems with multiple
234 // instances running in parallel, so only run one test task at a
236 env::set_var("RUST_TEST_THREADS", "1");
238 _ => { /* proceed */ }
241 let opts = test_opts(config);
242 let tests = make_tests(config);
243 // sadly osx needs some file descriptor limits raised for running tests in
244 // parallel (especially when we have lots and lots of child processes).
245 // For context, see #8904
247 fn raise_fd_limit() {
248 std::old_io::test::raise_fd_limit();
251 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
252 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
253 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
254 let res = test::run_tests_console(&opts, tests.into_iter().collect());
257 Ok(false) => panic!("Some tests failed"),
259 println!("I/O failure during tests: {:?}", e);
264 pub fn test_opts(config: &Config) -> test::TestOpts {
266 filter: match config.filter {
268 Some(ref filter) => Some(filter.clone()),
270 run_ignored: config.run_ignored,
271 logfile: config.logfile.clone(),
273 run_benchmarks: true,
274 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
275 color: test::AutoColor,
279 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
280 debug!("making tests from {:?}",
281 config.src_base.display());
282 let mut tests = Vec::new();
283 let dirs = fs::read_dir(&config.src_base).unwrap();
285 let file = file.unwrap().path();
286 debug!("inspecting file {:?}", file.display());
287 if is_test(config, &file) {
288 let t = make_test(config, &file, || {
290 Codegen => make_metrics_test_closure(config, &file),
291 _ => make_test_closure(config, &file)
300 pub fn is_test(config: &Config, testfile: &Path) -> bool {
301 // Pretty-printer does not work with .rc files yet
302 let valid_extensions =
304 Pretty => vec!(".rs".to_string()),
305 _ => vec!(".rc".to_string(), ".rs".to_string())
307 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
308 let name = testfile.file_name().unwrap().to_str().unwrap();
310 let mut valid = false;
312 for ext in &valid_extensions {
313 if name.ends_with(ext) {
318 for pre in &invalid_prefixes {
319 if name.starts_with(pre) {
327 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
328 F: FnOnce() -> test::TestFn,
330 test::TestDescAndFn {
331 desc: test::TestDesc {
332 name: make_test_name(config, testfile),
333 ignore: header::is_test_ignored(config, testfile),
334 should_panic: test::ShouldPanic::No,
340 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
342 // Try to elide redundant long paths
343 fn shorten(path: &Path) -> String {
344 let filename = path.file_name().unwrap().to_str();
345 let p = path.parent().unwrap();
346 let dir = p.file_name().unwrap().to_str();
347 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
350 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
353 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
354 let config = (*config).clone();
355 let testfile = testfile.to_path_buf();
356 test::DynTestFn(Thunk::new(move || {
357 runtest::run(config, &testfile)
361 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
362 let config = (*config).clone();
363 let testfile = testfile.to_path_buf();
364 test::DynMetricFn(box move |mm: &mut test::MetricMap| {
365 runtest::run_metrics(config, &testfile, mm)
369 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
370 match full_version_line {
371 Some(ref full_version_line)
372 if full_version_line.trim().len() > 0 => {
373 let full_version_line = full_version_line.trim();
375 // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
376 for (pos, c) in full_version_line.char_indices() {
377 if !c.is_digit(10) { continue }
378 if pos + 2 >= full_version_line.len() { continue }
379 if full_version_line.char_at(pos + 1) != '.' { continue }
380 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
381 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
384 if pos + 3 < full_version_line.len() &&
385 full_version_line.char_at(pos + 3).is_digit(10) {
388 return Some(full_version_line[pos..pos+3].to_string());
390 println!("Could not extract GDB version from line '{}'",
398 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
399 // Extract the major LLDB version from the given version string.
400 // LLDB version strings are different for Apple and non-Apple platforms.
401 // At the moment, this function only supports the Apple variant, which looks
404 // LLDB-179.5 (older versions)
405 // lldb-300.2.51 (new versions)
407 // We are only interested in the major version number, so this function
408 // will return `Some("179")` and `Some("300")` respectively.
410 match full_version_line {
411 Some(ref full_version_line)
412 if full_version_line.trim().len() > 0 => {
413 let full_version_line = full_version_line.trim();
415 for (pos, l) in full_version_line.char_indices() {
416 if l != 'l' && l != 'L' { continue }
417 if pos + 5 >= full_version_line.len() { continue }
418 let l = full_version_line.char_at(pos + 1);
419 if l != 'l' && l != 'L' { continue }
420 let d = full_version_line.char_at(pos + 2);
421 if d != 'd' && d != 'D' { continue }
422 let b = full_version_line.char_at(pos + 3);
423 if b != 'b' && b != 'B' { continue }
424 let dash = full_version_line.char_at(pos + 4);
425 if dash != '-' { continue }
427 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
429 }).collect::<String>();
430 if vers.len() > 0 { return Some(vers) }
432 println!("Could not extract LLDB version from line '{}'",