1#![crate_type = "lib"]
12#![cfg_attr(feature = "rustc", feature(rustc_private))]
13#![cfg_attr(feature = "rustc", feature(test))]
14#![deny(unused_imports)]
15
16#[cfg(feature = "rustc")]
17extern crate rustc_driver;
18#[cfg(feature = "rustc")]
19extern crate rustc_session;
20
21#[cfg(unix)]
22extern crate libc;
23#[cfg(feature = "rustc")]
24extern crate test;
25#[cfg(not(feature = "rustc"))]
26extern crate tester as test;
27
28#[cfg(feature = "tmp")]
29extern crate tempfile;
30
31#[macro_use]
32extern crate log;
33extern crate diff;
34extern crate filetime;
35extern crate regex;
36extern crate serde_json;
37#[macro_use]
38extern crate serde_derive;
39extern crate rustfix;
40
41use crate::common::{DebugInfoGdb, DebugInfoLldb, Pretty};
42use crate::common::{Mode, TestPaths};
43use std::env;
44use std::ffi::OsString;
45use std::fs;
46use std::io;
47use std::path::{Path, PathBuf};
48
49use self::header::EarlyProps;
50
51pub mod common;
52pub mod errors;
53pub mod header;
54mod json;
55mod read2;
56pub mod runtest;
57pub mod uidiff;
58pub mod util;
59
60pub use crate::common::Config;
61
62pub fn run_tests(config: &Config) {
63 if config.target.contains("android") {
64 if let DebugInfoGdb = config.mode {
65 println!(
66 "{} debug-info test uses tcp 5039 port.\
67 please reserve it",
68 config.target
69 );
70 }
71
72 env::set_var("RUST_TEST_THREADS", "1");
76 }
77
78 if let DebugInfoLldb = config.mode {
79 env::set_var("RUST_TEST_TASKS", "1");
83 }
84
85 if config.rustfix_coverage {
89 let mut coverage_file_path = config.build_base.clone();
90 coverage_file_path.push("rustfix_missing_coverage.txt");
91 if coverage_file_path.exists() {
92 if let Err(e) = fs::remove_file(&coverage_file_path) {
93 panic!(
94 "Could not delete {} due to {}",
95 coverage_file_path.display(),
96 e
97 )
98 }
99 }
100 }
101 let opts = test_opts(config);
102 let tests = make_tests(config);
103 env::set_var("__COMPAT_LAYER", "RunAsInvoker");
110 let res = test::run_tests_console(&opts, tests.into_iter().collect());
111 match res {
112 Ok(true) => {}
113 Ok(false) => panic!("Some tests failed"),
114 Err(e) => {
115 println!("I/O failure during tests: {:?}", e);
116 }
117 }
118}
119
120pub fn test_opts(config: &Config) -> test::TestOpts {
121 test::TestOpts {
122 filters: config.filters.clone(),
123 filter_exact: config.filter_exact,
124 exclude_should_panic: false,
125 force_run_in_process: false,
126 run_ignored: if config.run_ignored {
127 test::RunIgnored::Yes
128 } else {
129 test::RunIgnored::No
130 },
131 format: if config.quiet {
132 test::OutputFormat::Terse
133 } else {
134 test::OutputFormat::Pretty
135 },
136 logfile: config.logfile.clone(),
137 run_tests: true,
138 bench_benchmarks: true,
139 nocapture: match env::var("RUST_TEST_NOCAPTURE") {
140 Ok(val) => &val != "0",
141 Err(_) => false,
142 },
143 color: test::AutoColor,
144 test_threads: None,
145 skip: vec![],
146 list: false,
147 options: test::Options::new(),
148 time_options: None,
149 #[cfg(feature = "rustc")]
150 shuffle: false,
151 #[cfg(feature = "rustc")]
152 shuffle_seed: None,
153 #[cfg(feature = "rustc")]
154 fail_fast: false,
155 }
156}
157
158pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
159 debug!("making tests from {:?}", config.src_base.display());
160 let mut tests = Vec::new();
161 collect_tests_from_dir(
162 config,
163 &config.src_base,
164 &config.src_base,
165 &PathBuf::new(),
166 &mut tests,
167 )
168 .unwrap();
169 tests
170}
171
172fn collect_tests_from_dir(
173 config: &Config,
174 base: &Path,
175 dir: &Path,
176 relative_dir_path: &Path,
177 tests: &mut Vec<test::TestDescAndFn>,
178) -> io::Result<()> {
179 for file in fs::read_dir(dir)? {
182 let file = file?;
183 let name = file.file_name();
184 if name == *"compiletest-ignore-dir" {
185 return Ok(());
186 }
187 if name == *"Makefile" && config.mode == Mode::RunMake {
188 let paths = TestPaths {
189 file: dir.to_path_buf(),
190 base: base.to_path_buf(),
191 relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
192 };
193 tests.push(make_test(config, &paths));
194 return Ok(());
195 }
196 }
197
198 let build_dir = config.build_base.join(&relative_dir_path);
205 fs::create_dir_all(&build_dir).unwrap();
206
207 let dirs = fs::read_dir(dir)?;
210 for file in dirs {
211 let file = file?;
212 let file_path = file.path();
213 let file_name = file.file_name();
214 if is_test(&file_name) {
215 debug!("found test file: {:?}", file_path.display());
216 let build_dir = config.build_base.join(&relative_dir_path);
222 fs::create_dir_all(&build_dir).unwrap();
223
224 let paths = TestPaths {
225 file: file_path,
226 base: base.to_path_buf(),
227 relative_dir: relative_dir_path.to_path_buf(),
228 };
229 tests.push(make_test(config, &paths))
230 } else if file_path.is_dir() {
231 let relative_file_path = relative_dir_path.join(file.file_name());
232 if &file_name == "auxiliary" {
233 let build_dir = config.build_base.join(&relative_file_path);
239 fs::create_dir_all(&build_dir).unwrap();
240 } else {
241 debug!("found directory: {:?}", file_path.display());
242 collect_tests_from_dir(config, base, &file_path, &relative_file_path, tests)?;
243 }
244 } else {
245 debug!("found other file/directory: {:?}", file_path.display());
246 }
247 }
248 Ok(())
249}
250
251pub fn is_test(file_name: &OsString) -> bool {
252 let file_name = file_name.to_str().unwrap();
253
254 if !file_name.ends_with(".rs") {
255 return false;
256 }
257
258 let invalid_prefixes = &[".", "#", "~"];
260 !invalid_prefixes.iter().any(|p| file_name.starts_with(p))
261}
262
263pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
264 let early_props = EarlyProps::from_file(config, &testpaths.file);
265
266 let should_panic = match config.mode {
270 Pretty => test::ShouldPanic::No,
271 _ => {
272 if early_props.should_fail {
273 test::ShouldPanic::Yes
274 } else {
275 test::ShouldPanic::No
276 }
277 }
278 };
279
280 test::TestDescAndFn {
281 desc: test::TestDesc {
282 name: make_test_name(config, testpaths),
283 ignore: early_props.ignore,
284 should_panic: should_panic,
285 #[cfg(not(feature = "rustc"))]
286 allow_fail: false,
287 #[cfg(feature = "rustc")]
288 compile_fail: false,
289 #[cfg(feature = "rustc")]
290 no_run: false,
291 test_type: test::TestType::IntegrationTest,
292 #[cfg(feature = "rustc")]
293 ignore_message: None,
294 #[cfg(feature = "rustc")]
295 source_file: "",
296 #[cfg(feature = "rustc")]
297 start_line: 0,
298 #[cfg(feature = "rustc")]
299 start_col: 0,
300 #[cfg(feature = "rustc")]
301 end_line: 0,
302 #[cfg(feature = "rustc")]
303 end_col: 0,
304 },
305 testfn: make_test_closure(config, testpaths),
306 }
307}
308
309fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
310 let stamp_name = format!(
311 "{}-{}.stamp",
312 testpaths.file.file_name().unwrap().to_str().unwrap(),
313 config.stage_id
314 );
315 config
316 .build_base
317 .canonicalize()
318 .unwrap_or_else(|_| config.build_base.clone())
319 .join(stamp_name)
320}
321
322pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
323 let path = PathBuf::from(config.src_base.file_name().unwrap())
327 .join(&testpaths.relative_dir)
328 .join(&testpaths.file.file_name().unwrap());
329 test::DynTestName(format!("[{}] {}", config.mode, path.display()))
330}
331
332pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
333 let config = config.clone();
334 let testpaths = testpaths.clone();
335 test::DynTestFn(Box::new(move || {
336 let result = runtest::run(config, &testpaths);
337 #[cfg(feature = "rustc")]
338 let result = Ok(result);
339 result
340 }))
341}
342
343fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
344 let full_version_line = full_version_line.trim();
345
346 let mut prev_was_digit = false;
354 for (pos, c) in full_version_line.char_indices() {
355 if prev_was_digit || !c.is_digit(10) {
356 prev_was_digit = c.is_digit(10);
357 continue;
358 }
359
360 prev_was_digit = true;
361
362 let line = &full_version_line[pos..];
363
364 let next_split = match line.find(|c: char| !c.is_digit(10)) {
365 Some(idx) => idx,
366 None => continue, };
368
369 if line.as_bytes()[next_split] != b'.' {
370 continue; }
372
373 let major = &line[..next_split];
374 let line = &line[next_split + 1..];
375
376 let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
377 Some(idx) => {
378 if line.as_bytes()[idx] == b'.' {
379 let patch = &line[idx + 1..];
380
381 let patch_len = patch
382 .find(|c: char| !c.is_digit(10))
383 .unwrap_or_else(|| patch.len());
384 let patch = &patch[..patch_len];
385 let patch = if patch_len > 3 || patch_len == 0 {
386 None
387 } else {
388 Some(patch)
389 };
390
391 (&line[..idx], patch)
392 } else {
393 (&line[..idx], None)
394 }
395 }
396 None => (line, None),
397 };
398
399 if major.len() != 1 || minor.is_empty() {
400 continue;
401 }
402
403 let major: u32 = major.parse().unwrap();
404 let minor: u32 = minor.parse().unwrap();
405 let patch: u32 = patch.unwrap_or("0").parse().unwrap();
406
407 return Some(((major * 1000) + minor) * 1000 + patch);
408 }
409
410 None
411}
412
413#[allow(dead_code)]
414fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
415 if let Some(ref full_version_line) = full_version_line {
427 if !full_version_line.trim().is_empty() {
428 let full_version_line = full_version_line.trim();
429
430 for (pos, l) in full_version_line.char_indices() {
431 if l != 'l' && l != 'L' {
432 continue;
433 }
434 if pos + 5 >= full_version_line.len() {
435 continue;
436 }
437 let l = full_version_line[pos + 1..].chars().next().unwrap();
438 if l != 'l' && l != 'L' {
439 continue;
440 }
441 let d = full_version_line[pos + 2..].chars().next().unwrap();
442 if d != 'd' && d != 'D' {
443 continue;
444 }
445 let b = full_version_line[pos + 3..].chars().next().unwrap();
446 if b != 'b' && b != 'B' {
447 continue;
448 }
449 let dash = full_version_line[pos + 4..].chars().next().unwrap();
450 if dash != '-' {
451 continue;
452 }
453
454 let vers = full_version_line[pos + 5..]
455 .chars()
456 .take_while(|c| c.is_digit(10))
457 .collect::<String>();
458 if !vers.is_empty() {
459 return Some(vers);
460 }
461 }
462 println!(
463 "Could not extract LLDB version from line '{}'",
464 full_version_line
465 );
466 }
467 }
468 None
469}
470
471#[allow(dead_code)]
472fn is_blacklisted_lldb_version(version: &str) -> bool {
473 version == "350"
474}