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)]
19 #![feature(vec_push_all)]
20 #![feature(path_components_peek)]
33 use std::path::{Path, PathBuf};
34 use getopts::{optopt, optflag, reqopt};
36 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 optopt("", "logfile", "file to log test execution to", "FILE"),
82 optopt("", "target", "the target to build for", "TARGET"),
83 optopt("", "host", "the host to build for", "HOST"),
84 optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
85 optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
86 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
87 optopt("", "adb-path", "path to the android debugger", "PATH"),
88 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
89 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
90 optflag("h", "help", "show this message"));
92 let (argv0, args_) = args.split_first().unwrap();
93 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
94 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
95 println!("{}", getopts::usage(&message, &groups));
101 &match getopts::getopts(args_, &groups) {
103 Err(f) => panic!("{:?}", f)
106 if matches.opt_present("h") || matches.opt_present("help") {
107 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
108 println!("{}", getopts::usage(&message, &groups));
113 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
114 match m.opt_str(nm) {
115 Some(s) => PathBuf::from(&s),
116 None => panic!("no option (=path) found for {}", nm),
120 let filter = if !matches.free.is_empty() {
121 Some(matches.free[0].clone())
127 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
128 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
129 rustc_path: opt_path(matches, "rustc-path"),
130 rustdoc_path: opt_path(matches, "rustdoc-path"),
131 python: matches.opt_str("python").unwrap(),
132 valgrind_path: matches.opt_str("valgrind-path"),
133 force_valgrind: matches.opt_present("force-valgrind"),
134 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
135 src_base: opt_path(matches, "src-base"),
136 build_base: opt_path(matches, "build-base"),
137 aux_base: opt_path(matches, "aux-base"),
138 stage_id: matches.opt_str("stage-id").unwrap(),
139 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
140 run_ignored: matches.opt_present("ignored"),
142 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
143 runtool: matches.opt_str("runtool"),
144 host_rustcflags: matches.opt_str("host-rustcflags"),
145 target_rustcflags: matches.opt_str("target-rustcflags"),
146 target: opt_str2(matches.opt_str("target")),
147 host: opt_str2(matches.opt_str("host")),
148 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
149 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
150 android_cross_path: opt_path(matches, "android-cross-path"),
151 adb_path: opt_str2(matches.opt_str("adb-path")),
152 adb_test_dir: format!("{}/{}",
153 opt_str2(matches.opt_str("adb-test-dir")),
154 opt_str2(matches.opt_str("target"))),
156 opt_str2(matches.opt_str("target")).contains("android") &&
157 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
158 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
159 lldb_python_dir: matches.opt_str("lldb-python-dir"),
160 verbose: matches.opt_present("verbose"),
164 pub fn log_config(config: &Config) {
166 logv(c, format!("configuration:"));
167 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
168 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
169 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
170 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
171 logv(c, format!("src_base: {:?}", config.src_base.display()));
172 logv(c, format!("build_base: {:?}", config.build_base.display()));
173 logv(c, format!("stage_id: {}", config.stage_id));
174 logv(c, format!("mode: {}", config.mode));
175 logv(c, format!("run_ignored: {}", config.run_ignored));
176 logv(c, format!("filter: {}",
177 opt_str(&config.filter
179 .map(|re| re.to_owned()))));
180 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
181 logv(c, format!("host-rustcflags: {}",
182 opt_str(&config.host_rustcflags)));
183 logv(c, format!("target-rustcflags: {}",
184 opt_str(&config.target_rustcflags)));
185 logv(c, format!("target: {}", config.target));
186 logv(c, format!("host: {}", config.host));
187 logv(c, format!("android-cross-path: {:?}",
188 config.android_cross_path.display()));
189 logv(c, format!("adb_path: {:?}", config.adb_path));
190 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
191 logv(c, format!("adb_device_status: {}",
192 config.adb_device_status));
193 logv(c, format!("verbose: {}", config.verbose));
194 logv(c, format!("\n"));
197 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
204 pub fn opt_str2(maybestr: Option<String>) -> String {
206 None => "(none)".to_owned(),
211 pub fn run_tests(config: &Config) {
212 if config.target.contains("android") {
213 if let DebugInfoGdb = config.mode {
214 println!("{} debug-info test uses tcp 5039 port.\
215 please reserve it", config.target);
218 // android debug-info test uses remote debugger
219 // so, we test 1 thread at once.
220 // also trying to isolate problems with adb_run_wrapper.sh ilooping
221 env::set_var("RUST_TEST_THREADS","1");
226 // Some older versions of LLDB seem to have problems with multiple
227 // instances running in parallel, so only run one test thread at a
229 env::set_var("RUST_TEST_THREADS", "1");
231 _ => { /* proceed */ }
234 let opts = test_opts(config);
235 let tests = make_tests(config);
236 // sadly osx needs some file descriptor limits raised for running tests in
237 // parallel (especially when we have lots and lots of child processes).
238 // For context, see #8904
239 unsafe { raise_fd_limit::raise_fd_limit(); }
240 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
241 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
242 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
243 let res = test::run_tests_console(&opts, tests.into_iter().collect());
246 Ok(false) => panic!("Some tests failed"),
248 println!("I/O failure during tests: {:?}", e);
253 pub fn test_opts(config: &Config) -> test::TestOpts {
255 filter: match config.filter {
257 Some(ref filter) => Some(filter.clone()),
259 run_ignored: config.run_ignored,
260 logfile: config.logfile.clone(),
262 bench_benchmarks: true,
263 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
264 color: test::AutoColor,
268 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
269 debug!("making tests from {:?}",
270 config.src_base.display());
271 let mut tests = Vec::new();
272 let dirs = fs::read_dir(&config.src_base).unwrap();
274 let file = file.unwrap().path();
275 debug!("inspecting file {:?}", file.display());
276 if is_test(config, &file) {
277 tests.push(make_test(config, &file))
283 pub fn is_test(config: &Config, testfile: &Path) -> bool {
284 // Pretty-printer does not work with .rc files yet
285 let valid_extensions =
287 Pretty => vec!(".rs".to_owned()),
288 _ => vec!(".rc".to_owned(), ".rs".to_owned())
290 let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
291 let name = testfile.file_name().unwrap().to_str().unwrap();
293 let mut valid = false;
295 for ext in &valid_extensions {
296 if name.ends_with(ext) {
301 for pre in &invalid_prefixes {
302 if name.starts_with(pre) {
310 pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
312 test::TestDescAndFn {
313 desc: test::TestDesc {
314 name: make_test_name(config, testfile),
315 ignore: header::is_test_ignored(config, testfile),
316 should_panic: test::ShouldPanic::No,
318 testfn: make_test_closure(config, &testfile),
322 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
324 // Try to elide redundant long paths
325 fn shorten(path: &Path) -> String {
326 let filename = path.file_name().unwrap().to_str();
327 let p = path.parent().unwrap();
328 let dir = p.file_name().unwrap().to_str();
329 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
332 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
335 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
336 let config = (*config).clone();
337 let testfile = testfile.to_path_buf();
338 test::DynTestFn(Box::new(move || {
339 runtest::run(config, &testfile)
343 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
344 match full_version_line {
345 Some(ref full_version_line)
346 if !full_version_line.trim().is_empty() => {
347 let full_version_line = full_version_line.trim();
349 // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
350 for (pos, c) in full_version_line.char_indices() {
351 if !c.is_digit(10) { continue }
352 if pos + 2 >= full_version_line.len() { continue }
353 if full_version_line.char_at(pos + 1) != '.' { continue }
354 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
355 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
358 let mut end = pos + 3;
359 while end < full_version_line.len() &&
360 full_version_line.char_at(end).is_digit(10) {
363 return Some(full_version_line[pos..end].to_owned());
365 println!("Could not extract GDB version from line '{}'",
373 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
374 // Extract the major LLDB version from the given version string.
375 // LLDB version strings are different for Apple and non-Apple platforms.
376 // At the moment, this function only supports the Apple variant, which looks
379 // LLDB-179.5 (older versions)
380 // lldb-300.2.51 (new versions)
382 // We are only interested in the major version number, so this function
383 // will return `Some("179")` and `Some("300")` respectively.
385 if let Some(ref full_version_line) = full_version_line {
386 if !full_version_line.trim().is_empty() {
387 let full_version_line = full_version_line.trim();
389 for (pos, l) in full_version_line.char_indices() {
390 if l != 'l' && l != 'L' { continue }
391 if pos + 5 >= full_version_line.len() { continue }
392 let l = full_version_line.char_at(pos + 1);
393 if l != 'l' && l != 'L' { continue }
394 let d = full_version_line.char_at(pos + 2);
395 if d != 'd' && d != 'D' { continue }
396 let b = full_version_line.char_at(pos + 3);
397 if b != 'b' && b != 'B' { continue }
398 let dash = full_version_line.char_at(pos + 4);
399 if dash != '-' { continue }
401 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
403 }).collect::<String>();
404 if !vers.is_empty() { return Some(vers) }
406 println!("Could not extract LLDB version from line '{}'",