3 use std::path::{Path, PathBuf};
4 use std::{env, process::Command};
5 use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling};
7 fn miri_path() -> PathBuf {
8 PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri")))
11 // Build the shared object file for testing external C function calls.
12 fn build_so_for_c_ffi_tests() -> PathBuf {
13 let cc = option_env!("CC").unwrap_or("cc");
14 // Target directory that we can write to.
15 let so_target_dir = Path::new(&env::var_os("CARGO_TARGET_DIR").unwrap()).join("miri-extern-so");
16 // Create the directory if it does not already exist.
17 std::fs::create_dir_all(&so_target_dir)
18 .expect("Failed to create directory for shared object file");
19 let so_file_path = so_target_dir.join("libtestlib.so");
20 let cc_output = Command::new(cc)
24 so_file_path.to_str().unwrap(),
25 "tests/extern-so/test.c",
26 // Only add the functions specified in libcode.version to the shared object file.
27 // This is to avoid automatically adding `malloc`, etc.
28 // Source: https://anadoxin.org/blog/control-over-symbol-exports-in-gcc.html/
30 "-Wl,--version-script=tests/extern-so/libcode.version",
33 .expect("failed to generate shared object file for testing external C function calls");
34 if !cc_output.status.success() {
35 panic!("error in generating shared object file for testing external C function calls");
43 target: Option<String>,
44 with_dependencies: bool,
46 let mut config = Config {
48 stderr_filters: STDERR.clone(),
49 stdout_filters: STDOUT.clone(),
50 root_dir: PathBuf::from(path),
57 let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some();
59 // Add some flags we always want.
60 config.args.push("--edition".into());
61 config.args.push("2018".into());
62 if in_rustc_test_suite {
63 // Less aggressive warnings to make the rustc toolstate management less painful.
64 // (We often get warnings when e.g. a feature gets stabilized or some lint gets added/improved.)
65 config.args.push("-Astable-features".into());
66 config.args.push("-Aunused".into());
68 config.args.push("-Dwarnings".into());
69 config.args.push("-Dunused".into());
71 if let Ok(extra_flags) = env::var("MIRIFLAGS") {
72 for flag in extra_flags.split_whitespace() {
73 config.args.push(flag.into());
76 config.args.push("-Zui-testing".into());
77 if let Some(target) = &config.target {
78 config.args.push("--target".into());
79 config.args.push(target.into());
82 // If we're on linux, and we're testing the extern-so functionality,
83 // then build the shared object file for testing external C function calls
84 // and push the relevant compiler flag.
85 if cfg!(target_os = "linux") && path.starts_with("tests/extern-so/") {
86 let so_file_path = build_so_for_c_ffi_tests();
87 let mut flag = std::ffi::OsString::from("-Zmiri-extern-so-file=");
88 flag.push(so_file_path.into_os_string());
89 config.args.push(flag);
92 let skip_ui_checks = env::var_os("MIRI_SKIP_UI_CHECKS").is_some();
94 config.output_conflict_handling = match (env::var_os("MIRI_BLESS").is_some(), skip_ui_checks) {
95 (false, false) => OutputConflictHandling::Error,
96 (true, false) => OutputConflictHandling::Bless,
97 (false, true) => OutputConflictHandling::Ignore,
98 (true, true) => panic!("cannot use MIRI_BLESS and MIRI_SKIP_UI_CHECKS at the same time"),
101 // Handle command-line arguments.
102 config.path_filter.extend(std::env::args().skip(1).filter(|arg| {
112 let use_std = env::var_os("MIRI_NO_STD").is_none();
114 if with_dependencies && use_std {
115 config.dependencies_crate_manifest_path =
116 Some(Path::new("test_dependencies").join("Cargo.toml"));
117 config.dependency_builder.args = vec![
119 "--manifest-path".into(),
120 "cargo-miri/Cargo.toml".into(),
123 "run".into(), // There is no `cargo miri build` so we just use `cargo miri run`.
126 ui_test::run_tests(config)
129 macro_rules! regexes {
130 ($name:ident: $($regex:expr => $replacement:expr,)*) => {lazy_static::lazy_static! {
131 static ref $name: Vec<(Regex, &'static str)> = vec![
132 $((Regex::new($regex).unwrap(), $replacement),)*
139 // Windows file paths
145 // erase line and column info
146 r"\.rs:[0-9]+:[0-9]+(: [0-9]+:[0-9]+)?" => ".rs:LL:CC",
148 "alloc[0-9]+" => "ALLOC",
149 // erase Stacked Borrows tags
150 "<[0-9]+>" => "<TAG>",
151 // erase whitespace that differs between platforms
152 r" +at (.*\.rs)" => " at $1",
153 // erase generics in backtraces
154 "([0-9]+: .*)::<.*>" => "$1",
155 // erase addresses in backtraces
156 "([0-9]+: ) +0x[0-9a-f]+ - (.*)" => "$1$2",
157 // erase long hexadecimals
158 r"0x[0-9a-fA-F]+[0-9a-fA-F]{2,2}" => "$$HEX",
159 // erase specific alignments
160 "alignment [0-9]+" => "alignment ALIGN",
161 // erase thread caller ids
162 r"call [0-9]+" => "call ID",
163 // erase platform module paths
164 "sys::[a-z]+::" => "sys::PLATFORM::",
165 // Windows file paths
167 // erase Rust stdlib path
168 "[^ `]*/(rust[^/]*|checkout)/library/" => "RUSTLIB/",
169 // erase platform file paths
170 "sys/[a-z]+/" => "sys/PLATFORM/",
171 // erase paths into the crate registry
172 r"[^ ]*/\.?cargo/registry/.*/(.*\.rs)" => "CARGO_REGISTRY/.../$1",
182 fn ui(mode: Mode, path: &str, with_dependencies: Dependencies) -> Result<()> {
183 let target = get_target();
186 "## Running ui tests in {path} against miri for {}",
187 target.as_deref().unwrap_or("host")
189 eprintln!("{}", msg.green().bold());
191 let with_dependencies = match with_dependencies {
192 WithDependencies => true,
193 WithoutDependencies => false,
195 run_tests(mode, path, target, with_dependencies)
198 fn get_target() -> Option<String> {
199 env::var("MIRI_TEST_TARGET").ok()
202 fn main() -> Result<()> {
203 ui_test::color_eyre::install()?;
205 // Add a test env var to do environment communication tests.
206 env::set_var("MIRI_ENV_VAR_TEST", "0");
207 // Let the tests know where to store temp files (they might run for a different target, which can make this hard to find).
208 env::set_var("MIRI_TEMP", env::temp_dir());
210 ui(Mode::Pass, "tests/pass", WithoutDependencies)?;
211 ui(Mode::Pass, "tests/pass-dep", WithDependencies)?;
212 ui(Mode::Panic, "tests/panic", WithDependencies)?;
213 ui(Mode::Fail { require_patterns: true }, "tests/fail", WithDependencies)?;
214 if cfg!(target_os = "linux") {
215 ui(Mode::Pass, "tests/extern-so/pass", WithoutDependencies)?;
216 ui(Mode::Fail { require_patterns: true }, "tests/extern-so/fail", WithDependencies)?;