mod common;
use transcribe_cpp::{Model, RunOptions, StreamExtension, StreamOptions, StreamState};
use transcribe_cpp::{MoonshineStreamingOptions, Stream};
const COARSE_DECODE_INTERVAL_MS: i32 = 960;
fn feed_in_chunks(stream: &mut Stream<'_>, pcm: &[f32], chunk: usize) {
for frame in pcm.chunks(chunk) {
stream.feed(frame).expect("feed");
}
}
#[test]
fn streams_jfk_committed_text() {
let (Some(model_path), Some(pcm)) = (common::smoke_streaming_model(), common::smoke_audio())
else {
eprintln!("skip streams_jfk_committed_text: streaming model/audio absent");
return;
};
let model = Model::load(&model_path).unwrap();
assert!(
model.capabilities().supports_streaming,
"model does not advertise streaming"
);
let mut session = model.session().unwrap();
let mut stream = session
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
assert_eq!(stream.state(), StreamState::Active);
feed_in_chunks(&mut stream, &pcm, 1600); let update = stream.finalize().unwrap();
assert!(update.is_final);
assert_eq!(stream.state(), StreamState::Finished);
assert!(
stream.last_status().is_none(),
"a healthy finished stream has no failure: {:?}",
stream.last_status()
);
let text = stream.text();
assert!(
text.full.to_lowercase().contains("country"),
"stream text: {:?}",
text.full
);
}
#[test]
fn on_finalize_policy_commits_full_text() {
let (Some(model_path), Some(pcm)) = (common::smoke_streaming_model(), common::smoke_audio())
else {
return;
};
let mut session = Model::load(&model_path).unwrap().session().unwrap();
let opts = StreamOptions {
commit_policy: transcribe_cpp::CommitPolicy::OnFinalize,
family: Some(StreamExtension::MoonshineStreaming(
MoonshineStreamingOptions {
min_decode_interval_ms: Some(COARSE_DECODE_INTERVAL_MS),
},
)),
..Default::default()
};
let mut stream = session.stream(&RunOptions::default(), &opts).unwrap();
feed_in_chunks(&mut stream, &pcm, 1600);
stream.finalize().unwrap();
let text = stream.text();
assert!(
text.committed.to_lowercase().contains("country"),
"committed: {:?}",
text.committed
);
}
#[test]
fn stream_revision_advances() {
let (Some(model_path), Some(pcm)) = (common::smoke_streaming_model(), common::smoke_audio())
else {
return;
};
let mut session = Model::load(&model_path).unwrap().session().unwrap();
let opts = StreamOptions {
family: Some(StreamExtension::MoonshineStreaming(
MoonshineStreamingOptions {
min_decode_interval_ms: Some(COARSE_DECODE_INTERVAL_MS),
},
)),
..Default::default()
};
let mut stream = session.stream(&RunOptions::default(), &opts).unwrap();
let r0 = stream.revision();
feed_in_chunks(&mut stream, &pcm, 1600);
stream.finalize().unwrap();
assert!(stream.revision() >= r0, "revision regressed");
}
#[test]
fn stream_reset_returns_to_idle() {
let (Some(model_path), Some(pcm)) = (common::smoke_streaming_model(), common::smoke_audio())
else {
return;
};
let mut session = Model::load(&model_path).unwrap().session().unwrap();
{
let mut stream = session
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
stream.feed(&pcm[..pcm.len().min(1600)]).unwrap();
stream.reset();
assert_eq!(stream.state(), StreamState::Idle);
}
let stream = session
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
assert_eq!(stream.state(), StreamState::Active);
}
#[test]
fn concurrent_compute_on_one_model_is_refused() {
let (Some(model_path), Some(pcm)) = (common::smoke_streaming_model(), common::smoke_audio())
else {
return;
};
let model = Model::load(&model_path).unwrap();
let mut s1 = model.session().unwrap();
let mut s2 = model.session().unwrap();
let mut stream1 = s1
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
stream1.feed(&pcm[..pcm.len().min(1600)]).unwrap();
match s2.stream(&RunOptions::default(), &StreamOptions::default()) {
Err(transcribe_cpp::Error::Busy(_)) => {}
other => panic!("expected Error::Busy for a second stream, got {other:?}"),
}
match s2.run(&pcm, &RunOptions::default()) {
Err(transcribe_cpp::Error::Busy(_)) => {}
other => panic!("expected Error::Busy for a run mid-stream, got {other:?}"),
}
drop(stream1);
let stream2 = s2
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
assert_eq!(stream2.state(), StreamState::Active);
}
#[test]
fn compute_lease_frees_at_finalize_and_reset() {
let (Some(model_path), Some(pcm)) = (common::smoke_streaming_model(), common::smoke_audio())
else {
return;
};
let model = Model::load(&model_path).unwrap();
let mut s1 = model.session().unwrap();
let mut s2 = model.session().unwrap();
let chunk = &pcm[..pcm.len().min(1600)];
{
let mut stream1 = s1
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
stream1.feed(chunk).unwrap();
stream1.finalize().unwrap();
let stream2 = s2.stream(&RunOptions::default(), &StreamOptions::default());
assert!(
stream2.is_ok(),
"finalize() must release the lease before drop: {stream2:?}"
);
}
{
let mut stream1 = s1
.stream(&RunOptions::default(), &StreamOptions::default())
.unwrap();
stream1.feed(chunk).unwrap();
stream1.reset();
let stream2 = s2.stream(&RunOptions::default(), &StreamOptions::default());
assert!(
stream2.is_ok(),
"reset() must release the lease before drop: {stream2:?}"
);
}
}
#[test]
fn stream_family_extension_accepted_or_rejected() {
let Some(model_path) = common::smoke_streaming_model() else {
return;
};
let model = Model::load(&model_path).unwrap();
let mut session = model.session().unwrap();
let opts = StreamOptions {
family: Some(StreamExtension::MoonshineStreaming(
MoonshineStreamingOptions {
min_decode_interval_ms: Some(200),
},
)),
..Default::default()
};
let ok = session.stream(&RunOptions::default(), &opts);
assert!(ok.is_ok(), "moonshine ext should be accepted: {ok:?}");
drop(ok);
let wrong = StreamOptions {
family: Some(StreamExtension::ParakeetStream(Default::default())),
..Default::default()
};
let err = session.stream(&RunOptions::default(), &wrong);
assert!(err.is_err(), "wrong-family ext should be rejected");
}