#![allow(clippy::field_reassign_with_default)]
use super::*;
fn word_sequence(count: usize) -> String {
(0..count)
.map(|i| format!("word{i}"))
.collect::<Vec<_>>()
.join(" ")
}
#[test]
fn test_parity_result_pass() {
let result = ParityResult::Pass {
wer: 0.005,
timestamp_tolerance_ms: Some(50),
};
assert!(result.is_pass());
assert!(!result.is_fail());
}
#[test]
fn test_parity_result_fail() {
let result = ParityResult::Fail {
wer: 0.15,
expected: "hello world".to_string(),
actual: "hello word".to_string(),
differences: vec![],
};
assert!(result.is_fail());
assert!(!result.is_pass());
}
#[test]
fn test_parity_config_default() {
let config = ParityConfig::default();
assert!((config.max_wer - 0.01).abs() < f64::EPSILON);
assert_eq!(config.timestamp_tolerance_ms, 50);
assert!(config.normalize_whitespace);
assert!(!config.normalize_punctuation);
assert!(!config.case_insensitive);
}
#[test]
fn test_wer_identical() {
let wer = calculate_wer("hello world", "hello world");
assert!((wer - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_wer_one_word_different() {
let wer = calculate_wer("hello world", "hello word");
assert!((wer - 0.5).abs() < f64::EPSILON);
}
#[test]
fn test_wer_completely_different() {
let wer = calculate_wer("hello world", "goodbye earth");
assert!((wer - 1.0).abs() < f64::EPSILON);
}
#[test]
fn test_wer_empty_reference() {
let wer = calculate_wer("", "hello");
assert!((wer - 1.0).abs() < f64::EPSILON);
}
#[test]
fn test_wer_empty_both() {
let wer = calculate_wer("", "");
assert!((wer - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_wer_insertion() {
let wer = calculate_wer("a b", "a x b");
assert!((wer - 0.5).abs() < f64::EPSILON);
}
#[test]
fn test_wer_deletion() {
let wer = calculate_wer("a b c", "a c");
assert!((wer - 1.0 / 3.0).abs() < 0.01);
}
#[test]
fn test_levenshtein_identical() {
let dist = levenshtein_distance(&["a", "b", "c"], &["a", "b", "c"]);
assert_eq!(dist, 0);
}
#[test]
fn test_levenshtein_one_substitution() {
let dist = levenshtein_distance(&["a", "b", "c"], &["a", "x", "c"]);
assert_eq!(dist, 1);
}
#[test]
fn test_levenshtein_one_insertion() {
let dist = levenshtein_distance(&["a", "b"], &["a", "x", "b"]);
assert_eq!(dist, 1);
}
#[test]
fn test_levenshtein_one_deletion() {
let dist = levenshtein_distance(&["a", "b", "c"], &["a", "c"]);
assert_eq!(dist, 1);
}
#[test]
fn test_levenshtein_empty_first() {
let dist = levenshtein_distance::<&str>(&[], &["a", "b"]);
assert_eq!(dist, 2);
}
#[test]
fn test_levenshtein_empty_second() {
let dist = levenshtein_distance(&["a", "b"], &[]);
assert_eq!(dist, 2);
}
#[test]
fn test_parity_test_exact_match() {
let test = ParityTest::new(
PathBuf::from("test.wav"),
"Hello world".to_string(),
"Hello world".to_string(),
);
let result = test.verify_text_parity();
assert!(result.is_pass());
}
#[test]
fn test_parity_test_whitespace_normalization() {
let test = ParityTest::new(
PathBuf::from("test.wav"),
"Hello world".to_string(),
"Hello world".to_string(),
);
let result = test.verify_text_parity();
assert!(result.is_pass());
}
#[test]
fn test_parity_test_within_tolerance() {
let reference = word_sequence(100);
let mut hypothesis = reference.clone();
hypothesis = hypothesis.replace("word50", "changed");
let test = ParityTest::new(PathBuf::from("test.wav"), reference, hypothesis);
let result = test.verify_text_parity();
assert!(result.is_pass(), "WER at 1% should pass");
}
#[test]
fn test_parity_test_exceeds_tolerance() {
let reference = word_sequence(100);
let mut hypothesis = reference.clone();
hypothesis = hypothesis.replace("word10", "changed10");
hypothesis = hypothesis.replace("word20", "changed20");
hypothesis = hypothesis.replace("word30", "changed30");
hypothesis = hypothesis.replace("word40", "changed40");
hypothesis = hypothesis.replace("word50", "changed50");
let test = ParityTest::new(PathBuf::from("test.wav"), reference, hypothesis);
let result = test.verify_text_parity();
assert!(result.is_fail(), "WER at 5% should fail");
}
#[test]
fn test_parity_test_case_insensitive() {
let mut config = ParityConfig::default();
config.case_insensitive = true;
let test = ParityTest::new(
PathBuf::from("test.wav"),
"Hello World".to_string(),
"hello world".to_string(),
)
.with_config(config);
let result = test.verify_text_parity();
assert!(result.is_pass());
}
#[test]
fn test_parity_test_with_hf_output() {
let test = ParityTest::new(
PathBuf::from("test.wav"),
"Hello".to_string(),
"Hello".to_string(),
)
.with_hf_output("Hello".to_string());
assert!(test.hf_output.is_some());
}
#[test]
fn test_parity_benchmark_pass() {
let bench = ParityBenchmark::new(0.5, 0.52); assert!(bench.parity);
assert!(bench.verify().is_ok());
}
#[test]
fn test_parity_benchmark_fail() {
let bench = ParityBenchmark::new(0.5, 0.6); assert!(!bench.parity);
assert!(bench.verify().is_err());
}
#[test]
fn test_parity_benchmark_at_threshold() {
let bench = ParityBenchmark::new(1.0, 1.1); assert!(bench.parity);
assert!(bench.verify().is_ok());
}
#[test]
fn test_parity_benchmark_just_over_threshold() {
let bench = ParityBenchmark::new(1.0, 1.11); assert!(!bench.parity);
assert!(bench.verify().is_err());
}
#[test]
fn test_parity_error_display() {
let err = ParityError::PerformanceRegression {
cpp: 0.5,
apr: 0.6,
ratio: 1.2,
};
let msg = err.to_string();
assert!(msg.contains("Performance regression"));
assert!(msg.contains("1.2"));
}
#[test]
fn test_parity_error_text_parity() {
let err = ParityError::TextParityFailed {
wer: 0.15,
threshold: 0.01,
};
let msg = err.to_string();
assert!(msg.contains("Text parity failed"));
}
#[test]
fn test_parity_error_timestamp_parity() {
let err = ParityError::TimestampParityFailed {
delta_ms: 100,
tolerance_ms: 50,
};
let msg = err.to_string();
assert!(msg.contains("Timestamp parity failed"));
}
#[test]
fn test_parity_error_whisper_cpp_not_found() {
let err = ParityError::WhisperCppNotFound {
path: "/usr/bin/whisper-cli".to_string(),
};
let msg = err.to_string();
assert!(msg.contains("whisper.cpp"));
assert!(msg.contains("/usr/bin/whisper-cli"));
}
#[test]
fn test_difference_kind_eq() {
assert_eq!(DifferenceKind::Text, DifferenceKind::Text);
assert_ne!(DifferenceKind::Text, DifferenceKind::Timestamp);
}
#[test]
fn test_find_differences_identical() {
let diffs = find_text_differences("hello world", "hello world");
assert!(diffs.is_empty());
}
#[test]
fn test_find_differences_substitution() {
let diffs = find_text_differences("hello world", "hello word");
assert_eq!(diffs.len(), 1);
assert_eq!(diffs[0].kind, DifferenceKind::Word);
assert_eq!(diffs[0].expected, "world");
assert_eq!(diffs[0].actual, "word");
}
#[test]
fn test_find_differences_extra() {
let diffs = find_text_differences("a b", "a b c");
assert_eq!(diffs.len(), 1);
assert_eq!(diffs[0].kind, DifferenceKind::ExtraSegment);
}
#[test]
fn test_find_differences_missing() {
let diffs = find_text_differences("a b c", "a b");
assert_eq!(diffs.len(), 1);
assert_eq!(diffs[0].kind, DifferenceKind::MissingSegment);
}