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_name = "compiletest"]
13 #![feature(box_syntax)]
14 #![feature(rustc_private)]
16 #![feature(question_mark)]
24 extern crate serialize as rustc_serialize;
30 extern crate env_logger;
33 use std::ffi::OsString;
36 use std::path::{Path, PathBuf};
37 use getopts::{optopt, optflag, reqopt};
39 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Mode};
43 use self::header::EarlyProps;
57 fn log_init() { env_logger::init().unwrap(); }
58 #[cfg(not(cargobuild))]
62 let config = parse_config(env::args().collect());
64 if config.valgrind_path.is_none() && config.force_valgrind {
65 panic!("Can't find Valgrind to run Valgrind tests");
72 pub fn parse_config(args: Vec<String> ) -> Config {
74 let groups : Vec<getopts::OptGroup> =
75 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
76 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
77 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
78 reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
79 reqopt("", "lldb-python", "path to python to use for doc tests", "PATH"),
80 reqopt("", "docck-python", "path to python to use for doc tests", "PATH"),
81 optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
82 optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
83 optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR"),
84 reqopt("", "src-base", "directory to scan for test files", "PATH"),
85 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
86 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
87 reqopt("", "mode", "which sort of compile tests to run",
88 "(compile-fail|parse-fail|run-fail|run-pass|\
89 run-pass-valgrind|pretty|debug-info|incremental|mir-opt)"),
90 optflag("", "ignored", "run tests marked as ignored"),
91 optopt("", "runtool", "supervisor program to run tests under \
92 (eg. emulator, valgrind)", "PROGRAM"),
93 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
94 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
95 optflag("", "verbose", "run tests verbosely, showing all output"),
96 optflag("", "quiet", "print one character per test instead of one line"),
97 optopt("", "logfile", "file to log test execution to", "FILE"),
98 optopt("", "target", "the target to build for", "TARGET"),
99 optopt("", "host", "the host to build for", "HOST"),
100 optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
101 optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
102 optopt("", "llvm-version", "the version of LLVM used", "VERSION STRING"),
103 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
104 optopt("", "adb-path", "path to the android debugger", "PATH"),
105 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
106 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
107 reqopt("", "cc", "path to a C compiler", "PATH"),
108 reqopt("", "cxx", "path to a C++ compiler", "PATH"),
109 reqopt("", "cflags", "flags for the C compiler", "FLAGS"),
110 reqopt("", "llvm-components", "list of LLVM components built in", "LIST"),
111 reqopt("", "llvm-cxxflags", "C++ flags for LLVM", "FLAGS"),
112 optflag("h", "help", "show this message"));
114 let (argv0, args_) = args.split_first().unwrap();
115 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
116 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
117 println!("{}", getopts::usage(&message, &groups));
123 &match getopts::getopts(args_, &groups) {
125 Err(f) => panic!("{:?}", f)
128 if matches.opt_present("h") || matches.opt_present("help") {
129 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
130 println!("{}", getopts::usage(&message, &groups));
135 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
136 match m.opt_str(nm) {
137 Some(s) => PathBuf::from(&s),
138 None => panic!("no option (=path) found for {}", nm),
142 fn make_absolute(path: PathBuf) -> PathBuf {
143 if path.is_relative() {
144 env::current_dir().unwrap().join(path)
151 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
152 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
153 rustc_path: opt_path(matches, "rustc-path"),
154 rustdoc_path: opt_path(matches, "rustdoc-path"),
155 lldb_python: matches.opt_str("lldb-python").unwrap(),
156 docck_python: matches.opt_str("docck-python").unwrap(),
157 valgrind_path: matches.opt_str("valgrind-path"),
158 force_valgrind: matches.opt_present("force-valgrind"),
159 llvm_filecheck: matches.opt_str("llvm-filecheck").map(|s| PathBuf::from(&s)),
160 src_base: opt_path(matches, "src-base"),
161 build_base: opt_path(matches, "build-base"),
162 stage_id: matches.opt_str("stage-id").unwrap(),
163 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
164 run_ignored: matches.opt_present("ignored"),
165 filter: matches.free.first().cloned(),
166 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
167 runtool: matches.opt_str("runtool"),
168 host_rustcflags: matches.opt_str("host-rustcflags"),
169 target_rustcflags: matches.opt_str("target-rustcflags"),
170 target: opt_str2(matches.opt_str("target")),
171 host: opt_str2(matches.opt_str("host")),
172 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
173 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
174 llvm_version: matches.opt_str("llvm-version"),
175 android_cross_path: opt_path(matches, "android-cross-path"),
176 adb_path: opt_str2(matches.opt_str("adb-path")),
177 adb_test_dir: format!("{}/{}",
178 opt_str2(matches.opt_str("adb-test-dir")),
179 opt_str2(matches.opt_str("target"))),
181 opt_str2(matches.opt_str("target")).contains("android") &&
182 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
183 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
184 lldb_python_dir: matches.opt_str("lldb-python-dir"),
185 verbose: matches.opt_present("verbose"),
186 quiet: matches.opt_present("quiet"),
188 cc: matches.opt_str("cc").unwrap(),
189 cxx: matches.opt_str("cxx").unwrap(),
190 cflags: matches.opt_str("cflags").unwrap(),
191 llvm_components: matches.opt_str("llvm-components").unwrap(),
192 llvm_cxxflags: matches.opt_str("llvm-cxxflags").unwrap(),
196 pub fn log_config(config: &Config) {
198 logv(c, format!("configuration:"));
199 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
200 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
201 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
202 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
203 logv(c, format!("src_base: {:?}", config.src_base.display()));
204 logv(c, format!("build_base: {:?}", config.build_base.display()));
205 logv(c, format!("stage_id: {}", config.stage_id));
206 logv(c, format!("mode: {}", config.mode));
207 logv(c, format!("run_ignored: {}", config.run_ignored));
208 logv(c, format!("filter: {}",
209 opt_str(&config.filter
211 .map(|re| re.to_owned()))));
212 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
213 logv(c, format!("host-rustcflags: {}",
214 opt_str(&config.host_rustcflags)));
215 logv(c, format!("target-rustcflags: {}",
216 opt_str(&config.target_rustcflags)));
217 logv(c, format!("target: {}", config.target));
218 logv(c, format!("host: {}", config.host));
219 logv(c, format!("android-cross-path: {:?}",
220 config.android_cross_path.display()));
221 logv(c, format!("adb_path: {:?}", config.adb_path));
222 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
223 logv(c, format!("adb_device_status: {}",
224 config.adb_device_status));
225 logv(c, format!("verbose: {}", config.verbose));
226 logv(c, format!("quiet: {}", config.quiet));
227 logv(c, format!("\n"));
230 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
237 pub fn opt_str2(maybestr: Option<String>) -> String {
239 None => "(none)".to_owned(),
244 pub fn run_tests(config: &Config) {
245 if config.target.contains("android") {
246 if let DebugInfoGdb = config.mode {
247 println!("{} debug-info test uses tcp 5039 port.\
248 please reserve it", config.target);
251 // android debug-info test uses remote debugger
252 // so, we test 1 thread at once.
253 // also trying to isolate problems with adb_run_wrapper.sh ilooping
254 env::set_var("RUST_TEST_THREADS","1");
259 if let Some(lldb_version) = config.lldb_version.as_ref() {
260 if is_blacklisted_lldb_version(&lldb_version[..]) {
261 println!("WARNING: The used version of LLDB ({}) has a \
262 known issue that breaks debuginfo tests. See \
263 issue #32520 for more information. Skipping all \
270 // Some older versions of LLDB seem to have problems with multiple
271 // instances running in parallel, so only run one test thread at a
273 env::set_var("RUST_TEST_THREADS", "1");
275 _ => { /* proceed */ }
278 // FIXME(#33435) Avoid spurious failures in codegen-units/partitioning tests.
279 if let Mode::CodegenUnits = config.mode {
280 let _ = fs::remove_dir_all("tmp/partitioning-tests");
283 let opts = test_opts(config);
284 let tests = make_tests(config);
285 // sadly osx needs some file descriptor limits raised for running tests in
286 // parallel (especially when we have lots and lots of child processes).
287 // For context, see #8904
288 unsafe { raise_fd_limit::raise_fd_limit(); }
289 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
290 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
291 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
292 let res = test::run_tests_console(&opts, tests.into_iter().collect());
295 Ok(false) => panic!("Some tests failed"),
297 println!("I/O failure during tests: {:?}", e);
302 pub fn test_opts(config: &Config) -> test::TestOpts {
304 filter: config.filter.clone(),
305 run_ignored: config.run_ignored,
307 logfile: config.logfile.clone(),
309 bench_benchmarks: true,
310 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
311 Ok(val) => &val != "0",
314 color: test::AutoColor,
319 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
320 debug!("making tests from {:?}",
321 config.src_base.display());
322 let mut tests = Vec::new();
323 collect_tests_from_dir(config,
332 fn collect_tests_from_dir(config: &Config,
335 relative_dir_path: &Path,
336 tests: &mut Vec<test::TestDescAndFn>)
338 // Ignore directories that contain a file
339 // `compiletest-ignore-dir`.
340 for file in fs::read_dir(dir)? {
342 let name = file.file_name();
343 if name == *"compiletest-ignore-dir" {
346 if name == *"Makefile" && config.mode == Mode::RunMake {
347 let paths = TestPaths {
348 file: dir.to_path_buf(),
349 base: base.to_path_buf(),
350 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
352 tests.push(make_test(config, &paths));
357 // If we find a test foo/bar.rs, we have to build the
358 // output directory `$build/foo` so we can write
359 // `$build/foo/bar` into it. We do this *now* in this
360 // sequential loop because otherwise, if we do it in the
361 // tests themselves, they race for the privilege of
362 // creating the directories and sometimes fail randomly.
363 let build_dir = config.build_base.join(&relative_dir_path);
364 fs::create_dir_all(&build_dir).unwrap();
366 // Add each `.rs` file as a test, and recurse further on any
367 // subdirectories we find, except for `aux` directories.
368 let dirs = fs::read_dir(dir)?;
371 let file_path = file.path();
372 let file_name = file.file_name();
373 if is_test(&file_name) {
374 debug!("found test file: {:?}", file_path.display());
375 let paths = TestPaths {
377 base: base.to_path_buf(),
378 relative_dir: relative_dir_path.to_path_buf(),
380 tests.push(make_test(config, &paths))
381 } else if file_path.is_dir() {
382 let relative_file_path = relative_dir_path.join(file.file_name());
383 if &file_name == "auxiliary" {
384 // `aux` directories contain other crates used for
385 // cross-crate tests. Don't search them for tests, but
386 // do create a directory in the build dir for them,
387 // since we will dump intermediate output in there
389 let build_dir = config.build_base.join(&relative_file_path);
390 fs::create_dir_all(&build_dir).unwrap();
392 debug!("found directory: {:?}", file_path.display());
393 collect_tests_from_dir(config,
400 debug!("found other file/directory: {:?}", file_path.display());
406 pub fn is_test(file_name: &OsString) -> bool {
407 let file_name = file_name.to_str().unwrap();
409 if !file_name.ends_with(".rs") {
413 // `.`, `#`, and `~` are common temp-file prefixes.
414 let invalid_prefixes = &[".", "#", "~"];
415 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
418 pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
419 let early_props = EarlyProps::from_file(config, &testpaths.file);
421 // The `should-fail` annotation doesn't apply to pretty tests,
422 // since we run the pretty printer across all tests by default.
423 // If desired, we could add a `should-fail-pretty` annotation.
424 let should_panic = match config.mode {
425 Pretty => test::ShouldPanic::No,
426 _ => if early_props.should_fail {
427 test::ShouldPanic::Yes
429 test::ShouldPanic::No
433 test::TestDescAndFn {
434 desc: test::TestDesc {
435 name: make_test_name(config, testpaths),
436 ignore: early_props.ignore,
437 should_panic: should_panic,
439 testfn: make_test_closure(config, testpaths),
443 pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
444 // Convert a complete path to something like
446 // run-pass/foo/bar/baz.rs
448 PathBuf::from(config.mode.to_string())
449 .join(&testpaths.relative_dir)
450 .join(&testpaths.file.file_name().unwrap());
451 test::DynTestName(format!("[{}] {}", config.mode, path.display()))
454 pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
455 let config = config.clone();
456 let testpaths = testpaths.clone();
457 test::DynTestFn(Box::new(move || {
458 runtest::run(config, &testpaths)
462 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
463 match full_version_line {
464 Some(ref full_version_line)
465 if !full_version_line.trim().is_empty() => {
466 let full_version_line = full_version_line.trim();
468 // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
469 for (pos, c) in full_version_line.char_indices() {
473 if pos + 2 >= full_version_line.len() {
476 if full_version_line[pos + 1..].chars().next().unwrap() != '.' {
479 if !full_version_line[pos + 2..].chars().next().unwrap().is_digit(10) {
482 if pos > 0 && full_version_line[..pos].chars().next_back()
483 .unwrap().is_digit(10) {
486 let mut end = pos + 3;
487 while end < full_version_line.len() &&
488 full_version_line[end..].chars().next()
489 .unwrap().is_digit(10) {
492 return Some(full_version_line[pos..end].to_owned());
494 println!("Could not extract GDB version from line '{}'",
502 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
503 // Extract the major LLDB version from the given version string.
504 // LLDB version strings are different for Apple and non-Apple platforms.
505 // At the moment, this function only supports the Apple variant, which looks
508 // LLDB-179.5 (older versions)
509 // lldb-300.2.51 (new versions)
511 // We are only interested in the major version number, so this function
512 // will return `Some("179")` and `Some("300")` respectively.
514 if let Some(ref full_version_line) = full_version_line {
515 if !full_version_line.trim().is_empty() {
516 let full_version_line = full_version_line.trim();
518 for (pos, l) in full_version_line.char_indices() {
519 if l != 'l' && l != 'L' { continue }
520 if pos + 5 >= full_version_line.len() { continue }
521 let l = full_version_line[pos + 1..].chars().next().unwrap();
522 if l != 'l' && l != 'L' { continue }
523 let d = full_version_line[pos + 2..].chars().next().unwrap();
524 if d != 'd' && d != 'D' { continue }
525 let b = full_version_line[pos + 3..].chars().next().unwrap();
526 if b != 'b' && b != 'B' { continue }
527 let dash = full_version_line[pos + 4..].chars().next().unwrap();
528 if dash != '-' { continue }
530 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
532 }).collect::<String>();
533 if !vers.is_empty() { return Some(vers) }
535 println!("Could not extract LLDB version from line '{}'",
542 fn is_blacklisted_lldb_version(version: &str) -> bool {