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