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)]
17 #![feature(rustc_private)]
18 #![feature(slice_splits)]
21 #![feature(vec_push_all)]
34 use std::path::{Path, PathBuf};
35 use getopts::{optopt, optflag, reqopt};
37 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
49 let config = parse_config(env::args().collect());
51 if config.valgrind_path.is_none() && config.force_valgrind {
52 panic!("Can't find Valgrind to run Valgrind tests");
59 pub fn parse_config(args: Vec<String> ) -> Config {
61 let groups : Vec<getopts::OptGroup> =
62 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
63 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
64 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
65 reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
66 reqopt("", "python", "path to python to use for doc 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 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[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),
121 let filter = if !matches.free.is_empty() {
122 Some(matches.free[0].clone())
128 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
129 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
130 rustc_path: opt_path(matches, "rustc-path"),
131 rustdoc_path: opt_path(matches, "rustdoc-path"),
132 python: matches.opt_str("python").unwrap(),
133 valgrind_path: matches.opt_str("valgrind-path"),
134 force_valgrind: matches.opt_present("force-valgrind"),
135 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
136 src_base: opt_path(matches, "src-base"),
137 build_base: opt_path(matches, "build-base"),
138 aux_base: opt_path(matches, "aux-base"),
139 stage_id: matches.opt_str("stage-id").unwrap(),
140 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
141 run_ignored: matches.opt_present("ignored"),
143 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
144 runtool: matches.opt_str("runtool"),
145 host_rustcflags: matches.opt_str("host-rustcflags"),
146 target_rustcflags: matches.opt_str("target-rustcflags"),
147 target: opt_str2(matches.opt_str("target")),
148 host: opt_str2(matches.opt_str("host")),
149 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
150 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
151 android_cross_path: opt_path(matches, "android-cross-path"),
152 adb_path: opt_str2(matches.opt_str("adb-path")),
153 adb_test_dir: format!("{}/{}",
154 opt_str2(matches.opt_str("adb-test-dir")),
155 opt_str2(matches.opt_str("target"))),
157 opt_str2(matches.opt_str("target")).contains("android") &&
158 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
159 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
160 lldb_python_dir: matches.opt_str("lldb-python-dir"),
161 verbose: matches.opt_present("verbose"),
165 pub fn log_config(config: &Config) {
167 logv(c, format!("configuration:"));
168 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
169 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
170 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
171 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
172 logv(c, format!("src_base: {:?}", config.src_base.display()));
173 logv(c, format!("build_base: {:?}", config.build_base.display()));
174 logv(c, format!("stage_id: {}", config.stage_id));
175 logv(c, format!("mode: {}", config.mode));
176 logv(c, format!("run_ignored: {}", config.run_ignored));
177 logv(c, format!("filter: {}",
178 opt_str(&config.filter
180 .map(|re| re.to_string()))));
181 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
182 logv(c, format!("host-rustcflags: {}",
183 opt_str(&config.host_rustcflags)));
184 logv(c, format!("target-rustcflags: {}",
185 opt_str(&config.target_rustcflags)));
186 logv(c, format!("target: {}", config.target));
187 logv(c, format!("host: {}", config.host));
188 logv(c, format!("android-cross-path: {:?}",
189 config.android_cross_path.display()));
190 logv(c, format!("adb_path: {:?}", config.adb_path));
191 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
192 logv(c, format!("adb_device_status: {}",
193 config.adb_device_status));
194 logv(c, format!("verbose: {}", config.verbose));
195 logv(c, format!("\n"));
198 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
205 pub fn opt_str2(maybestr: Option<String>) -> String {
207 None => "(none)".to_string(),
212 pub fn run_tests(config: &Config) {
213 if config.target.contains("android") {
216 println!("{} debug-info test uses tcp 5039 port.\
217 please reserve it", config.target);
222 // android debug-info test uses remote debugger
223 // so, we test 1 thread at once.
224 // also trying to isolate problems with adb_run_wrapper.sh ilooping
225 env::set_var("RUST_TEST_THREADS","1");
230 // Some older versions of LLDB seem to have problems with multiple
231 // instances running in parallel, so only run one test thread at a
233 env::set_var("RUST_TEST_THREADS", "1");
235 _ => { /* proceed */ }
238 let opts = test_opts(config);
239 let tests = make_tests(config);
240 // sadly osx needs some file descriptor limits raised for running tests in
241 // parallel (especially when we have lots and lots of child processes).
242 // For context, see #8904
243 unsafe { raise_fd_limit::raise_fd_limit(); }
244 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
245 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
246 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
247 let res = test::run_tests_console(&opts, tests.into_iter().collect());
250 Ok(false) => panic!("Some tests failed"),
252 println!("I/O failure during tests: {:?}", e);
257 pub fn test_opts(config: &Config) -> test::TestOpts {
259 filter: match config.filter {
261 Some(ref filter) => Some(filter.clone()),
263 run_ignored: config.run_ignored,
264 logfile: config.logfile.clone(),
266 bench_benchmarks: true,
267 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
268 color: test::AutoColor,
272 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
273 debug!("making tests from {:?}",
274 config.src_base.display());
275 let mut tests = Vec::new();
276 let dirs = fs::read_dir(&config.src_base).unwrap();
278 let file = file.unwrap().path();
279 debug!("inspecting file {:?}", file.display());
280 if is_test(config, &file) {
281 tests.push(make_test(config, &file))
287 pub fn is_test(config: &Config, testfile: &Path) -> bool {
288 // Pretty-printer does not work with .rc files yet
289 let valid_extensions =
291 Pretty => vec!(".rs".to_string()),
292 _ => vec!(".rc".to_string(), ".rs".to_string())
294 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
295 let name = testfile.file_name().unwrap().to_str().unwrap();
297 let mut valid = false;
299 for ext in &valid_extensions {
300 if name.ends_with(ext) {
305 for pre in &invalid_prefixes {
306 if name.starts_with(pre) {
314 pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
316 test::TestDescAndFn {
317 desc: test::TestDesc {
318 name: make_test_name(config, testfile),
319 ignore: header::is_test_ignored(config, testfile),
320 should_panic: test::ShouldPanic::No,
322 testfn: make_test_closure(config, &testfile),
326 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
328 // Try to elide redundant long paths
329 fn shorten(path: &Path) -> String {
330 let filename = path.file_name().unwrap().to_str();
331 let p = path.parent().unwrap();
332 let dir = p.file_name().unwrap().to_str();
333 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
336 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
339 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
340 let config = (*config).clone();
341 let testfile = testfile.to_path_buf();
342 test::DynTestFn(Box::new(move || {
343 runtest::run(config, &testfile)
347 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
348 match full_version_line {
349 Some(ref full_version_line)
350 if !full_version_line.trim().is_empty() => {
351 let full_version_line = full_version_line.trim();
353 // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
354 for (pos, c) in full_version_line.char_indices() {
355 if !c.is_digit(10) { continue }
356 if pos + 2 >= full_version_line.len() { continue }
357 if full_version_line.char_at(pos + 1) != '.' { continue }
358 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
359 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
362 if pos + 3 < full_version_line.len() &&
363 full_version_line.char_at(pos + 3).is_digit(10) {
366 return Some(full_version_line[pos..pos+3].to_string());
368 println!("Could not extract GDB version from line '{}'",
376 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
377 // Extract the major LLDB version from the given version string.
378 // LLDB version strings are different for Apple and non-Apple platforms.
379 // At the moment, this function only supports the Apple variant, which looks
382 // LLDB-179.5 (older versions)
383 // lldb-300.2.51 (new versions)
385 // We are only interested in the major version number, so this function
386 // will return `Some("179")` and `Some("300")` respectively.
388 match full_version_line {
389 Some(ref full_version_line)
390 if !full_version_line.trim().is_empty() => {
391 let full_version_line = full_version_line.trim();
393 for (pos, l) in full_version_line.char_indices() {
394 if l != 'l' && l != 'L' { continue }
395 if pos + 5 >= full_version_line.len() { continue }
396 let l = full_version_line.char_at(pos + 1);
397 if l != 'l' && l != 'L' { continue }
398 let d = full_version_line.char_at(pos + 2);
399 if d != 'd' && d != 'D' { continue }
400 let b = full_version_line.char_at(pos + 3);
401 if b != 'b' && b != 'B' { continue }
402 let dash = full_version_line.char_at(pos + 4);
403 if dash != '-' { continue }
405 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
407 }).collect::<String>();
408 if !vers.is_empty() { return Some(vers) }
410 println!("Could not extract LLDB version from line '{}'",