1 // Copyright 2012 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 #[allow(non_camel_case_types)];
15 #[allow(deprecated_owned_vector)]; // NOTE: remove after stage0
20 #[phase(link, syntax)]
26 use getopts::{optopt, optflag, reqopt};
28 use common::mode_run_pass;
29 use common::mode_run_fail;
30 use common::mode_compile_fail;
31 use common::mode_pretty;
32 use common::mode_debug_info;
33 use common::mode_codegen;
45 let args = os::args();
46 let config = parse_config(args);
51 pub fn parse_config(args: ~[~str]) -> config {
53 let groups : ~[getopts::OptGroup] =
54 ~[reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
55 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
56 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
57 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
58 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
59 reqopt("", "src-base", "directory to scan for test files", "PATH"),
60 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
61 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
62 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
63 reqopt("", "mode", "which sort of compile tests to run",
64 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
65 optflag("", "ignored", "run tests marked as ignored"),
66 optopt("", "runtool", "supervisor program to run tests under \
67 (eg. emulator, valgrind)", "PROGRAM"),
68 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
69 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
70 optflag("", "verbose", "run tests verbosely, showing all output"),
71 optopt("", "logfile", "file to log test execution to", "FILE"),
72 optopt("", "save-metrics", "file to save metrics to", "FILE"),
73 optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
74 optopt("", "ratchet-noise-percent",
75 "percent change in metrics to consider noise", "N"),
76 optflag("", "jit", "run tests under the JIT"),
77 optopt("", "target", "the target to build for", "TARGET"),
78 optopt("", "host", "the host to build for", "HOST"),
79 optopt("", "adb-path", "path to the android debugger", "PATH"),
80 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
81 optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
82 optflag("h", "help", "show this message"),
85 assert!(!args.is_empty());
86 let argv0 = args[0].clone();
87 let args_ = args.tail();
88 if args[1] == ~"-h" || args[1] == ~"--help" {
89 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
90 println!("{}", getopts::usage(message, groups));
96 &match getopts::getopts(args_, groups) {
98 Err(f) => fail!("{}", f.to_err_msg())
101 if matches.opt_present("h") || matches.opt_present("help") {
102 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
103 println!("{}", getopts::usage(message, groups));
108 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
109 Path::new(m.opt_str(nm).unwrap())
113 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
114 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
115 rustc_path: opt_path(matches, "rustc-path"),
116 clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
117 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
118 src_base: opt_path(matches, "src-base"),
119 build_base: opt_path(matches, "build-base"),
120 aux_base: opt_path(matches, "aux-base"),
121 stage_id: matches.opt_str("stage-id").unwrap(),
122 mode: str_mode(matches.opt_str("mode").unwrap()),
123 run_ignored: matches.opt_present("ignored"),
125 if !matches.free.is_empty() {
126 Some(matches.free[0].clone())
130 logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
131 save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
133 matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
134 ratchet_noise_percent:
135 matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::<f64>(s)),
136 runtool: matches.opt_str("runtool"),
137 host_rustcflags: matches.opt_str("host-rustcflags"),
138 target_rustcflags: matches.opt_str("target-rustcflags"),
139 jit: matches.opt_present("jit"),
140 target: opt_str2(matches.opt_str("target")).to_str(),
141 host: opt_str2(matches.opt_str("host")).to_str(),
142 adb_path: opt_str2(matches.opt_str("adb-path")).to_str(),
144 opt_str2(matches.opt_str("adb-test-dir")).to_str(),
146 "arm-linux-androideabi" == opt_str2(matches.opt_str("target")) &&
147 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
148 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
149 test_shard: test::opt_shard(matches.opt_str("test-shard")),
150 verbose: matches.opt_present("verbose")
154 pub fn log_config(config: &config) {
156 logv(c, format!("configuration:"));
157 logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
158 logv(c, format!("run_lib_path: {}", config.run_lib_path));
159 logv(c, format!("rustc_path: {}", config.rustc_path.display()));
160 logv(c, format!("src_base: {}", config.src_base.display()));
161 logv(c, format!("build_base: {}", config.build_base.display()));
162 logv(c, format!("stage_id: {}", config.stage_id));
163 logv(c, format!("mode: {}", mode_str(config.mode)));
164 logv(c, format!("run_ignored: {}", config.run_ignored));
165 logv(c, format!("filter: {}", opt_str(&config.filter)));
166 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
167 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
168 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
169 logv(c, format!("jit: {}", config.jit));
170 logv(c, format!("target: {}", config.target));
171 logv(c, format!("host: {}", config.host));
172 logv(c, format!("adb_path: {}", config.adb_path));
173 logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
174 logv(c, format!("adb_device_status: {}", config.adb_device_status));
175 match config.test_shard {
176 None => logv(c, ~"test_shard: (all)"),
177 Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
179 logv(c, format!("verbose: {}", config.verbose));
180 logv(c, format!("\n"));
183 pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
193 pub fn opt_str2(maybestr: Option<~str>) -> ~str {
194 match maybestr { None => ~"(none)", Some(s) => { s } }
197 pub fn str_mode(s: ~str) -> mode {
199 "compile-fail" => mode_compile_fail,
200 "run-fail" => mode_run_fail,
201 "run-pass" => mode_run_pass,
202 "pretty" => mode_pretty,
203 "debug-info" => mode_debug_info,
204 "codegen" => mode_codegen,
205 _ => fail!("invalid mode")
209 pub fn mode_str(mode: mode) -> ~str {
211 mode_compile_fail => ~"compile-fail",
212 mode_run_fail => ~"run-fail",
213 mode_run_pass => ~"run-pass",
214 mode_pretty => ~"pretty",
215 mode_debug_info => ~"debug-info",
216 mode_codegen => ~"codegen",
220 pub fn run_tests(config: &config) {
221 if config.target == ~"arm-linux-androideabi" {
224 println!("arm-linux-androideabi debug-info \
225 test uses tcp 5039 port. please reserve it");
230 //arm-linux-androideabi debug-info test uses remote debugger
231 //so, we test 1 task at once.
232 // also trying to isolate problems with adb_run_wrapper.sh ilooping
233 os::setenv("RUST_TEST_TASKS","1");
236 let opts = test_opts(config);
237 let tests = make_tests(config);
238 // sadly osx needs some file descriptor limits raised for running tests in
239 // parallel (especially when we have lots and lots of child processes).
240 // For context, see #8904
241 io::test::raise_fd_limit();
242 let res = test::run_tests_console(&opts, tests);
245 Ok(false) => fail!("Some tests failed"),
247 println!("I/O failure during tests: {}", e);
252 pub fn test_opts(config: &config) -> test::TestOpts {
254 filter: config.filter.clone(),
255 run_ignored: config.run_ignored,
256 logfile: config.logfile.clone(),
258 run_benchmarks: true,
259 ratchet_metrics: config.ratchet_metrics.clone(),
260 ratchet_noise_percent: config.ratchet_noise_percent.clone(),
261 save_metrics: config.save_metrics.clone(),
262 test_shard: config.test_shard.clone()
266 pub fn make_tests(config: &config) -> ~[test::TestDescAndFn] {
267 debug!("making tests from {}",
268 config.src_base.display());
270 let dirs = fs::readdir(&config.src_base).unwrap();
271 for file in dirs.iter() {
272 let file = file.clone();
273 debug!("inspecting file {}", file.display());
274 if is_test(config, &file) {
275 let t = make_test(config, &file, || {
277 mode_codegen => make_metrics_test_closure(config, &file),
278 _ => make_test_closure(config, &file)
287 pub fn is_test(config: &config, testfile: &Path) -> bool {
288 // Pretty-printer does not work with .rc files yet
289 let valid_extensions =
291 mode_pretty => ~[~".rs"],
292 _ => ~[~".rc", ~".rs"]
294 let invalid_prefixes = ~[~".", ~"#", ~"~"];
295 let name = testfile.filename_str().unwrap();
297 let mut valid = false;
299 for ext in valid_extensions.iter() {
300 if name.ends_with(*ext) { valid = true; }
303 for pre in invalid_prefixes.iter() {
304 if name.starts_with(*pre) { valid = false; }
310 pub fn make_test(config: &config, testfile: &Path, f: || -> test::TestFn)
311 -> test::TestDescAndFn {
312 test::TestDescAndFn {
313 desc: test::TestDesc {
314 name: make_test_name(config, testfile),
315 ignore: header::is_test_ignored(config, testfile),
322 pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName {
324 // Try to elide redundant long paths
325 fn shorten(path: &Path) -> ~str {
326 let filename = path.filename_str();
327 let p = path.dir_path();
328 let dir = p.filename_str();
329 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
332 test::DynTestName(format!("[{}] {}",
333 mode_str(config.mode),
337 pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn {
338 let config = (*config).clone();
339 // FIXME (#9639): This needs to handle non-utf8 paths
340 let testfile = testfile.as_str().unwrap().to_owned();
341 test::DynTestFn(proc() { runtest::run(config, testfile) })
344 pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn {
345 let config = (*config).clone();
346 // FIXME (#9639): This needs to handle non-utf8 paths
347 let testfile = testfile.as_str().unwrap().to_owned();
348 test::DynMetricFn(proc(mm) {
349 runtest::run_metrics(config, testfile, mm)