]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
rollup merge of #23749: alexcrichton/remove-old-impl-check
[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(collections)]
15 #![feature(old_io)]
16 #![feature(old_path)]
17 #![feature(rustc_private)]
18 #![feature(unboxed_closures)]
19 #![feature(std_misc)]
20 #![feature(test)]
21 #![feature(path_ext)]
22 #![feature(convert)]
23 #![feature(str_char)]
24
25 #![deny(warnings)]
26
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 std::thunk::Thunk;
37 use getopts::{optopt, optflag, reqopt};
38 use common::Config;
39 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
40 use util::logv;
41
42 pub mod procsrv;
43 pub mod util;
44 pub mod header;
45 pub mod runtest;
46 pub mod common;
47 pub mod errors;
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           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         clang_path: matches.opt_str("clang-path").map(|s| PathBuf::from(&s)),
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         jit: matches.opt_present("jit"),
150         target: opt_str2(matches.opt_str("target")),
151         host: opt_str2(matches.opt_str("host")),
152         gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
153         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
154         android_cross_path: opt_path(matches, "android-cross-path"),
155         adb_path: opt_str2(matches.opt_str("adb-path")),
156         adb_test_dir: format!("{}/{}",
157             opt_str2(matches.opt_str("adb-test-dir")),
158             opt_str2(matches.opt_str("target"))),
159         adb_device_status:
160             opt_str2(matches.opt_str("target")).contains("android") &&
161             "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
162             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
163         lldb_python_dir: matches.opt_str("lldb-python-dir"),
164         verbose: matches.opt_present("verbose"),
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!("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!("jit: {}", config.jit));
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!("\n"));
199 }
200
201 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
202     match *maybestr {
203         None => "(none)",
204         Some(ref s) => s,
205     }
206 }
207
208 pub fn opt_str2(maybestr: Option<String>) -> String {
209     match maybestr {
210         None => "(none)".to_string(),
211         Some(s) => s,
212     }
213 }
214
215 pub fn run_tests(config: &Config) {
216     if config.target.contains("android") {
217         match config.mode {
218             DebugInfoGdb => {
219                 println!("{} debug-info test uses tcp 5039 port.\
220                          please reserve it", config.target);
221             }
222             _ =>{}
223         }
224
225         // android debug-info test uses remote debugger
226         // so, we test 1 task at once.
227         // also trying to isolate problems with adb_run_wrapper.sh ilooping
228         env::set_var("RUST_TEST_THREADS","1");
229     }
230
231     match config.mode {
232         DebugInfoLldb => {
233             // Some older versions of LLDB seem to have problems with multiple
234             // instances running in parallel, so only run one test task at a
235             // time.
236             env::set_var("RUST_TEST_THREADS", "1");
237         }
238         _ => { /* proceed */ }
239     }
240
241     let opts = test_opts(config);
242     let tests = make_tests(config);
243     // sadly osx needs some file descriptor limits raised for running tests in
244     // parallel (especially when we have lots and lots of child processes).
245     // For context, see #8904
246     #[allow(deprecated)]
247     fn raise_fd_limit() {
248         std::old_io::test::raise_fd_limit();
249     }
250     raise_fd_limit();
251     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
252     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
253     env::set_var("__COMPAT_LAYER", "RunAsInvoker");
254     let res = test::run_tests_console(&opts, tests.into_iter().collect());
255     match res {
256         Ok(true) => {}
257         Ok(false) => panic!("Some tests failed"),
258         Err(e) => {
259             println!("I/O failure during tests: {:?}", e);
260         }
261     }
262 }
263
264 pub fn test_opts(config: &Config) -> test::TestOpts {
265     test::TestOpts {
266         filter: match config.filter {
267             None => None,
268             Some(ref filter) => Some(filter.clone()),
269         },
270         run_ignored: config.run_ignored,
271         logfile: config.logfile.clone(),
272         run_tests: true,
273         run_benchmarks: true,
274         nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
275         color: test::AutoColor,
276     }
277 }
278
279 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
280     debug!("making tests from {:?}",
281            config.src_base.display());
282     let mut tests = Vec::new();
283     let dirs = fs::read_dir(&config.src_base).unwrap();
284     for file in dirs {
285         let file = file.unwrap().path();
286         debug!("inspecting file {:?}", file.display());
287         if is_test(config, &file) {
288             let t = make_test(config, &file, || {
289                 match config.mode {
290                     Codegen => make_metrics_test_closure(config, &file),
291                     _ => make_test_closure(config, &file)
292                 }
293             });
294             tests.push(t)
295         }
296     }
297     tests
298 }
299
300 pub fn is_test(config: &Config, testfile: &Path) -> bool {
301     // Pretty-printer does not work with .rc files yet
302     let valid_extensions =
303         match config.mode {
304           Pretty => vec!(".rs".to_string()),
305           _ => vec!(".rc".to_string(), ".rs".to_string())
306         };
307     let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
308     let name = testfile.file_name().unwrap().to_str().unwrap();
309
310     let mut valid = false;
311
312     for ext in &valid_extensions {
313         if name.ends_with(ext) {
314             valid = true;
315         }
316     }
317
318     for pre in &invalid_prefixes {
319         if name.starts_with(pre) {
320             valid = false;
321         }
322     }
323
324     return valid;
325 }
326
327 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
328     F: FnOnce() -> test::TestFn,
329 {
330     test::TestDescAndFn {
331         desc: test::TestDesc {
332             name: make_test_name(config, testfile),
333             ignore: header::is_test_ignored(config, testfile),
334             should_panic: test::ShouldPanic::No,
335         },
336         testfn: f(),
337     }
338 }
339
340 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
341
342     // Try to elide redundant long paths
343     fn shorten(path: &Path) -> String {
344         let filename = path.file_name().unwrap().to_str();
345         let p = path.parent().unwrap();
346         let dir = p.file_name().unwrap().to_str();
347         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
348     }
349
350     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
351 }
352
353 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
354     let config = (*config).clone();
355     let testfile = testfile.to_path_buf();
356     test::DynTestFn(Thunk::new(move || {
357         runtest::run(config, &testfile)
358     }))
359 }
360
361 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
362     let config = (*config).clone();
363     let testfile = testfile.to_path_buf();
364     test::DynMetricFn(box move |mm: &mut test::MetricMap| {
365         runtest::run_metrics(config, &testfile, mm)
366     })
367 }
368
369 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
370     match full_version_line {
371         Some(ref full_version_line)
372           if full_version_line.trim().len() > 0 => {
373             let full_version_line = full_version_line.trim();
374
375             // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
376             for (pos, c) in full_version_line.char_indices() {
377                 if !c.is_digit(10) { continue }
378                 if pos + 2 >= full_version_line.len() { continue }
379                 if full_version_line.char_at(pos + 1) != '.' { continue }
380                 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
381                 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
382                     continue
383                 }
384                 if pos + 3 < full_version_line.len() &&
385                    full_version_line.char_at(pos + 3).is_digit(10) {
386                     continue
387                 }
388                 return Some(full_version_line[pos..pos+3].to_string());
389             }
390             println!("Could not extract GDB version from line '{}'",
391                      full_version_line);
392             None
393         },
394         _ => None
395     }
396 }
397
398 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
399     // Extract the major LLDB version from the given version string.
400     // LLDB version strings are different for Apple and non-Apple platforms.
401     // At the moment, this function only supports the Apple variant, which looks
402     // like this:
403     //
404     // LLDB-179.5 (older versions)
405     // lldb-300.2.51 (new versions)
406     //
407     // We are only interested in the major version number, so this function
408     // will return `Some("179")` and `Some("300")` respectively.
409
410     match full_version_line {
411         Some(ref full_version_line)
412           if full_version_line.trim().len() > 0 => {
413             let full_version_line = full_version_line.trim();
414
415             for (pos, l) in full_version_line.char_indices() {
416                 if l != 'l' && l != 'L' { continue }
417                 if pos + 5 >= full_version_line.len() { continue }
418                 let l = full_version_line.char_at(pos + 1);
419                 if l != 'l' && l != 'L' { continue }
420                 let d = full_version_line.char_at(pos + 2);
421                 if d != 'd' && d != 'D' { continue }
422                 let b = full_version_line.char_at(pos + 3);
423                 if b != 'b' && b != 'B' { continue }
424                 let dash = full_version_line.char_at(pos + 4);
425                 if dash != '-' { continue }
426
427                 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
428                     c.is_digit(10)
429                 }).collect::<String>();
430                 if vers.len() > 0 { return Some(vers) }
431             }
432             println!("Could not extract LLDB version from line '{}'",
433                      full_version_line);
434             None
435         },
436         _ => None
437     }
438 }