]> git.lizzy.rs Git - rust.git/blob - src/tools/miri/tests/compiletest.rs
Rollup merge of #101431 - compiler-errors:move-place-ty-for-move-place-sugg, r=cjgillot
[rust.git] / src / tools / miri / tests / compiletest.rs
1 use colored::*;
2 use regex::Regex;
3 use std::path::{Path, PathBuf};
4 use std::{env, process::Command};
5 use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling};
6
7 fn miri_path() -> PathBuf {
8     PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri")))
9 }
10
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)
21         .args([
22             "-shared",
23             "-o",
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/
29             "-fPIC",
30             "-Wl,--version-script=tests/extern-so/libcode.version",
31         ])
32         .output()
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");
36     }
37     so_file_path
38 }
39
40 fn run_tests(
41     mode: Mode,
42     path: &str,
43     target: Option<String>,
44     with_dependencies: bool,
45 ) -> Result<()> {
46     let mut config = Config {
47         target,
48         stderr_filters: STDERR.clone(),
49         stdout_filters: STDOUT.clone(),
50         root_dir: PathBuf::from(path),
51         mode,
52         program: miri_path(),
53         quiet: false,
54         ..Config::default()
55     };
56
57     let in_rustc_test_suite = option_env!("RUSTC_STAGE").is_some();
58
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());
67     } else {
68         config.args.push("-Dwarnings".into());
69         config.args.push("-Dunused".into());
70     }
71     if let Ok(extra_flags) = env::var("MIRIFLAGS") {
72         for flag in extra_flags.split_whitespace() {
73             config.args.push(flag.into());
74         }
75     }
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());
80     }
81
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);
90     }
91
92     let skip_ui_checks = env::var_os("MIRI_SKIP_UI_CHECKS").is_some();
93
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"),
99     };
100
101     // Handle command-line arguments.
102     config.path_filter.extend(std::env::args().skip(1).filter(|arg| {
103         match &**arg {
104             "--quiet" => {
105                 config.quiet = true;
106                 false
107             }
108             _ => true,
109         }
110     }));
111
112     let use_std = env::var_os("MIRI_NO_STD").is_none();
113
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![
118             "run".into(),
119             "--manifest-path".into(),
120             "cargo-miri/Cargo.toml".into(),
121             "--".into(),
122             "miri".into(),
123             "run".into(), // There is no `cargo miri build` so we just use `cargo miri run`.
124         ];
125     }
126     ui_test::run_tests(config)
127 }
128
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),)*
133         ];
134     }};
135 }
136
137 regexes! {
138     STDOUT:
139     // Windows file paths
140     r"\\"                           => "/",
141 }
142
143 regexes! {
144     STDERR:
145     // erase line and column info
146     r"\.rs:[0-9]+:[0-9]+(: [0-9]+:[0-9]+)?" => ".rs:LL:CC",
147     // erase alloc ids
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
166     r"\\"                           => "/",
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",
173 }
174
175 enum Dependencies {
176     WithDependencies,
177     WithoutDependencies,
178 }
179
180 use Dependencies::*;
181
182 fn ui(mode: Mode, path: &str, with_dependencies: Dependencies) -> Result<()> {
183     let target = get_target();
184
185     let msg = format!(
186         "## Running ui tests in {path} against miri for {}",
187         target.as_deref().unwrap_or("host")
188     );
189     eprintln!("{}", msg.green().bold());
190
191     let with_dependencies = match with_dependencies {
192         WithDependencies => true,
193         WithoutDependencies => false,
194     };
195     run_tests(mode, path, target, with_dependencies)
196 }
197
198 fn get_target() -> Option<String> {
199     env::var("MIRI_TEST_TARGET").ok()
200 }
201
202 fn main() -> Result<()> {
203     ui_test::color_eyre::install()?;
204
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());
209
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)?;
217     }
218
219     Ok(())
220 }