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