]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
96b52eaa0ad0fb67bf95d7506c09c49fc026c877
[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(libc)]
15 #![feature(rustc_private)]
16 #![feature(str_char)]
17 #![feature(test)]
18 #![feature(question_mark)]
19
20 #![deny(warnings)]
21
22 extern crate libc;
23 extern crate test;
24 extern crate getopts;
25
26 #[macro_use]
27 extern crate log;
28
29 use std::env;
30 use std::fs;
31 use std::io;
32 use std::path::{Path, PathBuf};
33 use getopts::{optopt, optflag, reqopt};
34 use common::Config;
35 use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
36 use test::TestPaths;
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           optflag("", "quiet", "print one character per test instead of one line"),
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.len() == 1 || 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     Config {
122         compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
123         run_lib_path: matches.opt_str("run-lib-path").unwrap(),
124         rustc_path: opt_path(matches, "rustc-path"),
125         rustdoc_path: opt_path(matches, "rustdoc-path"),
126         python: matches.opt_str("python").unwrap(),
127         valgrind_path: matches.opt_str("valgrind-path"),
128         force_valgrind: matches.opt_present("force-valgrind"),
129         llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)),
130         src_base: opt_path(matches, "src-base"),
131         build_base: opt_path(matches, "build-base"),
132         aux_base: opt_path(matches, "aux-base"),
133         stage_id: matches.opt_str("stage-id").unwrap(),
134         mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"),
135         run_ignored: matches.opt_present("ignored"),
136         filter: matches.free.first().cloned(),
137         logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)),
138         runtool: matches.opt_str("runtool"),
139         host_rustcflags: matches.opt_str("host-rustcflags"),
140         target_rustcflags: matches.opt_str("target-rustcflags"),
141         target: opt_str2(matches.opt_str("target")),
142         host: opt_str2(matches.opt_str("host")),
143         gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
144         lldb_version: extract_lldb_version(matches.opt_str("lldb-version")),
145         android_cross_path: opt_path(matches, "android-cross-path"),
146         adb_path: opt_str2(matches.opt_str("adb-path")),
147         adb_test_dir: format!("{}/{}",
148             opt_str2(matches.opt_str("adb-test-dir")),
149             opt_str2(matches.opt_str("target"))),
150         adb_device_status:
151             opt_str2(matches.opt_str("target")).contains("android") &&
152             "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
153             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
154         lldb_python_dir: matches.opt_str("lldb-python-dir"),
155         verbose: matches.opt_present("verbose"),
156         quiet: matches.opt_present("quiet"),
157     }
158 }
159
160 pub fn log_config(config: &Config) {
161     let c = config;
162     logv(c, format!("configuration:"));
163     logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path));
164     logv(c, format!("run_lib_path: {:?}", config.run_lib_path));
165     logv(c, format!("rustc_path: {:?}", config.rustc_path.display()));
166     logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display()));
167     logv(c, format!("src_base: {:?}", config.src_base.display()));
168     logv(c, format!("build_base: {:?}", config.build_base.display()));
169     logv(c, format!("stage_id: {}", config.stage_id));
170     logv(c, format!("mode: {}", config.mode));
171     logv(c, format!("run_ignored: {}", config.run_ignored));
172     logv(c, format!("filter: {}",
173                     opt_str(&config.filter
174                                    .as_ref()
175                                    .map(|re| re.to_owned()))));
176     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
177     logv(c, format!("host-rustcflags: {}",
178                     opt_str(&config.host_rustcflags)));
179     logv(c, format!("target-rustcflags: {}",
180                     opt_str(&config.target_rustcflags)));
181     logv(c, format!("target: {}", config.target));
182     logv(c, format!("host: {}", config.host));
183     logv(c, format!("android-cross-path: {:?}",
184                     config.android_cross_path.display()));
185     logv(c, format!("adb_path: {:?}", config.adb_path));
186     logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir));
187     logv(c, format!("adb_device_status: {}",
188                     config.adb_device_status));
189     logv(c, format!("verbose: {}", config.verbose));
190     logv(c, format!("quiet: {}", config.quiet));
191     logv(c, format!("\n"));
192 }
193
194 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
195     match *maybestr {
196         None => "(none)",
197         Some(ref s) => s,
198     }
199 }
200
201 pub fn opt_str2(maybestr: Option<String>) -> String {
202     match maybestr {
203         None => "(none)".to_owned(),
204         Some(s) => s,
205     }
206 }
207
208 pub fn run_tests(config: &Config) {
209     if config.target.contains("android") {
210         if let DebugInfoGdb = config.mode {
211             println!("{} debug-info test uses tcp 5039 port.\
212                      please reserve it", config.target);
213         }
214
215         // android debug-info test uses remote debugger
216         // so, we test 1 thread at once.
217         // also trying to isolate problems with adb_run_wrapper.sh ilooping
218         env::set_var("RUST_TEST_THREADS","1");
219     }
220
221     match config.mode {
222         DebugInfoLldb => {
223             // Some older versions of LLDB seem to have problems with multiple
224             // instances running in parallel, so only run one test thread at a
225             // time.
226             env::set_var("RUST_TEST_THREADS", "1");
227         }
228         _ => { /* proceed */ }
229     }
230
231     let opts = test_opts(config);
232     let tests = make_tests(config);
233     // sadly osx needs some file descriptor limits raised for running tests in
234     // parallel (especially when we have lots and lots of child processes).
235     // For context, see #8904
236     unsafe { raise_fd_limit::raise_fd_limit(); }
237     // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows
238     // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary
239     env::set_var("__COMPAT_LAYER", "RunAsInvoker");
240     let res = test::run_tests_console(&opts, tests.into_iter().collect());
241     match res {
242         Ok(true) => {}
243         Ok(false) => panic!("Some tests failed"),
244         Err(e) => {
245             println!("I/O failure during tests: {:?}", e);
246         }
247     }
248 }
249
250 pub fn test_opts(config: &Config) -> test::TestOpts {
251     test::TestOpts {
252         filter: config.filter.clone(),
253         run_ignored: config.run_ignored,
254         quiet: config.quiet,
255         logfile: config.logfile.clone(),
256         run_tests: true,
257         bench_benchmarks: true,
258         nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
259         color: test::AutoColor,
260     }
261 }
262
263 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
264     debug!("making tests from {:?}",
265            config.src_base.display());
266     let mut tests = Vec::new();
267     collect_tests_from_dir(config,
268                            &config.src_base,
269                            &config.src_base,
270                            &PathBuf::new(),
271                            &mut tests)
272         .unwrap();
273     tests
274 }
275
276 fn collect_tests_from_dir(config: &Config,
277                           base: &Path,
278                           dir: &Path,
279                           relative_dir_path: &Path,
280                           tests: &mut Vec<test::TestDescAndFn>)
281                           -> io::Result<()> {
282     // Ignore directories that contain a file
283     // `compiletest-ignore-dir`.
284     for file in fs::read_dir(dir)? {
285         let file = file?;
286         if file.file_name() == *"compiletest-ignore-dir" {
287             return Ok(());
288         }
289     }
290
291     let dirs = fs::read_dir(dir)?;
292     for file in dirs {
293         let file = file?;
294         let file_path = file.path();
295         debug!("inspecting file {:?}", file_path.display());
296         if is_test(config, &file_path) {
297             // If we find a test foo/bar.rs, we have to build the
298             // output directory `$build/foo` so we can write
299             // `$build/foo/bar` into it. We do this *now* in this
300             // sequential loop because otherwise, if we do it in the
301             // tests themselves, they race for the privilege of
302             // creating the directories and sometimes fail randomly.
303             let build_dir = config.build_base.join(&relative_dir_path);
304             fs::create_dir_all(&build_dir).unwrap();
305
306             let paths = TestPaths {
307                 file: file_path,
308                 base: base.to_path_buf(),
309                 relative_dir: relative_dir_path.to_path_buf(),
310             };
311             tests.push(make_test(config, &paths))
312         } else if file_path.is_dir() {
313             let relative_file_path = relative_dir_path.join(file.file_name());
314             collect_tests_from_dir(config,
315                                    base,
316                                    &file_path,
317                                    &relative_file_path,
318                                    tests)?;
319         }
320     }
321     Ok(())
322 }
323
324 pub fn is_test(config: &Config, testfile: &Path) -> bool {
325     // Pretty-printer does not work with .rc files yet
326     let valid_extensions =
327         match config.mode {
328           Pretty => vec!(".rs".to_owned()),
329           _ => vec!(".rc".to_owned(), ".rs".to_owned())
330         };
331     let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
332     let name = testfile.file_name().unwrap().to_str().unwrap();
333
334     let mut valid = false;
335
336     for ext in &valid_extensions {
337         if name.ends_with(ext) {
338             valid = true;
339         }
340     }
341
342     for pre in &invalid_prefixes {
343         if name.starts_with(pre) {
344             valid = false;
345         }
346     }
347
348     return valid;
349 }
350
351 pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
352     let early_props = header::early_props(config, &testpaths.file);
353
354     // The `should-fail` annotation doesn't apply to pretty tests,
355     // since we run the pretty printer across all tests by default.
356     // If desired, we could add a `should-fail-pretty` annotation.
357     let should_panic = match config.mode {
358         Pretty => test::ShouldPanic::No,
359         _ => if early_props.should_fail {
360             test::ShouldPanic::Yes
361         } else {
362             test::ShouldPanic::No
363         }
364     };
365
366     test::TestDescAndFn {
367         desc: test::TestDesc {
368             name: make_test_name(config, testpaths),
369             ignore: early_props.ignore,
370             should_panic: should_panic,
371         },
372         testfn: make_test_closure(config, testpaths),
373     }
374 }
375
376 pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
377     // Convert a complete path to something like
378     //
379     //    run-pass/foo/bar/baz.rs
380     let path =
381         PathBuf::from(config.mode.to_string())
382         .join(&testpaths.relative_dir)
383         .join(&testpaths.file.file_name().unwrap());
384     test::DynTestName(format!("[{}] {}", config.mode, path.display()))
385 }
386
387 pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
388     let config = config.clone();
389     let testpaths = testpaths.clone();
390     test::DynTestFn(Box::new(move || {
391         runtest::run(config, &testpaths)
392     }))
393 }
394
395 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
396     match full_version_line {
397         Some(ref full_version_line)
398           if !full_version_line.trim().is_empty() => {
399             let full_version_line = full_version_line.trim();
400
401             // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)"
402             for (pos, c) in full_version_line.char_indices() {
403                 if !c.is_digit(10) { continue }
404                 if pos + 2 >= full_version_line.len() { continue }
405                 if full_version_line.char_at(pos + 1) != '.' { continue }
406                 if !full_version_line.char_at(pos + 2).is_digit(10) { continue }
407                 if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) {
408                     continue
409                 }
410                 let mut end = pos + 3;
411                 while end < full_version_line.len() &&
412                       full_version_line.char_at(end).is_digit(10) {
413                     end += 1;
414                 }
415                 return Some(full_version_line[pos..end].to_owned());
416             }
417             println!("Could not extract GDB version from line '{}'",
418                      full_version_line);
419             None
420         },
421         _ => None
422     }
423 }
424
425 fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
426     // Extract the major LLDB version from the given version string.
427     // LLDB version strings are different for Apple and non-Apple platforms.
428     // At the moment, this function only supports the Apple variant, which looks
429     // like this:
430     //
431     // LLDB-179.5 (older versions)
432     // lldb-300.2.51 (new versions)
433     //
434     // We are only interested in the major version number, so this function
435     // will return `Some("179")` and `Some("300")` respectively.
436
437     if let Some(ref full_version_line) = full_version_line {
438         if !full_version_line.trim().is_empty() {
439             let full_version_line = full_version_line.trim();
440
441             for (pos, l) in full_version_line.char_indices() {
442                 if l != 'l' && l != 'L' { continue }
443                 if pos + 5 >= full_version_line.len() { continue }
444                 let l = full_version_line.char_at(pos + 1);
445                 if l != 'l' && l != 'L' { continue }
446                 let d = full_version_line.char_at(pos + 2);
447                 if d != 'd' && d != 'D' { continue }
448                 let b = full_version_line.char_at(pos + 3);
449                 if b != 'b' && b != 'B' { continue }
450                 let dash = full_version_line.char_at(pos + 4);
451                 if dash != '-' { continue }
452
453                 let vers = full_version_line[pos + 5..].chars().take_while(|c| {
454                     c.is_digit(10)
455                 }).collect::<String>();
456                 if !vers.is_empty() { return Some(vers) }
457             }
458             println!("Could not extract LLDB version from line '{}'",
459                      full_version_line);
460         }
461     }
462     None
463 }