]>
Commit | Line | Data |
---|---|---|
abe05a73 XL |
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. | |
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 = "lib"] | |
12 | ||
13 | #![feature(rustc_private)] | |
14 | #![feature(test)] | |
ff7c6d11 | 15 | #![feature(slice_rotate)] |
abe05a73 XL |
16 | |
17 | #![deny(unused_imports)] | |
18 | ||
ff7c6d11 | 19 | #[cfg(unix)] |
abe05a73 XL |
20 | extern crate libc; |
21 | extern crate test; | |
22 | extern crate rustc; | |
23 | extern crate rustc_serialize; | |
24 | ||
25 | #[cfg(feature = "tmp")] extern crate tempdir; | |
26 | ||
27 | #[macro_use] | |
28 | extern crate log; | |
29 | extern crate filetime; | |
30 | extern crate diff; | |
31 | ||
32 | use std::env; | |
33 | use std::ffi::OsString; | |
34 | use std::fs; | |
35 | use std::io; | |
36 | use std::path::{Path, PathBuf}; | |
2c00a5a8 | 37 | use common::{Mode, TestPaths}; |
abe05a73 | 38 | use common::{Pretty, DebugInfoGdb, DebugInfoLldb}; |
abe05a73 XL |
39 | |
40 | use self::header::EarlyProps; | |
41 | ||
42 | pub mod uidiff; | |
abe05a73 | 43 | pub mod util; |
ff7c6d11 | 44 | mod json; |
abe05a73 XL |
45 | pub mod header; |
46 | pub mod runtest; | |
47 | pub mod common; | |
48 | pub mod errors; | |
ff7c6d11 | 49 | mod read2; |
abe05a73 XL |
50 | |
51 | pub use common::Config; | |
52 | ||
abe05a73 XL |
53 | pub fn run_tests(config: &Config) { |
54 | if config.target.contains("android") { | |
55 | if let DebugInfoGdb = config.mode { | |
56 | println!("{} debug-info test uses tcp 5039 port.\ | |
57 | please reserve it", config.target); | |
58 | } | |
59 | ||
60 | // android debug-info test uses remote debugger | |
61 | // so, we test 1 thread at once. | |
62 | // also trying to isolate problems with adb_run_wrapper.sh ilooping | |
63 | env::set_var("RUST_TEST_THREADS","1"); | |
64 | } | |
65 | ||
66 | if let DebugInfoLldb = config.mode { | |
67 | // Some older versions of LLDB seem to have problems with multiple | |
68 | // instances running in parallel, so only run one test task at a | |
69 | // time. | |
70 | env::set_var("RUST_TEST_TASKS", "1"); | |
71 | } | |
72 | ||
73 | let opts = test_opts(config); | |
74 | let tests = make_tests(config); | |
75 | // sadly osx needs some file descriptor limits raised for running tests in | |
76 | // parallel (especially when we have lots and lots of child processes). | |
77 | // For context, see #8904 | |
78 | // unsafe { raise_fd_limit::raise_fd_limit(); } | |
79 | // Prevent issue #21352 UAC blocking .exe containing 'patch' etc. on Windows | |
80 | // If #11207 is resolved (adding manifest to .exe) this becomes unnecessary | |
81 | env::set_var("__COMPAT_LAYER", "RunAsInvoker"); | |
82 | let res = test::run_tests_console(&opts, tests.into_iter().collect()); | |
83 | match res { | |
84 | Ok(true) => {} | |
85 | Ok(false) => panic!("Some tests failed"), | |
86 | Err(e) => { | |
87 | println!("I/O failure during tests: {:?}", e); | |
88 | } | |
89 | } | |
90 | } | |
91 | ||
92 | pub fn test_opts(config: &Config) -> test::TestOpts { | |
93 | test::TestOpts { | |
94 | filter: config.filter.clone(), | |
95 | filter_exact: config.filter_exact, | |
96 | run_ignored: config.run_ignored, | |
2c00a5a8 | 97 | format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty }, |
abe05a73 XL |
98 | logfile: config.logfile.clone(), |
99 | run_tests: true, | |
100 | bench_benchmarks: true, | |
101 | nocapture: match env::var("RUST_TEST_NOCAPTURE") { | |
102 | Ok(val) => &val != "0", | |
103 | Err(_) => false | |
104 | }, | |
105 | color: test::AutoColor, | |
106 | test_threads: None, | |
107 | skip: vec![], | |
108 | list: false, | |
109 | options: test::Options::new(), | |
110 | } | |
111 | } | |
112 | ||
113 | pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> { | |
114 | debug!("making tests from {:?}", | |
115 | config.src_base.display()); | |
116 | let mut tests = Vec::new(); | |
117 | collect_tests_from_dir(config, | |
118 | &config.src_base, | |
119 | &config.src_base, | |
120 | &PathBuf::new(), | |
121 | &mut tests) | |
122 | .unwrap(); | |
123 | tests | |
124 | } | |
125 | ||
126 | fn collect_tests_from_dir(config: &Config, | |
127 | base: &Path, | |
128 | dir: &Path, | |
129 | relative_dir_path: &Path, | |
130 | tests: &mut Vec<test::TestDescAndFn>) | |
131 | -> io::Result<()> { | |
132 | // Ignore directories that contain a file | |
133 | // `compiletest-ignore-dir`. | |
134 | for file in try!(fs::read_dir(dir)) { | |
135 | let file = try!(file); | |
136 | let name = file.file_name(); | |
137 | if name == *"compiletest-ignore-dir" { | |
138 | return Ok(()); | |
139 | } | |
140 | if name == *"Makefile" && config.mode == Mode::RunMake { | |
141 | let paths = TestPaths { | |
142 | file: dir.to_path_buf(), | |
143 | base: base.to_path_buf(), | |
144 | relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), | |
145 | }; | |
146 | tests.push(make_test(config, &paths)); | |
147 | return Ok(()) | |
148 | } | |
149 | } | |
150 | ||
151 | // If we find a test foo/bar.rs, we have to build the | |
152 | // output directory `$build/foo` so we can write | |
153 | // `$build/foo/bar` into it. We do this *now* in this | |
154 | // sequential loop because otherwise, if we do it in the | |
155 | // tests themselves, they race for the privilege of | |
156 | // creating the directories and sometimes fail randomly. | |
157 | let build_dir = config.build_base.join(&relative_dir_path); | |
158 | fs::create_dir_all(&build_dir).unwrap(); | |
159 | ||
160 | // Add each `.rs` file as a test, and recurse further on any | |
161 | // subdirectories we find, except for `aux` directories. | |
162 | let dirs = try!(fs::read_dir(dir)); | |
163 | for file in dirs { | |
164 | let file = try!(file); | |
165 | let file_path = file.path(); | |
166 | let file_name = file.file_name(); | |
167 | if is_test(&file_name) { | |
168 | debug!("found test file: {:?}", file_path.display()); | |
169 | // output directory `$build/foo` so we can write | |
170 | // `$build/foo/bar` into it. We do this *now* in this | |
171 | // sequential loop because otherwise, if we do it in the | |
172 | // tests themselves, they race for the privilege of | |
173 | // creating the directories and sometimes fail randomly. | |
174 | let build_dir = config.build_base.join(&relative_dir_path); | |
175 | fs::create_dir_all(&build_dir).unwrap(); | |
176 | ||
177 | let paths = TestPaths { | |
178 | file: file_path, | |
179 | base: base.to_path_buf(), | |
180 | relative_dir: relative_dir_path.to_path_buf(), | |
181 | }; | |
182 | tests.push(make_test(config, &paths)) | |
183 | } else if file_path.is_dir() { | |
184 | let relative_file_path = relative_dir_path.join(file.file_name()); | |
185 | if &file_name == "auxiliary" { | |
186 | // `aux` directories contain other crates used for | |
187 | // cross-crate tests. Don't search them for tests, but | |
188 | // do create a directory in the build dir for them, | |
189 | // since we will dump intermediate output in there | |
190 | // sometimes. | |
191 | let build_dir = config.build_base.join(&relative_file_path); | |
192 | fs::create_dir_all(&build_dir).unwrap(); | |
193 | } else { | |
194 | debug!("found directory: {:?}", file_path.display()); | |
195 | try!(collect_tests_from_dir(config, | |
196 | base, | |
197 | &file_path, | |
198 | &relative_file_path, | |
199 | tests)); | |
200 | } | |
201 | } else { | |
202 | debug!("found other file/directory: {:?}", file_path.display()); | |
203 | } | |
204 | } | |
205 | Ok(()) | |
206 | } | |
207 | ||
208 | pub fn is_test(file_name: &OsString) -> bool { | |
209 | let file_name = file_name.to_str().unwrap(); | |
210 | ||
211 | if !file_name.ends_with(".rs") { | |
212 | return false; | |
213 | } | |
214 | ||
215 | // `.`, `#`, and `~` are common temp-file prefixes. | |
216 | let invalid_prefixes = &[".", "#", "~"]; | |
217 | !invalid_prefixes.iter().any(|p| file_name.starts_with(p)) | |
218 | } | |
219 | ||
220 | pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn { | |
221 | let early_props = EarlyProps::from_file(config, &testpaths.file); | |
222 | ||
223 | // The `should-fail` annotation doesn't apply to pretty tests, | |
224 | // since we run the pretty printer across all tests by default. | |
225 | // If desired, we could add a `should-fail-pretty` annotation. | |
226 | let should_panic = match config.mode { | |
227 | Pretty => test::ShouldPanic::No, | |
228 | _ => if early_props.should_fail { | |
229 | test::ShouldPanic::Yes | |
230 | } else { | |
231 | test::ShouldPanic::No | |
232 | } | |
233 | }; | |
234 | ||
235 | test::TestDescAndFn { | |
236 | desc: test::TestDesc { | |
237 | name: make_test_name(config, testpaths), | |
238 | ignore: early_props.ignore, | |
239 | should_panic: should_panic, | |
240 | allow_fail: false, | |
241 | }, | |
242 | testfn: make_test_closure(config, testpaths), | |
243 | } | |
244 | } | |
245 | ||
246 | fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf { | |
247 | let stamp_name = format!("{}-{}.stamp", | |
248 | testpaths.file.file_name().unwrap() | |
249 | .to_str().unwrap(), | |
250 | config.stage_id); | |
251 | config.build_base.canonicalize() | |
252 | .unwrap_or_else(|_| config.build_base.clone()) | |
253 | .join(stamp_name) | |
254 | } | |
255 | ||
256 | pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName { | |
257 | // Convert a complete path to something like | |
258 | // | |
259 | // run-pass/foo/bar/baz.rs | |
260 | let path = | |
261 | PathBuf::from(config.src_base.file_name().unwrap()) | |
262 | .join(&testpaths.relative_dir) | |
263 | .join(&testpaths.file.file_name().unwrap()); | |
264 | test::DynTestName(format!("[{}] {}", config.mode, path.display())) | |
265 | } | |
266 | ||
267 | pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn { | |
268 | let config = config.clone(); | |
269 | let testpaths = testpaths.clone(); | |
2c00a5a8 | 270 | test::DynTestFn(Box::new(move || { |
abe05a73 XL |
271 | runtest::run(config, &testpaths) |
272 | })) | |
273 | } | |
274 | ||
275 | fn extract_gdb_version(full_version_line: &str) -> Option<u32> { | |
276 | let full_version_line = full_version_line.trim(); | |
277 | ||
278 | // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both | |
279 | // of the ? sections being optional | |
280 | ||
281 | // We will parse up to 3 digits for minor and patch, ignoring the date | |
282 | // We limit major to 1 digit, otherwise, on openSUSE, we parse the openSUSE version | |
283 | ||
284 | // don't start parsing in the middle of a number | |
285 | let mut prev_was_digit = false; | |
286 | for (pos, c) in full_version_line.char_indices() { | |
287 | if prev_was_digit || !c.is_digit(10) { | |
288 | prev_was_digit = c.is_digit(10); | |
289 | continue | |
290 | } | |
291 | ||
292 | prev_was_digit = true; | |
293 | ||
294 | let line = &full_version_line[pos..]; | |
295 | ||
296 | let next_split = match line.find(|c: char| !c.is_digit(10)) { | |
297 | Some(idx) => idx, | |
298 | None => continue, // no minor version | |
299 | }; | |
300 | ||
301 | if line.as_bytes()[next_split] != b'.' { | |
302 | continue; // no minor version | |
303 | } | |
304 | ||
305 | let major = &line[..next_split]; | |
306 | let line = &line[next_split + 1..]; | |
307 | ||
308 | let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) { | |
309 | Some(idx) => if line.as_bytes()[idx] == b'.' { | |
310 | let patch = &line[idx + 1..]; | |
311 | ||
312 | let patch_len = patch.find(|c: char| !c.is_digit(10)) | |
313 | .unwrap_or_else(|| patch.len()); | |
314 | let patch = &patch[..patch_len]; | |
315 | let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) }; | |
316 | ||
317 | (&line[..idx], patch) | |
318 | } else { | |
319 | (&line[..idx], None) | |
320 | }, | |
321 | None => (line, None), | |
322 | }; | |
323 | ||
324 | if major.len() != 1 || minor.is_empty() { | |
325 | continue; | |
326 | } | |
327 | ||
328 | let major: u32 = major.parse().unwrap(); | |
329 | let minor: u32 = minor.parse().unwrap(); | |
330 | let patch: u32 = patch.unwrap_or("0").parse().unwrap(); | |
331 | ||
332 | return Some(((major * 1000) + minor) * 1000 + patch); | |
333 | } | |
334 | ||
335 | None | |
336 | } | |
337 | ||
338 | #[allow(dead_code)] | |
339 | fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> { | |
340 | // Extract the major LLDB version from the given version string. | |
341 | // LLDB version strings are different for Apple and non-Apple platforms. | |
342 | // At the moment, this function only supports the Apple variant, which looks | |
343 | // like this: | |
344 | // | |
345 | // LLDB-179.5 (older versions) | |
346 | // lldb-300.2.51 (new versions) | |
347 | // | |
348 | // We are only interested in the major version number, so this function | |
349 | // will return `Some("179")` and `Some("300")` respectively. | |
350 | ||
351 | if let Some(ref full_version_line) = full_version_line { | |
352 | if !full_version_line.trim().is_empty() { | |
353 | let full_version_line = full_version_line.trim(); | |
354 | ||
355 | for (pos, l) in full_version_line.char_indices() { | |
356 | if l != 'l' && l != 'L' { continue } | |
357 | if pos + 5 >= full_version_line.len() { continue } | |
358 | let l = full_version_line[pos + 1..].chars().next().unwrap(); | |
359 | if l != 'l' && l != 'L' { continue } | |
360 | let d = full_version_line[pos + 2..].chars().next().unwrap(); | |
361 | if d != 'd' && d != 'D' { continue } | |
362 | let b = full_version_line[pos + 3..].chars().next().unwrap(); | |
363 | if b != 'b' && b != 'B' { continue } | |
364 | let dash = full_version_line[pos + 4..].chars().next().unwrap(); | |
365 | if dash != '-' { continue } | |
366 | ||
367 | let vers = full_version_line[pos + 5..].chars().take_while(|c| { | |
368 | c.is_digit(10) | |
369 | }).collect::<String>(); | |
370 | if !vers.is_empty() { return Some(vers) } | |
371 | } | |
372 | println!("Could not extract LLDB version from line '{}'", | |
373 | full_version_line); | |
374 | } | |
375 | } | |
376 | None | |
377 | } | |
378 | ||
379 | #[allow(dead_code)] | |
380 | fn is_blacklisted_lldb_version(version: &str) -> bool { | |
381 | version == "350" | |
382 | } |