]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
Fix compile-fail tests on windows
[rust.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_splits)]
19 #![feature(str_char)]
20 #![feature(test)]
21 #![feature(vec_push_all)]
22 #![feature(path_components_peek)]
23
24 #![deny(warnings)]
25
26 extern crate libc;
27 extern crate test;
28 extern crate getopts;
29
30 #[macro_use]
31 extern crate log;
32
33 use std::env;
34 use std::fs;
35 use std::path::{Path, PathBuf};
36 use getopts::{optopt, optflag, reqopt};
37 use common::Config;
38 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
39 use util::logv;
40
41 pub mod procsrv;
42 pub mod util;
43 pub mod header;
44 pub mod runtest;
45 pub mod common;
46 pub mod errors;
47 mod raise_fd_limit;
48
49 pub fn main() {
50     let config = parse_config(env::args().collect());
51
52     if config.valgrind_path.is_none() && config.force_valgrind {
53         panic!("Can't find Valgrind to run Valgrind tests");
54     }
55
56     log_config(&config);
57     run_tests(&config);
58 }
59
60 pub fn parse_config(args: Vec<String> ) -> Config {
61
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"));
93
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));
98         println!("");
99         panic!()
100     }
101
102     let matches =
103         &match getopts::getopts(args_, &groups) {
104           Ok(m) => m,
105           Err(f) => panic!("{:?}", f)
106         };
107
108     if matches.opt_present("h") || matches.opt_present("help") {
109         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
110         println!("{}", getopts::usage(&message, &groups));
111         println!("");
112         panic!()
113     }
114
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),
119         }
120     }
121
122     let filter = if !matches.free.is_empty() {
123         Some(matches.free[0].clone())
124     } else {
125         None
126     };
127
128     Config {
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"),
143         filter: filter,
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"))),
157         adb_device_status:
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"),
163     }
164 }
165
166 pub fn log_config(config: &Config) {
167     let c = 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
180                                    .as_ref()
181                                    .map(|re| re.to_string()))));
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"));
197 }
198
199 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
200     match *maybestr {
201         None => "(none)",
202         Some(ref s) => s,
203     }
204 }
205
206 pub fn opt_str2(maybestr: Option<String>) -> String {
207     match maybestr {
208         None => "(none)".to_string(),
209         Some(s) => s,
210     }
211 }
212
213 pub fn run_tests(config: &Config) {
214     if config.target.contains("android") {
215         match config.mode {
216             DebugInfoGdb => {
217                 println!("{} debug-info test uses tcp 5039 port.\
218                          please reserve it", config.target);
219             }
220             _ =>{}
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: match config.filter {
261             None => None,
262             Some(ref filter) => Some(filter.clone()),
263         },
264         run_ignored: config.run_ignored,
265         logfile: config.logfile.clone(),
266         run_tests: true,
267         bench_benchmarks: true,
268         nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
269         color: test::AutoColor,
270     }
271 }
272
273 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
274     debug!("making tests from {:?}",
275            config.src_base.display());
276     let mut tests = Vec::new();
277     let dirs = fs::read_dir(&config.src_base).unwrap();
278     for file in dirs {
279         let file = file.unwrap().path();
280         debug!("inspecting file {:?}", file.display());
281         if is_test(config, &file) {
282             tests.push(make_test(config, &file))
283         }
284     }
285     tests
286 }
287
288 pub fn is_test(config: &Config, testfile: &Path) -> bool {
289     // Pretty-printer does not work with .rc files yet
290     let valid_extensions =
291         match config.mode {
292           Pretty => vec!(".rs".to_string()),
293           _ => vec!(".rc".to_string(), ".rs".to_string())
294         };
295     let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
296     let name = testfile.file_name().unwrap().to_str().unwrap();
297
298     let mut valid = false;
299
300     for ext in &valid_extensions {
301         if name.ends_with(ext) {
302             valid = true;
303         }
304     }
305
306     for pre in &invalid_prefixes {
307         if name.starts_with(pre) {
308             valid = false;
309         }
310     }
311
312     return valid;
313 }
314
315 pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
316 {
317     test::TestDescAndFn {
318         desc: test::TestDesc {
319             name: make_test_name(config, testfile),
320             ignore: header::is_test_ignored(config, testfile),
321             should_panic: test::ShouldPanic::No,
322         },
323         testfn: make_test_closure(config, &testfile),
324     }
325 }
326
327 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
328
329     // Try to elide redundant long paths
330     fn shorten(path: &Path) -> String {
331         let filename = path.file_name().unwrap().to_str();
332         let p = path.parent().unwrap();
333         let dir = p.file_name().unwrap().to_str();
334         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
335     }
336
337     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
338 }
339
340 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
341     let config = (*config).clone();
342     let testfile = testfile.to_path_buf();
343     test::DynTestFn(Box::new(move || {
344         runtest::run(config, &testfile)
345     }))
346 }
347
348 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
349     match full_version_line {
350         Some(ref full_version_line)
351           if !full_version_line.trim().is_empty() => {
352             let full_version_line = full_version_line.trim();
353
354             // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
355             for (pos, c) in full_version_line.char_indices() {
356                 if !c.is_digit(10) { continue }
357                 if pos + 2 >= full_version_line.len() { continue }
358                 if full_version_line.char_at(pos + 1) != '.' { continue }
359                 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
360                 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
361                     continue
362                 }
363                 if pos + 3 < full_version_line.len() &&
364                    full_version_line.char_at(pos + 3).is_digit(10) {
365                     continue
366                 }
367                 return Some(full_version_line[pos..pos+3].to_string());
368             }
369             println!("Could not extract GDB version from line '{}'",
370                      full_version_line);
371             None
372         },
373         _ => None
374     }
375 }
376
377 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
378     // Extract the major LLDB version from the given version string.
379     // LLDB version strings are different for Apple and non-Apple platforms.
380     // At the moment, this function only supports the Apple variant, which looks
381     // like this:
382     //
383     // LLDB-179.5 (older versions)
384     // lldb-300.2.51 (new versions)
385     //
386     // We are only interested in the major version number, so this function
387     // will return `Some("179")` and `Some("300")` respectively.
388
389     match full_version_line {
390         Some(ref full_version_line)
391           if !full_version_line.trim().is_empty() => {
392             let full_version_line = full_version_line.trim();
393
394             for (pos, l) in full_version_line.char_indices() {
395                 if l != 'l' && l != 'L' { continue }
396                 if pos + 5 >= full_version_line.len() { continue }
397                 let l = full_version_line.char_at(pos + 1);
398                 if l != 'l' && l != 'L' { continue }
399                 let d = full_version_line.char_at(pos + 2);
400                 if d != 'd' && d != 'D' { continue }
401                 let b = full_version_line.char_at(pos + 3);
402                 if b != 'b' && b != 'B' { continue }
403                 let dash = full_version_line.char_at(pos + 4);
404                 if dash != '-' { continue }
405
406                 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
407                     c.is_digit(10)
408                 }).collect::<String>();
409                 if !vers.is_empty() { return Some(vers) }
410             }
411             println!("Could not extract LLDB version from line '{}'",
412                      full_version_line);
413             None
414         },
415         _ => None
416     }
417 }