]> git.proxmox.com Git - rustc.git/blob - src/compiletest/compiletest.rs
Imported Upstream version 1.1.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(collections)]
15 #![feature(rustc_private)]
16 #![feature(std_misc)]
17 #![feature(test)]
18 #![feature(path_ext)]
19 #![feature(str_char)]
20 #![feature(libc)]
21
22 #![deny(warnings)]
23
24 extern crate libc;
25 extern crate test;
26 extern crate getopts;
27
28 #[macro_use]
29 extern crate log;
30
31 use std::env;
32 use std::fs;
33 use std::path::{Path, PathBuf};
34 use getopts::{optopt, optflag, reqopt};
35 use common::Config;
36 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
37 use util::logv;
38
39 pub mod procsrv;
40 pub mod util;
41 pub mod header;
42 pub mod runtest;
43 pub mod common;
44 pub mod errors;
45 mod raise_fd_limit;
46
47 pub fn main() {
48 let config = parse_config(env::args().collect());
49
50 if config.valgrind_path.is_none() && config.force_valgrind {
51 panic!("Can't find Valgrind to run Valgrind tests");
52 }
53
54 log_config(&config);
55 run_tests(&config);
56 }
57
58 pub fn parse_config(args: Vec<String> ) -> Config {
59
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("", "clang-path", "path to executable for codegen 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 optflag("", "jit", "run tests under the JIT"),
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"));
93
94 assert!(!args.is_empty());
95 let argv0 = args[0].clone();
96 let args_ = args.tail();
97 if args[1] == "-h" || args[1] == "--help" {
98 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
99 println!("{}", getopts::usage(&message, &groups));
100 println!("");
101 panic!()
102 }
103
104 let matches =
105 &match getopts::getopts(args_, &groups) {
106 Ok(m) => m,
107 Err(f) => panic!("{:?}", f)
108 };
109
110 if matches.opt_present("h") || matches.opt_present("help") {
111 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
112 println!("{}", getopts::usage(&message, &groups));
113 println!("");
114 panic!()
115 }
116
117 fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf {
118 match m.opt_str(nm) {
119 Some(s) => PathBuf::from(&s),
120 None => panic!("no option (=path) found for {}", nm),
121 }
122 }
123
124 let filter = if !matches.free.is_empty() {
125 Some(matches.free[0].clone())
126 } else {
127 None
128 };
129
130 Config {
131 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
132 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
133 rustc_path: opt_path(matches, "rustc-path"),
134 rustdoc_path: opt_path(matches, "rustdoc-path"),
135 python: matches.opt_str("python").unwrap(),
136 clang_path: matches.opt_str("clang-path").map(|s| PathBuf::from(&s)),
137 valgrind_path: matches.opt_str("valgrind-path"),
138 force_valgrind: matches.opt_present("force-valgrind"),
139 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
140 src_base: opt_path(matches, "src-base"),
141 build_base: opt_path(matches, "build-base"),
142 aux_base: opt_path(matches, "aux-base"),
143 stage_id: matches.opt_str("stage-id").unwrap(),
144 mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
145 run_ignored: matches.opt_present("ignored"),
146 filter: filter,
147 logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
148 runtool: matches.opt_str("runtool"),
149 host_rustcflags: matches.opt_str("host-rustcflags"),
150 target_rustcflags: matches.opt_str("target-rustcflags"),
151 jit: matches.opt_present("jit"),
152 target: opt_str2(matches.opt_str("target")),
153 host: opt_str2(matches.opt_str("host")),
154 gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
155 lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
156 android_cross_path: opt_path(matches, "android-cross-path"),
157 adb_path: opt_str2(matches.opt_str("adb-path")),
158 adb_test_dir: format!("{}/{}",
159 opt_str2(matches.opt_str("adb-test-dir")),
160 opt_str2(matches.opt_str("target"))),
161 adb_device_status:
162 opt_str2(matches.opt_str("target")).contains("android") &&
163 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
164 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
165 lldb_python_dir: matches.opt_str("lldb-python-dir"),
166 verbose: matches.opt_present("verbose"),
167 }
168 }
169
170 pub fn log_config(config: &Config) {
171 let c = config;
172 logv(c, format!("configuration:"));
173 logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
174 logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
175 logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
176 logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
177 logv(c, format!("src_base: {:?}", config.src_base.display()));
178 logv(c, format!("build_base: {:?}", config.build_base.display()));
179 logv(c, format!("stage_id: {}", config.stage_id));
180 logv(c, format!("mode: {}", config.mode));
181 logv(c, format!("run_ignored: {}", config.run_ignored));
182 logv(c, format!("filter: {}",
183 opt_str(&config.filter
184 .as_ref()
185 .map(|re| re.to_string()))));
186 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
187 logv(c, format!("host-rustcflags: {}",
188 opt_str(&config.host_rustcflags)));
189 logv(c, format!("target-rustcflags: {}",
190 opt_str(&config.target_rustcflags)));
191 logv(c, format!("jit: {}", config.jit));
192 logv(c, format!("target: {}", config.target));
193 logv(c, format!("host: {}", config.host));
194 logv(c, format!("android-cross-path: {:?}",
195 config.android_cross_path.display()));
196 logv(c, format!("adb_path: {:?}", config.adb_path));
197 logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
198 logv(c, format!("adb_device_status: {}",
199 config.adb_device_status));
200 logv(c, format!("verbose: {}", config.verbose));
201 logv(c, format!("\n"));
202 }
203
204 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
205 match *maybestr {
206 None => "(none)",
207 Some(ref s) => s,
208 }
209 }
210
211 pub fn opt_str2(maybestr: Option<String>) -> String {
212 match maybestr {
213 None => "(none)".to_string(),
214 Some(s) => s,
215 }
216 }
217
218 pub fn run_tests(config: &Config) {
219 if config.target.contains("android") {
220 match config.mode {
221 DebugInfoGdb => {
222 println!("{} debug-info test uses tcp 5039 port.\
223 please reserve it", config.target);
224 }
225 _ =>{}
226 }
227
228 // android debug-info test uses remote debugger
229 // so, we test 1 thread at once.
230 // also trying to isolate problems with adb_run_wrapper.sh ilooping
231 env::set_var("RUST_TEST_THREADS","1");
232 }
233
234 match config.mode {
235 DebugInfoLldb => {
236 // Some older versions of LLDB seem to have problems with multiple
237 // instances running in parallel, so only run one test thread at a
238 // time.
239 env::set_var("RUST_TEST_THREADS", "1");
240 }
241 _ => { /* proceed */ }
242 }
243
244 let opts = test_opts(config);
245 let tests = make_tests(config);
246 // sadly osx needs some file descriptor limits raised for running tests in
247 // parallel (especially when we have lots and lots of child processes).
248 // For context, see #8904
249 unsafe { raise_fd_limit::raise_fd_limit(); }
250 // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
251 // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
252 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
253 let res = test::run_tests_console(&opts, tests.into_iter().collect());
254 match res {
255 Ok(true) => {}
256 Ok(false) => panic!("Some tests failed"),
257 Err(e) => {
258 println!("I/O failure during tests: {:?}", e);
259 }
260 }
261 }
262
263 pub fn test_opts(config: &Config) -> test::TestOpts {
264 test::TestOpts {
265 filter: match config.filter {
266 None => None,
267 Some(ref filter) => Some(filter.clone()),
268 },
269 run_ignored: config.run_ignored,
270 logfile: config.logfile.clone(),
271 run_tests: true,
272 bench_benchmarks: true,
273 nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
274 color: test::AutoColor,
275 }
276 }
277
278 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
279 debug!("making tests from {:?}",
280 config.src_base.display());
281 let mut tests = Vec::new();
282 let dirs = fs::read_dir(&config.src_base).unwrap();
283 for file in dirs {
284 let file = file.unwrap().path();
285 debug!("inspecting file {:?}", file.display());
286 if is_test(config, &file) {
287 let t = make_test(config, &file, || {
288 match config.mode {
289 Codegen => make_metrics_test_closure(config, &file),
290 _ => make_test_closure(config, &file)
291 }
292 });
293 tests.push(t)
294 }
295 }
296 tests
297 }
298
299 pub fn is_test(config: &Config, testfile: &Path) -> bool {
300 // Pretty-printer does not work with .rc files yet
301 let valid_extensions =
302 match config.mode {
303 Pretty => vec!(".rs".to_string()),
304 _ => vec!(".rc".to_string(), ".rs".to_string())
305 };
306 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
307 let name = testfile.file_name().unwrap().to_str().unwrap();
308
309 let mut valid = false;
310
311 for ext in &valid_extensions {
312 if name.ends_with(ext) {
313 valid = true;
314 }
315 }
316
317 for pre in &invalid_prefixes {
318 if name.starts_with(pre) {
319 valid = false;
320 }
321 }
322
323 return valid;
324 }
325
326 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
327 F: FnOnce() -> test::TestFn,
328 {
329 test::TestDescAndFn {
330 desc: test::TestDesc {
331 name: make_test_name(config, testfile),
332 ignore: header::is_test_ignored(config, testfile),
333 should_panic: test::ShouldPanic::No,
334 },
335 testfn: f(),
336 }
337 }
338
339 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
340
341 // Try to elide redundant long paths
342 fn shorten(path: &Path) -> String {
343 let filename = path.file_name().unwrap().to_str();
344 let p = path.parent().unwrap();
345 let dir = p.file_name().unwrap().to_str();
346 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
347 }
348
349 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
350 }
351
352 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
353 let config = (*config).clone();
354 let testfile = testfile.to_path_buf();
355 test::DynTestFn(Box::new(move || {
356 runtest::run(config, &testfile)
357 }))
358 }
359
360 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
361 let config = (*config).clone();
362 let testfile = testfile.to_path_buf();
363 test::DynMetricFn(box move |mm: &mut test::MetricMap| {
364 runtest::run_metrics(config, &testfile, mm)
365 })
366 }
367
368 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
369 match full_version_line {
370 Some(ref full_version_line)
371 if !full_version_line.trim().is_empty() => {
372 let full_version_line = full_version_line.trim();
373
374 // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
375 for (pos, c) in full_version_line.char_indices() {
376 if !c.is_digit(10) { continue }
377 if pos + 2 >= full_version_line.len() { continue }
378 if full_version_line.char_at(pos + 1) != '.' { continue }
379 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
380 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
381 continue
382 }
383 if pos + 3 < full_version_line.len() &&
384 full_version_line.char_at(pos + 3).is_digit(10) {
385 continue
386 }
387 return Some(full_version_line[pos..pos+3].to_string());
388 }
389 println!("Could not extract GDB version from line '{}'",
390 full_version_line);
391 None
392 },
393 _ => None
394 }
395 }
396
397 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
398 // Extract the major LLDB version from the given version string.
399 // LLDB version strings are different for Apple and non-Apple platforms.
400 // At the moment, this function only supports the Apple variant, which looks
401 // like this:
402 //
403 // LLDB-179.5 (older versions)
404 // lldb-300.2.51 (new versions)
405 //
406 // We are only interested in the major version number, so this function
407 // will return `Some("179")` and `Some("300")` respectively.
408
409 match full_version_line {
410 Some(ref full_version_line)
411 if !full_version_line.trim().is_empty() => {
412 let full_version_line = full_version_line.trim();
413
414 for (pos, l) in full_version_line.char_indices() {
415 if l != 'l' && l != 'L' { continue }
416 if pos + 5 >= full_version_line.len() { continue }
417 let l = full_version_line.char_at(pos + 1);
418 if l != 'l' && l != 'L' { continue }
419 let d = full_version_line.char_at(pos + 2);
420 if d != 'd' && d != 'D' { continue }
421 let b = full_version_line.char_at(pos + 3);
422 if b != 'b' && b != 'B' { continue }
423 let dash = full_version_line.char_at(pos + 4);
424 if dash != '-' { continue }
425
426 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
427 c.is_digit(10)
428 }).collect::<String>();
429 if !vers.is_empty() { return Some(vers) }
430 }
431 println!("Could not extract LLDB version from line '{}'",
432 full_version_line);
433 None
434 },
435 _ => None
436 }
437 }