use std::fs;
use std::thread;
use vyre_conform::{regression, ParityFailure};
#[test]
fn save_persists_full_failure_context_as_json() {
let failure = failure("audit.regression.full.context", vec![0xCA, 0xFE], 7);
let path = regression::save(&failure).unwrap();
assert_eq!(path.extension().and_then(|ext| ext.to_str()), Some("json"));
assert_eq!(
path.file_stem()
.and_then(|stem| stem.to_str())
.unwrap()
.len(),
64
);
let loaded_inputs = regression::load(&failure.op_id);
assert!(loaded_inputs
.iter()
.any(|(_, bytes)| bytes == &failure.input));
let loaded_failures = regression::load_failures_versioned(&failure.op_id, failure.spec_version);
assert_eq!(loaded_failures, vec![failure]);
cleanup_from_saved_path(&path);
}
#[test]
fn concurrent_saves_commit_one_complete_file() {
let op_id = "audit.regression.concurrent";
let failure = failure(op_id, vec![0, 1, 2, 3, 4, 5], 3);
let mut handles = Vec::new();
for _ in 0..16 {
let cloned = failure.clone();
handles.push(thread::spawn(move || regression::save(&cloned).unwrap()));
}
let paths: Vec<_> = handles
.into_iter()
.map(|handle| handle.join().unwrap())
.collect();
let first = paths.first().unwrap();
assert!(paths.iter().all(|path| path == first));
let bytes = fs::read(first).unwrap();
let text = String::from_utf8(bytes).unwrap();
assert!(text.contains("\"gpu_output\""));
assert!(text.contains("\"cpu_output\""));
assert_eq!(regression::load_failures_versioned(op_id, 3), vec![failure]);
cleanup_from_saved_path(first);
}
#[test]
fn op_id_path_encoding_keeps_dot_and_underscore_distinct() {
let dotted = failure("audit.math.add", vec![1], 1);
let underscored = failure("audit.math_add", vec![2], 1);
let dotted_path = regression::save(&dotted).unwrap();
let underscored_path = regression::save(&underscored).unwrap();
assert_ne!(
dotted_path.parent().unwrap().parent().unwrap(),
underscored_path.parent().unwrap().parent().unwrap()
);
assert_eq!(
regression::load_failures_versioned(&dotted.op_id, 1),
vec![dotted]
);
assert_eq!(
regression::load_failures_versioned(&underscored.op_id, 1),
vec![underscored]
);
cleanup_from_saved_path(&dotted_path);
cleanup_from_saved_path(&underscored_path);
}
fn failure(op_id: &str, input: Vec<u8>, spec_version: u32) -> ParityFailure {
ParityFailure {
op_id: op_id.to_string(),
generator: "adversarial".to_string(),
input_label: format!("case-{}", input.len()),
input,
gpu_output: vec![0x00, 0x01],
cpu_output: vec![0xFE, 0xFF],
message: "mismatch with full context".to_string(),
spec_version,
workgroup_size: 64,
}
}
fn cleanup_from_saved_path(path: &std::path::Path) {
if let Some(op_dir) = path.parent().and_then(|version| version.parent()) {
let _ = fs::remove_dir_all(op_dir);
}
}