use super::{
ApproximateMatch, Comparator, ComparatorKind, ExactMatch, SetEqualityMatch, UnorderedMatch,
};
#[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}");
}
#[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());
}
#[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}");
}
#[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);
}
#[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());
}