#![crate_type = "lib"]
#![feature(rustc_private)]
#![feature(test)]
#![deny(unused_imports)]
extern crate test;
extern crate rustc;
#[macro_use]
extern crate log;
use std::env;
use std::fs;
use std::io;
use std::path::{Path, PathBuf};
use common::{Config, Mode};
use common::{Pretty, DebugInfoGdb, DebugInfoLldb};
use test::TestPaths;
use std::borrow::ToOwned;
use rustc::session::config::host_triple;
pub mod procsrv;
pub mod util;
pub mod header;
pub mod runtest;
pub mod common;
pub mod errors;
pub fn default_config() -> Config {
Config {
compile_lib_path: "".to_owned(),
run_lib_path: "".to_owned(),
rustc_path: PathBuf::from("rustc"),
rustdoc_path: PathBuf::from("rustdoc-path"),
python: "python".to_owned(),
valgrind_path: None,
force_valgrind: false,
llvm_bin_path: None,
src_base: PathBuf::from("tests/run-pass"),
build_base: env::temp_dir(),
aux_base: PathBuf::from("aux-base"),
stage_id: "stage3".to_owned(),
mode: Mode::RunPass,
run_ignored: false,
filter: None,
logfile: None,
runtool: None,
host_rustcflags: None,
target_rustcflags: None,
target: host_triple().to_owned(),
host: "(none)".to_owned(),
gdb_version: None,
lldb_version: None,
lldb_python_dir: None,
android_cross_path: PathBuf::from("android-cross-path"),
adb_path: "adb-path".to_owned(),
adb_test_dir: "adb-test-dir/target".to_owned(),
adb_device_status: false,
verbose: false,
quiet: false,
}
}
pub fn run_tests(config: &Config) {
if config.target.contains("android") {
if config.mode == DebugInfoGdb {
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(),
run_ignored: config.run_ignored,
quiet: config.quiet,
logfile: config.logfile.clone(),
run_tests: true,
bench_benchmarks: true,
nocapture: env::var("RUST_TEST_NOCAPTURE").is_ok(),
color: test::AutoColor,
}
}
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);
if file.file_name() == *"compiletest-ignore-dir" {
return Ok(());
}
}
let dirs = try!(fs::read_dir(dir));
for file in dirs {
let file = try!(file);
let file_path = file.path();
debug!("inspecting file {:?}", file_path.display());
if is_test(config, &file_path) {
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());
try!(collect_tests_from_dir(config,
base,
&file_path,
&relative_file_path,
tests));
}
}
Ok(())
}
pub fn is_test(config: &Config, testfile: &Path) -> bool {
let valid_extensions =
match config.mode {
Pretty => vec!(".rs".to_owned()),
_ => vec!(".rc".to_owned(), ".rs".to_owned())
};
let invalid_prefixes = vec!(".".to_owned(), "#".to_owned(), "~".to_owned());
let name = testfile.file_name().unwrap().to_str().unwrap();
let mut valid = false;
for ext in &valid_extensions {
if name.ends_with(ext) {
valid = true;
}
}
for pre in &invalid_prefixes {
if name.starts_with(pre) {
valid = false;
}
}
valid
}
pub fn make_test(config: &Config, testpaths: &TestPaths) -> test::TestDescAndFn {
let early_props = header::early_props(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,
},
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
}