vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
pub use super::boundary::BoundaryLengths;
pub use super::edge_cases::EdgeCases;
pub use super::exhaustive::ExhaustiveByteRange;
pub use super::pathological::Pathological;
pub use super::random::RandomUniform;
pub use super::u32_pathological::U32Pathological;
use crate::spec::types::OpSignature;

#[cfg(test)]
mod tests {
    use super::*;
    use crate::spec::types::DataType;
    use std::collections::HashSet;

    fn unary_sig() -> OpSignature {
        OpSignature {
            inputs: vec![DataType::U32],
            output: DataType::U32,
        }
    }

    fn binary_sig() -> OpSignature {
        OpSignature {
            inputs: vec![DataType::U32, DataType::U32],
            output: DataType::U32,
        }
    }

    fn byte_sig() -> OpSignature {
        OpSignature {
            inputs: vec![DataType::Bytes],
            output: DataType::Bytes,
        }
    }

    #[test]
    fn u32_bytes_encoding() {
        assert_eq!(u32_bytes(0), vec![0, 0, 0, 0]);
        assert_eq!(u32_bytes(1), vec![1, 0, 0, 0]);
        assert_eq!(u32_bytes(0xDEAD_BEEF), vec![0xEF, 0xBE, 0xAD, 0xDE]);
    }

    #[test]
    fn pair_bytes_encoding() {
        let bytes = pair_bytes(1, 2);
        assert_eq!(bytes.len(), 8);
        assert_eq!(&bytes[..4], &[1, 0, 0, 0]);
        assert_eq!(&bytes[4..], &[2, 0, 0, 0]);
    }

    #[test]
    fn triple_bytes_encoding() {
        let bytes = triple_bytes(1, 2, 3);
        assert_eq!(bytes.len(), 12);
    }

    #[test]
    fn sig_matchers_unary() {
        assert!(sig::is_unary_u32(&unary_sig()));
        assert!(!sig::is_binary_u32(&unary_sig()));
        assert!(!sig::is_byte_input(&unary_sig()));
    }

    #[test]
    fn sig_matchers_binary() {
        assert!(!sig::is_unary_u32(&binary_sig()));
        assert!(sig::is_binary_u32(&binary_sig()));
    }

    #[test]
    fn sig_matchers_bytes() {
        assert!(!sig::is_unary_u32(&byte_sig()));
        assert!(sig::is_byte_input(&byte_sig()));
    }

    #[test]
    fn default_generators_non_empty() {
        let gens = default_generators();
        assert!(gens.len() >= 5, "expected at least 5 generators");
    }

    #[test]
    fn all_generators_have_unique_names() {
        let gens = default_generators();
        let names: HashSet<_> = gens.iter().map(|g| g.name()).collect();
        assert_eq!(
            names.len(),
            gens.len(),
            "duplicate generator names detected"
        );
    }

    #[test]
    fn every_generator_handles_unary_or_binary_or_bytes() {
        let gens = default_generators();
        let sigs = [unary_sig(), binary_sig(), byte_sig()];
        for gen in &gens {
            let handles_any = sigs.iter().any(|s| gen.handles(s));
            assert!(
                handles_any,
                "generator '{}' handles no known signature shape",
                gen.name()
            );
        }
    }

    #[test]
    fn generators_produce_deterministic_output() -> Result<(), String> {
        let gens = default_generators();
        let sig = binary_sig();
        let seed = 42;
        for gen in &gens {
            if !gen.handles(&sig) {
                continue;
            }
            let run1 = gen.generate(&sig, seed);
            let Some(fresh) = default_generators()
                .into_iter()
                .find(|g| g.name() == gen.name())
            else {
                return Err(format!(
                    "Fix: default_generators must preserve generator '{}' across calls",
                    gen.name()
                ));
            };
            let run2 = fresh.generate(&sig, seed);
            assert_eq!(
                run1.len(),
                run2.len(),
                "generator '{}' produced different counts on same seed",
                gen.name()
            );
            for (a, b) in run1.iter().zip(run2.iter()) {
                assert_eq!(
                    a,
                    b,
                    "generator '{}' produced different output on same seed",
                    gen.name()
                );
            }
        }
        Ok(())
    }

    #[test]
    fn generators_produce_deterministic_output_for_op() -> Result<(), String> {
        let gens = default_generators();
        let sig = binary_sig();
        let seed = 42;
        let op_id = "test_op";
        for gen in &gens {
            if !gen.handles(&sig) {
                continue;
            }
            let run1 = gen.generate_for_op(op_id, &sig, seed);
            let Some(fresh) = default_generators()
                .into_iter()
                .find(|g| g.name() == gen.name())
            else {
                return Err(format!(
                    "Fix: default_generators must preserve generator '{}' across calls",
                    gen.name()
                ));
            };
            let run2 = fresh.generate_for_op(op_id, &sig, seed);
            assert_eq!(
                run1.len(),
                run2.len(),
                "generator '{}' produced different counts on same seed for generate_for_op",
                gen.name()
            );
            for (a, b) in run1.iter().zip(run2.iter()) {
                assert_eq!(
                    a,
                    b,
                    "generator '{}' produced different output on same seed for generate_for_op",
                    gen.name()
                );
            }
        }
        Ok(())
    }

    #[test]
    fn generators_produce_correct_input_sizes_for_binary() {
        let gens = default_generators();
        let sig = binary_sig();
        for gen in &gens {
            if !gen.handles(&sig) {
                continue;
            }
            for (label, bytes) in gen.generate(&sig, 0) {
                assert!(
                    bytes.len() >= 8,
                    "generator '{}' produced {}-byte input for binary op (need >=8): {}",
                    gen.name(),
                    bytes.len(),
                    label
                );
            }
        }
    }

    #[test]
    fn generators_produce_correct_input_sizes_for_unary() {
        let gens = default_generators();
        let sig = unary_sig();
        for gen in &gens {
            if !gen.handles(&sig) {
                continue;
            }
            for (label, bytes) in gen.generate(&sig, 0) {
                assert!(
                    bytes.len() >= 4,
                    "generator '{}' produced {}-byte input for unary op (need >=4): {}",
                    gen.name(),
                    bytes.len(),
                    label
                );
            }
        }
    }

    #[test]
    fn edge_case_generator_produces_nonzero_count() {
        let gen = EdgeCases;
        let inputs = gen.generate(&binary_sig(), 0);
        // Edge values: ~36 unique values → 36*36 = ~1296 pairs
        assert!(inputs.len() > 100, "edge cases too few: {}", inputs.len());
    }

    #[test]
    fn exhaustive_byte_range_covers_0_to_255_for_unary() {
        let gen = ExhaustiveByteRange;
        let inputs = gen.generate(&unary_sig(), 0);
        assert_eq!(inputs.len(), 256, "expected exactly 256 exhaustive inputs");
    }

    #[test]
    fn random_uniform_seed_sensitivity() {
        let gen = RandomUniform;
        let a = gen.generate(&unary_sig(), 12345);
        let b = gen.generate(&unary_sig(), 98765);
        // Widely-separated seeds must produce different inputs.
        assert_ne!(a[0].1, b[0].1, "different seeds produced same first input");
    }

    #[test]
    fn u32_pathological_covers_shift_boundaries() {
        let gen = U32Pathological;
        let inputs = gen.generate(&binary_sig(), 0);
        let labels: HashSet<_> = inputs.iter().map(|i| i.0.as_str()).collect();
        assert!(labels.contains("patho:shift:0"), "missing shift=0");
        assert!(labels.contains("patho:shift:31"), "missing shift=31");
        assert!(labels.contains("patho:shift:32"), "missing shift=32");
        assert!(labels.contains("patho:rot_0"), "missing rot=0");
    }
}