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)]
36 use std::path::{Path, PathBuf};
37 use std::thunk::Thunk;
38 use getopts::{optopt, optflag, reqopt};
40 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
51 let config = parse_config(env::args().collect());
53 if config.valgrind_path.is_none() && config.force_valgrind {
54 panic!("Can't find Valgrind to run Valgrind tests");
61 pub fn parse_config(args: Vec<String> ) -> Config {
63 let groups : Vec<getopts::OptGroup> =
64 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
65 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
66 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
67 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
68 optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
69 optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
70 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
71 reqopt("", "src-base", "directory to scan for test files", "PATH"),
72 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
73 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
74 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
75 reqopt("", "mode", "which sort of compile tests to run",
76 "(compile-fail|parse-fail|run-fail|run-pass|run-pass-valgrind|pretty|debug-info)"),
77 optflag("", "ignored", "run tests marked as ignored"),
78 optopt("", "runtool", "supervisor program to run tests under \
79 (eg. emulator, valgrind)", "PROGRAM"),
80 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
81 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
82 optflag("", "verbose", "run tests verbosely, showing all output"),
83 optopt("", "logfile", "file to log test execution to", "FILE"),
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 optflag("h", "help", "show this message"));
95 assert!(!args.is_empty());
96 let argv0 = args[0].clone();
97 let args_ = args.tail();
98 if args[1] == "-h" || args[1] == "--help" {
99 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
100 println!("{}", getopts::usage(&message, &groups));
106 &match getopts::getopts(args_, &groups) {
108 Err(f) => panic!("{:?}", f)
111 if matches.opt_present("h") || matches.opt_present("help") {
112 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
113 println!("{}", getopts::usage(&message, &groups));
118 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
119 match m.opt_str(nm) {
120 Some(s) => PathBuf::from(&s),
121 None => panic!("no option (=path) found for {}", nm),
125 let filter = if !matches.free.is_empty() {
126 Some(matches.free[0].clone())
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| PathBuf::from(&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| PathBuf::from(&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: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
144 run_ignored: matches.opt_present("ignored"),
146 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
147 runtool: matches.opt_str("runtool"),
148 host_rustcflags: matches.opt_str("host-rustcflags"),
149 target_rustcflags: matches.opt_str("target-rustcflags"),
150 jit: matches.opt_present("jit"),
151 target: opt_str2(matches.opt_str("target")),
152 host: opt_str2(matches.opt_str("host")),
153 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
154 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
155 android_cross_path: opt_path(matches, "android-cross-path"),
156 adb_path: opt_str2(matches.opt_str("adb-path")),
157 adb_test_dir: format!("{}/{}",
158 opt_str2(matches.opt_str("adb-test-dir")),
159 opt_str2(matches.opt_str("target"))),
161 opt_str2(matches.opt_str("target")).contains("android") &&
162 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
163 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
164 lldb_python_dir: matches.opt_str("lldb-python-dir"),
165 verbose: matches.opt_present("verbose"),
169 pub fn log_config(config: &Config) {
171 logv(c, format!("configuration:"));
172 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
173 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
174 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
175 logv(c, format!("src_base: {:?}", config.src_base.display()));
176 logv(c, format!("build_base: {:?}", config.build_base.display()));
177 logv(c, format!("stage_id: {}", config.stage_id));
178 logv(c, format!("mode: {}", config.mode));
179 logv(c, format!("run_ignored: {}", config.run_ignored));
180 logv(c, format!("filter: {}",
181 opt_str(&config.filter
183 .map(|re| re.to_string()))));
184 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
185 logv(c, format!("host-rustcflags: {}",
186 opt_str(&config.host_rustcflags)));
187 logv(c, format!("target-rustcflags: {}",
188 opt_str(&config.target_rustcflags)));
189 logv(c, format!("jit: {}", config.jit));
190 logv(c, format!("target: {}", config.target));
191 logv(c, format!("host: {}", config.host));
192 logv(c, format!("android-cross-path: {:?}",
193 config.android_cross_path.display()));
194 logv(c, format!("adb_path: {:?}", config.adb_path));
195 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
196 logv(c, format!("adb_device_status: {}",
197 config.adb_device_status));
198 logv(c, format!("verbose: {}", config.verbose));
199 logv(c, format!("\n"));
202 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
209 pub fn opt_str2(maybestr: Option<String>) -> String {
211 None => "(none)".to_string(),
216 pub fn run_tests(config: &Config) {
217 if config.target.contains("android") {
220 println!("{} debug-info test uses tcp 5039 port.\
221 please reserve it", config.target);
226 // android debug-info test uses remote debugger
227 // so, we test 1 task at once.
228 // also trying to isolate problems with adb_run_wrapper.sh ilooping
229 env::set_var("RUST_TEST_THREADS","1");
234 // Some older versions of LLDB seem to have problems with multiple
235 // instances running in parallel, so only run one test task at a
237 env::set_var("RUST_TEST_THREADS", "1");
239 _ => { /* proceed */ }
242 let opts = test_opts(config);
243 let tests = make_tests(config);
244 // sadly osx needs some file descriptor limits raised for running tests in
245 // parallel (especially when we have lots and lots of child processes).
246 // For context, see #8904
248 fn raise_fd_limit() {
249 std::old_io::test::raise_fd_limit();
252 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
253 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
254 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
255 let res = test::run_tests_console(&opts, tests.into_iter().collect());
258 Ok(false) => panic!("Some tests failed"),
260 println!("I/O failure during tests: {:?}", e);
265 pub fn test_opts(config: &Config) -> test::TestOpts {
267 filter: match config.filter {
269 Some(ref filter) => Some(filter.clone()),
271 run_ignored: config.run_ignored,
272 logfile: config.logfile.clone(),
274 run_benchmarks: true,
275 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
276 color: test::AutoColor,
280 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
281 debug!("making tests from {:?}",
282 config.src_base.display());
283 let mut tests = Vec::new();
284 let dirs = fs::read_dir(&config.src_base).unwrap();
286 let file = file.unwrap().path();
287 debug!("inspecting file {:?}", file.display());
288 if is_test(config, &file) {
289 let t = make_test(config, &file, || {
291 Codegen => make_metrics_test_closure(config, &file),
292 _ => make_test_closure(config, &file)
301 pub fn is_test(config: &Config, testfile: &Path) -> bool {
302 // Pretty-printer does not work with .rc files yet
303 let valid_extensions =
305 Pretty => vec!(".rs".to_string()),
306 _ => vec!(".rc".to_string(), ".rs".to_string())
308 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
309 let name = testfile.file_name().unwrap().to_str().unwrap();
311 let mut valid = false;
313 for ext in &valid_extensions {
314 if name.ends_with(ext) {
319 for pre in &invalid_prefixes {
320 if name.starts_with(pre) {
328 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
329 F: FnOnce() -> test::TestFn,
331 test::TestDescAndFn {
332 desc: test::TestDesc {
333 name: make_test_name(config, testfile),
334 ignore: header::is_test_ignored(config, testfile),
335 should_panic: test::ShouldPanic::No,
341 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
343 // Try to elide redundant long paths
344 fn shorten(path: &Path) -> String {
345 let filename = path.file_name().unwrap().to_str();
346 let p = path.parent().unwrap();
347 let dir = p.file_name().unwrap().to_str();
348 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
351 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
354 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
355 let config = (*config).clone();
356 let testfile = testfile.to_path_buf();
357 test::DynTestFn(Thunk::new(move || {
358 runtest::run(config, &testfile)
362 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
363 let config = (*config).clone();
364 let testfile = testfile.to_path_buf();
365 test::DynMetricFn(box move |mm: &mut test::MetricMap| {
366 runtest::run_metrics(config, &testfile, mm)
370 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
371 match full_version_line {
372 Some(ref full_version_line)
373 if full_version_line.trim().len() > 0 => {
374 let full_version_line = full_version_line.trim();
376 // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
377 for (pos, c) in full_version_line.char_indices() {
378 if !c.is_digit(10) { continue }
379 if pos + 2 >= full_version_line.len() { continue }
380 if full_version_line.char_at(pos + 1) != '.' { continue }
381 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
382 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
385 if pos + 3 < full_version_line.len() &&
386 full_version_line.char_at(pos + 3).is_digit(10) {
389 return Some(full_version_line[pos..pos+3].to_string());
391 println!("Could not extract GDB version from line '{}'",
399 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
400 // Extract the major LLDB version from the given version string.
401 // LLDB version strings are different for Apple and non-Apple platforms.
402 // At the moment, this function only supports the Apple variant, which looks
405 // LLDB-179.5 (older versions)
406 // lldb-300.2.51 (new versions)
408 // We are only interested in the major version number, so this function
409 // will return `Some("179")` and `Some("300")` respectively.
411 match full_version_line {
412 Some(ref full_version_line)
413 if full_version_line.trim().len() > 0 => {
414 let full_version_line = full_version_line.trim();
416 for (pos, l) in full_version_line.char_indices() {
417 if l != 'l' && l != 'L' { continue }
418 if pos + 5 >= full_version_line.len() { continue }
419 let l = full_version_line.char_at(pos + 1);
420 if l != 'l' && l != 'L' { continue }
421 let d = full_version_line.char_at(pos + 2);
422 if d != 'd' && d != 'D' { continue }
423 let b = full_version_line.char_at(pos + 3);
424 if b != 'b' && b != 'B' { continue }
425 let dash = full_version_line.char_at(pos + 4);
426 if dash != '-' { continue }
428 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
430 }).collect::<String>();
431 if vers.len() > 0 { return Some(vers) }
433 println!("Could not extract LLDB version from line '{}'",