]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
Rollup merge of #26695 - rutsky:patch-2, r=Manishearth
[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
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     let (argv0, args_) = args.split_first().unwrap();
94     if 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     let filter = if !matches.free.is_empty() {
122         Some(matches.free[0].clone())
123     } else {
124         None
125     };
126
127     Config {
128         compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
129         run_lib_path: matches.opt_str("run-lib-path").unwrap(),
130         rustc_path: opt_path(matches, "rustc-path"),
131         rustdoc_path: opt_path(matches, "rustdoc-path"),
132         python: matches.opt_str("python").unwrap(),
133         valgrind_path: matches.opt_str("valgrind-path"),
134         force_valgrind: matches.opt_present("force-valgrind"),
135         llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
136         src_base: opt_path(matches, "src-base"),
137         build_base: opt_path(matches, "build-base"),
138         aux_base: opt_path(matches, "aux-base"),
139         stage_id: matches.opt_str("stage-id").unwrap(),
140         mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
141         run_ignored: matches.opt_present("ignored"),
142         filter: filter,
143         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
144         runtool: matches.opt_str("runtool"),
145         host_rustcflags: matches.opt_str("host-rustcflags"),
146         target_rustcflags: matches.opt_str("target-rustcflags"),
147         target: opt_str2(matches.opt_str("target")),
148         host: opt_str2(matches.opt_str("host")),
149         gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
150         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
151         android_cross_path: opt_path(matches, "android-cross-path"),
152         adb_path: opt_str2(matches.opt_str("adb-path")),
153         adb_test_dir: format!("{}/{}",
154             opt_str2(matches.opt_str("adb-test-dir")),
155             opt_str2(matches.opt_str("target"))),
156         adb_device_status:
157             opt_str2(matches.opt_str("target")).contains("android") &&
158             "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
159             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
160         lldb_python_dir: matches.opt_str("lldb-python-dir"),
161         verbose: matches.opt_present("verbose"),
162     }
163 }
164
165 pub fn log_config(config: &Config) {
166     let c = config;
167     logv(c, format!("configuration:"));
168     logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
169     logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
170     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
171     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
172     logv(c, format!("src_base: {:?}", config.src_base.display()));
173     logv(c, format!("build_base: {:?}", config.build_base.display()));
174     logv(c, format!("stage_id: {}", config.stage_id));
175     logv(c, format!("mode: {}", config.mode));
176     logv(c, format!("run_ignored: {}", config.run_ignored));
177     logv(c, format!("filter: {}",
178                     opt_str(&config.filter
179                                    .as_ref()
180                                    .map(|re| re.to_string()))));
181     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
182     logv(c, format!("host-rustcflags: {}",
183                     opt_str(&config.host_rustcflags)));
184     logv(c, format!("target-rustcflags: {}",
185                     opt_str(&config.target_rustcflags)));
186     logv(c, format!("target: {}", config.target));
187     logv(c, format!("host: {}", config.host));
188     logv(c, format!("android-cross-path: {:?}",
189                     config.android_cross_path.display()));
190     logv(c, format!("adb_path: {:?}", config.adb_path));
191     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
192     logv(c, format!("adb_device_status: {}",
193                     config.adb_device_status));
194     logv(c, format!("verbose: {}", config.verbose));
195     logv(c, format!("\n"));
196 }
197
198 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
199     match *maybestr {
200         None => "(none)",
201         Some(ref s) => s,
202     }
203 }
204
205 pub fn opt_str2(maybestr: Option<String>) -> String {
206     match maybestr {
207         None => "(none)".to_string(),
208         Some(s) => s,
209     }
210 }
211
212 pub fn run_tests(config: &Config) {
213     if config.target.contains("android") {
214         match config.mode {
215             DebugInfoGdb => {
216                 println!("{} debug-info test uses tcp 5039 port.\
217                          please reserve it", config.target);
218             }
219             _ =>{}
220         }
221
222         // android debug-info test uses remote debugger
223         // so, we test 1 thread at once.
224         // also trying to isolate problems with adb_run_wrapper.sh ilooping
225         env::set_var("RUST_TEST_THREADS","1");
226     }
227
228     match config.mode {
229         DebugInfoLldb => {
230             // Some older versions of LLDB seem to have problems with multiple
231             // instances running in parallel, so only run one test thread at a
232             // time.
233             env::set_var("RUST_TEST_THREADS", "1");
234         }
235         _ => { /* proceed */ }
236     }
237
238     let opts = test_opts(config);
239     let tests = make_tests(config);
240     // sadly osx needs some file descriptor limits raised for running tests in
241     // parallel (especially when we have lots and lots of child processes).
242     // For context, see #8904
243     unsafe { raise_fd_limit::raise_fd_limit(); }
244     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
245     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
246     env::set_var("__COMPAT_LAYER", "RunAsInvoker");
247     let res = test::run_tests_console(&opts, tests.into_iter().collect());
248     match res {
249         Ok(true) => {}
250         Ok(false) => panic!("Some tests failed"),
251         Err(e) => {
252             println!("I/O failure during tests: {:?}", e);
253         }
254     }
255 }
256
257 pub fn test_opts(config: &Config) -> test::TestOpts {
258     test::TestOpts {
259         filter: match config.filter {
260             None => None,
261             Some(ref filter) => Some(filter.clone()),
262         },
263         run_ignored: config.run_ignored,
264         logfile: config.logfile.clone(),
265         run_tests: true,
266         bench_benchmarks: true,
267         nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
268         color: test::AutoColor,
269     }
270 }
271
272 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
273     debug!("making tests from {:?}",
274            config.src_base.display());
275     let mut tests = Vec::new();
276     let dirs = fs::read_dir(&config.src_base).unwrap();
277     for file in dirs {
278         let file = file.unwrap().path();
279         debug!("inspecting file {:?}", file.display());
280         if is_test(config, &file) {
281             tests.push(make_test(config, &file))
282         }
283     }
284     tests
285 }
286
287 pub fn is_test(config: &Config, testfile: &Path) -> bool {
288     // Pretty-printer does not work with .rc files yet
289     let valid_extensions =
290         match config.mode {
291           Pretty => vec!(".rs".to_string()),
292           _ => vec!(".rc".to_string(), ".rs".to_string())
293         };
294     let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
295     let name = testfile.file_name().unwrap().to_str().unwrap();
296
297     let mut valid = false;
298
299     for ext in &valid_extensions {
300         if name.ends_with(ext) {
301             valid = true;
302         }
303     }
304
305     for pre in &invalid_prefixes {
306         if name.starts_with(pre) {
307             valid = false;
308         }
309     }
310
311     return valid;
312 }
313
314 pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn
315 {
316     test::TestDescAndFn {
317         desc: test::TestDesc {
318             name: make_test_name(config, testfile),
319             ignore: header::is_test_ignored(config, testfile),
320             should_panic: test::ShouldPanic::No,
321         },
322         testfn: make_test_closure(config, &testfile),
323     }
324 }
325
326 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
327
328     // Try to elide redundant long paths
329     fn shorten(path: &Path) -> String {
330         let filename = path.file_name().unwrap().to_str();
331         let p = path.parent().unwrap();
332         let dir = p.file_name().unwrap().to_str();
333         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
334     }
335
336     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
337 }
338
339 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
340     let config = (*config).clone();
341     let testfile = testfile.to_path_buf();
342     test::DynTestFn(Box::new(move || {
343         runtest::run(config, &testfile)
344     }))
345 }
346
347 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
348     match full_version_line {
349         Some(ref full_version_line)
350           if !full_version_line.trim().is_empty() => {
351             let full_version_line = full_version_line.trim();
352
353             // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
354             for (pos, c) in full_version_line.char_indices() {
355                 if !c.is_digit(10) { continue }
356                 if pos + 2 >= full_version_line.len() { continue }
357                 if full_version_line.char_at(pos + 1) != '.' { continue }
358                 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
359                 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
360                     continue
361                 }
362                 if pos + 3 < full_version_line.len() &&
363                    full_version_line.char_at(pos + 3).is_digit(10) {
364                     continue
365                 }
366                 return Some(full_version_line[pos..pos+3].to_string());
367             }
368             println!("Could not extract GDB version from line '{}'",
369                      full_version_line);
370             None
371         },
372         _ => None
373     }
374 }
375
376 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
377     // Extract the major LLDB version from the given version string.
378     // LLDB version strings are different for Apple and non-Apple platforms.
379     // At the moment, this function only supports the Apple variant, which looks
380     // like this:
381     //
382     // LLDB-179.5 (older versions)
383     // lldb-300.2.51 (new versions)
384     //
385     // We are only interested in the major version number, so this function
386     // will return `Some("179")` and `Some("300")` respectively.
387
388     match full_version_line {
389         Some(ref full_version_line)
390           if !full_version_line.trim().is_empty() => {
391             let full_version_line = full_version_line.trim();
392
393             for (pos, l) in full_version_line.char_indices() {
394                 if l != 'l' && l != 'L' { continue }
395                 if pos + 5 >= full_version_line.len() { continue }
396                 let l = full_version_line.char_at(pos + 1);
397                 if l != 'l' && l != 'L' { continue }
398                 let d = full_version_line.char_at(pos + 2);
399                 if d != 'd' && d != 'D' { continue }
400                 let b = full_version_line.char_at(pos + 3);
401                 if b != 'b' && b != 'B' { continue }
402                 let dash = full_version_line.char_at(pos + 4);
403                 if dash != '-' { continue }
404
405                 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
406                     c.is_digit(10)
407                 }).collect::<String>();
408                 if !vers.is_empty() { return Some(vers) }
409             }
410             println!("Could not extract LLDB version from line '{}'",
411                      full_version_line);
412             None
413         },
414         _ => None
415     }
416 }