]>
Commit | Line | Data |
---|---|---|
1a4d82fc | 1 | // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT |
970d7e83 LB |
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 | ||
1a4d82fc | 11 | #![crate_type = "bin"] |
85aaf69f | 12 | |
1a4d82fc | 13 | #![feature(box_syntax)] |
62682a34 SL |
14 | #![feature(dynamic_lib)] |
15 | #![feature(libc)] | |
62682a34 | 16 | #![feature(rustc_private)] |
c34b1796 | 17 | #![feature(str_char)] |
62682a34 | 18 | #![feature(test)] |
1a4d82fc JJ |
19 | |
20 | #![deny(warnings)] | |
21 | ||
9346a6ac | 22 | extern crate libc; |
1a4d82fc JJ |
23 | extern crate test; |
24 | extern crate getopts; | |
25 | ||
26 | #[macro_use] | |
27 | extern crate log; | |
1a4d82fc | 28 | |
85aaf69f | 29 | use std::env; |
c34b1796 AL |
30 | use std::fs; |
31 | use std::path::{Path, PathBuf}; | |
1a4d82fc JJ |
32 | use getopts::{optopt, optflag, reqopt}; |
33 | use common::Config; | |
62682a34 | 34 | use common::{Pretty, DebugInfoGdb, DebugInfoLldb}; |
970d7e83 LB |
35 | use util::logv; |
36 | ||
37 | pub mod procsrv; | |
38 | pub mod util; | |
39 | pub mod header; | |
40 | pub mod runtest; | |
41 | pub mod common; | |
42 | pub mod errors; | |
9346a6ac | 43 | mod raise_fd_limit; |
970d7e83 | 44 | |
970d7e83 | 45 | pub fn main() { |
85aaf69f | 46 | let config = parse_config(env::args().collect()); |
1a4d82fc JJ |
47 | |
48 | if config.valgrind_path.is_none() && config.force_valgrind { | |
49 | panic!("Can't find Valgrind to run Valgrind tests"); | |
50 | } | |
51 | ||
970d7e83 LB |
52 | log_config(&config); |
53 | run_tests(&config); | |
54 | } | |
55 | ||
1a4d82fc JJ |
56 | pub fn parse_config(args: Vec<String> ) -> Config { |
57 | ||
58 | let groups : Vec<getopts::OptGroup> = | |
59 | vec!(reqopt("", "compile-lib-path", "path to host shared libraries", "PATH"), | |
60 | reqopt("", "run-lib-path", "path to target shared libraries", "PATH"), | |
61 | reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH"), | |
9346a6ac AL |
62 | reqopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH"), |
63 | reqopt("", "python", "path to python to use for doc tests", "PATH"), | |
1a4d82fc JJ |
64 | optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM"), |
65 | optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind"), | |
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", | |
85aaf69f | 72 | "(compile-fail|parse-fail|run-fail|run-pass|run-pass-valgrind|pretty|debug-info)"), |
1a4d82fc JJ |
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"), | |
1a4d82fc JJ |
80 | optopt("", "target", "the target to build for", "TARGET"), |
81 | optopt("", "host", "the host to build for", "HOST"), | |
82 | optopt("", "gdb-version", "the version of GDB used", "VERSION STRING"), | |
83 | optopt("", "lldb-version", "the version of LLDB used", "VERSION STRING"), | |
84 | optopt("", "android-cross-path", "Android NDK standalone path", "PATH"), | |
85 | optopt("", "adb-path", "path to the android debugger", "PATH"), | |
86 | optopt("", "adb-test-dir", "path to tests for the android debugger", "PATH"), | |
87 | optopt("", "lldb-python-dir", "directory containing LLDB's python module", "PATH"), | |
1a4d82fc | 88 | optflag("h", "help", "show this message")); |
970d7e83 | 89 | |
c1a9b12d | 90 | let (argv0, args_) = args.split_first().unwrap(); |
b039eaaf | 91 | if args.len() == 1 || args[1] == "-h" || args[1] == "--help" { |
1a4d82fc | 92 | let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0); |
85aaf69f | 93 | println!("{}", getopts::usage(&message, &groups)); |
1a4d82fc JJ |
94 | println!(""); |
95 | panic!() | |
96 | } | |
97 | ||
970d7e83 | 98 | let matches = |
85aaf69f | 99 | &match getopts::getopts(args_, &groups) { |
970d7e83 | 100 | Ok(m) => m, |
1a4d82fc | 101 | Err(f) => panic!("{:?}", f) |
970d7e83 LB |
102 | }; |
103 | ||
1a4d82fc JJ |
104 | if matches.opt_present("h") || matches.opt_present("help") { |
105 | let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0); | |
85aaf69f | 106 | println!("{}", getopts::usage(&message, &groups)); |
1a4d82fc JJ |
107 | println!(""); |
108 | panic!() | |
109 | } | |
110 | ||
c34b1796 | 111 | fn opt_path(m: &getopts::Matches, nm: &str) -> PathBuf { |
85aaf69f | 112 | match m.opt_str(nm) { |
c34b1796 | 113 | Some(s) => PathBuf::from(&s), |
85aaf69f SL |
114 | None => panic!("no option (=path) found for {}", nm), |
115 | } | |
970d7e83 LB |
116 | } |
117 | ||
1a4d82fc | 118 | let filter = if !matches.free.is_empty() { |
85aaf69f | 119 | Some(matches.free[0].clone()) |
1a4d82fc JJ |
120 | } else { |
121 | None | |
122 | }; | |
123 | ||
124 | Config { | |
125 | compile_lib_path: matches.opt_str("compile-lib-path").unwrap(), | |
126 | run_lib_path: matches.opt_str("run-lib-path").unwrap(), | |
970d7e83 | 127 | rustc_path: opt_path(matches, "rustc-path"), |
9346a6ac AL |
128 | rustdoc_path: opt_path(matches, "rustdoc-path"), |
129 | python: matches.opt_str("python").unwrap(), | |
1a4d82fc JJ |
130 | valgrind_path: matches.opt_str("valgrind-path"), |
131 | force_valgrind: matches.opt_present("force-valgrind"), | |
c34b1796 | 132 | llvm_bin_path: matches.opt_str("llvm-bin-path").map(|s| PathBuf::from(&s)), |
970d7e83 LB |
133 | src_base: opt_path(matches, "src-base"), |
134 | build_base: opt_path(matches, "build-base"), | |
135 | aux_base: opt_path(matches, "aux-base"), | |
1a4d82fc | 136 | stage_id: matches.opt_str("stage-id").unwrap(), |
85aaf69f | 137 | mode: matches.opt_str("mode").unwrap().parse().ok().expect("invalid mode"), |
1a4d82fc JJ |
138 | run_ignored: matches.opt_present("ignored"), |
139 | filter: filter, | |
c34b1796 | 140 | logfile: matches.opt_str("logfile").map(|s| PathBuf::from(&s)), |
1a4d82fc JJ |
141 | runtool: matches.opt_str("runtool"), |
142 | host_rustcflags: matches.opt_str("host-rustcflags"), | |
143 | target_rustcflags: matches.opt_str("target-rustcflags"), | |
1a4d82fc JJ |
144 | target: opt_str2(matches.opt_str("target")), |
145 | host: opt_str2(matches.opt_str("host")), | |
146 | gdb_version: extract_gdb_version(matches.opt_str("gdb-version")), | |
147 | lldb_version: extract_lldb_version(matches.opt_str("lldb-version")), | |
148 | android_cross_path: opt_path(matches, "android-cross-path"), | |
149 | adb_path: opt_str2(matches.opt_str("adb-path")), | |
85aaf69f SL |
150 | adb_test_dir: format!("{}/{}", |
151 | opt_str2(matches.opt_str("adb-test-dir")), | |
152 | opt_str2(matches.opt_str("target"))), | |
970d7e83 | 153 | adb_device_status: |
85aaf69f SL |
154 | opt_str2(matches.opt_str("target")).contains("android") && |
155 | "(none)" != opt_str2(matches.opt_str("adb-test-dir")) && | |
1a4d82fc JJ |
156 | !opt_str2(matches.opt_str("adb-test-dir")).is_empty(), |
157 | lldb_python_dir: matches.opt_str("lldb-python-dir"), | |
1a4d82fc | 158 | verbose: matches.opt_present("verbose"), |
970d7e83 LB |
159 | } |
160 | } | |
161 | ||
1a4d82fc | 162 | pub fn log_config(config: &Config) { |
970d7e83 | 163 | let c = config; |
1a4d82fc JJ |
164 | logv(c, format!("configuration:")); |
165 | logv(c, format!("compile_lib_path: {:?}", config.compile_lib_path)); | |
166 | logv(c, format!("run_lib_path: {:?}", config.run_lib_path)); | |
167 | logv(c, format!("rustc_path: {:?}", config.rustc_path.display())); | |
9346a6ac | 168 | logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path.display())); |
1a4d82fc JJ |
169 | logv(c, format!("src_base: {:?}", config.src_base.display())); |
170 | logv(c, format!("build_base: {:?}", config.build_base.display())); | |
171 | logv(c, format!("stage_id: {}", config.stage_id)); | |
172 | logv(c, format!("mode: {}", config.mode)); | |
173 | logv(c, format!("run_ignored: {}", config.run_ignored)); | |
174 | logv(c, format!("filter: {}", | |
175 | opt_str(&config.filter | |
176 | .as_ref() | |
e9174d1e | 177 | .map(|re| re.to_owned())))); |
1a4d82fc JJ |
178 | logv(c, format!("runtool: {}", opt_str(&config.runtool))); |
179 | logv(c, format!("host-rustcflags: {}", | |
180 | opt_str(&config.host_rustcflags))); | |
181 | logv(c, format!("target-rustcflags: {}", | |
182 | opt_str(&config.target_rustcflags))); | |
1a4d82fc JJ |
183 | logv(c, format!("target: {}", config.target)); |
184 | logv(c, format!("host: {}", config.host)); | |
185 | logv(c, format!("android-cross-path: {:?}", | |
186 | config.android_cross_path.display())); | |
187 | logv(c, format!("adb_path: {:?}", config.adb_path)); | |
188 | logv(c, format!("adb_test_dir: {:?}", config.adb_test_dir)); | |
189 | logv(c, format!("adb_device_status: {}", | |
190 | config.adb_device_status)); | |
1a4d82fc JJ |
191 | logv(c, format!("verbose: {}", config.verbose)); |
192 | logv(c, format!("\n")); | |
970d7e83 LB |
193 | } |
194 | ||
1a4d82fc | 195 | pub fn opt_str<'a>(maybestr: &'a Option<String>) -> &'a str { |
970d7e83 | 196 | match *maybestr { |
1a4d82fc | 197 | None => "(none)", |
85aaf69f | 198 | Some(ref s) => s, |
970d7e83 LB |
199 | } |
200 | } | |
201 | ||
1a4d82fc JJ |
202 | pub fn opt_str2(maybestr: Option<String>) -> String { |
203 | match maybestr { | |
e9174d1e | 204 | None => "(none)".to_owned(), |
1a4d82fc JJ |
205 | Some(s) => s, |
206 | } | |
970d7e83 LB |
207 | } |
208 | ||
1a4d82fc | 209 | pub fn run_tests(config: &Config) { |
85aaf69f | 210 | if config.target.contains("android") { |
e9174d1e SL |
211 | if let DebugInfoGdb = config.mode { |
212 | println!("{} debug-info test uses tcp 5039 port.\ | |
213 | please reserve it", config.target); | |
1a4d82fc | 214 | } |
970d7e83 | 215 | |
85aaf69f | 216 | // android debug-info test uses remote debugger |
bd371182 | 217 | // so, we test 1 thread at once. |
1a4d82fc | 218 | // also trying to isolate problems with adb_run_wrapper.sh ilooping |
c34b1796 | 219 | env::set_var("RUST_TEST_THREADS","1"); |
970d7e83 | 220 | } |
970d7e83 | 221 | |
1a4d82fc JJ |
222 | match config.mode { |
223 | DebugInfoLldb => { | |
224 | // Some older versions of LLDB seem to have problems with multiple | |
bd371182 | 225 | // instances running in parallel, so only run one test thread at a |
1a4d82fc | 226 | // time. |
c34b1796 | 227 | env::set_var("RUST_TEST_THREADS", "1"); |
1a4d82fc JJ |
228 | } |
229 | _ => { /* proceed */ } | |
970d7e83 | 230 | } |
970d7e83 | 231 | |
970d7e83 LB |
232 | let opts = test_opts(config); |
233 | let tests = make_tests(config); | |
1a4d82fc JJ |
234 | // sadly osx needs some file descriptor limits raised for running tests in |
235 | // parallel (especially when we have lots and lots of child processes). | |
236 | // For context, see #8904 | |
9346a6ac | 237 | unsafe { raise_fd_limit::raise_fd_limit(); } |
85aaf69f SL |
238 | // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows |
239 | // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary | |
240 | env::set_var("__COMPAT_LAYER", "RunAsInvoker"); | |
1a4d82fc JJ |
241 | let res = test::run_tests_console(&opts, tests.into_iter().collect()); |
242 | match res { | |
243 | Ok(true) => {} | |
244 | Ok(false) => panic!("Some tests failed"), | |
245 | Err(e) => { | |
246 | println!("I/O failure during tests: {:?}", e); | |
247 | } | |
248 | } | |
970d7e83 LB |
249 | } |
250 | ||
1a4d82fc | 251 | pub fn test_opts(config: &Config) -> test::TestOpts { |
970d7e83 | 252 | test::TestOpts { |
1a4d82fc JJ |
253 | filter: match config.filter { |
254 | None => None, | |
255 | Some(ref filter) => Some(filter.clone()), | |
256 | }, | |
970d7e83 | 257 | run_ignored: config.run_ignored, |
1a4d82fc | 258 | logfile: config.logfile.clone(), |
970d7e83 | 259 | run_tests: true, |
d9579d0f | 260 | bench_benchmarks: true, |
c34b1796 | 261 | nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(), |
1a4d82fc | 262 | color: test::AutoColor, |
970d7e83 LB |
263 | } |
264 | } | |
265 | ||
1a4d82fc JJ |
266 | pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> { |
267 | debug!("making tests from {:?}", | |
268 | config.src_base.display()); | |
269 | let mut tests = Vec::new(); | |
c34b1796 AL |
270 | let dirs = fs::read_dir(&config.src_base).unwrap(); |
271 | for file in dirs { | |
272 | let file = file.unwrap().path(); | |
1a4d82fc JJ |
273 | debug!("inspecting file {:?}", file.display()); |
274 | if is_test(config, &file) { | |
62682a34 | 275 | tests.push(make_test(config, &file)) |
970d7e83 LB |
276 | } |
277 | } | |
278 | tests | |
279 | } | |
280 | ||
1a4d82fc | 281 | pub fn is_test(config: &Config, testfile: &Path) -> bool { |
970d7e83 LB |
282 | // Pretty-printer does not work with .rc files yet |
283 | let valid_extensions = | |
284 | match config.mode { | |
e9174d1e SL |
285 | Pretty => vec!(".rs".to_owned()), |
286 | _ => vec!(".rc".to_owned(), ".rs".to_owned()) | |
970d7e83 | 287 | }; |
e9174d1e | 288 | let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned()); |
c34b1796 | 289 | let name = testfile.file_name().unwrap().to_str().unwrap(); |
970d7e83 LB |
290 | |
291 | let mut valid = false; | |
292 | ||
85aaf69f SL |
293 | for ext in &valid_extensions { |
294 | if name.ends_with(ext) { | |
1a4d82fc JJ |
295 | valid = true; |
296 | } | |
970d7e83 LB |
297 | } |
298 | ||
85aaf69f SL |
299 | for pre in &invalid_prefixes { |
300 | if name.starts_with(pre) { | |
1a4d82fc JJ |
301 | valid = false; |
302 | } | |
970d7e83 LB |
303 | } |
304 | ||
305 | return valid; | |
306 | } | |
307 | ||
62682a34 | 308 | pub fn make_test(config: &Config, testfile: &Path) -> test::TestDescAndFn |
1a4d82fc | 309 | { |
970d7e83 LB |
310 | test::TestDescAndFn { |
311 | desc: test::TestDesc { | |
312 | name: make_test_name(config, testfile), | |
313 | ignore: header::is_test_ignored(config, testfile), | |
c34b1796 | 314 | should_panic: test::ShouldPanic::No, |
970d7e83 | 315 | }, |
62682a34 | 316 | testfn: make_test_closure(config, &testfile), |
970d7e83 LB |
317 | } |
318 | } | |
319 | ||
1a4d82fc | 320 | pub fn make_test_name(config: &Config, testfile: &Path) -> test::TestName { |
970d7e83 LB |
321 | |
322 | // Try to elide redundant long paths | |
1a4d82fc | 323 | fn shorten(path: &Path) -> String { |
c34b1796 AL |
324 | let filename = path.file_name().unwrap().to_str(); |
325 | let p = path.parent().unwrap(); | |
326 | let dir = p.file_name().unwrap().to_str(); | |
1a4d82fc | 327 | format!("{}/{}", dir.unwrap_or(""), filename.unwrap_or("")) |
970d7e83 LB |
328 | } |
329 | ||
1a4d82fc | 330 | test::DynTestName(format!("[{}] {}", config.mode, shorten(testfile))) |
970d7e83 LB |
331 | } |
332 | ||
1a4d82fc JJ |
333 | pub fn make_test_closure(config: &Config, testfile: &Path) -> test::TestFn { |
334 | let config = (*config).clone(); | |
c34b1796 AL |
335 | let testfile = testfile.to_path_buf(); |
336 | test::DynTestFn(Box::new(move || { | |
337 | runtest::run(config, &testfile) | |
1a4d82fc JJ |
338 | })) |
339 | } | |
340 | ||
1a4d82fc JJ |
341 | fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> { |
342 | match full_version_line { | |
343 | Some(ref full_version_line) | |
9346a6ac | 344 | if !full_version_line.trim().is_empty() => { |
85aaf69f SL |
345 | let full_version_line = full_version_line.trim(); |
346 | ||
b039eaaf | 347 | // used to be a regex "(^|[^0-9])([0-9]\.[0-9]+)" |
85aaf69f SL |
348 | for (pos, c) in full_version_line.char_indices() { |
349 | if !c.is_digit(10) { continue } | |
350 | if pos + 2 >= full_version_line.len() { continue } | |
351 | if full_version_line.char_at(pos + 1) != '.' { continue } | |
352 | if !full_version_line.char_at(pos + 2).is_digit(10) { continue } | |
353 | if pos > 0 && full_version_line.char_at_reverse(pos).is_digit(10) { | |
354 | continue | |
1a4d82fc | 355 | } |
b039eaaf SL |
356 | let mut end = pos + 3; |
357 | while end < full_version_line.len() && | |
358 | full_version_line.char_at(end).is_digit(10) { | |
359 | end += 1; | |
1a4d82fc | 360 | } |
b039eaaf | 361 | return Some(full_version_line[pos..end].to_owned()); |
1a4d82fc | 362 | } |
85aaf69f SL |
363 | println!("Could not extract GDB version from line '{}'", |
364 | full_version_line); | |
365 | None | |
1a4d82fc JJ |
366 | }, |
367 | _ => None | |
368 | } | |
369 | } | |
370 | ||
371 | fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> { | |
372 | // Extract the major LLDB version from the given version string. | |
373 | // LLDB version strings are different for Apple and non-Apple platforms. | |
374 | // At the moment, this function only supports the Apple variant, which looks | |
375 | // like this: | |
376 | // | |
377 | // LLDB-179.5 (older versions) | |
378 | // lldb-300.2.51 (new versions) | |
379 | // | |
380 | // We are only interested in the major version number, so this function | |
381 | // will return `Some("179")` and `Some("300")` respectively. | |
382 | ||
e9174d1e SL |
383 | if let Some(ref full_version_line) = full_version_line { |
384 | if !full_version_line.trim().is_empty() { | |
85aaf69f SL |
385 | let full_version_line = full_version_line.trim(); |
386 | ||
387 | for (pos, l) in full_version_line.char_indices() { | |
388 | if l != 'l' && l != 'L' { continue } | |
389 | if pos + 5 >= full_version_line.len() { continue } | |
390 | let l = full_version_line.char_at(pos + 1); | |
391 | if l != 'l' && l != 'L' { continue } | |
392 | let d = full_version_line.char_at(pos + 2); | |
393 | if d != 'd' && d != 'D' { continue } | |
394 | let b = full_version_line.char_at(pos + 3); | |
395 | if b != 'b' && b != 'B' { continue } | |
396 | let dash = full_version_line.char_at(pos + 4); | |
397 | if dash != '-' { continue } | |
398 | ||
399 | let vers = full_version_line[pos + 5..].chars().take_while(|c| { | |
400 | c.is_digit(10) | |
401 | }).collect::<String>(); | |
9346a6ac | 402 | if !vers.is_empty() { return Some(vers) } |
1a4d82fc | 403 | } |
85aaf69f SL |
404 | println!("Could not extract LLDB version from line '{}'", |
405 | full_version_line); | |
e9174d1e | 406 | } |
1a4d82fc | 407 | } |
e9174d1e | 408 | None |
970d7e83 | 409 | } |