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.
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.
11 #![crate_type = "bin"]
14 // we use our own (green) start below; do not link in libnative; issue #13247.
23 #[phase(plugin, link)] extern crate log;
30 use std::from_str::FromStr;
31 use getopts::{optopt, optflag, reqopt};
33 use common::{Pretty, DebugInfoGdb, DebugInfoLldb, Codegen};
45 fn start(argc: int, argv: *const *const u8) -> int {
46 green::start(argc, argv, rustuv::event_loop, main)
50 let args = os::args();
51 let config = parse_config(args);
56 pub fn parse_config(args: Vec<String> ) -> Config {
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("", "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 optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
89 optflag("h", "help", "show this message"));
91 assert!(!args.is_empty());
92 let argv0 = (*args.get(0)).clone();
93 let args_ = args.tail();
94 if args.get(1).as_slice() == "-h" || args.get(1).as_slice() == "--help" {
95 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
96 println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
102 &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
104 Err(f) => fail!("{}", f)
107 if matches.opt_present("h") || matches.opt_present("help") {
108 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
109 println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
114 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
115 Path::new(m.opt_str(nm).unwrap())
118 let filter = if !matches.free.is_empty() {
119 let s = matches.free.get(0).as_slice();
120 match regex::Regex::new(s) {
123 println!("failed to parse filter /{}/: {}", s, e);
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| Path::new(s)),
136 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
137 src_base: opt_path(matches, "src-base"),
138 build_base: opt_path(matches, "build-base"),
139 aux_base: opt_path(matches, "aux-base"),
140 stage_id: matches.opt_str("stage-id").unwrap(),
141 mode: FromStr::from_str(matches.opt_str("mode")
143 .as_slice()).expect("invalid mode"),
144 run_ignored: matches.opt_present("ignored"),
146 cfail_regex: Regex::new(errors::EXPECTED_PATTERN).unwrap(),
147 logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
148 save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
150 matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
151 ratchet_noise_percent:
152 matches.opt_str("ratchet-noise-percent")
153 .and_then(|s| from_str::<f64>(s.as_slice())),
154 runtool: matches.opt_str("runtool"),
155 host_rustcflags: matches.opt_str("host-rustcflags"),
156 target_rustcflags: matches.opt_str("target-rustcflags"),
157 jit: matches.opt_present("jit"),
158 target: opt_str2(matches.opt_str("target")),
159 host: opt_str2(matches.opt_str("host")),
160 android_cross_path: opt_path(matches, "android-cross-path"),
161 adb_path: opt_str2(matches.opt_str("adb-path")),
162 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")),
164 "arm-linux-androideabi" ==
165 opt_str2(matches.opt_str("target")).as_slice() &&
167 opt_str2(matches.opt_str("adb-test-dir")).as_slice() &&
168 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
169 lldb_python_dir: matches.opt_str("lldb-python-dir"),
170 test_shard: test::opt_shard(matches.opt_str("test-shard")),
171 verbose: matches.opt_present("verbose")
175 pub fn log_config(config: &Config) {
177 logv(c, format!("configuration:"));
178 logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
179 logv(c, format!("run_lib_path: {}", config.run_lib_path));
180 logv(c, format!("rustc_path: {}", config.rustc_path.display()));
181 logv(c, format!("src_base: {}", config.src_base.display()));
182 logv(c, format!("build_base: {}", config.build_base.display()));
183 logv(c, format!("stage_id: {}", config.stage_id));
184 logv(c, format!("mode: {}", config.mode));
185 logv(c, format!("run_ignored: {}", config.run_ignored));
186 logv(c, format!("filter: {}",
187 opt_str(&config.filter
190 re.to_string().into_string()
192 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
193 logv(c, format!("host-rustcflags: {}",
194 opt_str(&config.host_rustcflags)));
195 logv(c, format!("target-rustcflags: {}",
196 opt_str(&config.target_rustcflags)));
197 logv(c, format!("jit: {}", config.jit));
198 logv(c, format!("target: {}", config.target));
199 logv(c, format!("host: {}", config.host));
200 logv(c, format!("android-cross-path: {}",
201 config.android_cross_path.display()));
202 logv(c, format!("adb_path: {}", config.adb_path));
203 logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
204 logv(c, format!("adb_device_status: {}",
205 config.adb_device_status));
206 match config.test_shard {
207 None => logv(c, "test_shard: (all)".to_string()),
208 Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
210 logv(c, format!("verbose: {}", config.verbose));
211 logv(c, format!("\n"));
214 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
217 Some(ref s) => s.as_slice(),
221 pub fn opt_str2(maybestr: Option<String>) -> String {
223 None => "(none)".to_string(),
228 pub fn run_tests(config: &Config) {
229 if config.target.as_slice() == "arm-linux-androideabi" {
232 println!("arm-linux-androideabi debug-info \
233 test uses tcp 5039 port. please reserve it");
238 //arm-linux-androideabi debug-info test uses remote debugger
239 //so, we test 1 task at once.
240 // also trying to isolate problems with adb_run_wrapper.sh ilooping
241 os::setenv("RUST_TEST_TASKS","1");
246 // Some older versions of LLDB seem to have problems with multiple
247 // instances running in parallel, so only run one test task at a
249 os::setenv("RUST_TEST_TASKS", "1");
251 _ => { /* proceed */ }
254 let opts = test_opts(config);
255 let tests = make_tests(config);
256 // sadly osx needs some file descriptor limits raised for running tests in
257 // parallel (especially when we have lots and lots of child processes).
258 // For context, see #8904
259 io::test::raise_fd_limit();
260 let res = test::run_tests_console(&opts, tests.move_iter().collect());
263 Ok(false) => fail!("Some tests failed"),
265 println!("I/O failure during tests: {}", e);
270 pub fn test_opts(config: &Config) -> test::TestOpts {
272 filter: match config.filter {
274 Some(ref filter) => Some(filter.clone()),
276 run_ignored: config.run_ignored,
277 logfile: config.logfile.clone(),
279 run_benchmarks: true,
280 ratchet_metrics: config.ratchet_metrics.clone(),
281 ratchet_noise_percent: config.ratchet_noise_percent.clone(),
282 save_metrics: config.save_metrics.clone(),
283 test_shard: config.test_shard.clone(),
285 color: test::AutoColor,
289 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
290 debug!("making tests from {}",
291 config.src_base.display());
292 let mut tests = Vec::new();
293 let dirs = fs::readdir(&config.src_base).unwrap();
294 for file in dirs.iter() {
295 let file = file.clone();
296 debug!("inspecting file {}", file.display());
297 if is_test(config, &file) {
298 let t = make_test(config, &file, || {
300 Codegen => make_metrics_test_closure(config, &file),
301 _ => make_test_closure(config, &file)
310 pub fn is_test(config: &Config, testfile: &Path) -> bool {
311 // Pretty-printer does not work with .rc files yet
312 let valid_extensions =
314 Pretty => vec!(".rs".to_string()),
315 _ => vec!(".rc".to_string(), ".rs".to_string())
317 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
318 let name = testfile.filename_str().unwrap();
320 let mut valid = false;
322 for ext in valid_extensions.iter() {
323 if name.ends_with(ext.as_slice()) {
328 for pre in invalid_prefixes.iter() {
329 if name.starts_with(pre.as_slice()) {
337 pub fn make_test(config: &Config, testfile: &Path, f: || -> test::TestFn)
338 -> test::TestDescAndFn {
339 test::TestDescAndFn {
340 desc: test::TestDesc {
341 name: make_test_name(config, testfile),
342 ignore: header::is_test_ignored(config, testfile),
349 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
351 // Try to elide redundant long paths
352 fn shorten(path: &Path) -> String {
353 let filename = path.filename_str();
354 let p = path.dir_path();
355 let dir = p.filename_str();
356 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
359 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
362 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
363 let config = (*config).clone();
364 // FIXME (#9639): This needs to handle non-utf8 paths
365 let testfile = testfile.as_str().unwrap().to_string();
366 test::DynTestFn(proc() {
367 runtest::run(config, testfile)
371 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
372 let config = (*config).clone();
373 // FIXME (#9639): This needs to handle non-utf8 paths
374 let testfile = testfile.as_str().unwrap().to_string();
375 test::DynMetricFn(proc(mm) {
376 runtest::run_metrics(config, testfile, mm)