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"];
13 #[allow(non_camel_case_types)];
15 #[allow(deprecated_owned_vector)];
23 use getopts::{optopt, optflag, reqopt};
25 use common::mode_run_pass;
26 use common::mode_run_fail;
27 use common::mode_compile_fail;
28 use common::mode_pretty;
29 use common::mode_debug_info;
30 use common::mode_codegen;
42 let args = os::args();
43 let config = parse_config(args);
48 pub fn parse_config(args: ~[~str]) -> config {
50 let groups : ~[getopts::OptGroup] =
51 ~[reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
52 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
53 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
54 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
55 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
56 reqopt("", "src-base", "directory to scan for test files", "PATH"),
57 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
58 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
59 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
60 reqopt("", "mode", "which sort of compile tests to run",
61 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
62 optflag("", "ignored", "run tests marked as ignored"),
63 optopt("", "runtool", "supervisor program to run tests under \
64 (eg. emulator, valgrind)", "PROGRAM"),
65 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
66 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
67 optflag("", "verbose", "run tests verbosely, showing all output"),
68 optopt("", "logfile", "file to log test execution to", "FILE"),
69 optopt("", "save-metrics", "file to save metrics to", "FILE"),
70 optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
71 optopt("", "ratchet-noise-percent",
72 "percent change in metrics to consider noise", "N"),
73 optflag("", "jit", "run tests under the JIT"),
74 optopt("", "target", "the target to build for", "TARGET"),
75 optopt("", "host", "the host to build for", "HOST"),
76 optopt("", "adb-path", "path to the android debugger", "PATH"),
77 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
78 optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
79 optflag("h", "help", "show this message"),
82 assert!(!args.is_empty());
83 let argv0 = args[0].clone();
84 let args_ = args.tail();
85 if args[1] == ~"-h" || args[1] == ~"--help" {
86 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
87 println!("{}", getopts::usage(message, groups));
93 &match getopts::getopts(args_, groups) {
95 Err(f) => fail!("{}", f.to_err_msg())
98 if matches.opt_present("h") || matches.opt_present("help") {
99 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
100 println!("{}", getopts::usage(message, groups));
105 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
106 Path::new(m.opt_str(nm).unwrap())
110 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
111 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
112 rustc_path: opt_path(matches, "rustc-path"),
113 clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
114 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
115 src_base: opt_path(matches, "src-base"),
116 build_base: opt_path(matches, "build-base"),
117 aux_base: opt_path(matches, "aux-base"),
118 stage_id: matches.opt_str("stage-id").unwrap(),
119 mode: str_mode(matches.opt_str("mode").unwrap()),
120 run_ignored: matches.opt_present("ignored"),
122 if !matches.free.is_empty() {
123 Some(matches.free[0].clone())
127 logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
128 save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
130 matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
131 ratchet_noise_percent:
132 matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::<f64>(s)),
133 runtool: matches.opt_str("runtool"),
134 host_rustcflags: matches.opt_str("host-rustcflags"),
135 target_rustcflags: matches.opt_str("target-rustcflags"),
136 jit: matches.opt_present("jit"),
137 target: opt_str2(matches.opt_str("target")).to_str(),
138 host: opt_str2(matches.opt_str("host")).to_str(),
139 adb_path: opt_str2(matches.opt_str("adb-path")).to_str(),
141 opt_str2(matches.opt_str("adb-test-dir")).to_str(),
143 "arm-linux-androideabi" == opt_str2(matches.opt_str("target")) &&
144 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
145 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
146 test_shard: test::opt_shard(matches.opt_str("test-shard")),
147 verbose: matches.opt_present("verbose")
151 pub fn log_config(config: &config) {
153 logv(c, format!("configuration:"));
154 logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
155 logv(c, format!("run_lib_path: {}", config.run_lib_path));
156 logv(c, format!("rustc_path: {}", config.rustc_path.display()));
157 logv(c, format!("src_base: {}", config.src_base.display()));
158 logv(c, format!("build_base: {}", config.build_base.display()));
159 logv(c, format!("stage_id: {}", config.stage_id));
160 logv(c, format!("mode: {}", mode_str(config.mode)));
161 logv(c, format!("run_ignored: {}", config.run_ignored));
162 logv(c, format!("filter: {}", opt_str(&config.filter)));
163 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
164 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
165 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
166 logv(c, format!("jit: {}", config.jit));
167 logv(c, format!("target: {}", config.target));
168 logv(c, format!("host: {}", config.host));
169 logv(c, format!("adb_path: {}", config.adb_path));
170 logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
171 logv(c, format!("adb_device_status: {}", config.adb_device_status));
172 match config.test_shard {
173 None => logv(c, ~"test_shard: (all)"),
174 Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
176 logv(c, format!("verbose: {}", config.verbose));
177 logv(c, format!("\n"));
180 pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
190 pub fn opt_str2(maybestr: Option<~str>) -> ~str {
191 match maybestr { None => ~"(none)", Some(s) => { s } }
194 pub fn str_mode(s: ~str) -> mode {
196 "compile-fail" => mode_compile_fail,
197 "run-fail" => mode_run_fail,
198 "run-pass" => mode_run_pass,
199 "pretty" => mode_pretty,
200 "debug-info" => mode_debug_info,
201 "codegen" => mode_codegen,
202 _ => fail!("invalid mode")
206 pub fn mode_str(mode: mode) -> ~str {
208 mode_compile_fail => ~"compile-fail",
209 mode_run_fail => ~"run-fail",
210 mode_run_pass => ~"run-pass",
211 mode_pretty => ~"pretty",
212 mode_debug_info => ~"debug-info",
213 mode_codegen => ~"codegen",
217 pub fn run_tests(config: &config) {
218 if config.target == ~"arm-linux-androideabi" {
221 println!("arm-linux-androideabi debug-info \
222 test uses tcp 5039 port. please reserve it");
227 //arm-linux-androideabi debug-info test uses remote debugger
228 //so, we test 1 task at once.
229 // also trying to isolate problems with adb_run_wrapper.sh ilooping
230 os::setenv("RUST_TEST_TASKS","1");
233 let opts = test_opts(config);
234 let tests = make_tests(config);
235 // sadly osx needs some file descriptor limits raised for running tests in
236 // parallel (especially when we have lots and lots of child processes).
237 // For context, see #8904
238 io::test::raise_fd_limit();
239 let res = test::run_tests_console(&opts, tests);
242 Ok(false) => fail!("Some tests failed"),
244 println!("I/O failure during tests: {}", e);
249 pub fn test_opts(config: &config) -> test::TestOpts {
251 filter: config.filter.clone(),
252 run_ignored: config.run_ignored,
253 logfile: config.logfile.clone(),
255 run_benchmarks: true,
256 ratchet_metrics: config.ratchet_metrics.clone(),
257 ratchet_noise_percent: config.ratchet_noise_percent.clone(),
258 save_metrics: config.save_metrics.clone(),
259 test_shard: config.test_shard.clone()
263 pub fn make_tests(config: &config) -> ~[test::TestDescAndFn] {
264 debug!("making tests from {}",
265 config.src_base.display());
267 let dirs = fs::readdir(&config.src_base).unwrap();
268 for file in dirs.iter() {
269 let file = file.clone();
270 debug!("inspecting file {}", file.display());
271 if is_test(config, &file) {
272 let t = make_test(config, &file, || {
274 mode_codegen => make_metrics_test_closure(config, &file),
275 _ => make_test_closure(config, &file)
284 pub fn is_test(config: &config, testfile: &Path) -> bool {
285 // Pretty-printer does not work with .rc files yet
286 let valid_extensions =
288 mode_pretty => ~[~".rs"],
289 _ => ~[~".rc", ~".rs"]
291 let invalid_prefixes = ~[~".", ~"#", ~"~"];
292 let name = testfile.filename_str().unwrap();
294 let mut valid = false;
296 for ext in valid_extensions.iter() {
297 if name.ends_with(*ext) { valid = true; }
300 for pre in invalid_prefixes.iter() {
301 if name.starts_with(*pre) { valid = false; }
307 pub fn make_test(config: &config, testfile: &Path, f: || -> test::TestFn)
308 -> test::TestDescAndFn {
309 test::TestDescAndFn {
310 desc: test::TestDesc {
311 name: make_test_name(config, testfile),
312 ignore: header::is_test_ignored(config, testfile),
319 pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName {
321 // Try to elide redundant long paths
322 fn shorten(path: &Path) -> ~str {
323 let filename = path.filename_str();
324 let p = path.dir_path();
325 let dir = p.filename_str();
326 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
329 test::DynTestName(format!("[{}] {}",
330 mode_str(config.mode),
334 pub fn make_test_closure(config: &config, testfile: &Path) -> test::TestFn {
335 let config = (*config).clone();
336 // FIXME (#9639): This needs to handle non-utf8 paths
337 let testfile = testfile.as_str().unwrap().to_owned();
338 test::DynTestFn(proc() { runtest::run(config, testfile) })
341 pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn {
342 let config = (*config).clone();
343 // FIXME (#9639): This needs to handle non-utf8 paths
344 let testfile = testfile.as_str().unwrap().to_owned();
345 test::DynMetricFn(proc(mm) {
346 runtest::run_metrics(config, testfile, mm)