]> git.proxmox.com Git - rustc.git/blob - src/compiletest/compiletest.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / compiletest / compiletest.rs
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.
4 //
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.
10
11 #![crate_type = "bin"]
12
13 #![feature(box_syntax)]
14 #![feature(libc)]
15 #![feature(rustc_private)]
16 #![feature(test)]
17 #![feature(question_mark)]
18
19 #![deny(warnings)]
20
21 extern crate libc;
22 extern crate test;
23 extern crate getopts;
24
25 #[macro_use]
26 extern crate log;
27
28 use std::env;
29 use std::fs;
30 use std::io;
31 use std::path::{Path, PathBuf};
32 use getopts::{optopt, optflag, reqopt};
33 use common::Config;
34 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
35 use test::TestPaths;
36 use util::logv;
37
38 pub mod procsrv;
39 pub mod util;
40 pub mod header;
41 pub mod runtest;
42 pub mod common;
43 pub mod errors;
44 mod raise_fd_limit;
45
46 pub fn main() {
47 let config = parse_config(env::args().collect());
48
49 if config.valgrind_path.is_none() && config.force_valgrind {
50 panic!("Can't find Valgrind to run Valgrind tests");
51 }
52
53 log_config(&config);
54 run_tests(&config);
55 }
56
57 pub fn parse_config(args: Vec<String> ) -> Config {
58
59 let groups : Vec<getopts::OptGroup> =
60 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
61 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
62 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
63 reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"),
64 reqopt("", "python", "path to python to use for doc tests", "PATH"),
65 optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
66 optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
67 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
68 reqopt("", "src-base", "directory to scan for test files", "PATH"),
69 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
70 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
71 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
72 reqopt("", "mode", "which sort of compile tests to run",
73 "(compile-fail|parse-fail|run-fail|run-pass|\
74 run-pass-valgrind|pretty|debug-info|incremental)"),
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 optflag("", "quiet", "print one character per test instead of one line"),
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"));
92
93 let (argv0, args_) = args.split_first().unwrap();
94 if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
95 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
96 println!("{}", getopts::usage(&message, &groups));
97 println!("");
98 panic!()
99 }
100
101 let matches =
102 &match getopts::getopts(args_, &groups) {
103 Ok(m) => m,
104 Err(f) => panic!("{:?}", f)
105 };
106
107 if matches.opt_present("h") || matches.opt_present("help") {
108 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
109 println!("{}", getopts::usage(&message, &groups));
110 println!("");
111 panic!()
112 }
113
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),
118 }
119 }
120
121 fn make_absolute(path: PathBuf) -> PathBuf {
122 if path.is_relative() {
123 env::current_dir().unwrap().join(path)
124 } else {
125 path
126 }
127 }
128
129 Config {
130 compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
131 run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
132 rustc_path: opt_path(matches, "rustc-path"),
133 rustdoc_path: opt_path(matches, "rustdoc-path"),
134 python: matches.opt_str("python").unwrap(),
135 valgrind_path: matches.opt_str("valgrind-path"),
136 force_valgrind: matches.opt_present("force-valgrind"),
137 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
138 src_base: opt_path(matches, "src-base"),
139 build_base: opt_path(matches, "build-base"),
140 aux_base: opt_path(matches, "aux-base"),
141 stage_id: matches.opt_str("stage-id").unwrap(),
142 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
143 run_ignored: matches.opt_present("ignored"),
144 filter: matches.free.first().cloned(),
145 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
146 runtool: matches.opt_str("runtool"),
147 host_rustcflags: matches.opt_str("host-rustcflags"),
148 target_rustcflags: matches.opt_str("target-rustcflags"),
149 target: opt_str2(matches.opt_str("target")),
150 host: opt_str2(matches.opt_str("host")),
151 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
152 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
153 android_cross_path: opt_path(matches, "android-cross-path"),
154 adb_path: opt_str2(matches.opt_str("adb-path")),
155 adb_test_dir: format!("{}/{}",
156 opt_str2(matches.opt_str("adb-test-dir")),
157 opt_str2(matches.opt_str("target"))),
158 adb_device_status:
159 opt_str2(matches.opt_str("target")).contains("android") &&
160 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
161 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
162 lldb_python_dir: matches.opt_str("lldb-python-dir"),
163 verbose: matches.opt_present("verbose"),
164 quiet: matches.opt_present("quiet"),
165 }
166 }
167
168 pub fn log_config(config: &Config) {
169 let c = config;
170 logv(c, format!("configuration:"));
171 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
172 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
173 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
174 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
175 logv(c, format!("src_base: {:?}", config.src_base.display()));
176 logv(c, format!("build_base: {:?}", config.build_base.display()));
177 logv(c, format!("stage_id: {}", config.stage_id));
178 logv(c, format!("mode: {}", config.mode));
179 logv(c, format!("run_ignored: {}", config.run_ignored));
180 logv(c, format!("filter: {}",
181 opt_str(&config.filter
182 .as_ref()
183 .map(|re| re.to_owned()))));
184 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
185 logv(c, format!("host-rustcflags: {}",
186 opt_str(&config.host_rustcflags)));
187 logv(c, format!("target-rustcflags: {}",
188 opt_str(&config.target_rustcflags)));
189 logv(c, format!("target: {}", config.target));
190 logv(c, format!("host: {}", config.host));
191 logv(c, format!("android-cross-path: {:?}",
192 config.android_cross_path.display()));
193 logv(c, format!("adb_path: {:?}", config.adb_path));
194 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
195 logv(c, format!("adb_device_status: {}",
196 config.adb_device_status));
197 logv(c, format!("verbose: {}", config.verbose));
198 logv(c, format!("quiet: {}", config.quiet));
199 logv(c, format!("\n"));
200 }
201
202 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
203 match *maybestr {
204 None => "(none)",
205 Some(ref s) => s,
206 }
207 }
208
209 pub fn opt_str2(maybestr: Option<String>) -> String {
210 match maybestr {
211 None => "(none)".to_owned(),
212 Some(s) => s,
213 }
214 }
215
216 pub fn run_tests(config: &Config) {
217 if config.target.contains("android") {
218 if let DebugInfoGdb = config.mode {
219 println!("{} debug-info test uses tcp 5039 port.\
220 please reserve it", config.target);
221 }
222
223 // android debug-info test uses remote debugger
224 // so, we test 1 thread at once.
225 // also trying to isolate problems with adb_run_wrapper.sh ilooping
226 env::set_var("RUST_TEST_THREADS","1");
227 }
228
229 match config.mode {
230 DebugInfoLldb => {
231 // Some older versions of LLDB seem to have problems with multiple
232 // instances running in parallel, so only run one test thread at a
233 // time.
234 env::set_var("RUST_TEST_THREADS", "1");
235 }
236 _ => { /* proceed */ }
237 }
238
239 let opts = test_opts(config);
240 let tests = make_tests(config);
241 // sadly osx needs some file descriptor limits raised for running tests in
242 // parallel (especially when we have lots and lots of child processes).
243 // For context, see #8904
244 unsafe { raise_fd_limit::raise_fd_limit(); }
245 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
246 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
247 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
248 let res = test::run_tests_console(&opts, tests.into_iter().collect());
249 match res {
250 Ok(true) => {}
251 Ok(false) => panic!("Some tests failed"),
252 Err(e) => {
253 println!("I/O failure during tests: {:?}", e);
254 }
255 }
256 }
257
258 pub fn test_opts(config: &Config) -> test::TestOpts {
259 test::TestOpts {
260 filter: config.filter.clone(),
261 run_ignored: config.run_ignored,
262 quiet: config.quiet,
263 logfile: config.logfile.clone(),
264 run_tests: true,
265 bench_benchmarks: true,
266 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
267 Ok(val) => &val != "0",
268 Err(_) => false
269 },
270 color: test::AutoColor,
271 }
272 }
273
274 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
275 debug!("making tests from {:?}",
276 config.src_base.display());
277 let mut tests = Vec::new();
278 collect_tests_from_dir(config,
279 &config.src_base,
280 &config.src_base,
281 &PathBuf::new(),
282 &mut tests)
283 .unwrap();
284 tests
285 }
286
287 fn collect_tests_from_dir(config: &Config,
288 base: &Path,
289 dir: &Path,
290 relative_dir_path: &Path,
291 tests: &mut Vec<test::TestDescAndFn>)
292 -> io::Result<()> {
293 // Ignore directories that contain a file
294 // `compiletest-ignore-dir`.
295 for file in fs::read_dir(dir)? {
296 let file = file?;
297 if file.file_name() == *"compiletest-ignore-dir" {
298 return Ok(());
299 }
300 }
301
302 let dirs = fs::read_dir(dir)?;
303 for file in dirs {
304 let file = file?;
305 let file_path = file.path();
306 debug!("inspecting file {:?}", file_path.display());
307 if is_test(config, &file_path) {
308 // If we find a test foo/bar.rs, we have to build the
309 // output directory `$build/foo` so we can write
310 // `$build/foo/bar` into it. We do this *now* in this
311 // sequential loop because otherwise, if we do it in the
312 // tests themselves, they race for the privilege of
313 // creating the directories and sometimes fail randomly.
314 let build_dir = config.build_base.join(&relative_dir_path);
315 fs::create_dir_all(&build_dir).unwrap();
316
317 let paths = TestPaths {
318 file: file_path,
319 base: base.to_path_buf(),
320 relative_dir: relative_dir_path.to_path_buf(),
321 };
322 tests.push(make_test(config, &paths))
323 } else if file_path.is_dir() {
324 let relative_file_path = relative_dir_path.join(file.file_name());
325 collect_tests_from_dir(config,
326 base,
327 &file_path,
328 &relative_file_path,
329 tests)?;
330 }
331 }
332 Ok(())
333 }
334
335 pub fn is_test(config: &Config, testfile: &Path) -> bool {
336 // Pretty-printer does not work with .rc files yet
337 let valid_extensions =
338 match config.mode {
339 Pretty => vec!(".rs".to_owned()),
340 _ => vec!(".rc".to_owned(), ".rs".to_owned())
341 };
342 let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
343 let name = testfile.file_name().unwrap().to_str().unwrap();
344
345 let mut valid = false;
346
347 for ext in &valid_extensions {
348 if name.ends_with(ext) {
349 valid = true;
350 }
351 }
352
353 for pre in &invalid_prefixes {
354 if name.starts_with(pre) {
355 valid = false;
356 }
357 }
358
359 return valid;
360 }
361
362 pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
363 let early_props = header::early_props(config, &testpaths.file);
364
365 // The `should-fail` annotation doesn't apply to pretty tests,
366 // since we run the pretty printer across all tests by default.
367 // If desired, we could add a `should-fail-pretty` annotation.
368 let should_panic = match config.mode {
369 Pretty => test::ShouldPanic::No,
370 _ => if early_props.should_fail {
371 test::ShouldPanic::Yes
372 } else {
373 test::ShouldPanic::No
374 }
375 };
376
377 test::TestDescAndFn {
378 desc: test::TestDesc {
379 name: make_test_name(config, testpaths),
380 ignore: early_props.ignore,
381 should_panic: should_panic,
382 },
383 testfn: make_test_closure(config, testpaths),
384 }
385 }
386
387 pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
388 // Convert a complete path to something like
389 //
390 // run-pass/foo/bar/baz.rs
391 let path =
392 PathBuf::from(config.mode.to_string())
393 .join(&testpaths.relative_dir)
394 .join(&testpaths.file.file_name().unwrap());
395 test::DynTestName(format!("[{}] {}", config.mode, path.display()))
396 }
397
398 pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
399 let config = config.clone();
400 let testpaths = testpaths.clone();
401 test::DynTestFn(Box::new(move || {
402 runtest::run(config, &testpaths)
403 }))
404 }
405
406 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
407 match full_version_line {
408 Some(ref full_version_line)
409 if !full_version_line.trim().is_empty() => {
410 let full_version_line = full_version_line.trim();
411
412 // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
413 for (pos, c) in full_version_line.char_indices() {
414 if !c.is_digit(10) {
415 continue
416 }
417 if pos + 2 >= full_version_line.len() {
418 continue
419 }
420 if full_version_line[pos + 1..].chars().next().unwrap() != '.' {
421 continue
422 }
423 if !full_version_line[pos + 2..].chars().next().unwrap().is_digit(10) {
424 continue
425 }
426 if pos > 0 && full_version_line[..pos].chars().next_back()
427 .unwrap().is_digit(10) {
428 continue
429 }
430 let mut end = pos + 3;
431 while end < full_version_line.len() &&
432 full_version_line[end..].chars().next()
433 .unwrap().is_digit(10) {
434 end += 1;
435 }
436 return Some(full_version_line[pos..end].to_owned());
437 }
438 println!("Could not extract GDB version from line '{}'",
439 full_version_line);
440 None
441 },
442 _ => None
443 }
444 }
445
446 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
447 // Extract the major LLDB version from the given version string.
448 // LLDB version strings are different for Apple and non-Apple platforms.
449 // At the moment, this function only supports the Apple variant, which looks
450 // like this:
451 //
452 // LLDB-179.5 (older versions)
453 // lldb-300.2.51 (new versions)
454 //
455 // We are only interested in the major version number, so this function
456 // will return `Some("179")` and `Some("300")` respectively.
457
458 if let Some(ref full_version_line) = full_version_line {
459 if !full_version_line.trim().is_empty() {
460 let full_version_line = full_version_line.trim();
461
462 for (pos, l) in full_version_line.char_indices() {
463 if l != 'l' && l != 'L' { continue }
464 if pos + 5 >= full_version_line.len() { continue }
465 let l = full_version_line[pos + 1..].chars().next().unwrap();
466 if l != 'l' && l != 'L' { continue }
467 let d = full_version_line[pos + 2..].chars().next().unwrap();
468 if d != 'd' && d != 'D' { continue }
469 let b = full_version_line[pos + 3..].chars().next().unwrap();
470 if b != 'b' && b != 'B' { continue }
471 let dash = full_version_line[pos + 4..].chars().next().unwrap();
472 if dash != '-' { continue }
473
474 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
475 c.is_digit(10)
476 }).collect::<String>();
477 if !vers.is_empty() { return Some(vers) }
478 }
479 println!("Could not extract LLDB version from line '{}'",
480 full_version_line);
481 }
482 }
483 None
484 }