vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use crate::pipeline::backend::{wrap_shader, ConformDispatchConfig, VyreBackend};
use crate::verify::regression;
use crate::{ChainSpec, OpSpec, ParityFailure};

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn input_case_report_label() {
        let case = InputCase::new("random", "case_42".into(), vec![0xDE, 0xAD]);
        assert_eq!(case.report_label(), "random/case_42");
    }

    #[test]
    fn input_case_failure_preserves_fields() {
        let case = InputCase::new("edge", "max_val".into(), vec![0xFF; 4]);
        let f = case.failure("test.op", vec![0x00], vec![0xFF], "mismatch".into(), 2, 64);
        assert_eq!(f.op_id, "test.op");
        assert_eq!(f.generator, "edge");
        assert_eq!(f.input_label, "max_val");
        assert_eq!(f.input, vec![0xFF; 4]);
        assert_eq!(f.gpu_output, vec![0x00]);
        assert_eq!(f.cpu_output, vec![0xFF]);
        assert_eq!(f.spec_version, 2);
        assert_eq!(f.workgroup_size, 64);
    }

    #[test]
    fn seed_from_is_deterministic() {
        let a = seed_from("primitive.bitwise.xor");
        let b = seed_from("primitive.bitwise.xor");
        assert_eq!(a, b);
    }

    #[test]
    fn seed_from_differs_for_different_ops() {
        let a = seed_from("primitive.bitwise.xor");
        let b = seed_from("primitive.bitwise.and");
        assert_ne!(a, b);
    }

    #[test]
    fn seed_from_empty_string() {
        let s = seed_from("");
        // Must produce a valid u64, not panic.
        assert_ne!(s, 0);
    }

    #[test]
    fn dispatch_config_single_word() {
        let op = crate::spec::primitive::xor::spec();
        let config = dispatch_config(&op, 4, 1);
        assert_eq!(config.workgroup_size, 1);
        assert_eq!(config.workgroup_count, 1);
    }

    #[test]
    fn dispatch_config_multi_word() {
        let op = crate::spec::primitive::xor::spec();
        // 256 bytes = 64 words → workgroup_count = 64/64 = 1 with wg_size=64
        let config = dispatch_config(&op, 256, 64);
        assert_eq!(config.workgroup_count, 1);
    }

    #[test]
    fn dispatch_config_zero_output_clamps_to_one() {
        let op = crate::spec::primitive::xor::spec();
        let config = dispatch_config(&op, 0, 1);
        // output_words = max(ceil(0/4), 1) = 1
        assert_eq!(config.workgroup_count, 1);
    }

    #[test]
    fn checked_dispatch_config_rejects_zero_workgroup_size() {
        let op = crate::spec::primitive::xor::spec();
        let err = checked_dispatch_config(&op, 4, 0).unwrap_err();
        assert!(err.contains("workgroup_size=0"));
        assert!(err.contains("Fix:"));
    }

    #[test]
    fn checked_dispatch_config_rejects_workgroup_count_overflow() -> Result<(), String> {
        let op = crate::spec::primitive::xor::spec();
        let output_size = (u32::MAX as usize)
            .checked_add(1)
            .and_then(|words| words.checked_mul(4))
            .ok_or_else(|| {
                "Fix: overflow fixture must fit usize on this target before dispatch validation"
                    .to_string()
            })?;
        let err = checked_dispatch_config(&op, output_size, 1).unwrap_err();
        assert!(err.contains("workgroup_count overflow"));
        assert!(err.contains("Fix:"));
        Ok(())
    }
}