vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use crate::{Convention, DataType, OpSignature, OpSpec};

#[cfg(test)]
mod tests {
    use super::*;
    use super::{cpu_fn, example_table, scan_dfa_cpu, validate, DfaSpec, BYTE_CLASSES};

    fn make_spec(table: &super::OwnedExampleTable) -> DfaSpec<'_> {
        DfaSpec {
            transitions: &table.transitions,
            state_count: table.state_count,
            accept_states: &table.accept_states,
            pattern_lengths: &table.pattern_lengths,
        }
    }

    #[test]
    fn scans_ab_and_cd() {
        let table = example_table();
        let spec = make_spec(&table);
        let matches = scan_dfa_cpu(&spec, b"xxabxxcdxx").expect("valid spec");
        assert_eq!(matches.len(), 2);
        assert_eq!(
            (matches[0].pattern_id, matches[0].start, matches[0].end),
            (0, 2, 4)
        );
        assert_eq!(
            (matches[1].pattern_id, matches[1].start, matches[1].end),
            (1, 6, 8)
        );
    }

    #[test]
    fn empty_input_returns_no_matches() {
        let table = example_table();
        let spec = make_spec(&table);
        let matches = scan_dfa_cpu(&spec, b"").expect("valid spec");
        assert!(matches.is_empty());
    }

    #[test]
    fn no_match_in_input() {
        let table = example_table();
        let spec = make_spec(&table);
        let matches = scan_dfa_cpu(&spec, b"xxxxxxxxxxx").expect("valid spec");
        assert!(matches.is_empty());
    }

    #[test]
    fn validate_zero_states() {
        let spec = DfaSpec {
            transitions: &[],
            state_count: 0,
            accept_states: &[],
            pattern_lengths: &[],
        };
        assert!(validate(&spec).is_err());
    }

    #[test]
    fn validate_wrong_transition_count() {
        let spec = DfaSpec {
            transitions: &[0; 100], // should be 256
            state_count: 1,
            accept_states: &[],
            pattern_lengths: &[],
        };
        assert!(validate(&spec).is_err());
    }

    #[test]
    fn validate_invalid_transition_target() {
        let mut transitions = vec![0u32; BYTE_CLASSES];
        transitions[0] = 5; // invalid — only 1 state
        let spec = DfaSpec {
            transitions: &transitions,
            state_count: 1,
            accept_states: &[],
            pattern_lengths: &[],
        };
        assert!(validate(&spec).is_err());
    }

    #[test]
    fn validate_invalid_accept_state() {
        let transitions = vec![0u32; BYTE_CLASSES];
        let spec = DfaSpec {
            transitions: &transitions,
            state_count: 1,
            accept_states: &[(5, 0)], // state 5 doesn't exist
            pattern_lengths: &[1],
        };
        assert!(validate(&spec).is_err());
    }

    #[test]
    fn validate_duplicate_accept_state() {
        let transitions = vec![0u32; 2 * BYTE_CLASSES];
        let spec = DfaSpec {
            transitions: &transitions,
            state_count: 2,
            accept_states: &[(1, 0), (1, 1)], // state 1 appears twice
            pattern_lengths: &[1, 1],
        };
        assert!(validate(&spec).is_err());
    }

    #[test]
    fn cpu_fn_empty_input_no_panic() {
        // Empty input produces 0 matches → 4-byte u32 count.
        let result = cpu_fn(b"");
        assert_eq!(
            result,
            vec![0, 0, 0, 0],
            "empty input should produce 0 matches as u32"
        );
    }

    #[test]
    fn cpu_fn_match_returns_u32_count() {
        let result = cpu_fn(b"ab");
        // cpu_fn returns match count as a 4-byte u32.
        assert_eq!(
            result.len(),
            4,
            "match output should be exactly 4 bytes (u32 count)"
        );
        let count = u32::from_le_bytes([result[0], result[1], result[2], result[3]]);
        assert_eq!(count, 1, "input 'ab' should produce exactly 1 match");
    }
}