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