vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use std::cell::{Cell, RefCell};
use std::fmt;
use std::time::{Duration, Instant};

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

    #[test]
    fn default_budget_hash_bytes_to_u32() {
        let b = default_budget_for_archetype(&Archetype("hash-bytes-to-u32"));
        assert_eq!(b.max_per_case_ms, 1);
        assert_eq!(b.max_total_seconds, 60);
        assert_eq!(b.max_input_bytes, 64 * 1024);
    }

    #[test]
    fn default_budget_decode_bytes_to_bytes() {
        let b = default_budget_for_archetype(&Archetype("decode-bytes-to-bytes"));
        assert_eq!(b.max_per_case_ms, 5);
        assert_eq!(b.max_total_seconds, 120);
        assert_eq!(b.max_input_bytes, 64 * 1024);
    }

    #[test]
    fn default_budget_compression_bytes_to_bytes() {
        let b = default_budget_for_archetype(&Archetype("compression-bytes-to-bytes"));
        assert_eq!(b.max_per_case_ms, 50);
        assert_eq!(b.max_total_seconds, 300);
        assert_eq!(b.max_input_bytes, 64 * 1024);
    }

    #[test]
    fn default_budget_unknown_falls_back() {
        let b = default_budget_for_archetype(&Archetype("unknown-archetype"));
        assert_eq!(b.max_per_case_ms, 1);
        assert_eq!(b.max_total_seconds, 30);
        assert_eq!(b.max_input_bytes, 1024);
    }

    #[test]
    fn tracker_rejects_per_case_overrun() {
        let budget = ReferenceBudget {
            max_per_case_ms: 5,
            max_total_seconds: 60,
            max_input_bytes: 1024,
        };
        let mut tracker = BudgetTracker::new(budget, "test.op");
        let err = tracker.record_case(Duration::from_millis(6)).unwrap_err();
        assert_eq!(err.case_index, 0);
        assert_eq!(err.elapsed_ms, 6);
        assert_eq!(err.budget_ms, 5);
    }

    #[test]
    fn tracker_rejects_total_overrun() {
        let budget = ReferenceBudget {
            max_per_case_ms: 1000,
            max_total_seconds: 1,
            max_input_bytes: 1024,
        };
        let mut tracker = BudgetTracker::new(budget, "test.op");
        tracker.record_case(Duration::from_millis(500)).unwrap();
        let err = tracker.record_case(Duration::from_millis(600)).unwrap_err();
        assert_eq!(err.budget_ms, 1000);
    }

    #[test]
    fn tracker_rejects_oversized_input() {
        let budget = ReferenceBudget {
            max_per_case_ms: 1000,
            max_total_seconds: 60,
            max_input_bytes: 4,
        };
        let tracker = BudgetTracker::new(budget, "test.op");
        let err = tracker.check_input(&[0; 5]).unwrap_err();
        assert_eq!(err.case_index, 0);
    }

    #[test]
    fn certify_wrapper_detects_bomb() {
        let budget = ReferenceBudget {
            max_per_case_ms: 1,
            max_total_seconds: 60,
            max_input_bytes: 1024,
        };
        let tracker = BudgetTracker::new(budget, "test.op");
        let slow_fn: fn(&[u8]) -> Vec<u8> = |_input| {
            std::thread::sleep(std::time::Duration::from_millis(10));
            vec![1]
        };

        with_certify_budget(tracker, slow_fn, || {
            certify_budget_wrapper(&[]);
        })
        .unwrap();

        let bomb = take_last_bomb().expect("expected a bomb");
        assert_eq!(bomb.op_id, "test.op");
        assert!(bomb.elapsed_ms >= 10);
    }
}