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)]
22 #![feature(path_components_peek)]
35 use std::path::{Path, PathBuf};
36 use getopts::{optopt, optflag, reqopt};
38 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
50 let config = parse_config(env::args().collect());
52 if config.valgrind_path.is_none() && config.force_valgrind {
53 panic!("Can't find Valgrind to run Valgrind tests");
60 pub fn parse_config(args: Vec<String> ) -> Config {
62 let groups : Vec<getopts::OptGroup> =
63 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
64 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
65 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
66 reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
67 reqopt("", "python", "path to python to use for doc tests", "PATH"),
68 optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
69 optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
70 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
71 reqopt("", "src-base", "directory to scan for test files", "PATH"),
72 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
73 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
74 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
75 reqopt("", "mode", "which sort of compile tests to run",
76 "(compile-fail|parse-fail|run-fail|run-pass|run-pass-valgrind|pretty|debug-info)"),
77 optflag("", "ignored", "run tests marked as ignored"),
78 optopt("", "runtool", "supervisor program to run tests under \
79 (eg. emulator, valgrind)", "PROGRAM"),
80 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
81 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
82 optflag("", "verbose", "run tests verbosely, showing all output"),
83 optopt("", "logfile", "file to log test execution to", "FILE"),
84 optopt("", "target", "the target to build for", "TARGET"),
85 optopt("", "host", "the host to build for", "HOST"),
86 optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"),
87 optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"),
88 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
89 optopt("", "adb-path", "path to the android debugger", "PATH"),
90 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
91 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
92 optflag("h", "help", "show this message"));
94 let (argv0, args_) = args.split_first().unwrap();
95 if args[1] == "-h" || args[1] == "--help" {
96 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
97 println!("{}", getopts::usage(&message, &groups));
103 &match getopts::getopts(args_, &groups) {
105 Err(f) => panic!("{:?}", f)
108 if matches.opt_present("h") || matches.opt_present("help") {
109 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
110 println!("{}", getopts::usage(&message, &groups));
115 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
116 match m.opt_str(nm) {
117 Some(s) => PathBuf::from(&s),
118 None => panic!("no option (=path) found for {}", nm),
122 let filter = if !matches.free.is_empty() {
123 Some(matches.free[0].clone())
129 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
130 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
131 rustc_path: opt_path(matches, "rustc-path"),
132 rustdoc_path: opt_path(matches, "rustdoc-path"),
133 python: matches.opt_str("python").unwrap(),
134 valgrind_path: matches.opt_str("valgrind-path"),
135 force_valgrind: matches.opt_present("force-valgrind"),
136 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
137 src_base: opt_path(matches, "src-base"),
138 build_base: opt_path(matches, "build-base"),
139 aux_base: opt_path(matches, "aux-base"),
140 stage_id: matches.opt_str("stage-id").unwrap(),
141 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
142 run_ignored: matches.opt_present("ignored"),
144 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
145 runtool: matches.opt_str("runtool"),
146 host_rustcflags: matches.opt_str("host-rustcflags"),
147 target_rustcflags: matches.opt_str("target-rustcflags"),
148 target: opt_str2(matches.opt_str("target")),
149 host: opt_str2(matches.opt_str("host")),
150 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
151 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
152 android_cross_path: opt_path(matches, "android-cross-path"),
153 adb_path: opt_str2(matches.opt_str("adb-path")),
154 adb_test_dir: format!("{}/{}",
155 opt_str2(matches.opt_str("adb-test-dir")),
156 opt_str2(matches.opt_str("target"))),
158 opt_str2(matches.opt_str("target")).contains("android") &&
159 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
160 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
161 lldb_python_dir: matches.opt_str("lldb-python-dir"),
162 verbose: matches.opt_present("verbose"),
166 pub fn log_config(config: &Config) {
168 logv(c, format!("configuration:"));
169 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
170 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
171 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
172 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
173 logv(c, format!("src_base: {:?}", config.src_base.display()));
174 logv(c, format!("build_base: {:?}", config.build_base.display()));
175 logv(c, format!("stage_id: {}", config.stage_id));
176 logv(c, format!("mode: {}", config.mode));
177 logv(c, format!("run_ignored: {}", config.run_ignored));
178 logv(c, format!("filter: {}",
179 opt_str(&config.filter
181 .map(|re| re.to_owned()))));
182 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
183 logv(c, format!("host-rustcflags: {}",
184 opt_str(&config.host_rustcflags)));
185 logv(c, format!("target-rustcflags: {}",
186 opt_str(&config.target_rustcflags)));
187 logv(c, format!("target: {}", config.target));
188 logv(c, format!("host: {}", config.host));
189 logv(c, format!("android-cross-path: {:?}",
190 config.android_cross_path.display()));
191 logv(c, format!("adb_path: {:?}", config.adb_path));
192 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
193 logv(c, format!("adb_device_status: {}",
194 config.adb_device_status));
195 logv(c, format!("verbose: {}", config.verbose));
196 logv(c, format!("\n"));
199 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
206 pub fn opt_str2(maybestr: Option<String>) -> String {
208 None => "(none)".to_owned(),
213 pub fn run_tests(config: &Config) {
214 if config.target.contains("android") {
215 if let DebugInfoGdb = config.mode {
216 println!("{} debug-info test uses tcp 5039 port.\
217 please reserve it", config.target);
220 // android debug-info test uses remote debugger
221 // so, we test 1 thread at once.
222 // also trying to isolate problems with adb_run_wrapper.sh ilooping
223 env::set_var("RUST_TEST_THREADS","1");
228 // Some older versions of LLDB seem to have problems with multiple
229 // instances running in parallel, so only run one test thread at a
231 env::set_var("RUST_TEST_THREADS", "1");
233 _ => { /* proceed */ }
236 let opts = test_opts(config);
237 let tests = make_tests(config);
238 // sadly osx needs some file descriptor limits raised for running tests in
239 // parallel (especially when we have lots and lots of child processes).
240 // For context, see #8904
241 unsafe { raise_fd_limit::raise_fd_limit(); }
242 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
243 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
244 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
245 let res = test::run_tests_console(&opts, tests.into_iter().collect());
248 Ok(false) => panic!("Some tests failed"),
250 println!("I/O failure during tests: {:?}", e);
255 pub fn test_opts(config: &Config) -> test::TestOpts {
257 filter: match config.filter {
259 Some(ref filter) => Some(filter.clone()),
261 run_ignored: config.run_ignored,
262 logfile: config.logfile.clone(),
264 bench_benchmarks: true,
265 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
266 color: test::AutoColor,
270 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
271 debug!("making tests from {:?}",
272 config.src_base.display());
273 let mut tests = Vec::new();
274 let dirs = fs::read_dir(&config.src_base).unwrap();
276 let file = file.unwrap().path();
277 debug!("inspecting file {:?}", file.display());
278 if is_test(config, &file) {
279 tests.push(make_test(config, &file))
285 pub fn is_test(config: &Config, testfile: &Path) -> bool {
286 // Pretty-printer does not work with .rc files yet
287 let valid_extensions =
289 Pretty => vec!(".rs".to_owned()),
290 _ => vec!(".rc".to_owned(), ".rs".to_owned())
292 let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
293 let name = testfile.file_name().unwrap().to_str().unwrap();
295 let mut valid = false;
297 for ext in &valid_extensions {
298 if name.ends_with(ext) {
303 for pre in &invalid_prefixes {
304 if name.starts_with(pre) {
312 pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
314 test::TestDescAndFn {
315 desc: test::TestDesc {
316 name: make_test_name(config, testfile),
317 ignore: header::is_test_ignored(config, testfile),
318 should_panic: test::ShouldPanic::No,
320 testfn: make_test_closure(config, &testfile),
324 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
326 // Try to elide redundant long paths
327 fn shorten(path: &Path) -> String {
328 let filename = path.file_name().unwrap().to_str();
329 let p = path.parent().unwrap();
330 let dir = p.file_name().unwrap().to_str();
331 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
334 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
337 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
338 let config = (*config).clone();
339 let testfile = testfile.to_path_buf();
340 test::DynTestFn(Box::new(move || {
341 runtest::run(config, &testfile)
345 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
346 match full_version_line {
347 Some(ref full_version_line)
348 if !full_version_line.trim().is_empty() => {
349 let full_version_line = full_version_line.trim();
351 // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
352 for (pos, c) in full_version_line.char_indices() {
353 if !c.is_digit(10) { continue }
354 if pos + 2 >= full_version_line.len() { continue }
355 if full_version_line.char_at(pos + 1) != '.' { continue }
356 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
357 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
360 let mut end = pos + 3;
361 while end < full_version_line.len() &&
362 full_version_line.char_at(end).is_digit(10) {
365 return Some(full_version_line[pos..end].to_owned());
367 println!("Could not extract GDB version from line '{}'",
375 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
376 // Extract the major LLDB version from the given version string.
377 // LLDB version strings are different for Apple and non-Apple platforms.
378 // At the moment, this function only supports the Apple variant, which looks
381 // LLDB-179.5 (older versions)
382 // lldb-300.2.51 (new versions)
384 // We are only interested in the major version number, so this function
385 // will return `Some("179")` and `Some("300")` respectively.
387 if let Some(ref full_version_line) = full_version_line {
388 if !full_version_line.trim().is_empty() {
389 let full_version_line = full_version_line.trim();
391 for (pos, l) in full_version_line.char_indices() {
392 if l != 'l' && l != 'L' { continue }
393 if pos + 5 >= full_version_line.len() { continue }
394 let l = full_version_line.char_at(pos + 1);
395 if l != 'l' && l != 'L' { continue }
396 let d = full_version_line.char_at(pos + 2);
397 if d != 'd' && d != 'D' { continue }
398 let b = full_version_line.char_at(pos + 3);
399 if b != 'b' && b != 'B' { continue }
400 let dash = full_version_line.char_at(pos + 4);
401 if dash != '-' { continue }
403 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
405 }).collect::<String>();
406 if !vers.is_empty() { return Some(vers) }
408 println!("Could not extract LLDB version from line '{}'",