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(dynamic_lib)]
16 #![feature(rustc_private)]
31 use std::path::{Path, PathBuf};
32 use getopts::{optopt, optflag, reqopt};
34 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
46 let config = parse_config(env::args().collect());
48 if config.valgrind_path.is_none() && config.force_valgrind {
49 panic!("Can't find Valgrind to run Valgrind tests");
56 pub fn parse_config(args: Vec<String> ) -> Config {
58 let groups : Vec<getopts::OptGroup> =
59 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
60 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
61 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
62 reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
63 reqopt("", "python", "path to python to use for doc 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|parse-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 optopt("", "target", "the target to build for", "TARGET"),
81 optopt("", "host", "the host to build for", "HOST"),
82 optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
83 optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
84 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
85 optopt("", "adb-path", "path to the android debugger", "PATH"),
86 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
87 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
88 optflag("h", "help", "show this message"));
90 let (argv0, args_) = args.split_first().unwrap();
91 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
92 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
93 println!("{}", getopts::usage(&message, &groups));
99 &match getopts::getopts(args_, &groups) {
101 Err(f) => panic!("{:?}", f)
104 if matches.opt_present("h") || matches.opt_present("help") {
105 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
106 println!("{}", getopts::usage(&message, &groups));
111 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
112 match m.opt_str(nm) {
113 Some(s) => PathBuf::from(&s),
114 None => panic!("no option (=path) found for {}", nm),
118 let filter = if !matches.free.is_empty() {
119 Some(matches.free[0].clone())
125 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
126 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
127 rustc_path: opt_path(matches, "rustc-path"),
128 rustdoc_path: opt_path(matches, "rustdoc-path"),
129 python: matches.opt_str("python").unwrap(),
130 valgrind_path: matches.opt_str("valgrind-path"),
131 force_valgrind: matches.opt_present("force-valgrind"),
132 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
133 src_base: opt_path(matches, "src-base"),
134 build_base: opt_path(matches, "build-base"),
135 aux_base: opt_path(matches, "aux-base"),
136 stage_id: matches.opt_str("stage-id").unwrap(),
137 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
138 run_ignored: matches.opt_present("ignored"),
140 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
141 runtool: matches.opt_str("runtool"),
142 host_rustcflags: matches.opt_str("host-rustcflags"),
143 target_rustcflags: matches.opt_str("target-rustcflags"),
144 target: opt_str2(matches.opt_str("target")),
145 host: opt_str2(matches.opt_str("host")),
146 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
147 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
148 android_cross_path: opt_path(matches, "android-cross-path"),
149 adb_path: opt_str2(matches.opt_str("adb-path")),
150 adb_test_dir: format!("{}/{}",
151 opt_str2(matches.opt_str("adb-test-dir")),
152 opt_str2(matches.opt_str("target"))),
154 opt_str2(matches.opt_str("target")).contains("android") &&
155 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
156 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
157 lldb_python_dir: matches.opt_str("lldb-python-dir"),
158 verbose: matches.opt_present("verbose"),
162 pub fn log_config(config: &Config) {
164 logv(c, format!("configuration:"));
165 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
166 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
167 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
168 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
169 logv(c, format!("src_base: {:?}", config.src_base.display()));
170 logv(c, format!("build_base: {:?}", config.build_base.display()));
171 logv(c, format!("stage_id: {}", config.stage_id));
172 logv(c, format!("mode: {}", config.mode));
173 logv(c, format!("run_ignored: {}", config.run_ignored));
174 logv(c, format!("filter: {}",
175 opt_str(&config.filter
177 .map(|re| re.to_owned()))));
178 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
179 logv(c, format!("host-rustcflags: {}",
180 opt_str(&config.host_rustcflags)));
181 logv(c, format!("target-rustcflags: {}",
182 opt_str(&config.target_rustcflags)));
183 logv(c, format!("target: {}", config.target));
184 logv(c, format!("host: {}", config.host));
185 logv(c, format!("android-cross-path: {:?}",
186 config.android_cross_path.display()));
187 logv(c, format!("adb_path: {:?}", config.adb_path));
188 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
189 logv(c, format!("adb_device_status: {}",
190 config.adb_device_status));
191 logv(c, format!("verbose: {}", config.verbose));
192 logv(c, format!("\n"));
195 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
202 pub fn opt_str2(maybestr: Option<String>) -> String {
204 None => "(none)".to_owned(),
209 pub fn run_tests(config: &Config) {
210 if config.target.contains("android") {
211 if let DebugInfoGdb = config.mode {
212 println!("{} debug-info test uses tcp 5039 port.\
213 please reserve it", config.target);
216 // android debug-info test uses remote debugger
217 // so, we test 1 thread at once.
218 // also trying to isolate problems with adb_run_wrapper.sh ilooping
219 env::set_var("RUST_TEST_THREADS","1");
224 // Some older versions of LLDB seem to have problems with multiple
225 // instances running in parallel, so only run one test thread at a
227 env::set_var("RUST_TEST_THREADS", "1");
229 _ => { /* proceed */ }
232 let opts = test_opts(config);
233 let tests = make_tests(config);
234 // sadly osx needs some file descriptor limits raised for running tests in
235 // parallel (especially when we have lots and lots of child processes).
236 // For context, see #8904
237 unsafe { raise_fd_limit::raise_fd_limit(); }
238 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
239 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
240 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
241 let res = test::run_tests_console(&opts, tests.into_iter().collect());
244 Ok(false) => panic!("Some tests failed"),
246 println!("I/O failure during tests: {:?}", e);
251 pub fn test_opts(config: &Config) -> test::TestOpts {
253 filter: match config.filter {
255 Some(ref filter) => Some(filter.clone()),
257 run_ignored: config.run_ignored,
258 logfile: config.logfile.clone(),
260 bench_benchmarks: true,
261 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
262 color: test::AutoColor,
266 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
267 debug!("making tests from {:?}",
268 config.src_base.display());
269 let mut tests = Vec::new();
270 let dirs = fs::read_dir(&config.src_base).unwrap();
272 let file = file.unwrap().path();
273 debug!("inspecting file {:?}", file.display());
274 if is_test(config, &file) {
275 tests.push(make_test(config, &file))
281 pub fn is_test(config: &Config, testfile: &Path) -> bool {
282 // Pretty-printer does not work with .rc files yet
283 let valid_extensions =
285 Pretty => vec!(".rs".to_owned()),
286 _ => vec!(".rc".to_owned(), ".rs".to_owned())
288 let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
289 let name = testfile.file_name().unwrap().to_str().unwrap();
291 let mut valid = false;
293 for ext in &valid_extensions {
294 if name.ends_with(ext) {
299 for pre in &invalid_prefixes {
300 if name.starts_with(pre) {
308 pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
310 test::TestDescAndFn {
311 desc: test::TestDesc {
312 name: make_test_name(config, testfile),
313 ignore: header::is_test_ignored(config, testfile),
314 should_panic: test::ShouldPanic::No,
316 testfn: make_test_closure(config, &testfile),
320 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
322 // Try to elide redundant long paths
323 fn shorten(path: &Path) -> String {
324 let filename = path.file_name().unwrap().to_str();
325 let p = path.parent().unwrap();
326 let dir = p.file_name().unwrap().to_str();
327 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
330 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
333 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
334 let config = (*config).clone();
335 let testfile = testfile.to_path_buf();
336 test::DynTestFn(Box::new(move || {
337 runtest::run(config, &testfile)
341 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
342 match full_version_line {
343 Some(ref full_version_line)
344 if !full_version_line.trim().is_empty() => {
345 let full_version_line = full_version_line.trim();
347 // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
348 for (pos, c) in full_version_line.char_indices() {
349 if !c.is_digit(10) { continue }
350 if pos + 2 >= full_version_line.len() { continue }
351 if full_version_line.char_at(pos + 1) != '.' { continue }
352 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
353 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
356 let mut end = pos + 3;
357 while end < full_version_line.len() &&
358 full_version_line.char_at(end).is_digit(10) {
361 return Some(full_version_line[pos..end].to_owned());
363 println!("Could not extract GDB version from line '{}'",
371 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
372 // Extract the major LLDB version from the given version string.
373 // LLDB version strings are different for Apple and non-Apple platforms.
374 // At the moment, this function only supports the Apple variant, which looks
377 // LLDB-179.5 (older versions)
378 // lldb-300.2.51 (new versions)
380 // We are only interested in the major version number, so this function
381 // will return `Some("179")` and `Some("300")` respectively.
383 if let Some(ref full_version_line) = full_version_line {
384 if !full_version_line.trim().is_empty() {
385 let full_version_line = full_version_line.trim();
387 for (pos, l) in full_version_line.char_indices() {
388 if l != 'l' && l != 'L' { continue }
389 if pos + 5 >= full_version_line.len() { continue }
390 let l = full_version_line.char_at(pos + 1);
391 if l != 'l' && l != 'L' { continue }
392 let d = full_version_line.char_at(pos + 2);
393 if d != 'd' && d != 'D' { continue }
394 let b = full_version_line.char_at(pos + 3);
395 if b != 'b' && b != 'B' { continue }
396 let dash = full_version_line.char_at(pos + 4);
397 if dash != '-' { continue }
399 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
401 }).collect::<String>();
402 if !vers.is_empty() { return Some(vers) }
404 println!("Could not extract LLDB version from line '{}'",