]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
rollup merge of #16807 : nham/guide_added_ownership_rule
[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 #![feature(phase)]
13
14 // we use our own (green) start below; do not link in libnative; issue #13247.
15 #![no_start]
16
17 #![deny(warnings)]
18
19 extern crate test;
20 extern crate getopts;
21 extern crate green;
22 extern crate rustuv;
23 #[phase(plugin, link)] extern crate log;
24
25 extern crate regex;
26
27 use std::os;
28 use std::io;
29 use std::io::fs;
30 use std::from_str::FromStr;
31 use getopts::{optopt, optflag, reqopt};
32 use common::Config;
33 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
34 use util::logv;
35 use regex::Regex;
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
44 #[start]
45 fn start(argc: int, argv: *const *const u8) -> int {
46     green::start(argc, argv, rustuv::event_loop, main)
47 }
48
49 pub fn main() {
50     let args = os::args();
51     let config = parse_config(args);
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           optopt("", "clang-path", "path to  executable for codegen tests", "PATH"),
63           optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
64           reqopt("", "src-base", "directory to scan for test files", "PATH"),
65           reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
66           reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
67           reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
68           reqopt("", "mode", "which sort of compile tests to run",
69                  "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
70           optflag("", "ignored", "run tests marked as ignored"),
71           optopt("", "runtool", "supervisor program to run tests under \
72                                  (eg. emulator, valgrind)", "PROGRAM"),
73           optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
74           optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
75           optflag("", "verbose", "run tests verbosely, showing all output"),
76           optopt("", "logfile", "file to log test execution to", "FILE"),
77           optopt("", "save-metrics", "file to save metrics to", "FILE"),
78           optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
79           optopt("", "ratchet-noise-percent",
80                  "percent change in metrics to consider noise", "N"),
81           optflag("", "jit", "run tests under the JIT"),
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", "MAJOR.MINOR"),
85           optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
86           optopt("", "adb-path", "path to the android debugger", "PATH"),
87           optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
88           optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
89           optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
90           optflag("h", "help", "show this message"));
91
92     assert!(!args.is_empty());
93     let argv0 = args[0].clone();
94     let args_ = args.tail();
95     if args[1].as_slice() == "-h" || args[1].as_slice() == "--help" {
96         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
97         println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
98         println!("");
99         fail!()
100     }
101
102     let matches =
103         &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
104           Ok(m) => m,
105           Err(f) => fail!("{}", f)
106         };
107
108     if matches.opt_present("h") || matches.opt_present("help") {
109         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
110         println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
111         println!("");
112         fail!()
113     }
114
115     fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
116         Path::new(m.opt_str(nm).unwrap())
117     }
118
119     let filter = if !matches.free.is_empty() {
120         let s = matches.free[0].as_slice();
121         match regex::Regex::new(s) {
122             Ok(re) => Some(re),
123             Err(e) => {
124                 println!("failed to parse filter /{}/: {}", s, e);
125                 fail!()
126             }
127         }
128     } else {
129         None
130     };
131
132     Config {
133         compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
134         run_lib_path: matches.opt_str("run-lib-path").unwrap(),
135         rustc_path: opt_path(matches, "rustc-path"),
136         clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
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: FromStr::from_str(matches.opt_str("mode")
143                                        .unwrap()
144                                        .as_slice()).expect("invalid mode"),
145         run_ignored: matches.opt_present("ignored"),
146         filter: filter,
147         cfail_regex: Regex::new(errors::EXPECTED_PATTERN).unwrap(),
148         logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
149         save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
150         ratchet_metrics:
151             matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
152         ratchet_noise_percent:
153             matches.opt_str("ratchet-noise-percent")
154                    .and_then(|s| from_str::<f64>(s.as_slice())),
155         runtool: matches.opt_str("runtool"),
156         host_rustcflags: matches.opt_str("host-rustcflags"),
157         target_rustcflags: matches.opt_str("target-rustcflags"),
158         jit: matches.opt_present("jit"),
159         target: opt_str2(matches.opt_str("target")),
160         host: opt_str2(matches.opt_str("host")),
161         gdb_version: extract_gdb_version(matches.opt_str("gdb-version")),
162         android_cross_path: opt_path(matches, "android-cross-path"),
163         adb_path: opt_str2(matches.opt_str("adb-path")),
164         adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
165         adb_device_status:
166             "arm-linux-androideabi" ==
167                 opt_str2(matches.opt_str("target")).as_slice() &&
168             "(none)" !=
169                 opt_str2(matches.opt_str("adb-test-dir")).as_slice() &&
170             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
171         lldb_python_dir: matches.opt_str("lldb-python-dir"),
172         test_shard: test::opt_shard(matches.opt_str("test-shard")),
173         verbose: matches.opt_present("verbose")
174     }
175 }
176
177 pub fn log_config(config: &Config) {
178     let c = config;
179     logv(c, format!("configuration:"));
180     logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
181     logv(c, format!("run_lib_path: {}", config.run_lib_path));
182     logv(c, format!("rustc_path: {}", config.rustc_path.display()));
183     logv(c, format!("src_base: {}", config.src_base.display()));
184     logv(c, format!("build_base: {}", config.build_base.display()));
185     logv(c, format!("stage_id: {}", config.stage_id));
186     logv(c, format!("mode: {}", config.mode));
187     logv(c, format!("run_ignored: {}", config.run_ignored));
188     logv(c, format!("filter: {}",
189                     opt_str(&config.filter
190                                    .as_ref()
191                                    .map(|re| {
192                                        re.to_string().into_string()
193                                    }))));
194     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
195     logv(c, format!("host-rustcflags: {}",
196                     opt_str(&config.host_rustcflags)));
197     logv(c, format!("target-rustcflags: {}",
198                     opt_str(&config.target_rustcflags)));
199     logv(c, format!("jit: {}", config.jit));
200     logv(c, format!("target: {}", config.target));
201     logv(c, format!("host: {}", config.host));
202     logv(c, format!("android-cross-path: {}",
203                     config.android_cross_path.display()));
204     logv(c, format!("adb_path: {}", config.adb_path));
205     logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
206     logv(c, format!("adb_device_status: {}",
207                     config.adb_device_status));
208     match config.test_shard {
209         None => logv(c, "test_shard: (all)".to_string()),
210         Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
211     }
212     logv(c, format!("verbose: {}", config.verbose));
213     logv(c, format!("\n"));
214 }
215
216 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
217     match *maybestr {
218         None => "(none)",
219         Some(ref s) => s.as_slice(),
220     }
221 }
222
223 pub fn opt_str2(maybestr: Option<String>) -> String {
224     match maybestr {
225         None => "(none)".to_string(),
226         Some(s) => s,
227     }
228 }
229
230 pub fn run_tests(config: &Config) {
231     if config.target.as_slice() == "arm-linux-androideabi" {
232         match config.mode {
233             DebugInfoGdb => {
234                 println!("arm-linux-androideabi debug-info \
235                          test uses tcp 5039 port. please reserve it");
236             }
237             _ =>{}
238         }
239
240         //arm-linux-androideabi debug-info test uses remote debugger
241         //so, we test 1 task at once.
242         // also trying to isolate problems with adb_run_wrapper.sh ilooping
243         os::setenv("RUST_TEST_TASKS","1");
244     }
245
246     match config.mode {
247         DebugInfoLldb => {
248             // Some older versions of LLDB seem to have problems with multiple
249             // instances running in parallel, so only run one test task at a
250             // time.
251             os::setenv("RUST_TEST_TASKS", "1");
252         }
253         _ => { /* proceed */ }
254     }
255
256     let opts = test_opts(config);
257     let tests = make_tests(config);
258     // sadly osx needs some file descriptor limits raised for running tests in
259     // parallel (especially when we have lots and lots of child processes).
260     // For context, see #8904
261     io::test::raise_fd_limit();
262     let res = test::run_tests_console(&opts, tests.move_iter().collect());
263     match res {
264         Ok(true) => {}
265         Ok(false) => fail!("Some tests failed"),
266         Err(e) => {
267             println!("I/O failure during tests: {}", e);
268         }
269     }
270 }
271
272 pub fn test_opts(config: &Config) -> test::TestOpts {
273     test::TestOpts {
274         filter: match config.filter {
275             None => None,
276             Some(ref filter) => Some(filter.clone()),
277         },
278         run_ignored: config.run_ignored,
279         logfile: config.logfile.clone(),
280         run_tests: true,
281         run_benchmarks: true,
282         ratchet_metrics: config.ratchet_metrics.clone(),
283         ratchet_noise_percent: config.ratchet_noise_percent.clone(),
284         save_metrics: config.save_metrics.clone(),
285         test_shard: config.test_shard.clone(),
286         nocapture: false,
287         color: test::AutoColor,
288     }
289 }
290
291 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
292     debug!("making tests from {}",
293            config.src_base.display());
294     let mut tests = Vec::new();
295     let dirs = fs::readdir(&config.src_base).unwrap();
296     for file in dirs.iter() {
297         let file = file.clone();
298         debug!("inspecting file {}", file.display());
299         if is_test(config, &file) {
300             let t = make_test(config, &file, || {
301                 match config.mode {
302                     Codegen => make_metrics_test_closure(config, &file),
303                     _ => make_test_closure(config, &file)
304                 }
305             });
306             tests.push(t)
307         }
308     }
309     tests
310 }
311
312 pub fn is_test(config: &Config, testfile: &Path) -> bool {
313     // Pretty-printer does not work with .rc files yet
314     let valid_extensions =
315         match config.mode {
316           Pretty => vec!(".rs".to_string()),
317           _ => vec!(".rc".to_string(), ".rs".to_string())
318         };
319     let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
320     let name = testfile.filename_str().unwrap();
321
322     let mut valid = false;
323
324     for ext in valid_extensions.iter() {
325         if name.ends_with(ext.as_slice()) {
326             valid = true;
327         }
328     }
329
330     for pre in invalid_prefixes.iter() {
331         if name.starts_with(pre.as_slice()) {
332             valid = false;
333         }
334     }
335
336     return valid;
337 }
338
339 pub fn make_test(config: &Config, testfile: &Path, f: || -> test::TestFn)
340                  -> test::TestDescAndFn {
341     test::TestDescAndFn {
342         desc: test::TestDesc {
343             name: make_test_name(config, testfile),
344             ignore: header::is_test_ignored(config, testfile),
345             should_fail: false
346         },
347         testfn: f(),
348     }
349 }
350
351 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
352
353     // Try to elide redundant long paths
354     fn shorten(path: &Path) -> String {
355         let filename = path.filename_str();
356         let p = path.dir_path();
357         let dir = p.filename_str();
358         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
359     }
360
361     test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
362 }
363
364 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
365     let config = (*config).clone();
366     // FIXME (#9639): This needs to handle non-utf8 paths
367     let testfile = testfile.as_str().unwrap().to_string();
368     test::DynTestFn(proc() {
369         runtest::run(config, testfile)
370     })
371 }
372
373 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
374     let config = (*config).clone();
375     // FIXME (#9639): This needs to handle non-utf8 paths
376     let testfile = testfile.as_str().unwrap().to_string();
377     test::DynMetricFn(proc(mm) {
378         runtest::run_metrics(config, testfile, mm)
379     })
380 }
381
382 fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
383     match full_version_line {
384         Some(ref full_version_line)
385           if full_version_line.as_slice().trim().len() > 0 => {
386             let full_version_line = full_version_line.as_slice().trim();
387
388             let re = Regex::new(r"(^|[^0-9])([0-9]\.[0-9])([^0-9]|$)").unwrap();
389
390             match re.captures(full_version_line) {
391                 Some(captures) => {
392                     Some(captures.at(2).to_string())
393                 }
394                 None => {
395                     println!("Could not extract GDB version from line '{}'",
396                              full_version_line);
397                     None
398                 }
399             }
400         },
401         _ => None
402     }
403 }