#![crate_type = "lib"]
#![cfg_attr(not(feature = "norustc"), feature(rustc_private))]
#![feature(test)]
#![feature(slice_rotate)]
#![deny(unused_imports)]
#[cfg(not(feature = "norustc"))]
extern crate rustc;
#[cfg(unix)]
extern crate libc;
extern crate test;
#[cfg(feature = "tmp")] extern crate tempfile;
#[macro_use]
extern crate log;
extern crate filetime;
extern crate diff;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use common::{Mode, TestPaths};
use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
use self::header::EarlyProps;
pub mod uidiff;
pub mod util;
mod json;
pub mod header;
pub mod runtest;
pub mod common;
pub mod errors;
mod read2;
pub use common::Config;
pub fn run_tests(config: &Config) {
if config.target.contains("android") {
if let DebugInfoGdb = config.mode {
println!("{} debug-info test uses tcp 5039 port.\
please reserve it", config.target);
}
env::set_var("RUST_TEST_THREADS","1");
}
if let DebugInfoLldb = config.mode {
env::set_var("RUST_TEST_TASKS", "1");
}
let opts = test_opts(config);
let tests = make_tests(config);
env::set_var("__COMPAT_LAYER", "RunAsInvoker");
let res = test::run_tests_console(&opts, tests.into_iter().collect());
match res {
Ok(true) => {}
Ok(false) => panic!("Some tests failed"),
Err(e) => {
println!("I/O failure during tests: {:?}", e);
}
}
}
pub fn test_opts(config: &Config) -> test::TestOpts {
test::TestOpts {
filter: config.filter.clone(),
filter_exact: config.filter_exact,
run_ignored: config.run_ignored,
format: if config.quiet { test::OutputFormat::Terse } else { test::OutputFormat::Pretty },
logfile: config.logfile.clone(),
run_tests: true,
bench_benchmarks: true,
nocapture: match env::var("RUST_TEST_NOCAPTURE") {
Ok(val) => &val != "0",
Err(_) => false
},
color: test::AutoColor,
test_threads: None,
skip: vec![],
list: false,
options: test::Options::new(),
}
}
pub fn make_tests(config: &Config) -> Vec<test::TestDescAndFn> {
debug!("making tests from {:?}",
config.src_base.display());
let mut tests = Vec::new();
collect_tests_from_dir(config,
&config.src_base,
&config.src_base,
&PathBuf::new(),
&mut tests)
.unwrap();
tests
}
fn collect_tests_from_dir(config: &Config,
base: &Path,
dir: &Path,
relative_dir_path: &Path,
tests: &mut Vec<test::TestDescAndFn>)
-> io::Result<()> {
for file in try!(fs::read_dir(dir)) {
let file = try!(file);
let name = file.file_name();
if name == *"compiletest-ignore-dir" {
return Ok(());
}
if name == *"Makefile" && config.mode == Mode::RunMake {
let paths = TestPaths {
file: dir.to_path_buf(),
base: base.to_path_buf(),
relative_dir: relative_dir_path.parent().unwrap().to_path_buf(),
};
tests.push(make_test(config, &paths));
return Ok(())
}
}
let build_dir = config.build_base.join(&relative_dir_path);
fs::create_dir_all(&build_dir).unwrap();
let dirs = try!(fs::read_dir(dir));
for file in dirs {
let file = try!(file);
let file_path = file.path();
let file_name = file.file_name();
if is_test(&file_name) {
debug!("found test file: {:?}", file_path.display());
let build_dir = config.build_base.join(&relative_dir_path);
fs::create_dir_all(&build_dir).unwrap();
let paths = TestPaths {
file: file_path,
base: base.to_path_buf(),
relative_dir: relative_dir_path.to_path_buf(),
};
tests.push(make_test(config, &paths))
} else if file_path.is_dir() {
let relative_file_path = relative_dir_path.join(file.file_name());
if &file_name == "auxiliary" {
let build_dir = config.build_base.join(&relative_file_path);
fs::create_dir_all(&build_dir).unwrap();
} else {
debug!("found directory: {:?}", file_path.display());
try!(collect_tests_from_dir(config,
base,
&file_path,
&relative_file_path,
tests));
}
} else {
debug!("found other file/directory: {:?}", file_path.display());
}
}
Ok(())
}
pub fn is_test(file_name: &OsString) -> bool {
let file_name = file_name.to_str().unwrap();
if !file_name.ends_with(".rs") {
return false;
}
let invalid_prefixes = &[".", "#", "~"];
!invalid_prefixes.iter().any(|p| file_name.starts_with(p))
}
pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
let early_props = EarlyProps::from_file(config, &testpaths.file);
let should_panic = match config.mode {
Pretty => test::ShouldPanic::No,
_ => if early_props.should_fail {
test::ShouldPanic::Yes
} else {
test::ShouldPanic::No
}
};
test::TestDescAndFn {
desc: test::TestDesc {
name: make_test_name(config, testpaths),
ignore: early_props.ignore,
should_panic: should_panic,
allow_fail: false,
},
testfn: make_test_closure(config, testpaths),
}
}
fn stamp(config: &Config, testpaths: &TestPaths) -> PathBuf {
let stamp_name = format!("{}-{}.stamp",
testpaths.file.file_name().unwrap()
.to_str().unwrap(),
config.stage_id);
config.build_base.canonicalize()
.unwrap_or_else(|_| config.build_base.clone())
.join(stamp_name)
}
pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
let path =
PathBuf::from(config.src_base.file_name().unwrap())
.join(&testpaths.relative_dir)
.join(&testpaths.file.file_name().unwrap());
test::DynTestName(format!("[{}] {}", config.mode, path.display()))
}
pub fn make_test_closure(config: &Config, testpaths: &TestPaths) -> test::TestFn {
let config = config.clone();
let testpaths = testpaths.clone();
test::DynTestFn(Box::new(move || {
runtest::run(config, &testpaths)
}))
}
fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
let full_version_line = full_version_line.trim();
let mut prev_was_digit = false;
for (pos, c) in full_version_line.char_indices() {
if prev_was_digit || !c.is_digit(10) {
prev_was_digit = c.is_digit(10);
continue
}
prev_was_digit = true;
let line = &full_version_line[pos..];
let next_split = match line.find(|c: char| !c.is_digit(10)) {
Some(idx) => idx,
None => continue, };
if line.as_bytes()[next_split] != b'.' {
continue; }
let major = &line[..next_split];
let line = &line[next_split + 1..];
let (minor, patch) = match line.find(|c: char| !c.is_digit(10)) {
Some(idx) => if line.as_bytes()[idx] == b'.' {
let patch = &line[idx + 1..];
let patch_len = patch.find(|c: char| !c.is_digit(10))
.unwrap_or_else(|| patch.len());
let patch = &patch[..patch_len];
let patch = if patch_len > 3 || patch_len == 0 { None } else { Some(patch) };
(&line[..idx], patch)
} else {
(&line[..idx], None)
},
None => (line, None),
};
if major.len() != 1 || minor.is_empty() {
continue;
}
let major: u32 = major.parse().unwrap();
let minor: u32 = minor.parse().unwrap();
let patch: u32 = patch.unwrap_or("0").parse().unwrap();
return Some(((major * 1000) + minor) * 1000 + patch);
}
None
}
#[allow(dead_code)]
fn extract_lldb_version(full_version_line: Option<String>) -> Option<String> {
if let Some(ref full_version_line) = full_version_line {
if !full_version_line.trim().is_empty() {
let full_version_line = full_version_line.trim();
for (pos, l) in full_version_line.char_indices() {
if l != 'l' && l != 'L' { continue }
if pos + 5 >= full_version_line.len() { continue }
let l = full_version_line[pos + 1..].chars().next().unwrap();
if l != 'l' && l != 'L' { continue }
let d = full_version_line[pos + 2..].chars().next().unwrap();
if d != 'd' && d != 'D' { continue }
let b = full_version_line[pos + 3..].chars().next().unwrap();
if b != 'b' && b != 'B' { continue }
let dash = full_version_line[pos + 4..].chars().next().unwrap();
if dash != '-' { continue }
let vers = full_version_line[pos + 5..].chars().take_while(|c| {
c.is_digit(10)
}).collect::<String>();
if !vers.is_empty() { return Some(vers) }
}
println!("Could not extract LLDB version from line '{}'",
full_version_line);
}
}
None
}
#[allow(dead_code)]
fn is_blacklisted_lldb_version(version: &str) -> bool {
version == "350"
}