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 // we use our own (green) start below; do not link in libnative; issue #13247.
21 #[phase(link, syntax)]
29 use std::from_str::FromStr;
30 use getopts::{optopt, optflag, reqopt};
32 use common::{Pretty, DebugInfoGdb, Codegen};
43 fn start(argc: int, argv: **u8) -> int {
44 green::start(argc, argv, rustuv::event_loop, main)
48 let args = os::args();
49 let config = parse_config(args.move_iter().collect());
54 pub fn parse_config(args: Vec<~str> ) -> Config {
56 let groups : Vec<getopts::OptGroup> =
57 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
58 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
59 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
60 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
61 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
62 reqopt("", "src-base", "directory to scan for test files", "PATH"),
63 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
64 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
65 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
66 reqopt("", "mode", "which sort of compile tests to run",
67 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
68 optflag("", "ignored", "run tests marked as ignored"),
69 optopt("", "runtool", "supervisor program to run tests under \
70 (eg. emulator, valgrind)", "PROGRAM"),
71 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
72 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
73 optflag("", "verbose", "run tests verbosely, showing all output"),
74 optopt("", "logfile", "file to log test execution to", "FILE"),
75 optopt("", "save-metrics", "file to save metrics to", "FILE"),
76 optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
77 optopt("", "ratchet-noise-percent",
78 "percent change in metrics to consider noise", "N"),
79 optflag("", "jit", "run tests under the JIT"),
80 optopt("", "target", "the target to build for", "TARGET"),
81 optopt("", "host", "the host to build for", "HOST"),
82 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
83 optopt("", "adb-path", "path to the android debugger", "PATH"),
84 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
85 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
86 optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
87 optflag("h", "help", "show this message"));
89 assert!(!args.is_empty());
90 let argv0 = (*args.get(0)).clone();
91 let args_ = args.tail();
92 if *args.get(1) == "-h".to_owned() || *args.get(1) == "--help".to_owned() {
93 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
94 println!("{}", getopts::usage(message, groups.as_slice()));
100 &match getopts::getopts(args_, groups.as_slice()) {
102 Err(f) => fail!("{}", f.to_err_msg())
105 if matches.opt_present("h") || matches.opt_present("help") {
106 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
107 println!("{}", getopts::usage(message, groups.as_slice()));
112 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
113 Path::new(m.opt_str(nm).unwrap())
117 compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
118 run_lib_path: matches.opt_str("run-lib-path").unwrap(),
119 rustc_path: opt_path(matches, "rustc-path"),
120 clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
121 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
122 src_base: opt_path(matches, "src-base"),
123 build_base: opt_path(matches, "build-base"),
124 aux_base: opt_path(matches, "aux-base"),
125 stage_id: matches.opt_str("stage-id").unwrap(),
126 mode: FromStr::from_str(matches.opt_str("mode").unwrap()).expect("invalid mode"),
127 run_ignored: matches.opt_present("ignored"),
129 if !matches.free.is_empty() {
130 Some((*matches.free.get(0)).clone())
134 logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
135 save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
137 matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
138 ratchet_noise_percent:
139 matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::<f64>(s)),
140 runtool: matches.opt_str("runtool"),
141 host_rustcflags: matches.opt_str("host-rustcflags"),
142 target_rustcflags: matches.opt_str("target-rustcflags"),
143 jit: matches.opt_present("jit"),
144 target: opt_str2(matches.opt_str("target")).to_str(),
145 host: opt_str2(matches.opt_str("host")).to_str(),
146 android_cross_path: opt_path(matches, "android-cross-path"),
147 adb_path: opt_str2(matches.opt_str("adb-path")).to_str(),
149 opt_str2(matches.opt_str("adb-test-dir")).to_str(),
151 "arm-linux-androideabi" == opt_str2(matches.opt_str("target")) &&
152 "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
153 !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
154 lldb_python_dir: matches.opt_str("lldb-python-dir"),
155 test_shard: test::opt_shard(matches.opt_str("test-shard")),
156 verbose: matches.opt_present("verbose")
160 pub fn log_config(config: &Config) {
162 logv(c, format!("configuration:"));
163 logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
164 logv(c, format!("run_lib_path: {}", config.run_lib_path));
165 logv(c, format!("rustc_path: {}", config.rustc_path.display()));
166 logv(c, format!("src_base: {}", config.src_base.display()));
167 logv(c, format!("build_base: {}", config.build_base.display()));
168 logv(c, format!("stage_id: {}", config.stage_id));
169 logv(c, format!("mode: {}", config.mode));
170 logv(c, format!("run_ignored: {}", config.run_ignored));
171 logv(c, format!("filter: {}", opt_str(&config.filter)));
172 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
173 logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
174 logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
175 logv(c, format!("jit: {}", config.jit));
176 logv(c, format!("target: {}", config.target));
177 logv(c, format!("host: {}", config.host));
178 logv(c, format!("android-cross-path: {}", config.android_cross_path.display()));
179 logv(c, format!("adb_path: {}", config.adb_path));
180 logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
181 logv(c, format!("adb_device_status: {}", config.adb_device_status));
182 match config.test_shard {
183 None => logv(c, "test_shard: (all)".to_owned()),
184 Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
186 logv(c, format!("verbose: {}", config.verbose));
187 logv(c, format!("\n"));
190 pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
200 pub fn opt_str2(maybestr: Option<~str>) -> ~str {
201 match maybestr { None => "(none)".to_owned(), Some(s) => { s } }
204 pub fn run_tests(config: &Config) {
205 if config.target == "arm-linux-androideabi".to_owned() {
208 println!("arm-linux-androideabi debug-info \
209 test uses tcp 5039 port. please reserve it");
214 //arm-linux-androideabi debug-info test uses remote debugger
215 //so, we test 1 task at once.
216 // also trying to isolate problems with adb_run_wrapper.sh ilooping
217 os::setenv("RUST_TEST_TASKS","1");
220 let opts = test_opts(config);
221 let tests = make_tests(config);
222 // sadly osx needs some file descriptor limits raised for running tests in
223 // parallel (especially when we have lots and lots of child processes).
224 // For context, see #8904
225 io::test::raise_fd_limit();
226 let res = test::run_tests_console(&opts, tests.move_iter().collect());
229 Ok(false) => fail!("Some tests failed"),
231 println!("I/O failure during tests: {}", e);
236 pub fn test_opts(config: &Config) -> test::TestOpts {
238 filter: config.filter.clone(),
239 run_ignored: config.run_ignored,
240 logfile: config.logfile.clone(),
242 run_benchmarks: true,
243 ratchet_metrics: config.ratchet_metrics.clone(),
244 ratchet_noise_percent: config.ratchet_noise_percent.clone(),
245 save_metrics: config.save_metrics.clone(),
246 test_shard: config.test_shard.clone(),
251 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
252 debug!("making tests from {}",
253 config.src_base.display());
254 let mut tests = Vec::new();
255 let dirs = fs::readdir(&config.src_base).unwrap();
256 for file in dirs.iter() {
257 let file = file.clone();
258 debug!("inspecting file {}", file.display());
259 if is_test(config, &file) {
260 let t = make_test(config, &file, || {
262 Codegen => make_metrics_test_closure(config, &file),
263 _ => make_test_closure(config, &file)
272 pub fn is_test(config: &Config, testfile: &Path) -> bool {
273 // Pretty-printer does not work with .rc files yet
274 let valid_extensions =
276 Pretty => vec!(".rs".to_owned()),
277 _ => vec!(".rc".to_owned(), ".rs".to_owned())
279 let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
280 let name = testfile.filename_str().unwrap();
282 let mut valid = false;
284 for ext in valid_extensions.iter() {
285 if name.ends_with(*ext) { valid = true; }
288 for pre in invalid_prefixes.iter() {
289 if name.starts_with(*pre) { valid = false; }
295 pub fn make_test(config: &Config, testfile: &Path, f: || -> test::TestFn)
296 -> test::TestDescAndFn {
297 test::TestDescAndFn {
298 desc: test::TestDesc {
299 name: make_test_name(config, testfile),
300 ignore: header::is_test_ignored(config, testfile),
307 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
309 // Try to elide redundant long paths
310 fn shorten(path: &Path) -> ~str {
311 let filename = path.filename_str();
312 let p = path.dir_path();
313 let dir = p.filename_str();
314 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
317 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
320 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
321 let config = (*config).clone();
322 // FIXME (#9639): This needs to handle non-utf8 paths
323 let testfile = testfile.as_str().unwrap().to_owned();
324 test::DynTestFn(proc() { runtest::run(config, testfile) })
327 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
328 let config = (*config).clone();
329 // FIXME (#9639): This needs to handle non-utf8 paths
330 let testfile = testfile.as_str().unwrap().to_owned();
331 test::DynMetricFn(proc(mm) {
332 runtest::run_metrics(config, testfile, mm)