| use std::io::Read; |
| use std::path::Path; |
| use std::str::FromStr; |
| |
| use crate::common::{Config, Debugger, Mode}; |
| use crate::header::{parse_normalization_string, EarlyProps, HeadersCache}; |
| |
| fn make_test_description<R: Read>( |
| config: &Config, |
| name: test::TestName, |
| path: &Path, |
| src: R, |
| cfg: Option<&str>, |
| ) -> test::TestDesc { |
| let cache = HeadersCache::load(config); |
| let mut poisoned = false; |
| let test = |
| crate::header::make_test_description(config, &cache, name, path, src, cfg, &mut poisoned); |
| if poisoned { |
| panic!("poisoned!"); |
| } |
| test |
| } |
| |
| #[test] |
| fn test_parse_normalization_string() { |
| let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)\"."; |
| let first = parse_normalization_string(&mut s); |
| assert_eq!(first, Some("something (32 bits)".to_owned())); |
| assert_eq!(s, " -> \"something ($WORD bits)\"."); |
| |
| // Nothing to normalize (No quotes) |
| let mut s = "normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."; |
| let first = parse_normalization_string(&mut s); |
| assert_eq!(first, None); |
| assert_eq!(s, r#"normalize-stderr-32bit: something (32 bits) -> something ($WORD bits)."#); |
| |
| // Nothing to normalize (Only a single quote) |
| let mut s = "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."; |
| let first = parse_normalization_string(&mut s); |
| assert_eq!(first, None); |
| assert_eq!(s, "normalize-stderr-32bit: \"something (32 bits) -> something ($WORD bits)."); |
| |
| // Nothing to normalize (Three quotes) |
| let mut s = "normalize-stderr-32bit: \"something (32 bits)\" -> \"something ($WORD bits)."; |
| let first = parse_normalization_string(&mut s); |
| assert_eq!(first, Some("something (32 bits)".to_owned())); |
| assert_eq!(s, " -> \"something ($WORD bits)."); |
| |
| // Nothing to normalize (No quotes, 16-bit) |
| let mut s = "normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."; |
| let first = parse_normalization_string(&mut s); |
| assert_eq!(first, None); |
| assert_eq!(s, r#"normalize-stderr-16bit: something (16 bits) -> something ($WORD bits)."#); |
| } |
| |
| #[derive(Default)] |
| struct ConfigBuilder { |
| mode: Option<String>, |
| channel: Option<String>, |
| host: Option<String>, |
| target: Option<String>, |
| stage_id: Option<String>, |
| llvm_version: Option<String>, |
| git_hash: bool, |
| system_llvm: bool, |
| profiler_support: bool, |
| } |
| |
| impl ConfigBuilder { |
| fn mode(&mut self, s: &str) -> &mut Self { |
| self.mode = Some(s.to_owned()); |
| self |
| } |
| |
| fn channel(&mut self, s: &str) -> &mut Self { |
| self.channel = Some(s.to_owned()); |
| self |
| } |
| |
| fn host(&mut self, s: &str) -> &mut Self { |
| self.host = Some(s.to_owned()); |
| self |
| } |
| |
| fn target(&mut self, s: &str) -> &mut Self { |
| self.target = Some(s.to_owned()); |
| self |
| } |
| |
| fn stage_id(&mut self, s: &str) -> &mut Self { |
| self.stage_id = Some(s.to_owned()); |
| self |
| } |
| |
| fn llvm_version(&mut self, s: &str) -> &mut Self { |
| self.llvm_version = Some(s.to_owned()); |
| self |
| } |
| |
| fn git_hash(&mut self, b: bool) -> &mut Self { |
| self.git_hash = b; |
| self |
| } |
| |
| fn system_llvm(&mut self, s: bool) -> &mut Self { |
| self.system_llvm = s; |
| self |
| } |
| |
| fn profiler_support(&mut self, s: bool) -> &mut Self { |
| self.profiler_support = s; |
| self |
| } |
| |
| fn build(&mut self) -> Config { |
| let args = &[ |
| "compiletest", |
| "--mode", |
| self.mode.as_deref().unwrap_or("ui"), |
| "--suite=ui", |
| "--compile-lib-path=", |
| "--run-lib-path=", |
| "--python=", |
| "--jsondocck-path=", |
| "--src-base=", |
| "--build-base=", |
| "--sysroot-base=", |
| "--cc=c", |
| "--cxx=c++", |
| "--cflags=", |
| "--cxxflags=", |
| "--llvm-components=", |
| "--android-cross-path=", |
| "--stage-id", |
| self.stage_id.as_deref().unwrap_or("stage2-x86_64-unknown-linux-gnu"), |
| "--channel", |
| self.channel.as_deref().unwrap_or("nightly"), |
| "--host", |
| self.host.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), |
| "--target", |
| self.target.as_deref().unwrap_or("x86_64-unknown-linux-gnu"), |
| "--git-repository=", |
| "--nightly-branch=", |
| ]; |
| let mut args: Vec<String> = args.iter().map(ToString::to_string).collect(); |
| |
| if let Some(ref llvm_version) = self.llvm_version { |
| args.push("--llvm-version".to_owned()); |
| args.push(llvm_version.clone()); |
| } |
| |
| if self.git_hash { |
| args.push("--git-hash".to_owned()); |
| } |
| if self.system_llvm { |
| args.push("--system-llvm".to_owned()); |
| } |
| if self.profiler_support { |
| args.push("--profiler-support".to_owned()); |
| } |
| |
| args.push("--rustc-path".to_string()); |
| // This is a subtle/fragile thing. On rust-lang CI, there is no global |
| // `rustc`, and Cargo doesn't offer a convenient way to get the path to |
| // `rustc`. Fortunately bootstrap sets `RUSTC` for us, which is pointing |
| // to the stage0 compiler. |
| // |
| // Otherwise, if you are running compiletests's tests manually, you |
| // probably don't have `RUSTC` set, in which case this falls back to the |
| // global rustc. If your global rustc is too far out of sync with stage0, |
| // then this may cause confusing errors. Or if for some reason you don't |
| // have rustc in PATH, that would also fail. |
| args.push(std::env::var("RUSTC").unwrap_or_else(|_| { |
| eprintln!( |
| "warning: RUSTC not set, using global rustc (are you not running via bootstrap?)" |
| ); |
| "rustc".to_string() |
| })); |
| crate::parse_config(args) |
| } |
| } |
| |
| fn cfg() -> ConfigBuilder { |
| ConfigBuilder::default() |
| } |
| |
| fn parse_rs(config: &Config, contents: &str) -> EarlyProps { |
| let bytes = contents.as_bytes(); |
| EarlyProps::from_reader(config, Path::new("a.rs"), bytes) |
| } |
| |
| fn check_ignore(config: &Config, contents: &str) -> bool { |
| let tn = test::DynTestName(String::new()); |
| let p = Path::new("a.rs"); |
| let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None); |
| d.ignore |
| } |
| |
| fn parse_makefile(config: &Config, contents: &str) -> EarlyProps { |
| let bytes = contents.as_bytes(); |
| EarlyProps::from_reader(config, Path::new("Makefile"), bytes) |
| } |
| |
| #[test] |
| fn should_fail() { |
| let config: Config = cfg().build(); |
| let tn = test::DynTestName(String::new()); |
| let p = Path::new("a.rs"); |
| |
| let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None); |
| assert_eq!(d.should_panic, test::ShouldPanic::No); |
| let d = make_test_description(&config, tn, p, std::io::Cursor::new("// should-fail"), None); |
| assert_eq!(d.should_panic, test::ShouldPanic::Yes); |
| } |
| |
| #[test] |
| fn revisions() { |
| let config: Config = cfg().build(); |
| |
| assert_eq!(parse_rs(&config, "// revisions: a b c").revisions, vec!["a", "b", "c"],); |
| assert_eq!( |
| parse_makefile(&config, "# revisions: hello there").revisions, |
| vec!["hello", "there"], |
| ); |
| } |
| |
| #[test] |
| fn aux_build() { |
| let config: Config = cfg().build(); |
| |
| assert_eq!( |
| parse_rs( |
| &config, |
| r" |
| // aux-build: a.rs |
| // aux-build: b.rs |
| " |
| ) |
| .aux, |
| vec!["a.rs", "b.rs"], |
| ); |
| } |
| |
| #[test] |
| fn llvm_version() { |
| let config: Config = cfg().llvm_version("8.1.2").build(); |
| assert!(check_ignore(&config, "// min-llvm-version: 9.0")); |
| |
| let config: Config = cfg().llvm_version("9.0.1").build(); |
| assert!(check_ignore(&config, "// min-llvm-version: 9.2")); |
| |
| let config: Config = cfg().llvm_version("9.3.1").build(); |
| assert!(!check_ignore(&config, "// min-llvm-version: 9.2")); |
| |
| let config: Config = cfg().llvm_version("10.0.0").build(); |
| assert!(!check_ignore(&config, "// min-llvm-version: 9.0")); |
| } |
| |
| #[test] |
| fn system_llvm_version() { |
| let config: Config = cfg().system_llvm(true).llvm_version("17.0.0").build(); |
| assert!(check_ignore(&config, "// min-system-llvm-version: 18.0")); |
| |
| let config: Config = cfg().system_llvm(true).llvm_version("18.0.0").build(); |
| assert!(!check_ignore(&config, "// min-system-llvm-version: 18.0")); |
| |
| let config: Config = cfg().llvm_version("17.0.0").build(); |
| assert!(!check_ignore(&config, "// min-system-llvm-version: 18.0")); |
| } |
| |
| #[test] |
| fn ignore_target() { |
| let config: Config = cfg().target("x86_64-unknown-linux-gnu").build(); |
| |
| assert!(check_ignore(&config, "// ignore-x86_64-unknown-linux-gnu")); |
| assert!(check_ignore(&config, "// ignore-x86_64")); |
| assert!(check_ignore(&config, "// ignore-linux")); |
| assert!(check_ignore(&config, "// ignore-gnu")); |
| assert!(check_ignore(&config, "// ignore-64bit")); |
| |
| assert!(!check_ignore(&config, "// ignore-x86")); |
| assert!(!check_ignore(&config, "// ignore-windows")); |
| assert!(!check_ignore(&config, "// ignore-msvc")); |
| assert!(!check_ignore(&config, "// ignore-32bit")); |
| } |
| |
| #[test] |
| fn only_target() { |
| let config: Config = cfg().target("x86_64-pc-windows-gnu").build(); |
| |
| assert!(check_ignore(&config, "// only-x86")); |
| assert!(check_ignore(&config, "// only-linux")); |
| assert!(check_ignore(&config, "// only-msvc")); |
| assert!(check_ignore(&config, "// only-32bit")); |
| |
| assert!(!check_ignore(&config, "// only-x86_64-pc-windows-gnu")); |
| assert!(!check_ignore(&config, "// only-x86_64")); |
| assert!(!check_ignore(&config, "// only-windows")); |
| assert!(!check_ignore(&config, "// only-gnu")); |
| assert!(!check_ignore(&config, "// only-64bit")); |
| } |
| |
| #[test] |
| fn stage() { |
| let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build(); |
| |
| assert!(check_ignore(&config, "// ignore-stage1")); |
| assert!(!check_ignore(&config, "// ignore-stage2")); |
| } |
| |
| #[test] |
| fn cross_compile() { |
| let config: Config = cfg().host("x86_64-apple-darwin").target("wasm32-unknown-unknown").build(); |
| assert!(check_ignore(&config, "// ignore-cross-compile")); |
| |
| let config: Config = cfg().host("x86_64-apple-darwin").target("x86_64-apple-darwin").build(); |
| assert!(!check_ignore(&config, "// ignore-cross-compile")); |
| } |
| |
| #[test] |
| fn debugger() { |
| let mut config = cfg().build(); |
| config.debugger = None; |
| assert!(!check_ignore(&config, "// ignore-cdb")); |
| |
| config.debugger = Some(Debugger::Cdb); |
| assert!(check_ignore(&config, "// ignore-cdb")); |
| |
| config.debugger = Some(Debugger::Gdb); |
| assert!(check_ignore(&config, "// ignore-gdb")); |
| |
| config.debugger = Some(Debugger::Lldb); |
| assert!(check_ignore(&config, "// ignore-lldb")); |
| } |
| |
| #[test] |
| fn git_hash() { |
| let config: Config = cfg().git_hash(false).build(); |
| assert!(check_ignore(&config, "// needs-git-hash")); |
| |
| let config: Config = cfg().git_hash(true).build(); |
| assert!(!check_ignore(&config, "// needs-git-hash")); |
| } |
| |
| #[test] |
| fn sanitizers() { |
| // Target that supports all sanitizers: |
| let config: Config = cfg().target("x86_64-unknown-linux-gnu").build(); |
| assert!(!check_ignore(&config, "// needs-sanitizer-address")); |
| assert!(!check_ignore(&config, "// needs-sanitizer-leak")); |
| assert!(!check_ignore(&config, "// needs-sanitizer-memory")); |
| assert!(!check_ignore(&config, "// needs-sanitizer-thread")); |
| |
| // Target that doesn't support sanitizers: |
| let config: Config = cfg().target("wasm32-unknown-emscripten").build(); |
| assert!(check_ignore(&config, "// needs-sanitizer-address")); |
| assert!(check_ignore(&config, "// needs-sanitizer-leak")); |
| assert!(check_ignore(&config, "// needs-sanitizer-memory")); |
| assert!(check_ignore(&config, "// needs-sanitizer-thread")); |
| } |
| |
| #[test] |
| fn profiler_support() { |
| let config: Config = cfg().profiler_support(false).build(); |
| assert!(check_ignore(&config, "// needs-profiler-support")); |
| |
| let config: Config = cfg().profiler_support(true).build(); |
| assert!(!check_ignore(&config, "// needs-profiler-support")); |
| } |
| |
| #[test] |
| fn asm_support() { |
| let asms = [ |
| ("avr-unknown-gnu-atmega328", false), |
| ("i686-unknown-netbsd", true), |
| ("riscv32gc-unknown-linux-gnu", true), |
| ("riscv64imac-unknown-none-elf", true), |
| ("x86_64-unknown-linux-gnu", true), |
| ("i686-unknown-netbsd", true), |
| ]; |
| for (target, has_asm) in asms { |
| let config = cfg().target(target).build(); |
| assert_eq!(config.has_asm_support(), has_asm); |
| assert_eq!(check_ignore(&config, "// needs-asm-support"), !has_asm) |
| } |
| } |
| |
| #[test] |
| fn channel() { |
| let config: Config = cfg().channel("beta").build(); |
| |
| assert!(check_ignore(&config, "// ignore-beta")); |
| assert!(check_ignore(&config, "// only-nightly")); |
| assert!(check_ignore(&config, "// only-stable")); |
| |
| assert!(!check_ignore(&config, "// only-beta")); |
| assert!(!check_ignore(&config, "// ignore-nightly")); |
| assert!(!check_ignore(&config, "// ignore-stable")); |
| } |
| |
| #[test] |
| fn test_extract_version_range() { |
| use super::{extract_llvm_version, extract_version_range}; |
| |
| assert_eq!(extract_version_range("1.2.3 - 4.5.6", extract_llvm_version), Some((10203, 40506))); |
| assert_eq!(extract_version_range("0 - 4.5.6", extract_llvm_version), Some((0, 40506))); |
| assert_eq!(extract_version_range("1.2.3 -", extract_llvm_version), None); |
| assert_eq!(extract_version_range("1.2.3 - ", extract_llvm_version), None); |
| assert_eq!(extract_version_range("- 4.5.6", extract_llvm_version), None); |
| assert_eq!(extract_version_range("-", extract_llvm_version), None); |
| assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None); |
| assert_eq!(extract_version_range(" - 4.5.6", extract_llvm_version), None); |
| assert_eq!(extract_version_range("0 -", extract_llvm_version), None); |
| } |
| |
| #[test] |
| #[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")] |
| fn test_duplicate_revisions() { |
| let config: Config = cfg().build(); |
| parse_rs(&config, "// revisions: rpass1 rpass1"); |
| } |
| |
| #[test] |
| fn ignore_arch() { |
| let archs = [ |
| ("x86_64-unknown-linux-gnu", "x86_64"), |
| ("i686-unknown-linux-gnu", "x86"), |
| ("nvptx64-nvidia-cuda", "nvptx64"), |
| ("thumbv7m-none-eabi", "thumb"), |
| ]; |
| for (target, arch) in archs { |
| let config: Config = cfg().target(target).build(); |
| assert!(config.matches_arch(arch), "{target} {arch}"); |
| assert!(check_ignore(&config, &format!("// ignore-{arch}"))); |
| } |
| } |
| |
| #[test] |
| fn matches_os() { |
| let oss = [ |
| ("x86_64-unknown-linux-gnu", "linux"), |
| ("x86_64-fortanix-unknown-sgx", "unknown"), |
| ("wasm32-unknown-unknown", "unknown"), |
| ("x86_64-unknown-none", "none"), |
| ]; |
| for (target, os) in oss { |
| let config = cfg().target(target).build(); |
| assert!(config.matches_os(os), "{target} {os}"); |
| assert!(check_ignore(&config, &format!("// ignore-{os}"))); |
| } |
| } |
| |
| #[test] |
| fn matches_env() { |
| let envs = [ |
| ("x86_64-unknown-linux-gnu", "gnu"), |
| ("x86_64-fortanix-unknown-sgx", "sgx"), |
| ("arm-unknown-linux-musleabi", "musl"), |
| ]; |
| for (target, env) in envs { |
| let config: Config = cfg().target(target).build(); |
| assert!(config.matches_env(env), "{target} {env}"); |
| assert!(check_ignore(&config, &format!("// ignore-{env}"))); |
| } |
| } |
| |
| #[test] |
| fn matches_abi() { |
| let abis = [ |
| ("aarch64-apple-ios-macabi", "macabi"), |
| ("x86_64-unknown-linux-gnux32", "x32"), |
| ("arm-unknown-linux-gnueabi", "eabi"), |
| ]; |
| for (target, abi) in abis { |
| let config: Config = cfg().target(target).build(); |
| assert!(config.matches_abi(abi), "{target} {abi}"); |
| assert!(check_ignore(&config, &format!("// ignore-{abi}"))); |
| } |
| } |
| |
| #[test] |
| fn is_big_endian() { |
| let endians = [ |
| ("x86_64-unknown-linux-gnu", false), |
| ("bpfeb-unknown-none", true), |
| ("m68k-unknown-linux-gnu", true), |
| ("aarch64_be-unknown-linux-gnu", true), |
| ("powerpc64-unknown-linux-gnu", true), |
| ]; |
| for (target, is_big) in endians { |
| let config = cfg().target(target).build(); |
| assert_eq!(config.is_big_endian(), is_big, "{target} {is_big}"); |
| assert_eq!(check_ignore(&config, "// ignore-endian-big"), is_big); |
| } |
| } |
| |
| #[test] |
| fn pointer_width() { |
| let widths = [ |
| ("x86_64-unknown-linux-gnu", 64), |
| ("i686-unknown-linux-gnu", 32), |
| ("arm64_32-apple-watchos", 32), |
| ("msp430-none-elf", 16), |
| ]; |
| for (target, width) in widths { |
| let config: Config = cfg().target(target).build(); |
| assert_eq!(config.get_pointer_width(), width, "{target} {width}"); |
| assert_eq!(check_ignore(&config, "// ignore-16bit"), width == 16); |
| assert_eq!(check_ignore(&config, "// ignore-32bit"), width == 32); |
| assert_eq!(check_ignore(&config, "// ignore-64bit"), width == 64); |
| } |
| } |
| |
| #[test] |
| fn wasm_special() { |
| let ignores = [ |
| ("wasm32-unknown-unknown", "emscripten", true), |
| ("wasm32-unknown-unknown", "wasm32", true), |
| ("wasm32-unknown-unknown", "wasm32-bare", true), |
| ("wasm32-unknown-unknown", "wasm64", false), |
| ("wasm32-unknown-emscripten", "emscripten", true), |
| ("wasm32-unknown-emscripten", "wasm32", true), |
| ("wasm32-unknown-emscripten", "wasm32-bare", false), |
| ("wasm32-wasi", "emscripten", false), |
| ("wasm32-wasi", "wasm32", true), |
| ("wasm32-wasi", "wasm32-bare", false), |
| ("wasm32-wasi", "wasi", true), |
| ("wasm64-unknown-unknown", "emscripten", false), |
| ("wasm64-unknown-unknown", "wasm32", false), |
| ("wasm64-unknown-unknown", "wasm32-bare", false), |
| ("wasm64-unknown-unknown", "wasm64", true), |
| ]; |
| for (target, pattern, ignore) in ignores { |
| let config: Config = cfg().target(target).build(); |
| assert_eq!( |
| check_ignore(&config, &format!("// ignore-{pattern}")), |
| ignore, |
| "{target} {pattern}" |
| ); |
| } |
| } |
| |
| #[test] |
| fn families() { |
| let families = [ |
| ("x86_64-unknown-linux-gnu", "unix"), |
| ("x86_64-pc-windows-gnu", "windows"), |
| ("wasm32-unknown-unknown", "wasm"), |
| ("wasm32-unknown-emscripten", "wasm"), |
| ("wasm32-unknown-emscripten", "unix"), |
| ]; |
| for (target, family) in families { |
| let config: Config = cfg().target(target).build(); |
| assert!(config.matches_family(family)); |
| let other = if family == "windows" { "unix" } else { "windows" }; |
| assert!(!config.matches_family(other)); |
| assert!(check_ignore(&config, &format!("// ignore-{family}"))); |
| assert!(!check_ignore(&config, &format!("// ignore-{other}"))); |
| } |
| } |
| |
| #[test] |
| fn ignore_mode() { |
| for &mode in Mode::STR_VARIANTS { |
| // Indicate profiler support so that "coverage-run" tests aren't skipped. |
| let config: Config = cfg().mode(mode).profiler_support(true).build(); |
| let other = if mode == "coverage-run" { "coverage-map" } else { "coverage-run" }; |
| assert_ne!(mode, other); |
| assert_eq!(config.mode, Mode::from_str(mode).unwrap()); |
| assert_ne!(config.mode, Mode::from_str(other).unwrap()); |
| assert!(check_ignore(&config, &format!("// ignore-mode-{mode}"))); |
| assert!(!check_ignore(&config, &format!("// ignore-mode-{other}"))); |
| } |
| } |