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.
25 #[phase(syntax, link)]
29 #[phase(plugin, link)]
37 use std::from_str::FromStr;
38 use getopts::{optopt, optflag, reqopt};
40 use common::{Pretty, DebugInfoGdb, Codegen};
52 fn start(argc: int, argv: **u8) -> int {
53 green::start(argc, argv, rustuv::event_loop, main)
57 let args = os::args();
58 let config = parse_config(args.move_iter()
59 .map(|x| x.to_string())
65 pub fn parse_config(args: Vec<String> ) -> Config {
67 let groups : Vec<getopts::OptGroup> =
68 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
69 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
70 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
71 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
72 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
73 reqopt("", "src-base", "directory to scan for test files", "PATH"),
74 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
75 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
76 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
77 reqopt("", "mode", "which sort of compile tests to run",
78 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
79 optflag("", "ignored", "run tests marked as ignored"),
80 optopt("", "runtool", "supervisor program to run tests under \
81 (eg. emulator, valgrind)", "PROGRAM"),
82 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
83 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
84 optflag("", "verbose", "run tests verbosely, showing all output"),
85 optopt("", "logfile", "file to log test execution to", "FILE"),
86 optopt("", "save-metrics", "file to save metrics to", "FILE"),
87 optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
88 optopt("", "ratchet-noise-percent",
89 "percent change in metrics to consider noise", "N"),
90 optflag("", "jit", "run tests under the JIT"),
91 optopt("", "target", "the target to build for", "TARGET"),
92 optopt("", "host", "the host to build for", "HOST"),
93 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
94 optopt("", "adb-path", "path to the android debugger", "PATH"),
95 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
96 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
97 optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
98 optflag("h", "help", "show this message"));
100 assert!(!args.is_empty());
101 let argv0 = (*args.get(0)).clone();
102 let args_ = args.tail();
103 if args.get(1).as_slice() == "-h" || args.get(1).as_slice() == "--help" {
104 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
105 println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
111 &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
113 Err(f) => fail!("{}", f)
116 if matches.opt_present("h") || matches.opt_present("help") {
117 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
118 println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
123 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
124 Path::new(m.opt_str(nm).unwrap())
127 let filter = if !matches.free.is_empty() {
128 let s = matches.free.get(0).as_slice();
129 match regex::Regex::new(s) {
132 println!("failed to parse filter /{}/: {}", s, e);
141 compile_lib_path: matches.opt_str("compile-lib-path")
144 run_lib_path: matches.opt_str("run-lib-path").unwrap().to_string(),
145 rustc_path: opt_path(matches, "rustc-path"),
146 clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
147 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
148 src_base: opt_path(matches, "src-base"),
149 build_base: opt_path(matches, "build-base"),
150 aux_base: opt_path(matches, "aux-base"),
151 stage_id: matches.opt_str("stage-id").unwrap().to_string(),
152 mode: FromStr::from_str(matches.opt_str("mode")
154 .as_slice()).expect("invalid mode"),
155 run_ignored: matches.opt_present("ignored"),
157 cfail_regex: Regex::new(errors::EXPECTED_PATTERN).unwrap(),
158 logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
159 save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
161 matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
162 ratchet_noise_percent:
163 matches.opt_str("ratchet-noise-percent")
164 .and_then(|s| from_str::<f64>(s.as_slice())),
165 runtool: matches.opt_str("runtool").map(|x| x.to_string()),
166 host_rustcflags: matches.opt_str("host-rustcflags")
167 .map(|x| x.to_string()),
168 target_rustcflags: matches.opt_str("target-rustcflags")
169 .map(|x| x.to_string()),
170 jit: matches.opt_present("jit"),
171 target: opt_str2(matches.opt_str("target").map(|x| x.to_string())),
172 host: opt_str2(matches.opt_str("host").map(|x| x.to_string())),
173 android_cross_path: opt_path(matches, "android-cross-path"),
174 adb_path: opt_str2(matches.opt_str("adb-path")
175 .map(|x| x.to_string())),
176 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")
177 .map(|x| x.to_string())),
179 "arm-linux-androideabi" ==
180 opt_str2(matches.opt_str("target")
181 .map(|x| x.to_string())).as_slice() &&
183 opt_str2(matches.opt_str("adb-test-dir")
184 .map(|x| x.to_string())).as_slice() &&
185 !opt_str2(matches.opt_str("adb-test-dir")
186 .map(|x| x.to_string())).is_empty(),
187 lldb_python_dir: matches.opt_str("lldb-python-dir")
188 .map(|x| x.to_string()),
189 test_shard: test::opt_shard(matches.opt_str("test-shard")
190 .map(|x| x.to_string())),
191 verbose: matches.opt_present("verbose")
195 pub fn log_config(config: &Config) {
197 logv(c, format!("configuration:"));
198 logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
199 logv(c, format!("run_lib_path: {}", config.run_lib_path));
200 logv(c, format!("rustc_path: {}", config.rustc_path.display()));
201 logv(c, format!("src_base: {}", config.src_base.display()));
202 logv(c, format!("build_base: {}", config.build_base.display()));
203 logv(c, format!("stage_id: {}", config.stage_id));
204 logv(c, format!("mode: {}", config.mode));
205 logv(c, format!("run_ignored: {}", config.run_ignored));
206 logv(c, format!("filter: {}",
207 opt_str(&config.filter
210 re.to_str().into_string()
212 logv(c, format!("runtool: {}", opt_str(&config.runtool)));
213 logv(c, format!("host-rustcflags: {}",
214 opt_str(&config.host_rustcflags)));
215 logv(c, format!("target-rustcflags: {}",
216 opt_str(&config.target_rustcflags)));
217 logv(c, format!("jit: {}", config.jit));
218 logv(c, format!("target: {}", config.target));
219 logv(c, format!("host: {}", config.host));
220 logv(c, format!("android-cross-path: {}",
221 config.android_cross_path.display()));
222 logv(c, format!("adb_path: {}", config.adb_path));
223 logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
224 logv(c, format!("adb_device_status: {}",
225 config.adb_device_status));
226 match config.test_shard {
227 None => logv(c, "test_shard: (all)".to_string()),
228 Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
230 logv(c, format!("verbose: {}", config.verbose));
231 logv(c, format!("\n"));
234 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
237 Some(ref s) => s.as_slice(),
241 pub fn opt_str2(maybestr: Option<String>) -> String {
243 None => "(none)".to_string(),
248 pub fn run_tests(config: &Config) {
249 if config.target.as_slice() == "arm-linux-androideabi" {
252 println!("arm-linux-androideabi debug-info \
253 test uses tcp 5039 port. please reserve it");
258 //arm-linux-androideabi debug-info test uses remote debugger
259 //so, we test 1 task at once.
260 // also trying to isolate problems with adb_run_wrapper.sh ilooping
261 os::setenv("RUST_TEST_TASKS","1");
264 let opts = test_opts(config);
265 let tests = make_tests(config);
266 // sadly osx needs some file descriptor limits raised for running tests in
267 // parallel (especially when we have lots and lots of child processes).
268 // For context, see #8904
269 io::test::raise_fd_limit();
270 let res = test::run_tests_console(&opts, tests.move_iter().collect());
273 Ok(false) => fail!("Some tests failed"),
275 println!("I/O failure during tests: {}", e);
280 pub fn test_opts(config: &Config) -> test::TestOpts {
282 filter: match config.filter {
284 Some(ref filter) => Some(filter.clone()),
286 run_ignored: config.run_ignored,
287 logfile: config.logfile.clone(),
289 run_benchmarks: true,
290 ratchet_metrics: config.ratchet_metrics.clone(),
291 ratchet_noise_percent: config.ratchet_noise_percent.clone(),
292 save_metrics: config.save_metrics.clone(),
293 test_shard: config.test_shard.clone(),
295 color: test::AutoColor,
299 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
300 debug!("making tests from {}",
301 config.src_base.display());
302 let mut tests = Vec::new();
303 let dirs = fs::readdir(&config.src_base).unwrap();
304 for file in dirs.iter() {
305 let file = file.clone();
306 debug!("inspecting file {}", file.display());
307 if is_test(config, &file) {
308 let t = make_test(config, &file, || {
310 Codegen => make_metrics_test_closure(config, &file),
311 _ => make_test_closure(config, &file)
320 pub fn is_test(config: &Config, testfile: &Path) -> bool {
321 // Pretty-printer does not work with .rc files yet
322 let valid_extensions =
324 Pretty => vec!(".rs".to_string()),
325 _ => vec!(".rc".to_string(), ".rs".to_string())
327 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
328 let name = testfile.filename_str().unwrap();
330 let mut valid = false;
332 for ext in valid_extensions.iter() {
333 if name.ends_with(ext.as_slice()) {
338 for pre in invalid_prefixes.iter() {
339 if name.starts_with(pre.as_slice()) {
347 pub fn make_test(config: &Config, testfile: &Path, f: || -> test::TestFn)
348 -> test::TestDescAndFn {
349 test::TestDescAndFn {
350 desc: test::TestDesc {
351 name: make_test_name(config, testfile),
352 ignore: header::is_test_ignored(config, testfile),
359 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
361 // Try to elide redundant long paths
362 fn shorten(path: &Path) -> String {
363 let filename = path.filename_str();
364 let p = path.dir_path();
365 let dir = p.filename_str();
366 format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
369 test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile)))
372 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
373 let config = (*config).clone();
374 // FIXME (#9639): This needs to handle non-utf8 paths
375 let testfile = testfile.as_str().unwrap().to_string();
376 test::DynTestFn(proc() {
377 runtest::run(config, testfile)
381 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
382 let config = (*config).clone();
383 // FIXME (#9639): This needs to handle non-utf8 paths
384 let testfile = testfile.as_str().unwrap().to_string();
385 test::DynMetricFn(proc(mm) {
386 runtest::run_metrics(config, testfile, mm)