]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
Rollup merge of #23803 - richo:unused-braces, 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(collections)]
15 #![feature(old_io)]
16 #![feature(rustc_private)]
17 #![feature(unboxed_closures)]
18 #![feature(std_misc)]
19 #![feature(test)]
20 #![feature(path_ext)]
21 #![feature(convert)]
22 #![feature(str_char)]
23
24 #![deny(warnings)]
25
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 std::thunk::Thunk;
36 use getopts::{optopt, optflag, reqopt};
37 use common::Config;
38 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
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
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           optopt("", "clang-path", "path to  executable for codegen tests", "PATH"),
66           optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"),
67           optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"),
68           optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
69           reqopt("", "src-base", "directory to scan for test files", "PATH"),
70           reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
71           reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
72           reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
73           reqopt("", "mode", "which sort of compile tests to run",
74                  "(compile-fail|parse-fail|run-fail|run-pass|run-pass-valgrind|pretty|debug-info)"),
75           optflag("", "ignored", "run tests marked as ignored"),
76           optopt("", "runtool", "supervisor program to run tests under \
77                                  (eg. emulator, valgrind)", "PROGRAM"),
78           optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
79           optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
80           optflag("", "verbose", "run tests verbosely, showing all output"),
81           optopt("", "logfile", "file to log test execution to", "FILE"),
82           optflag("", "jit", "run tests under the JIT"),
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         clang_path: matches.opt_str("clang-path").map(|s| PathBuf::from(&s)),
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         jit: matches.opt_present("jit"),
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!("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!("jit: {}", config.jit));
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 task 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 task 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     #[allow(deprecated)]
246     fn raise_fd_limit() {
247         std::old_io::test::raise_fd_limit();
248     }
249     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         run_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(Thunk::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().len() > 0 => {
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().len() > 0 => {
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.len() > 0 { return Some(vers) }
430             }
431             println!("Could not extract LLDB version from line '{}'",
432                      full_version_line);
433             None
434         },
435         _ => None
436     }
437 }