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.
21 #[phase(link, syntax)]
31 use std::from_str::FromStr;
32 use getopts::{optopt, optflag, reqopt};
34 use common::{Pretty, DebugInfoGdb, Codegen};
46 fn start(argc: int, argv: **u8) -> int {
47 green::start(argc, argv, rustuv::event_loop, main)
51 let args = os::args();
52 let config = parse_config(args.move_iter()
53 .map(|x| x.to_string())
59 pub fn parse_config(args: Vec<String> ) -> Config {
61 let groups : Vec<getopts::OptGroup> =
62 vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
63 reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
64 reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
65 optopt("", "clang-path", "path to executable for codegen tests", "PATH"),
66 optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
67 reqopt("", "src-base", "directory to scan for test files", "PATH"),
68 reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
69 reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
70 reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
71 reqopt("", "mode", "which sort of compile tests to run",
72 "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
73 optflag("", "ignored", "run tests marked as ignored"),
74 optopt("", "runtool", "supervisor program to run tests under \
75 (eg. emulator, valgrind)", "PROGRAM"),
76 optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
77 optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
78 optflag("", "verbose", "run tests verbosely, showing all output"),
79 optopt("", "logfile", "file to log test execution to", "FILE"),
80 optopt("", "save-metrics", "file to save metrics to", "FILE"),
81 optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
82 optopt("", "ratchet-noise-percent",
83 "percent change in metrics to consider noise", "N"),
84 optflag("", "jit", "run tests under the JIT"),
85 optopt("", "target", "the target to build for", "TARGET"),
86 optopt("", "host", "the host to build for", "HOST"),
87 optopt("", "android-cross-path", "Android NDK standalone path", "PATH"),
88 optopt("", "adb-path", "path to the android debugger", "PATH"),
89 optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"),
90 optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"),
91 optopt("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
92 optflag("h", "help", "show this message"));
94 assert!(!args.is_empty());
95 let argv0 = (*args.get(0)).clone();
96 let args_ = args.tail();
97 if args.get(1).as_slice() == "-h" || args.get(1).as_slice() == "--help" {
98 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
99 println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
105 &match getopts::getopts(args_.as_slice(), groups.as_slice()) {
107 Err(f) => fail!("{}", f.to_err_msg())
110 if matches.opt_present("h") || matches.opt_present("help") {
111 let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
112 println!("{}", getopts::usage(message.as_slice(), groups.as_slice()));
117 fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
118 Path::new(m.opt_str(nm).unwrap())
121 let filter = if !matches.free.is_empty() {
122 let s = matches.free.get(0).as_slice();
123 match regex::Regex::new(s) {
126 println!("failed to parse filter /{}/: {}", s, e);
135 compile_lib_path: matches.opt_str("compile-lib-path")
138 run_lib_path: matches.opt_str("run-lib-path").unwrap().to_string(),
139 rustc_path: opt_path(matches, "rustc-path"),
140 clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
141 llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
142 src_base: opt_path(matches, "src-base"),
143 build_base: opt_path(matches, "build-base"),
144 aux_base: opt_path(matches, "aux-base"),
145 stage_id: matches.opt_str("stage-id").unwrap().to_string(),
146 mode: FromStr::from_str(matches.opt_str("mode")
148 .as_slice()).expect("invalid mode"),
149 run_ignored: matches.opt_present("ignored"),
151 cfail_regex: Regex::new(errors::EXPECTED_PATTERN).unwrap(),
152 logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
153 save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
155 matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
156 ratchet_noise_percent:
157 matches.opt_str("ratchet-noise-percent")
158 .and_then(|s| from_str::<f64>(s.as_slice())),
159 runtool: matches.opt_str("runtool").map(|x| x.to_string()),
160 host_rustcflags: matches.opt_str("host-rustcflags")
161 .map(|x| x.to_string()),
162 target_rustcflags: matches.opt_str("target-rustcflags")
163 .map(|x| x.to_string()),
164 jit: matches.opt_present("jit"),
165 target: opt_str2(matches.opt_str("target").map(|x| x.to_string())),
166 host: opt_str2(matches.opt_str("host").map(|x| x.to_string())),
167 android_cross_path: opt_path(matches, "android-cross-path"),
168 adb_path: opt_str2(matches.opt_str("adb-path")
169 .map(|x| x.to_string())),
170 adb_test_dir: opt_str2(matches.opt_str("adb-test-dir")
171 .map(|x| x.to_string())),
173 "arm-linux-androideabi" ==
174 opt_str2(matches.opt_str("target")
175 .map(|x| x.to_string())).as_slice() &&
177 opt_str2(matches.opt_str("adb-test-dir")
178 .map(|x| x.to_string())).as_slice() &&
179 !opt_str2(matches.opt_str("adb-test-dir")
180 .map(|x| x.to_string())).is_empty(),
181 lldb_python_dir: matches.opt_str("lldb-python-dir")
182 .map(|x| x.to_string()),
183 test_shard: test::opt_shard(matches.opt_str("test-shard")
184 .map(|x| x.to_string())),
185 verbose: matches.opt_present("verbose")
189 pub fn log_config(config: &Config) {
191 logv(c, format_strbuf!("configuration:"));
192 logv(c, format_strbuf!("compile_lib_path: {}", config.compile_lib_path));
193 logv(c, format_strbuf!("run_lib_path: {}", config.run_lib_path));
194 logv(c, format_strbuf!("rustc_path: {}", config.rustc_path.display()));
195 logv(c, format_strbuf!("src_base: {}", config.src_base.display()));
196 logv(c, format_strbuf!("build_base: {}", config.build_base.display()));
197 logv(c, format_strbuf!("stage_id: {}", config.stage_id));
198 logv(c, format_strbuf!("mode: {}", config.mode));
199 logv(c, format_strbuf!("run_ignored: {}", config.run_ignored));
200 logv(c, format_strbuf!("filter: {}",
201 opt_str(&config.filter
204 re.to_str().into_string()
206 logv(c, format_strbuf!("runtool: {}", opt_str(&config.runtool)));
207 logv(c, format_strbuf!("host-rustcflags: {}",
208 opt_str(&config.host_rustcflags)));
209 logv(c, format_strbuf!("target-rustcflags: {}",
210 opt_str(&config.target_rustcflags)));
211 logv(c, format_strbuf!("jit: {}", config.jit));
212 logv(c, format_strbuf!("target: {}", config.target));
213 logv(c, format_strbuf!("host: {}", config.host));
214 logv(c, format_strbuf!("android-cross-path: {}",
215 config.android_cross_path.display()));
216 logv(c, format_strbuf!("adb_path: {}", config.adb_path));
217 logv(c, format_strbuf!("adb_test_dir: {}", config.adb_test_dir));
218 logv(c, format_strbuf!("adb_device_status: {}",
219 config.adb_device_status));
220 match config.test_shard {
221 None => logv(c, "test_shard: (all)".to_string()),
222 Some((a,b)) => logv(c, format_strbuf!("test_shard: {}.{}", a, b))
224 logv(c, format_strbuf!("verbose: {}", config.verbose));
225 logv(c, format_strbuf!("\n"));
228 pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str {
231 Some(ref s) => s.as_slice(),
235 pub fn opt_str2(maybestr: Option<String>) -> String {
237 None => "(none)".to_string(),
242 pub fn run_tests(config: &Config) {
243 if config.target.as_slice() == "arm-linux-androideabi" {
246 println!("arm-linux-androideabi debug-info \
247 test uses tcp 5039 port. please reserve it");
252 //arm-linux-androideabi debug-info test uses remote debugger
253 //so, we test 1 task at once.
254 // also trying to isolate problems with adb_run_wrapper.sh ilooping
255 os::setenv("RUST_TEST_TASKS","1");
258 let opts = test_opts(config);
259 let tests = make_tests(config);
260 // sadly osx needs some file descriptor limits raised for running tests in
261 // parallel (especially when we have lots and lots of child processes).
262 // For context, see #8904
263 io::test::raise_fd_limit();
264 let res = test::run_tests_console(&opts, tests.move_iter().collect());
267 Ok(false) => fail!("Some tests failed"),
269 println!("I/O failure during tests: {}", e);
274 pub fn test_opts(config: &Config) -> test::TestOpts {
276 filter: match config.filter {
278 Some(ref filter) => Some(filter.clone()),
280 run_ignored: config.run_ignored,
281 logfile: config.logfile.clone(),
283 run_benchmarks: true,
284 ratchet_metrics: config.ratchet_metrics.clone(),
285 ratchet_noise_percent: config.ratchet_noise_percent.clone(),
286 save_metrics: config.save_metrics.clone(),
287 test_shard: config.test_shard.clone(),
292 pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
293 debug!("making tests from {}",
294 config.src_base.display());
295 let mut tests = Vec::new();
296 let dirs = fs::readdir(&config.src_base).unwrap();
297 for file in dirs.iter() {
298 let file = file.clone();
299 debug!("inspecting file {}", file.display());
300 if is_test(config, &file) {
301 let t = make_test(config, &file, || {
303 Codegen => make_metrics_test_closure(config, &file),
304 _ => make_test_closure(config, &file)
313 pub fn is_test(config: &Config, testfile: &Path) -> bool {
314 // Pretty-printer does not work with .rc files yet
315 let valid_extensions =
317 Pretty => vec!(".rs".to_string()),
318 _ => vec!(".rc".to_string(), ".rs".to_string())
320 let invalid_prefixes = vec!(".".to_string(), "#".to_string(), "~".to_string());
321 let name = testfile.filename_str().unwrap();
323 let mut valid = false;
325 for ext in valid_extensions.iter() {
326 if name.ends_with(ext.as_slice()) {
331 for pre in invalid_prefixes.iter() {
332 if name.starts_with(pre.as_slice()) {
340 pub fn make_test(config: &Config, testfile: &Path, f: || -> test::TestFn)
341 -> test::TestDescAndFn {
342 test::TestDescAndFn {
343 desc: test::TestDesc {
344 name: make_test_name(config, testfile),
345 ignore: header::is_test_ignored(config, testfile),
352 pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName {
354 // Try to elide redundant long paths
355 fn shorten(path: &Path) -> String {
356 let filename = path.filename_str();
357 let p = path.dir_path();
358 let dir = p.filename_str();
359 format_strbuf!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
362 test::DynTestName(format_strbuf!("[{}] {}",
367 pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
368 let config = (*config).clone();
369 // FIXME (#9639): This needs to handle non-utf8 paths
370 let testfile = testfile.as_str().unwrap().to_string();
371 test::DynTestFn(proc() {
372 runtest::run(config, testfile)
376 pub fn make_metrics_test_closure(config: &Config, testfile: &Path) -> test::TestFn {
377 let config = (*config).clone();
378 // FIXME (#9639): This needs to handle non-utf8 paths
379 let testfile = testfile.as_str().unwrap().to_string();
380 test::DynMetricFn(proc(mm) {
381 runtest::run_metrics(config, testfile, mm)