fix(transcription): scope streamer-test closures so out isn't borrow-locked

This commit is contained in:
iamdoubz
2026-07-11 09:08:20 -05:00
parent 931a2b78b4
commit 95f07e1dba
+30 -14
View File
@@ -533,6 +533,26 @@ pub fn run_streaming_worker<T, F>(
mod tests {
use super::*;
// Collect a step's emitted segments into `out` — the pushing closure lives
// and dies inside the call, so `out` is free to read in the asserts after
// (a single long-lived `on` closure would keep `out` mutably borrowed).
fn feed_into<D: Fn(&[f32], u64) -> String>(
s: &mut Streamer,
chunk: &[f32],
decode: &D,
out: &mut Vec<TranscriptSegment>,
) {
s.feed(chunk, decode, &mut |seg| out.push(seg));
}
fn flush_into<D: Fn(&[f32], u64) -> String>(
s: &mut Streamer,
decode: &D,
out: &mut Vec<TranscriptSegment>,
) {
s.flush(decode, &mut |seg| out.push(seg));
}
#[test]
fn streamer_grows_interim_then_commits_on_a_pause() {
// Text grows for two steps, then repeats (the speaker paused, so the
@@ -551,12 +571,11 @@ mod tests {
texts.get(i).copied().unwrap_or("hello world").to_string()
};
let mut out: Vec<TranscriptSegment> = Vec::new();
let mut on = |seg: TranscriptSegment| out.push(seg);
let step = vec![0.0f32; 16_000]; // exactly one 1s step
s.feed(&step, &decode, &mut on); // "hello" — interim
s.feed(&step, &decode, &mut on); // "hello world" — interim
s.feed(&step, &decode, &mut on); // stable + past min_commit → commit
feed_into(&mut s, &step, &decode, &mut out); // "hello" — interim
feed_into(&mut s, &step, &decode, &mut out); // "hello world" — interim
feed_into(&mut s, &step, &decode, &mut out); // stable + past min_commit → commit
assert_eq!(out.len(), 3);
assert!(out[0].interim && out[0].text == "hello");
@@ -566,7 +585,7 @@ mod tests {
// A new line after the commit uses a fresh id and a later offset.
let d2 = |_: &[f32], _o: u64| "next sentence".to_string();
s.feed(&step, &d2, &mut on);
feed_into(&mut s, &step, &d2, &mut out);
assert!(out[3].interim && out[3].text == "next sentence");
assert_ne!(out[3].id, out[2].id);
assert!(out[3].start_ms >= 3000, "starts after the 3 committed steps");
@@ -577,10 +596,9 @@ mod tests {
let mut s = Streamer::new(StreamTuning::new(false));
let decode = |_: &[f32], _o: u64| String::new();
let mut out: Vec<TranscriptSegment> = Vec::new();
let mut on = |seg: TranscriptSegment| out.push(seg);
let step = vec![0.0f32; 16_000];
for _ in 0..5 {
s.feed(&step, &decode, &mut on);
feed_into(&mut s, &step, &decode, &mut out);
}
assert!(out.is_empty(), "silence must not emit an empty line");
}
@@ -590,10 +608,9 @@ mod tests {
let mut s = Streamer::new(StreamTuning::new(false));
let decode = |_: &[f32], _o: u64| "partial".to_string();
let mut out: Vec<TranscriptSegment> = Vec::new();
let mut on = |seg: TranscriptSegment| out.push(seg);
s.feed(&vec![0.0f32; 16_000], &decode, &mut on);
feed_into(&mut s, &vec![0.0f32; 16_000], &decode, &mut out);
assert!(out.last().unwrap().interim, "still growing before flush");
s.flush(&decode, &mut on);
flush_into(&mut s, &decode, &mut out);
let last = out.last().unwrap();
assert!(!last.interim && last.text == "partial", "flush finalizes it");
}
@@ -616,11 +633,10 @@ mod tests {
format!("word{i}")
};
let mut out: Vec<TranscriptSegment> = Vec::new();
let mut on = |seg: TranscriptSegment| out.push(seg);
let step = vec![0.0f32; 16_000];
s.feed(&step, &decode, &mut on); // 1s
s.feed(&step, &decode, &mut on); // 2s
s.feed(&step, &decode, &mut on); // 3s == max → force commit despite changing text
feed_into(&mut s, &step, &decode, &mut out); // 1s
feed_into(&mut s, &step, &decode, &mut out); // 2s
feed_into(&mut s, &step, &decode, &mut out); // 3s == max → force commit
assert!(!out.last().unwrap().interim);
}