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