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)]
15 #![feature(rustc_private)]
18 #![feature(question_mark)]
32 use std::path::{Path, PathBuf};
33 use getopts::{optopt, optflag, reqopt};
35 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
48 let config = parse_config(env::args().collect());
50 if config.valgrind_path.is_none() && config.force_valgrind {
51 panic!("Can't find Valgrind to run Valgrind tests");
58 pub fn parse_config(args: Vec<String> ) -> Config {
60 let groups : Vec<getopts::OptGroup> =
61 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
62 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
63 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
64 reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
65 reqopt("", "python", "path to python to use for doc 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|parse-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 optflag("", "quiet", "print one character per test instead of one line"),
82 optopt("", "logfile", "file to log test execution to", "FILE"),
83 optopt("", "target", "the target to build for", "TARGET"),
84 optopt("", "host", "the host to build for", "HOST"),
85 optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
86 optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
87 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
88 optopt("", "adb-path", "path to the android debugger", "PATH"),
89 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
90 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
91 optflag("h", "help", "show this message"));
93 let (argv0, args_) = args.split_first().unwrap();
94 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
95 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
96 println!("{}", getopts::usage(&message, &groups));
102 &match getopts::getopts(args_, &groups) {
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, &groups));
114 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
115 match m.opt_str(nm) {
116 Some(s) => PathBuf::from(&s),
117 None => panic!("no option (=path) found for {}", nm),
122 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
123 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
124 rustc_path: opt_path(matches, "rustc-path"),
125 rustdoc_path: opt_path(matches, "rustdoc-path"),
126 python: matches.opt_str("python").unwrap(),
127 valgrind_path: matches.opt_str("valgrind-path"),
128 force_valgrind: matches.opt_present("force-valgrind"),
129 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
130 src_base: opt_path(matches, "src-base"),
131 build_base: opt_path(matches, "build-base"),
132 aux_base: opt_path(matches, "aux-base"),
133 stage_id: matches.opt_str("stage-id").unwrap(),
134 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
135 run_ignored: matches.opt_present("ignored"),
136 filter: matches.free.first().cloned(),
137 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
138 runtool: matches.opt_str("runtool"),
139 host_rustcflags: matches.opt_str("host-rustcflags"),
140 target_rustcflags: matches.opt_str("target-rustcflags"),
141 target: opt_str2(matches.opt_str("target")),
142 host: opt_str2(matches.opt_str("host")),
143 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
144 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
145 android_cross_path: opt_path(matches, "android-cross-path"),
146 adb_path: opt_str2(matches.opt_str("adb-path")),
147 adb_test_dir: format!("{}/{}",
148 opt_str2(matches.opt_str("adb-test-dir")),
149 opt_str2(matches.opt_str("target"))),
151 opt_str2(matches.opt_str("target")).contains("android") &&
152 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
153 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
154 lldb_python_dir: matches.opt_str("lldb-python-dir"),
155 verbose: matches.opt_present("verbose"),
156 quiet: matches.opt_present("quiet"),
160 pub fn log_config(config: &Config) {
162 logv(c, format!("configuration:"));
163 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
164 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
165 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
166 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
167 logv(c, format!("src_base: {:?}", config.src_base.display()));
168 logv(c, format!("build_base: {:?}", config.build_base.display()));
169 logv(c, format!("stage_id: {}", config.stage_id));
170 logv(c, format!("mode: {}", config.mode));
171 logv(c, format!("run_ignored: {}", config.run_ignored));
172 logv(c, format!("filter: {}",
173 opt_str(&config.filter
175 .map(|re| re.to_owned()))));
176 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
177 logv(c, format!("host-rustcflags: {}",
178 opt_str(&config.host_rustcflags)));
179 logv(c, format!("target-rustcflags: {}",
180 opt_str(&config.target_rustcflags)));
181 logv(c, format!("target: {}", config.target));
182 logv(c, format!("host: {}", config.host));
183 logv(c, format!("android-cross-path: {:?}",
184 config.android_cross_path.display()));
185 logv(c, format!("adb_path: {:?}", config.adb_path));
186 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
187 logv(c, format!("adb_device_status: {}",
188 config.adb_device_status));
189 logv(c, format!("verbose: {}", config.verbose));
190 logv(c, format!("quiet: {}", config.quiet));
191 logv(c, format!("\n"));
194 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
201 pub fn opt_str2(maybestr: Option<String>) -> String {
203 None => "(none)".to_owned(),
208 pub fn run_tests(config: &Config) {
209 if config.target.contains("android") {
210 if let DebugInfoGdb = config.mode {
211 println!("{} debug-info test uses tcp 5039 port.\
212 please reserve it", config.target);
215 // android debug-info test uses remote debugger
216 // so, we test 1 thread at once.
217 // also trying to isolate problems with adb_run_wrapper.sh ilooping
218 env::set_var("RUST_TEST_THREADS","1");
223 // Some older versions of LLDB seem to have problems with multiple
224 // instances running in parallel, so only run one test thread at a
226 env::set_var("RUST_TEST_THREADS", "1");
228 _ => { /* proceed */ }
231 let opts = test_opts(config);
232 let tests = make_tests(config);
233 // sadly osx needs some file descriptor limits raised for running tests in
234 // parallel (especially when we have lots and lots of child processes).
235 // For context, see #8904
236 unsafe { raise_fd_limit::raise_fd_limit(); }
237 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
238 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
239 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
240 let res = test::run_tests_console(&opts, tests.into_iter().collect());
243 Ok(false) => panic!("Some tests failed"),
245 println!("I/O failure during tests: {:?}", e);
250 pub fn test_opts(config: &Config) -> test::TestOpts {
252 filter: config.filter.clone(),
253 run_ignored: config.run_ignored,
255 logfile: config.logfile.clone(),
257 bench_benchmarks: true,
258 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
259 color: test::AutoColor,
263 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
264 debug!("making tests from {:?}",
265 config.src_base.display());
266 let mut tests = Vec::new();
267 collect_tests_from_dir(config,
276 fn collect_tests_from_dir(config: &Config,
279 relative_dir_path: &Path,
280 tests: &mut Vec<test::TestDescAndFn>)
282 // Ignore directories that contain a file
283 // `compiletest-ignore-dir`.
284 for file in fs::read_dir(dir)? {
286 if file.file_name() == *"compiletest-ignore-dir" {
291 let dirs = fs::read_dir(dir)?;
294 let file_path = file.path();
295 debug!("inspecting file {:?}", file_path.display());
296 if is_test(config, &file_path) {
297 // If we find a test foo/bar.rs, we have to build the
298 // output directory `$build/foo` so we can write
299 // `$build/foo/bar` into it. We do this *now* in this
300 // sequential loop because otherwise, if we do it in the
301 // tests themselves, they race for the privilege of
302 // creating the directories and sometimes fail randomly.
303 let build_dir = config.build_base.join(&relative_dir_path);
304 fs::create_dir_all(&build_dir).unwrap();
306 let paths = TestPaths {
308 base: base.to_path_buf(),
309 relative_dir: relative_dir_path.to_path_buf(),
311 tests.push(make_test(config, &paths))
312 } else if file_path.is_dir() {
313 let relative_file_path = relative_dir_path.join(file.file_name());
314 collect_tests_from_dir(config,
324 pub fn is_test(config: &Config, testfile: &Path) -> bool {
325 // Pretty-printer does not work with .rc files yet
326 let valid_extensions =
328 Pretty => vec!(".rs".to_owned()),
329 _ => vec!(".rc".to_owned(), ".rs".to_owned())
331 let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
332 let name = testfile.file_name().unwrap().to_str().unwrap();
334 let mut valid = false;
336 for ext in &valid_extensions {
337 if name.ends_with(ext) {
342 for pre in &invalid_prefixes {
343 if name.starts_with(pre) {
351 pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
352 let early_props = header::early_props(config, &testpaths.file);
354 // The `should-fail` annotation doesn't apply to pretty tests,
355 // since we run the pretty printer across all tests by default.
356 // If desired, we could add a `should-fail-pretty` annotation.
357 let should_panic = match config.mode {
358 Pretty => test::ShouldPanic::No,
359 _ => if early_props.should_fail {
360 test::ShouldPanic::Yes
362 test::ShouldPanic::No
366 test::TestDescAndFn {
367 desc: test::TestDesc {
368 name: make_test_name(config, testpaths),
369 ignore: early_props.ignore,
370 should_panic: should_panic,
372 testfn: make_test_closure(config, testpaths),
376 pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
377 // Convert a complete path to something like
379 // run-pass/foo/bar/baz.rs
381 PathBuf::from(config.mode.to_string())
382 .join(&testpaths.relative_dir)
383 .join(&testpaths.file.file_name().unwrap());
384 test::DynTestName(format!("[{}] {}", config.mode, path.display()))
387 pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
388 let config = config.clone();
389 let testpaths = testpaths.clone();
390 test::DynTestFn(Box::new(move || {
391 runtest::run(config, &testpaths)
395 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
396 match full_version_line {
397 Some(ref full_version_line)
398 if !full_version_line.trim().is_empty() => {
399 let full_version_line = full_version_line.trim();
401 // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
402 for (pos, c) in full_version_line.char_indices() {
403 if !c.is_digit(10) { continue }
404 if pos + 2 >= full_version_line.len() { continue }
405 if full_version_line.char_at(pos + 1) != '.' { continue }
406 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
407 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
410 let mut end = pos + 3;
411 while end < full_version_line.len() &&
412 full_version_line.char_at(end).is_digit(10) {
415 return Some(full_version_line[pos..end].to_owned());
417 println!("Could not extract GDB version from line '{}'",
425 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
426 // Extract the major LLDB version from the given version string.
427 // LLDB version strings are different for Apple and non-Apple platforms.
428 // At the moment, this function only supports the Apple variant, which looks
431 // LLDB-179.5 (older versions)
432 // lldb-300.2.51 (new versions)
434 // We are only interested in the major version number, so this function
435 // will return `Some("179")` and `Some("300")` respectively.
437 if let Some(ref full_version_line) = full_version_line {
438 if !full_version_line.trim().is_empty() {
439 let full_version_line = full_version_line.trim();
441 for (pos, l) in full_version_line.char_indices() {
442 if l != 'l' && l != 'L' { continue }
443 if pos + 5 >= full_version_line.len() { continue }
444 let l = full_version_line.char_at(pos + 1);
445 if l != 'l' && l != 'L' { continue }
446 let d = full_version_line.char_at(pos + 2);
447 if d != 'd' && d != 'D' { continue }
448 let b = full_version_line.char_at(pos + 3);
449 if b != 'b' && b != 'B' { continue }
450 let dash = full_version_line.char_at(pos + 4);
451 if dash != '-' { continue }
453 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
455 }).collect::<String>();
456 if !vers.is_empty() { return Some(vers) }
458 println!("Could not extract LLDB version from line '{}'",