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