]> git.lizzy.rs Git - rust.git/blob - src/compiletest/compiletest.rs
Snapshot cleanup
[rust.git] / src / compiletest / compiletest.rs
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.
4 //
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.
10
11 #[crate_type = "bin"];
12 #[feature(phase)];
13
14 #[allow(non_camel_case_types)];
15 #[deny(warnings)];
16
17 extern crate test;
18 extern crate getopts;
19 #[phase(link, syntax)]
20 extern crate log;
21 extern crate green;
22 extern crate rustuv;
23
24 use std::os;
25 use std::io;
26 use std::io::fs;
27 use getopts::{optopt, optflag, reqopt};
28 use common::config;
29 use common::mode_run_pass;
30 use common::mode_run_fail;
31 use common::mode_compile_fail;
32 use common::mode_pretty;
33 use common::mode_debug_info;
34 use common::mode_codegen;
35 use common::mode;
36 use util::logv;
37
38 pub mod procsrv;
39 pub mod util;
40 pub mod header;
41 pub mod runtest;
42 pub mod common;
43 pub mod errors;
44
45 #[start]
46 fn start(argc: int, argv: **u8) -> int { green::start(argc, argv, main) }
47
48 pub fn main() {
49     let args = os::args();
50     let config = parse_config(args.move_iter().collect());
51     log_config(&config);
52     run_tests(&config);
53 }
54
55 pub fn parse_config(args: Vec<~str> ) -> config {
56
57     let groups : Vec<getopts::OptGroup> =
58         vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"),
59           reqopt("", "run-lib-path", "path to target shared libraries", "PATH"),
60           reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"),
61           optopt("", "clang-path", "path to  executable for codegen tests", "PATH"),
62           optopt("", "llvm-bin-path", "path to directory holding llvm binaries", "DIR"),
63           reqopt("", "src-base", "directory to scan for test files", "PATH"),
64           reqopt("", "build-base", "directory to deposit test outputs", "PATH"),
65           reqopt("", "aux-base", "directory to find auxiliary test files", "PATH"),
66           reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
67           reqopt("", "mode", "which sort of compile tests to run",
68                  "(compile-fail|run-fail|run-pass|pretty|debug-info)"),
69           optflag("", "ignored", "run tests marked as ignored"),
70           optopt("", "runtool", "supervisor program to run tests under \
71                                  (eg. emulator, valgrind)", "PROGRAM"),
72           optopt("", "host-rustcflags", "flags to pass to rustc for host", "FLAGS"),
73           optopt("", "target-rustcflags", "flags to pass to rustc for target", "FLAGS"),
74           optflag("", "verbose", "run tests verbosely, showing all output"),
75           optopt("", "logfile", "file to log test execution to", "FILE"),
76           optopt("", "save-metrics", "file to save metrics to", "FILE"),
77           optopt("", "ratchet-metrics", "file to ratchet metrics against", "FILE"),
78           optopt("", "ratchet-noise-percent",
79                  "percent change in metrics to consider noise", "N"),
80           optflag("", "jit", "run tests under the JIT"),
81           optopt("", "target", "the target to build for", "TARGET"),
82           optopt("", "host", "the host to build for", "HOST"),
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("", "test-shard", "run shard A, of B shards, worth of the testsuite", "A.B"),
86           optflag("h", "help", "show this message"));
87
88     assert!(!args.is_empty());
89     let argv0 = (*args.get(0)).clone();
90     let args_ = args.tail();
91     if *args.get(1) == ~"-h" || *args.get(1) == ~"--help" {
92         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
93         println!("{}", getopts::usage(message, groups.as_slice()));
94         println!("");
95         fail!()
96     }
97
98     let matches =
99         &match getopts::getopts(args_, groups.as_slice()) {
100           Ok(m) => m,
101           Err(f) => fail!("{}", f.to_err_msg())
102         };
103
104     if matches.opt_present("h") || matches.opt_present("help") {
105         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
106         println!("{}", getopts::usage(message, groups.as_slice()));
107         println!("");
108         fail!()
109     }
110
111     fn opt_path(m: &getopts::Matches, nm: &str) -> Path {
112         Path::new(m.opt_str(nm).unwrap())
113     }
114
115     config {
116         compile_lib_path: matches.opt_str("compile-lib-path").unwrap(),
117         run_lib_path: matches.opt_str("run-lib-path").unwrap(),
118         rustc_path: opt_path(matches, "rustc-path"),
119         clang_path: matches.opt_str("clang-path").map(|s| Path::new(s)),
120         llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| Path::new(s)),
121         src_base: opt_path(matches, "src-base"),
122         build_base: opt_path(matches, "build-base"),
123         aux_base: opt_path(matches, "aux-base"),
124         stage_id: matches.opt_str("stage-id").unwrap(),
125         mode: str_mode(matches.opt_str("mode").unwrap()),
126         run_ignored: matches.opt_present("ignored"),
127         filter:
128             if !matches.free.is_empty() {
129                  Some((*matches.free.get(0)).clone())
130             } else {
131                 None
132             },
133         logfile: matches.opt_str("logfile").map(|s| Path::new(s)),
134         save_metrics: matches.opt_str("save-metrics").map(|s| Path::new(s)),
135         ratchet_metrics:
136             matches.opt_str("ratchet-metrics").map(|s| Path::new(s)),
137         ratchet_noise_percent:
138             matches.opt_str("ratchet-noise-percent").and_then(|s| from_str::<f64>(s)),
139         runtool: matches.opt_str("runtool"),
140         host_rustcflags: matches.opt_str("host-rustcflags"),
141         target_rustcflags: matches.opt_str("target-rustcflags"),
142         jit: matches.opt_present("jit"),
143         target: opt_str2(matches.opt_str("target")).to_str(),
144         host: opt_str2(matches.opt_str("host")).to_str(),
145         adb_path: opt_str2(matches.opt_str("adb-path")).to_str(),
146         adb_test_dir:
147             opt_str2(matches.opt_str("adb-test-dir")).to_str(),
148         adb_device_status:
149             "arm-linux-androideabi" == opt_str2(matches.opt_str("target")) &&
150             "(none)" != opt_str2(matches.opt_str("adb-test-dir")) &&
151             !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
152         test_shard: test::opt_shard(matches.opt_str("test-shard")),
153         verbose: matches.opt_present("verbose")
154     }
155 }
156
157 pub fn log_config(config: &config) {
158     let c = config;
159     logv(c, format!("configuration:"));
160     logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
161     logv(c, format!("run_lib_path: {}", config.run_lib_path));
162     logv(c, format!("rustc_path: {}", config.rustc_path.display()));
163     logv(c, format!("src_base: {}", config.src_base.display()));
164     logv(c, format!("build_base: {}", config.build_base.display()));
165     logv(c, format!("stage_id: {}", config.stage_id));
166     logv(c, format!("mode: {}", mode_str(config.mode)));
167     logv(c, format!("run_ignored: {}", config.run_ignored));
168     logv(c, format!("filter: {}", opt_str(&config.filter)));
169     logv(c, format!("runtool: {}", opt_str(&config.runtool)));
170     logv(c, format!("host-rustcflags: {}", opt_str(&config.host_rustcflags)));
171     logv(c, format!("target-rustcflags: {}", opt_str(&config.target_rustcflags)));
172     logv(c, format!("jit: {}", config.jit));
173     logv(c, format!("target: {}", config.target));
174     logv(c, format!("host: {}", config.host));
175     logv(c, format!("adb_path: {}", config.adb_path));
176     logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
177     logv(c, format!("adb_device_status: {}", config.adb_device_status));
178     match config.test_shard {
179         None => logv(c, ~"test_shard: (all)"),
180         Some((a,b)) => logv(c, format!("test_shard: {}.{}", a, b))
181     }
182     logv(c, format!("verbose: {}", config.verbose));
183     logv(c, format!("\n"));
184 }
185
186 pub fn opt_str<'a>(maybestr: &'a Option<~str>) -> &'a str {
187     match *maybestr {
188         None => "(none)",
189         Some(ref s) => {
190             let s: &'a str = *s;
191             s
192         }
193     }
194 }
195
196 pub fn opt_str2(maybestr: Option<~str>) -> ~str {
197     match maybestr { None => ~"(none)", Some(s) => { s } }
198 }
199
200 pub fn str_mode(s: ~str) -> mode {
201     match s.as_slice() {
202       "compile-fail" => mode_compile_fail,
203       "run-fail" => mode_run_fail,
204       "run-pass" => mode_run_pass,
205       "pretty" => mode_pretty,
206       "debug-info" => mode_debug_info,
207       "codegen" => mode_codegen,
208       _ => fail!("invalid mode")
209     }
210 }
211
212 pub fn mode_str(mode: mode) -> ~str {
213     match mode {
214       mode_compile_fail => ~"compile-fail",
215       mode_run_fail => ~"run-fail",
216       mode_run_pass => ~"run-pass",
217       mode_pretty => ~"pretty",
218       mode_debug_info => ~"debug-info",
219       mode_codegen => ~"codegen",
220     }
221 }
222
223 pub fn run_tests(config: &config) {
224     if config.target == ~"arm-linux-androideabi" {
225         match config.mode{
226             mode_debug_info => {
227                 println!("arm-linux-androideabi debug-info \
228                          test uses tcp 5039 port. please reserve it");
229             }
230             _ =>{}
231         }
232
233         //arm-linux-androideabi debug-info test uses remote debugger
234         //so, we test 1 task at once.
235         // also trying to isolate problems with adb_run_wrapper.sh ilooping
236         os::setenv("RUST_TEST_TASKS","1");
237     }
238
239     let opts = test_opts(config);
240     let tests = make_tests(config);
241     // sadly osx needs some file descriptor limits raised for running tests in
242     // parallel (especially when we have lots and lots of child processes).
243     // For context, see #8904
244     io::test::raise_fd_limit();
245     let res = test::run_tests_console(&opts, tests.move_iter().collect());
246     match res {
247         Ok(true) => {}
248         Ok(false) => fail!("Some tests failed"),
249         Err(e) => {
250             println!("I/O failure during tests: {}", e);
251         }
252     }
253 }
254
255 pub fn test_opts(config: &config) -> test::TestOpts {
256     test::TestOpts {
257         filter: config.filter.clone(),
258         run_ignored: config.run_ignored,
259         logfile: config.logfile.clone(),
260         run_tests: true,
261         run_benchmarks: true,
262         ratchet_metrics: config.ratchet_metrics.clone(),
263         ratchet_noise_percent: config.ratchet_noise_percent.clone(),
264         save_metrics: config.save_metrics.clone(),
265         test_shard: config.test_shard.clone()
266     }
267 }
268
269 pub fn make_tests(config: &config) -> Vec<test::TestDescAndFn> {
270     debug!("making tests from {}",
271            config.src_base.display());
272     let mut tests = Vec::new();
273     let dirs = fs::readdir(&config.src_base).unwrap();
274     for file in dirs.iter() {
275         let file = file.clone();
276         debug!("inspecting file {}", file.display());
277         if is_test(config, &file) {
278             let t = make_test(config, &file, || {
279                 match config.mode {
280                     mode_codegen => make_metrics_test_closure(config, &file),
281                     _ => make_test_closure(config, &file)
282                 }
283             });
284             tests.push(t)
285         }
286     }
287     tests
288 }
289
290 pub fn is_test(config: &config, testfile: &Path) -> bool {
291     // Pretty-printer does not work with .rc files yet
292     let valid_extensions =
293         match config.mode {
294           mode_pretty => vec!(~".rs"),
295           _ => vec!(~".rc", ~".rs")
296         };
297     let invalid_prefixes = vec!(~".", ~"#", ~"~");
298     let name = testfile.filename_str().unwrap();
299
300     let mut valid = false;
301
302     for ext in valid_extensions.iter() {
303         if name.ends_with(*ext) { valid = true; }
304     }
305
306     for pre in invalid_prefixes.iter() {
307         if name.starts_with(*pre) { valid = false; }
308     }
309
310     return valid;
311 }
312
313 pub fn make_test(config: &config, testfile: &Path, f: || -> test::TestFn)
314                  -> test::TestDescAndFn {
315     test::TestDescAndFn {
316         desc: test::TestDesc {
317             name: make_test_name(config, testfile),
318             ignore: header::is_test_ignored(config, testfile),
319             should_fail: false
320         },
321         testfn: f(),
322     }
323 }
324
325 pub fn make_test_name(config: &config, testfile: &Path) -> test::TestName {
326
327     // Try to elide redundant long paths
328     fn shorten(path: &Path) -> ~str {
329         let filename = path.filename_str();
330         let p = path.dir_path();
331         let dir = p.filename_str();
332         format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or(""))
333     }
334
335     test::DynTestName(format!("[{}] {}",
336                               mode_str(config.mode),
337                               shorten(testfile)))
338 }
339
340 pub fn make_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::DynTestFn(proc() { runtest::run(config, testfile) })
345 }
346
347 pub fn make_metrics_test_closure(config: &config, testfile: &Path) -> test::TestFn {
348     let config = (*config).clone();
349     // FIXME (#9639): This needs to handle non-utf8 paths
350     let testfile = testfile.as_str().unwrap().to_owned();
351     test::DynMetricFn(proc(mm) {
352         runtest::run_metrics(config, testfile, mm)
353     })
354 }