use super::*;
fn segments_of(text: &str, mode: TextSegmentation) -> Vec<&str> {
segment_ranges(text, mode)
.into_iter()
.map(|(s, e)| &text[s..e])
.collect()
}
#[test]
fn segment_ranges_newlines_exact_byte_ranges() {
let text = "first\nsecond\nthird";
let ranges = segment_ranges(text, TextSegmentation::Newlines);
assert_eq!(ranges, vec![(0, 5), (6, 12), (13, 18)]);
assert_eq!(
segments_of(text, TextSegmentation::Newlines),
["first", "second", "third"]
);
}
#[test]
fn segment_ranges_newlines_collapses_and_drops_blanks() {
let text = "\nalpha\n\n \nbeta\n\n";
let ranges = segment_ranges(text, TextSegmentation::Newlines);
assert_eq!(ranges, vec![(1, 6), (12, 16)]);
assert_eq!(
segments_of(text, TextSegmentation::Newlines),
["alpha", "beta"]
);
}
#[test]
fn segment_ranges_newlines_preserves_interior_whitespace() {
let text = " hello world \nx";
let segs = segments_of(text, TextSegmentation::Newlines);
assert_eq!(segs, [" hello world ", "x"]);
}
#[test]
fn segment_ranges_newlines_does_not_split_on_carriage_return() {
let text = "a\r\nb";
let segs = segments_of(text, TextSegmentation::Newlines);
assert_eq!(segs, ["a\r", "b"]);
}
#[test]
fn segment_ranges_newlines_multibyte_utf8_boundaries() {
let text = "héllo\nwörld";
let ranges = segment_ranges(text, TextSegmentation::Newlines);
assert_eq!(ranges, vec![(0, 6), (7, 13)]);
assert_eq!(
segments_of(text, TextSegmentation::Newlines),
["héllo", "wörld"]
);
}
#[test]
fn segment_ranges_newlines_single_line() {
let text = "no newline here";
assert_eq!(
segment_ranges(text, TextSegmentation::Newlines),
vec![(0, text.len())]
);
}
#[test]
fn segment_ranges_newlines_all_blank_is_empty() {
assert!(segment_ranges("", TextSegmentation::Newlines).is_empty());
assert!(segment_ranges("\n\n\n", TextSegmentation::Newlines).is_empty());
assert!(
segment_ranges(" \n \t \n ", TextSegmentation::Newlines).is_empty(),
"whitespace-only lines all dropped"
);
}
#[test]
fn segment_ranges_whole_is_single_full_span() {
let text = " line one\nline two ";
let ranges = segment_ranges(text, TextSegmentation::Whole);
assert_eq!(ranges, vec![(0, text.len())]);
assert_eq!(
segments_of(text, TextSegmentation::Whole),
[" line one\nline two "]
);
}
#[test]
fn segment_ranges_whole_all_blank_is_empty() {
assert!(segment_ranges("", TextSegmentation::Whole).is_empty());
assert!(segment_ranges(" \n\t ", TextSegmentation::Whole).is_empty());
}
#[test]
fn segment_ranges_whole_keeps_padded_nonblank() {
let text = " hi ";
assert_eq!(
segment_ranges(text, TextSegmentation::Whole),
vec![(0, text.len())]
);
}
#[test]
fn push_if_nonblank_keeps_nonblank_drops_blank() {
let text = "ab cd";
let mut out = Vec::new();
push_if_nonblank(&mut out, text, 0, 2);
push_if_nonblank(&mut out, text, 2, 5);
push_if_nonblank(&mut out, text, 5, 7);
assert_eq!(out, vec![(0, 2), (5, 7)]);
}
#[test]
fn push_if_nonblank_drops_empty_range() {
let text = "xyz";
let mut out = Vec::new();
push_if_nonblank(&mut out, text, 1, 1);
assert!(out.is_empty());
}
#[test]
fn audio_format_as_str_and_display() {
assert_eq!(AudioFormat::Wav.as_str(), "wav");
assert_eq!(AudioFormat::Flac.as_str(), "flac");
assert_eq!(AudioFormat::Wav.to_string(), "wav");
assert_eq!(format!("{}", AudioFormat::Flac), "flac");
}
#[test]
fn audio_format_is_variant_predicates() {
assert!(AudioFormat::Wav.is_wav());
assert!(!AudioFormat::Wav.is_flac());
assert!(AudioFormat::Flac.is_flac());
assert!(!AudioFormat::Flac.is_wav());
}
#[test]
fn audio_format_default_is_wav() {
assert_eq!(AudioFormat::default(), AudioFormat::Wav);
assert!(AudioFormat::default().is_wav());
}
#[test]
fn text_segmentation_as_str_and_display() {
assert_eq!(TextSegmentation::Newlines.as_str(), "newlines");
assert_eq!(TextSegmentation::Whole.as_str(), "whole");
assert_eq!(TextSegmentation::Newlines.to_string(), "newlines");
assert_eq!(format!("{}", TextSegmentation::Whole), "whole");
}
#[test]
fn text_segmentation_is_variant_predicates() {
assert!(TextSegmentation::Newlines.is_newlines());
assert!(!TextSegmentation::Newlines.is_whole());
assert!(TextSegmentation::Whole.is_whole());
assert!(!TextSegmentation::Whole.is_newlines());
}
#[test]
fn text_segmentation_default_is_newlines() {
assert_eq!(TextSegmentation::default(), TextSegmentation::Newlines);
assert!(TextSegmentation::default().is_newlines());
}
#[test]
fn tts_gen_config_new_equals_default_and_carries_defaults() {
let c = TtsGenConfig::new();
assert_eq!(c, TtsGenConfig::default());
assert_eq!(c.voice(), DEFAULT_VOICE);
assert_eq!(c.language(), DEFAULT_LANGUAGE);
assert!((c.speed() - 1.0).abs() < 1e-6);
assert!((c.temperature() - DEFAULT_TEMPERATURE).abs() < 1e-6);
assert!((c.top_p() - 0.0).abs() < 1e-6);
assert_eq!(c.top_k(), 0);
assert_eq!(c.repetition_penalty(), None);
assert_eq!(c.max_tokens(), DEFAULT_MAX_TOKENS);
assert_eq!(c.segmentation(), TextSegmentation::Newlines);
assert_eq!(c.audio_format(), AudioFormat::Wav);
assert!((c.streaming_interval() - DEFAULT_STREAMING_INTERVAL).abs() < 1e-6);
}
#[test]
fn tts_gen_config_builders_round_trip_all_fields() {
let c = TtsGenConfig::new()
.with_voice("bf_emma")
.with_language("en-gb")
.with_speed(1.25)
.with_temperature(0.4)
.with_top_p(0.9)
.with_top_k(40)
.with_repetition_penalty(Some(1.1))
.with_max_tokens(256)
.with_segmentation(TextSegmentation::Whole)
.with_audio_format(AudioFormat::Flac)
.with_streaming_interval(3.5);
assert_eq!(c.voice(), "bf_emma");
assert_eq!(c.language(), "en-gb");
assert!((c.speed() - 1.25).abs() < 1e-6);
assert!((c.temperature() - 0.4).abs() < 1e-6);
assert!((c.top_p() - 0.9).abs() < 1e-6);
assert_eq!(c.top_k(), 40);
assert_eq!(c.repetition_penalty(), Some(1.1));
assert_eq!(c.max_tokens(), 256);
assert!(c.segmentation().is_whole());
assert!(c.audio_format().is_flac());
assert!((c.streaming_interval() - 3.5).abs() < 1e-6);
}
#[test]
fn tts_gen_config_builder_is_field_isolated() {
let base = TtsGenConfig::default();
let only_topk = TtsGenConfig::default().with_top_k(7);
assert_eq!(only_topk.top_k(), 7);
assert_eq!(only_topk.voice(), base.voice());
assert_eq!(only_topk.language(), base.language());
assert!((only_topk.speed() - base.speed()).abs() < 1e-6);
assert!((only_topk.temperature() - base.temperature()).abs() < 1e-6);
assert!((only_topk.top_p() - base.top_p()).abs() < 1e-6);
assert_eq!(only_topk.repetition_penalty(), base.repetition_penalty());
assert_eq!(only_topk.max_tokens(), base.max_tokens());
assert_eq!(only_topk.segmentation(), base.segmentation());
assert_eq!(only_topk.audio_format(), base.audio_format());
assert!((only_topk.streaming_interval() - base.streaming_interval()).abs() < 1e-6);
}
#[test]
fn tts_gen_config_repetition_penalty_can_be_cleared() {
let c = TtsGenConfig::new()
.with_repetition_penalty(Some(1.3))
.with_repetition_penalty(None);
assert_eq!(c.repetition_penalty(), None);
}
#[test]
fn tts_gen_config_clone_and_partial_eq() {
let a = TtsGenConfig::new().with_voice("v").with_top_k(3);
let b = a.clone();
assert_eq!(a, b);
let c = a.clone().with_top_k(4);
assert_ne!(a, c, "differing top_k ⇒ unequal");
}
#[test]
fn tts_reference_new_accessors() {
let wav = Array::from_slice::<f32>(&[0.1_f32, 0.2, 0.3], &[3]).unwrap();
let r = TtsReference::new(Some(&wav), Some("caption"));
assert!(r.ref_audio().is_some());
assert_eq!(r.ref_text(), Some("caption"));
assert_eq!(r.ref_audio().unwrap().shape(), vec![3]);
}
#[test]
fn tts_reference_default_is_both_none() {
let r = TtsReference::default();
assert!(r.ref_audio().is_none());
assert!(r.ref_text().is_none());
}
#[test]
fn tts_reference_fields_are_independent() {
let wav = Array::from_slice::<f32>(&[0.0_f32], &[1]).unwrap();
let audio_only = TtsReference::new(Some(&wav), None);
assert!(audio_only.ref_audio().is_some() && audio_only.ref_text().is_none());
let text_only = TtsReference::new(None, Some("t"));
assert!(text_only.ref_audio().is_none() && text_only.ref_text() == Some("t"));
}
#[test]
fn tts_segment_new_all_accessors() {
let wav = Array::from_slice::<f32>(&[0.5_f32, -0.5], &[2]).unwrap();
let seg = TtsSegment::new(
"the text",
"af_heart",
"en",
1.5,
0.6,
0.85,
50,
Some(1.2),
900,
2.5,
4,
Some(&wav),
Some("ref transcript"),
);
assert_eq!(seg.text(), "the text");
assert_eq!(seg.voice(), "af_heart");
assert_eq!(seg.language(), "en");
assert!((seg.speed() - 1.5).abs() < 1e-6);
assert!((seg.temperature() - 0.6).abs() < 1e-6);
assert!((seg.top_p() - 0.85).abs() < 1e-6);
assert_eq!(seg.top_k(), 50);
assert_eq!(seg.repetition_penalty(), Some(1.2));
assert_eq!(seg.max_tokens(), 900);
assert!((seg.streaming_interval() - 2.5).abs() < 1e-6);
assert_eq!(seg.segment_idx(), 4);
assert!(seg.ref_audio().is_some());
assert_eq!(seg.ref_text(), Some("ref transcript"));
}
#[test]
fn tts_segment_without_reference() {
let seg = TtsSegment::new(
"x", "v", "en", 1.0, 0.7, 0.0, 0, None, 1200, 2.0, 0, None, None,
);
assert!(seg.ref_audio().is_none());
assert!(seg.ref_text().is_none());
assert_eq!(seg.repetition_penalty(), None);
assert_eq!(seg.segment_idx(), 0);
}
#[test]
fn audio_chunk_new_accessors_no_eval() {
let audio = Array::from_slice::<f32>(&[0.0_f32, 0.1, 0.2, 0.3], &[4]).unwrap();
let chunk = AudioChunk::new(audio, 16_000, 2, true, false);
assert_eq!(chunk.sample_rate(), 16_000);
assert_eq!(chunk.segment_idx(), 2);
assert!(chunk.is_streaming_chunk());
assert!(!chunk.is_final_chunk());
assert_eq!(chunk.len_samples(), 4);
assert!(!chunk.is_empty());
assert_eq!(chunk.audio_ref().shape(), vec![4], "no-eval shape read");
}
#[test]
fn audio_chunk_duration_seconds_is_samples_over_rate() {
let audio = Array::from_slice::<f32>(&[0.0_f32; 24_000], &[24_000]).unwrap();
let chunk = AudioChunk::new(audio, 24_000, 0, false, true);
assert!((chunk.duration_seconds() - 1.0).abs() < 1e-12);
}
#[test]
fn audio_chunk_duration_seconds_zero_rate_is_zero() {
let audio = Array::from_slice::<f32>(&[0.0_f32, 0.1], &[2]).unwrap();
let chunk = AudioChunk::new(audio, 0, 0, false, true);
assert_eq!(chunk.duration_seconds(), 0.0);
assert!(
chunk.duration_seconds().is_finite(),
"no NaN/inf for rate 0"
);
}
#[test]
fn audio_chunk_empty_waveform() {
let audio = Array::from_slice::<f32>(&[], &[0]).unwrap();
let chunk = AudioChunk::new(audio, 24_000, 0, false, true);
assert!(chunk.is_empty());
assert_eq!(chunk.len_samples(), 0);
assert_eq!(chunk.duration_seconds(), 0.0);
}
#[test]
fn audio_chunk_into_audio_and_samples() {
let audio = Array::from_slice::<f32>(&[0.0_f32, 0.25, 0.5], &[3]).unwrap();
let mut chunk = AudioChunk::new(audio, 24_000, 0, false, true);
let pcm = chunk.samples().unwrap();
assert_eq!(pcm, vec![0.0, 0.25, 0.5]);
let moved = AudioChunk::new(
Array::from_slice::<f32>(&[1.0_f32, 2.0], &[2]).unwrap(),
24_000,
0,
false,
true,
)
.into_audio();
assert_eq!(moved.shape(), vec![2]);
}
#[test]
fn default_constants_match_mlx_audio() {
assert_eq!(DEFAULT_VOICE, "af_heart");
assert_eq!(DEFAULT_LANGUAGE, "en");
assert!((DEFAULT_TEMPERATURE - 0.7).abs() < 1e-6);
assert_eq!(DEFAULT_MAX_TOKENS, 1200);
assert!((DEFAULT_STREAMING_INTERVAL - 2.0).abs() < 1e-6);
assert_eq!(MAX_TEXT_BYTES, 1024 * 1024);
}