rust/tests/compiletest.rs

214 lines
6.8 KiB
Rust
Raw Normal View History

#![feature(slice_concat_ext, custom_test_frameworks)]
#![test_runner(test_runner)]
2016-04-22 03:34:14 -05:00
2017-07-20 09:05:14 -05:00
use std::slice::SliceConcatExt;
use std::path::{PathBuf, Path};
use std::env;
2016-04-22 03:34:14 -05:00
use compiletest_rs as compiletest;
use colored::*;
fn miri_path() -> PathBuf {
if rustc_test_suite().is_some() {
PathBuf::from(option_env!("MIRI_PATH").unwrap())
} else {
PathBuf::from(concat!("target/", env!("PROFILE"), "/miri"))
}
}
fn rustc_test_suite() -> Option<PathBuf> {
option_env!("RUSTC_TEST_SUITE").map(PathBuf::from)
}
fn rustc_lib_path() -> PathBuf {
option_env!("RUSTC_LIB_PATH").unwrap().into()
}
2017-08-02 11:28:12 -05:00
fn have_fullmir() -> bool {
// We assume we have full MIR when MIRI_SYSROOT is set or when we are in rustc
std::env::var("MIRI_SYSROOT").is_ok() || rustc_test_suite().is_some()
}
fn mk_config(mode: &str) -> compiletest::common::ConfigWithTemp {
let mut config = compiletest::Config::default().tempdir();
config.mode = mode.parse().expect("Invalid mode");
config.rustc_path = miri_path();
if rustc_test_suite().is_some() {
config.run_lib_path = rustc_lib_path();
config.compile_lib_path = rustc_lib_path();
}
config.filter = env::args().nth(1);
config
}
fn compile_fail(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool, opt: bool) {
if need_fullmir && !have_fullmir() {
eprintln!("{}\n", format!(
"## Skipping compile-fail tests in {} against miri for target {} due to missing mir",
path,
target
2018-07-12 04:26:09 -05:00
).yellow().bold());
return;
}
let opt_str = if opt { " with optimizations" } else { "" };
2018-07-12 04:26:09 -05:00
eprintln!("{}", format!(
"## Running compile-fail tests in {} against miri for target {}{}",
path,
target,
opt_str
2018-07-12 04:26:09 -05:00
).green().bold());
let mut flags = Vec::new();
flags.push(format!("--sysroot {}", sysroot.display()));
flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
2018-11-20 05:41:34 -06:00
flags.push("--edition 2018".to_owned());
if opt {
// Optimizing too aggressivley makes UB detection harder, but test at least
// the default value.
// FIXME: Opt level 3 ICEs during stack trace generation.
flags.push("-Zmir-opt-level=1".to_owned());
}
let mut config = mk_config("compile-fail");
config.src_base = PathBuf::from(path);
config.target = target.to_owned();
config.host = host.to_owned();
config.target_rustcflags = Some(flags.join(" "));
compiletest::run_tests(&config);
}
fn miri_pass(sysroot: &Path, path: &str, target: &str, host: &str, need_fullmir: bool, opt: bool) {
if need_fullmir && !have_fullmir() {
eprintln!("{}\n", format!(
"## Skipping run-pass tests in {} against miri for target {} due to missing mir",
path,
target
2018-07-12 04:26:09 -05:00
).yellow().bold());
return;
}
let opt_str = if opt { " with optimizations" } else { "" };
2018-07-12 04:26:09 -05:00
eprintln!("{}", format!(
"## Running run-pass tests in {} against miri for target {}{}",
path,
target,
opt_str
2018-07-12 04:26:09 -05:00
).green().bold());
2017-07-20 09:05:14 -05:00
let mut flags = Vec::new();
flags.push(format!("--sysroot {}", sysroot.display()));
flags.push("-Dwarnings -Dunused".to_owned()); // overwrite the -Aunused in compiletest-rs
2018-11-20 05:41:34 -06:00
flags.push("--edition 2018".to_owned());
2017-07-20 09:05:14 -05:00
if opt {
// FIXME: We use opt level 1 because MIR inlining defeats the validation
// whitelist.
flags.push("-Zmir-opt-level=1".to_owned());
}
if !have_fullmir() {
2018-12-02 04:14:24 -06:00
// FIXME: Validation relies on the EscapeToRaw statements being emitted
flags.push("-Zmiri-disable-validation".to_owned());
}
let mut config = mk_config("ui");
config.src_base = PathBuf::from(path);
config.target = target.to_owned();
config.host = host.to_owned();
2017-07-20 09:05:14 -05:00
config.target_rustcflags = Some(flags.join(" "));
compiletest::run_tests(&config);
}
2016-12-17 02:54:37 -06:00
fn is_target_dir<P: Into<PathBuf>>(path: P) -> bool {
let mut path = path.into();
path.push("lib");
path.metadata().map(|m| m.is_dir()).unwrap_or(false)
}
fn target_has_std<P: Into<PathBuf>>(path: P) -> bool {
let mut path = path.into();
path.push("lib");
std::fs::read_dir(path)
.expect("invalid target")
.map(|entry| entry.unwrap())
.filter(|entry| entry.file_type().unwrap().is_file())
.filter_map(|entry| entry.file_name().into_string().ok())
.any(|file_name| file_name.starts_with("libstd") && file_name.ends_with(".rlib"))
}
fn for_all_targets<F: FnMut(String)>(sysroot: &Path, f: F) {
2016-12-20 02:58:41 -06:00
let target_dir = sysroot.join("lib").join("rustlib");
let mut targets = std::fs::read_dir(target_dir)
.expect("invalid sysroot")
.map(|entry| entry.unwrap())
.filter(|entry| is_target_dir(entry.path()))
.filter(|entry| target_has_std(entry.path()))
.map(|entry| entry.file_name().into_string().unwrap())
.peekable();
if targets.peek().is_none() {
panic!("No valid targets found");
2016-05-31 05:05:25 -05:00
}
targets.for_each(f);
2016-04-22 03:34:14 -05:00
}
2017-07-19 14:52:20 -05:00
fn get_sysroot() -> PathBuf {
let sysroot = std::env::var("MIRI_SYSROOT").unwrap_or_else(|_| {
let sysroot = std::process::Command::new("rustc")
.arg("--print")
.arg("sysroot")
.output()
.expect("rustc not found")
.stdout;
String::from_utf8(sysroot).expect("sysroot is not utf8")
});
2017-07-19 14:52:20 -05:00
PathBuf::from(sysroot.trim())
}
fn get_host() -> String {
2017-09-11 04:01:26 -05:00
let rustc = rustc_test_suite().unwrap_or(PathBuf::from("rustc"));
let host = std::process::Command::new(rustc)
2016-12-20 02:58:41 -06:00
.arg("-vV")
.output()
.expect("rustc not found for -vV")
.stdout;
let host = std::str::from_utf8(&host).expect("sysroot is not utf8");
let host = host.split("\nhost: ").nth(1).expect(
"no host: part in rustc -vV",
);
let host = host.split('\n').next().expect("no \n after host");
2017-07-19 14:52:20 -05:00
String::from(host)
}
2017-02-07 05:32:39 -06:00
fn run_pass_miri(opt: bool) {
2017-07-19 14:52:20 -05:00
let sysroot = get_sysroot();
let host = get_host();
for_all_targets(&sysroot, |target| {
miri_pass(&sysroot, "tests/run-pass", &target, &host, false, opt);
});
miri_pass(&sysroot, "tests/run-pass-fullmir", &host, &host, true, opt);
}
fn compile_fail_miri(opt: bool) {
2017-07-19 14:52:20 -05:00
let sysroot = get_sysroot();
let host = get_host();
2018-03-18 07:18:41 -05:00
// FIXME: run tests for other targets, too
compile_fail(&sysroot, "tests/compile-fail", &host, &host, false, opt);
compile_fail(&sysroot, "tests/compile-fail-fullmir", &host, &host, true, opt);
2017-07-19 14:52:20 -05:00
}
fn test_runner(_tests: &[&()]) {
// We put everything into a single test to avoid the parallelism `cargo test`
// introduces. We still get parallelism within our tests because `compiletest`
// uses `libtest` which runs jobs in parallel.
run_pass_miri(false);
2018-10-16 10:57:03 -05:00
run_pass_miri(true);
compile_fail_miri(false);
compile_fail_miri(true);
}