vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! Regression persistence tests for audit findings.

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);
    }
}