vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
use super::{
    ApproximateMatch, Comparator, ComparatorKind, ExactMatch, SetEqualityMatch, UnorderedMatch,
};

// ── ExactMatch ──────────────────────────────────────────

#[test]
fn exact_match_identical_passes() {
    let data = &[1, 2, 3, 4];
    assert!(ExactMatch.compare(data, data).is_ok());
}

#[test]
fn exact_match_empty_passes() {
    assert!(ExactMatch.compare(&[], &[]).is_ok());
}

#[test]
fn exact_match_length_mismatch_fails() {
    let err = ExactMatch.compare(&[1, 2], &[1, 2, 3]).unwrap_err();
    assert!(err.contains("length mismatch"), "got: {err}");
}

#[test]
fn exact_match_byte_difference_hides_index() {
    let err = ExactMatch.compare(&[1, 2, 3], &[1, 9, 3]).unwrap_err();
    assert!(err.contains("mismatch at offset >="), "got: {err}");
    assert!(!err.contains("byte 1"), "got: {err}");
}

#[test]
fn exact_match_u32_aligned_hides_word_context() {
    let gpu = 0xDEAD_BEEF_u32.to_le_bytes();
    let cpu = 0xDEAD_BEFF_u32.to_le_bytes();
    let err = ExactMatch.compare(&gpu, &cpu).unwrap_err();
    assert!(!err.contains("word[0]"), "got: {err}");
    assert!(!err.contains("0xdeadbeef"), "got: {err}");
}

#[test]
fn exact_match_non_aligned_no_word_context() {
    let err = ExactMatch.compare(&[1, 2, 3], &[1, 2, 9]).unwrap_err();
    assert!(!err.contains("word["), "got: {err}");
}

// ── UnorderedMatch ──────────────────────────────────────

#[test]
fn unordered_match_byte_elements_pass() {
    assert!(UnorderedMatch.compare(&[3, 1, 2], &[1, 2, 3]).is_ok());
}

#[test]
fn unordered_match_empty_passes() {
    assert!(UnorderedMatch.compare(&[], &[]).is_ok());
}

#[test]
fn unordered_match_length_mismatch_fails() {
    let err = UnorderedMatch.compare(&[1, 2], &[1]).unwrap_err();
    assert!(err.contains("length mismatch"), "got: {err}");
}

#[test]
fn unordered_match_different_elements_fails() {
    let err = UnorderedMatch.compare(&[1, 2, 3], &[1, 2, 4]).unwrap_err();
    assert!(err.contains("mismatch at offset >="), "got: {err}");
}

#[test]
fn unordered_match_duplicate_counts_matter() {
    let err = UnorderedMatch.compare(&[1, 1, 2], &[1, 2, 2]).unwrap_err();
    assert!(
        err.contains("differs after sorting") || err.contains("mismatch"),
        "got: {err}"
    );
}

#[test]
fn unordered_match_rejects_scrambled_u32_bytes() {
    let one = 1_u32.to_le_bytes();
    let big = 0x0100_0000_u32.to_le_bytes();
    assert!(UnorderedMatch.compare(&one, &big).is_err());
}

// ── ApproximateMatch ────────────────────────────────────

#[test]
fn approximate_within_epsilon_passes() {
    let cmp = ApproximateMatch { epsilon: 2 };
    let gpu = 1.0_f32.to_bits().wrapping_add(2).to_le_bytes();
    let cpu = 1.0_f32.to_le_bytes();
    assert!(cmp.compare(&gpu, &cpu).is_ok());
}

#[test]
fn approximate_exact_passes() {
    let cmp = ApproximateMatch { epsilon: 0 };
    assert!(cmp
        .compare(&1.25_f32.to_le_bytes(), &1.25_f32.to_le_bytes())
        .is_ok());
}

#[test]
fn approximate_exceeds_epsilon_fails() {
    let cmp = ApproximateMatch { epsilon: 1 };
    let gpu = 1.0_f32.to_bits().wrapping_add(3).to_le_bytes();
    let cpu = 1.0_f32.to_le_bytes();
    let err = cmp.compare(&gpu, &cpu).unwrap_err();
    assert!(err.contains("ULP distance 3"), "got: {err}");
    assert!(err.contains("max_ulps 1"), "got: {err}");
}

#[test]
fn approximate_length_mismatch_fails() {
    let cmp = ApproximateMatch { epsilon: 10 };
    let err = cmp.compare(&[1, 2, 3], &[1, 2]).unwrap_err();
    assert!(err.contains("length mismatch"), "got: {err}");
}

// ── ComparatorKind dispatch ─────────────────────────────

#[test]
fn kind_dispatches_exact() {
    assert!(ComparatorKind::ExactMatch.compare(&[1], &[1]).is_ok());
    assert!(ComparatorKind::ExactMatch.compare(&[1], &[2]).is_err());
}

#[test]
fn kind_dispatches_unordered() {
    assert!(ComparatorKind::UnorderedMatch
        .compare(&[2, 1], &[1, 2])
        .is_ok());
}

#[test]
fn kind_dispatches_approximate() {
    let kind = ComparatorKind::Approximate { epsilon: 5 };
    let cpu = 1.0_f32.to_le_bytes();
    let close = 1.0_f32.to_bits().wrapping_add(4).to_le_bytes();
    let far = 1.0_f32.to_bits().wrapping_add(6).to_le_bytes();
    assert!(kind.compare(&close, &cpu).is_ok());
    assert!(kind.compare(&far, &cpu).is_err());
}

#[test]
fn approximate_treats_positive_and_negative_zero_as_equal() {
    let cmp = ApproximateMatch { epsilon: 0 };
    assert!(cmp
        .compare(&(-0.0_f32).to_le_bytes(), &0.0_f32.to_le_bytes())
        .is_ok());
}

#[test]
fn approximate_treats_nan_payloads_as_equal() {
    let cmp = ApproximateMatch { epsilon: 0 };
    assert!(cmp
        .compare(
            &0x7fc0_0001_u32.to_le_bytes(),
            &0x7fc0_0002_u32.to_le_bytes()
        )
        .is_ok());
}

#[test]
fn kind_default_is_exact_match() {
    assert_eq!(ComparatorKind::default(), ComparatorKind::ExactMatch);
}

// ── SetEqualityMatch ────────────────────────────────────

#[test]
fn set_equality_same_order_passes() {
    let cmp = SetEqualityMatch { element_size: 4 };
    let data = &[1, 2, 3, 4, 5, 6, 7, 8];
    assert!(cmp.compare(data, data).is_ok());
}

#[test]
fn set_equality_different_order_passes() {
    let cmp = SetEqualityMatch { element_size: 4 };
    let gpu = &[5, 6, 7, 8, 1, 2, 3, 4];
    let cpu = &[1, 2, 3, 4, 5, 6, 7, 8];
    assert!(cmp.compare(gpu, cpu).is_ok());
}

#[test]
fn set_equality_different_elements_fails() {
    let cmp = SetEqualityMatch { element_size: 4 };
    let gpu = &[1, 2, 3, 4, 5, 6, 7, 9];
    let cpu = &[1, 2, 3, 4, 5, 6, 7, 8];
    let err = cmp.compare(gpu, cpu).unwrap_err();
    assert!(err.contains("mismatch at offset >="), "got: {err}");
}

#[test]
fn set_equality_length_mismatch_fails() {
    let cmp = SetEqualityMatch { element_size: 4 };
    let err = cmp
        .compare(&[1, 2, 3, 4], &[1, 2, 3, 4, 5, 6, 7, 8])
        .unwrap_err();
    assert!(err.contains("length mismatch"), "got: {err}");
}

#[test]
fn set_equality_non_aligned_length_fails() {
    let cmp = SetEqualityMatch { element_size: 4 };
    let err = cmp.compare(&[1, 2, 3], &[1, 2, 3]).unwrap_err();
    assert!(err.contains("not a multiple"), "got: {err}");
}

#[test]
fn set_equality_empty_passes() {
    let cmp = SetEqualityMatch { element_size: 4 };
    assert!(cmp.compare(&[], &[]).is_ok());
}

#[test]
fn set_equality_zero_element_size_empty_ok() {
    let cmp = SetEqualityMatch { element_size: 0 };
    assert!(cmp.compare(&[], &[]).is_ok());
}

#[test]
fn set_equality_zero_element_size_nonempty_fails() {
    let cmp = SetEqualityMatch { element_size: 0 };
    let err = cmp.compare(&[1], &[1]).unwrap_err();
    assert!(err.contains("element_size is 0"), "got: {err}");
}

#[test]
fn kind_dispatches_set_equality() {
    let kind = ComparatorKind::SetEquality { element_size: 2 };
    assert!(kind.compare(&[3, 4, 1, 2], &[1, 2, 3, 4]).is_ok());
    assert!(kind.compare(&[3, 4, 1, 2], &[1, 2, 3, 5]).is_err());
}