#![cfg(not(miri))]
#![cfg(feature = "slow-tests")]
use std::collections::hash_map::DefaultHasher;
use std::fs;
use std::hash::{Hash, Hasher};
use std::path::Path;
use facet_testhelpers::test;
struct CompilationTest {
source: &'static str,
expected_errors: &'static [&'static str],
name: &'static str,
}
fn strip_ansi_escapes(s: &str) -> String {
let mut result = String::with_capacity(s.len());
let mut chars = s.chars().peekable();
while let Some(c) = chars.next() {
if c == '\x1B' {
if let Some(&'[') = chars.peek() {
chars.next(); for c in chars.by_ref() {
if c.is_ascii_alphabetic() || c == 'm' {
break;
}
}
} else {
result.push(c);
}
} else {
result.push(c);
}
}
result
}
fn hash_source(name: &str, source: &str) -> u64 {
let mut hasher = DefaultHasher::new();
name.hash(&mut hasher);
source.hash(&mut hasher);
hasher.finish()
}
fn run_compilation_test(test: &CompilationTest) {
println!("{}", format_args!("Running test: {}", test.name));
let temp_dir = tempfile::tempdir().expect("Failed to create temp directory");
let project_dir = temp_dir.path();
println!(
"{}",
format_args!(" Project directory: {}", project_dir.display())
);
let workspace_dir = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap();
let facet_path = workspace_dir.join("facet");
fs::create_dir(project_dir.join("src")).expect("Failed to create src directory");
let cargo_toml = format!(
r#"
[package]
name = "facet-test-project"
version = "0.1.0"
edition = "2021"
[dependencies]
facet = {{ path = {:?} }}
"#,
facet_path.display(),
);
fs::write(project_dir.join("Cargo.toml"), cargo_toml).expect("Failed to write Cargo.toml");
fs::write(project_dir.join("src").join("main.rs"), test.source)
.expect("Failed to write main.rs");
let source_hash = hash_source(test.name, test.source);
let target_dir = format!("/tmp/ui_tests/target_{source_hash}");
println!("{}", format_args!(" Target directory: {target_dir}"));
let mut cmd = std::process::Command::new("cargo");
cmd.current_dir(project_dir)
.args(["build", "--color=always"])
.env("CARGO_TERM_COLOR", "always")
.env("CARGO_TARGET_DIR", &target_dir);
let output = cmd.output().expect("Failed to execute cargo build");
let exit_code = output.status.code().unwrap_or(0);
let stderr = String::from_utf8_lossy(&output.stderr);
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr_clean = strip_ansi_escapes(&stderr);
if exit_code == 0 {
println!("❌ Test failed:");
println!(" The code compiled successfully, but it should have failed");
panic!(
"Test '{}' compiled successfully but should have failed",
test.name
);
} else {
println!(" ✓ Compilation failed as expected");
}
let mut missing_errors = Vec::new();
for &expected_error in test.expected_errors {
if !stderr_clean.contains(expected_error) {
missing_errors.push(expected_error);
} else {
println!(" ✓ Found expected error: '{expected_error}'");
}
}
if !missing_errors.is_empty() {
println!("\n❌ MISSING EXPECTED ERRORS:");
for error in &missing_errors {
println!(" - '{error}'");
}
println!("\nCompiler error output:");
println!("{stderr}");
if !stdout.is_empty() {
println!("\nCompiler standard output:");
println!("{stdout}");
}
panic!(
"Test '{}' did not produce the expected error messages: {:?}",
test.name, missing_errors
);
}
println!("{}", format_args!(" ✓ Test '{}' passed", test.name));
}
#[test]
#[cfg(not(miri))]
fn test_proxy_unknown_type() {
let test = CompilationTest {
name: "proxy_unknown_type",
source: include_str!("../compile_tests/proxy_unknown_type.rs"),
expected_errors: &["NonExistentProxyType", "not found in this scope"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_default_unknown_expr() {
let test = CompilationTest {
name: "default_unknown_expr",
source: include_str!("../compile_tests/default_unknown_expr.rs"),
expected_errors: &["MissingDefault", "undeclared"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_invariants_unknown_fn() {
let test = CompilationTest {
name: "invariants_unknown_fn",
source: include_str!("../compile_tests/invariants_unknown_fn.rs"),
expected_errors: &["missing_validator", "not found in this scope"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_skip_serializing_if_unknown_fn() {
let test = CompilationTest {
name: "skip_serializing_if_unknown_fn",
source: include_str!("../compile_tests/skip_serializing_if_unknown_fn.rs"),
expected_errors: &["nonexistent_predicate", "not found in this scope"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_truthy_unknown_fn() {
let test = CompilationTest {
name: "truthy_unknown_fn",
source: include_str!("../compile_tests/truthy_unknown_fn.rs"),
expected_errors: &["missing_truthy_fn", "not found in this scope"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_opaque_adapter_field_level_rejected() {
let test = CompilationTest {
name: "opaque_adapter_field_level",
source: include_str!("../compile_tests/opaque_adapter_field_level.rs"),
expected_errors: &["container-level only for now"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_opaque_adapter_send_mismatch() {
let test = CompilationTest {
name: "opaque_adapter_send_mismatch",
source: include_str!("../compile_tests/opaque_adapter_send_mismatch.rs"),
expected_errors: &["mismatched types", "&u32"],
};
run_compilation_test(&test);
}
#[test]
#[cfg(not(miri))]
fn test_opaque_adapter_recv_mismatch() {
let test = CompilationTest {
name: "opaque_adapter_recv_mismatch",
source: include_str!("../compile_tests/opaque_adapter_recv_mismatch.rs"),
expected_errors: &["mismatched types", "BadPayload"],
};
run_compilation_test(&test);
}