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