use super::*;
use crate::audio::lid::{MAX_FRAMES, MAX_SAMPLES, MIN_FRAMES, MIN_SAMPLES, frame_count};
#[test]
fn frame_count_out_of_range_carries_both_unit_systems() {
let detail = FrameCountOutOfRange::for_samples(1_000_000);
assert_eq!(detail.samples(), 1_000_000);
assert_eq!(detail.frames(), frame_count(1_000_000));
assert_eq!(detail.min_frames(), MIN_FRAMES);
assert_eq!(detail.max_frames(), MAX_FRAMES);
assert_eq!(detail.min_samples(), MIN_SAMPLES);
assert_eq!(detail.max_samples(), MAX_SAMPLES);
assert!(!detail.is_too_short());
assert!(FrameCountOutOfRange::for_samples(0).is_too_short());
assert!(FrameCountOutOfRange::for_samples(MIN_SAMPLES - 1).is_too_short());
assert!(!FrameCountOutOfRange::for_samples(MIN_SAMPLES).is_too_short());
}
#[test]
fn frame_count_out_of_range_renders_the_actionable_numbers() {
let rendered = Error::from(FrameCountOutOfRange::for_samples(800)).to_string();
for needle in ["800 samples", "6 mel frames", "10..=3001", "1440..=480159"] {
assert!(
rendered.contains(needle),
"{needle:?} missing from {rendered:?}"
);
}
assert!(
!rendered.contains("dimension"),
"the CoreML runtime's axis wording must not surface: {rendered}"
);
}
#[test]
fn contract_mismatch_reports_the_feature_and_both_sides() {
let detail = ContractMismatch::new(
"mel_features",
"[1, 10..=3001, 60] float32".to_owned(),
"[1, 301, 80] float32".to_owned(),
);
assert_eq!(detail.feature(), "mel_features");
assert_eq!(detail.expected(), "[1, 10..=3001, 60] float32");
assert_eq!(detail.actual(), "[1, 301, 80] float32");
let rendered = Error::from(detail).to_string();
assert!(rendered.contains("mel_features"), "{rendered}");
assert!(rendered.contains("[1, 301, 80] float32"), "{rendered}");
}
#[test]
fn output_shape_reports_both_shapes() {
let detail = OutputShape::new(vec![107, 1], vec![1, 107]);
assert_eq!(detail.got(), [107, 1]);
assert_eq!(detail.expected(), [1, 107]);
assert!(Error::from(detail).to_string().contains("[107, 1]"));
}
#[test]
fn every_payload_variant_is_a_newtype() {
let cases: Vec<Error> = vec![
ContractMismatch::new("mel_features", "a".to_owned(), "b".to_owned()).into(),
OutputShape::new(vec![2], vec![1]).into(),
FrameCountOutOfRange::for_samples(0).into(),
Error::NonFiniteInput(3),
Error::NonFiniteOutput(4),
Error::UnknownLanguageIndex(999),
WinditError::TooManyWindows { got: 9, max: 4 }.into(),
Error::EmptyWindows,
Error::ZeroMassAggregate(ScorePooling::MeanLogProbability),
Error::UnnormalizableWindow(7),
NotADistribution::new(ScorePooling::MeanProbability, 0.5).into(),
Error::LanguageCountMismatch(106),
InvalidLogProbability::new(12, 0.5).into(),
Error::PositiveOutput(InvalidLogProbability::new(94, 0.25)),
];
for error in cases {
let rendered = error.to_string();
assert!(!rendered.is_empty(), "every variant must render");
match error {
Error::ContractMismatch(detail) => assert_eq!(detail.feature(), "mel_features"),
Error::OutputShape(detail) => assert_eq!(detail.expected(), [1]),
Error::FrameCountOutOfRange(detail) => assert!(detail.is_too_short()),
Error::NonFiniteInput(index) => assert_eq!(index, 3),
Error::NonFiniteOutput(index) => assert_eq!(index, 4),
Error::UnknownLanguageIndex(index) => assert_eq!(index, 999),
Error::Windowing(detail) => {
assert!(matches!(detail, WinditError::TooManyWindows { got: 9, .. }));
}
Error::EmptyWindows => {}
Error::ZeroMassAggregate(pooling) => {
assert_eq!(pooling, ScorePooling::MeanLogProbability);
}
Error::UnnormalizableWindow(position) => assert_eq!(position, 7),
Error::NotADistribution(detail) => {
assert_eq!(detail.pooling(), ScorePooling::MeanProbability);
assert!((detail.mass() - 0.5).abs() < f64::EPSILON);
}
Error::LanguageCountMismatch(got) => assert_eq!(got, 106),
Error::InvalidLogProbability(detail) => {
assert_eq!(detail.index(), 12);
assert_eq!(detail.value(), 0.5);
}
Error::PositiveOutput(detail) => {
assert_eq!(detail.index(), 94);
assert_eq!(detail.value(), 0.25);
}
other => panic!("unexpected variant {other:?}"),
}
}
}
#[test]
fn index_variants_render_their_index() {
assert!(Error::NonFiniteInput(1_234).to_string().contains("1234"));
assert!(Error::NonFiniteOutput(56).to_string().contains("56"));
assert!(Error::UnknownLanguageIndex(107).to_string().contains("107"));
assert!(
Error::UnnormalizableWindow(41)
.to_string()
.contains("window 41")
);
}
#[test]
fn foreign_errors_convert_and_keep_their_message() {
let load = crate::LoadError::NotFound(std::path::PathBuf::from("/nonexistent/lid.mlmodelc"));
let inner = load.to_string();
let error = Error::from(load);
assert!(matches!(error, Error::Load(_)));
assert!(error.to_string().contains(&inner), "{error}");
let tensor = crate::TensorError::ShapeMismatch(crate::ShapeMismatch::new(60, 61));
assert!(matches!(Error::from(tensor), Error::Tensor(_)));
}
#[test]
fn the_long_clip_payloads_name_what_was_wrong() {
let rendered = Error::LanguageCountMismatch(106).to_string();
assert!(rendered.contains("106"), "{rendered}");
assert!(
rendered.contains("107"),
"the expected width too: {rendered}"
);
let detail = InvalidLogProbability::new(94, f32::NAN);
assert_eq!(detail.index(), 94);
assert!(detail.value().is_nan());
let rendered = Error::from(detail).to_string();
assert!(rendered.contains("94"), "{rendered}");
assert!(rendered.contains("NaN"), "{rendered}");
let inner = WinditError::TooManyWindows {
got: 40_001,
max: 10,
};
let text = inner.to_string();
assert!(Error::from(inner).to_string().contains(&text));
}