]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
Auto merge of #22517 - brson:relnotes, r=Gankro
[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(int_uint)]
16 #![feature(old_io)]
17 #![feature(old_path)]
18 #![feature(rustc_private)]
19 #![feature(unboxed_closures)]
20 #![feature(std_misc)]
21 #![feature(test)]
22 #![feature(unicode)]
23 #![feature(env)]
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::old_io;
35 use std::old_io::fs;
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|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) -> Path {
118         match m.opt_str(nm) {
119             Some(s) => Path::new(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| Path::new(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| Path::new(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| Path::new(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_TASKS","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_TASKS", "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     old_io::test::raise_fd_limit();
247     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
248     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
249     env::set_var("__COMPAT_LAYER", "RunAsInvoker");
250     let res = test::run_tests_console(&opts, tests.into_iter().collect());
251     match res {
252         Ok(true) => {}
253         Ok(false) => panic!("Some tests failed"),
254         Err(e) => {
255             println!("I/O failure during tests: {:?}", e);
256         }
257     }
258 }
259
260 pub fn test_opts(config: &Config) -> test::TestOpts {
261     test::TestOpts {
262         filter: match config.filter {
263             None => None,
264             Some(ref filter) => Some(filter.clone()),
265         },
266         run_ignored: config.run_ignored,
267         logfile: config.logfile.clone(),
268         run_tests: true,
269         run_benchmarks: true,
270         nocapture: false,
271         color: test::AutoColor,
272     }
273 }
274
275 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
276     debug!("making tests from {:?}",
277            config.src_base.display());
278     let mut tests = Vec::new();
279     let dirs = fs::readdir(&config.src_base).unwrap();
280     for file in &dirs {
281         let file = file.clone();
282         debug!("inspecting file {:?}", file.display());
283         if is_test(config, &file) {
284             let t = make_test(config, &file, || {
285                 match config.mode {
286                     Codegen => make_metrics_test_closure(config, &file),
287                     _ => make_test_closure(config, &file)
288                 }
289             });
290             tests.push(t)
291         }
292     }
293     tests
294 }
295
296 pub fn is_test(config: &Config, testfile: &Path) -> bool {
297     // Pretty-printer does not work with .rc files yet
298     let valid_extensions =
299         match config.mode {
300           Pretty => vec!(".rs".to_string()),
301           _ => vec!(".rc".to_string(), ".rs".to_string())
302         };
303     let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
304     let name = testfile.filename_str().unwrap();
305
306     let mut valid = false;
307
308     for ext in &valid_extensions {
309         if name.ends_with(ext) {
310             valid = true;
311         }
312     }
313
314     for pre in &invalid_prefixes {
315         if name.starts_with(pre) {
316             valid = false;
317         }
318     }
319
320     return valid;
321 }
322
323 pub fn make_test<F>(config: &Config, testfile: &Path, f: F) -> test::TestDescAndFn where
324     F: FnOnce() -> test::TestFn,
325 {
326     test::TestDescAndFn {
327         desc: test::TestDesc {
328             name: make_test_name(config, testfile),
329             ignore: header::is_test_ignored(config, testfile),
330             should_fail: test::ShouldFail::No,
331         },
332         testfn: f(),
333     }
334 }
335
336 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
337
338     // Try to elide redundant long paths
339     fn shorten(path: &Path) -> String {
340         let filename = path.filename_str();
341         let p = path.dir_path();
342         let dir = p.filename_str();
343         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
344     }
345
346     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
347 }
348
349 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
350     let config = (*config).clone();
351     // FIXME (#9639): This needs to handle non-utf8 paths
352     let testfile = testfile.as_str().unwrap().to_string();
353     test::DynTestFn(Thunk::new(move || {
354         runtest::run(config, testfile)
355     }))
356 }
357
358 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
359     let config = (*config).clone();
360     // FIXME (#9639): This needs to handle non-utf8 paths
361     let testfile = testfile.as_str().unwrap().to_string();
362     test::DynMetricFn(box move |mm: &mut test::MetricMap| {
363         runtest::run_metrics(config, testfile, mm)
364     })
365 }
366
367 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
368     match full_version_line {
369         Some(ref full_version_line)
370           if full_version_line.trim().len() > 0 => {
371             let full_version_line = full_version_line.trim();
372
373             // used to be a regex "(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)"
374             for (pos, c) in full_version_line.char_indices() {
375                 if !c.is_digit(10) { continue }
376                 if pos + 2 >= full_version_line.len() { continue }
377                 if full_version_line.char_at(pos + 1) != '.' { continue }
378                 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
379                 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
380                     continue
381                 }
382                 if pos + 3 < full_version_line.len() &&
383                    full_version_line.char_at(pos + 3).is_digit(10) {
384                     continue
385                 }
386                 return Some(full_version_line[pos..pos+3].to_string());
387             }
388             println!("Could not extract GDB version from line '{}'",
389                      full_version_line);
390             None
391         },
392         _ => None
393     }
394 }
395
396 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
397     // Extract the major LLDB version from the given version string.
398     // LLDB version strings are different for Apple and non-Apple platforms.
399     // At the moment, this function only supports the Apple variant, which looks
400     // like this:
401     //
402     // LLDB-179.5 (older versions)
403     // lldb-300.2.51 (new versions)
404     //
405     // We are only interested in the major version number, so this function
406     // will return `Some("179")` and `Some("300")` respectively.
407
408     match full_version_line {
409         Some(ref full_version_line)
410           if full_version_line.trim().len() > 0 => {
411             let full_version_line = full_version_line.trim();
412
413             for (pos, l) in full_version_line.char_indices() {
414                 if l != 'l' && l != 'L' { continue }
415                 if pos + 5 >= full_version_line.len() { continue }
416                 let l = full_version_line.char_at(pos + 1);
417                 if l != 'l' && l != 'L' { continue }
418                 let d = full_version_line.char_at(pos + 2);
419                 if d != 'd' && d != 'D' { continue }
420                 let b = full_version_line.char_at(pos + 3);
421                 if b != 'b' && b != 'B' { continue }
422                 let dash = full_version_line.char_at(pos + 4);
423                 if dash != '-' { continue }
424
425                 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
426                     c.is_digit(10)
427                 }).collect::<String>();
428                 if vers.len() > 0 { return Some(vers) }
429             }
430             println!("Could not extract LLDB version from line '{}'",
431                      full_version_line);
432             None
433         },
434         _ => None
435     }
436 }