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