#![crate_type = "lib"]
#![feature(rustc_private)]
#![feature(test)]
#![deny(unused_imports)]
extern crate test;
extern crate rustc;
extern crate rustc_serialize;
#[cfg(feature = "tmp")] extern crate tempdir;
#[macro_use]
extern crate log;
use std::env;
use std::ffi::OsString;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use common::Mode;
use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
use test::TestPaths;
use std::borrow::ToOwned;
use self::header::EarlyProps;
pub mod uidiff;
pub mod json;
pub mod procsrv;
pub mod util;
pub mod header;
pub mod runtest;
pub mod common;
pub mod errors;
pub use common::Config;
#[deprecated(since="0.2.9",
note="Use Config::default() instead. This method will be removed in version 0.3.0")]
pub fn default_config() -> Config {
Config::default()
}
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,
quiet: config.quiet,
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),
}
}
pub fn make_test_name(config: &Config, testpaths: &TestPaths) -> test::TestName {
let path =
PathBuf::from(config.mode.to_string())
.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)
}))
}
#[allow(dead_code)]
fn extract_gdb_version(full_version_line: Option<String>) -> Option<String> {
match full_version_line {
Some(ref full_version_line)
if !full_version_line.trim().is_empty() => {
let full_version_line = full_version_line.trim();
for (pos, c) in full_version_line.char_indices() {
if !c.is_digit(10) {
continue
}
if pos + 2 >= full_version_line.len() {
continue
}
if full_version_line[pos + 1..].chars().next().unwrap() != '.' {
continue
}
if !full_version_line[pos + 2..].chars().next().unwrap().is_digit(10) {
continue
}
if pos > 0 && full_version_line[..pos].chars().next_back()
.unwrap().is_digit(10) {
continue
}
let mut end = pos + 3;
while end < full_version_line.len() &&
full_version_line[end..].chars().next()
.unwrap().is_digit(10) {
end += 1;
}
return Some(full_version_line[pos..end].to_owned());
}
println!("Could not extract GDB version from line '{}'",
full_version_line);
None
},
_ => 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"
}