use super::*;
#[test]
fn test_home_dir_returns_some() {
assert!(
home_dir().is_some(),
"home_dir() must return Some on this platform"
);
}
#[test]
fn test_hash_file_sha256_and_verify() {
let tmp = tempfile::tempdir().expect("tempdir");
let path = tmp.path().join("blob.bin");
std::fs::write(&path, b"hello").unwrap();
let digest = hash_file_sha256(&path).expect("hash");
assert_eq!(digest.len(), 64);
verify_pinned_checksum(&path, &digest).expect("matching digest");
let wrong = "00".repeat(32);
let err = verify_pinned_checksum(&path, &wrong).expect_err("wrong digest");
let msg = format!("{err:#}");
assert!(
msg.contains("SHA-256 mismatch") || msg.contains("model load error"),
"unexpected: {msg}"
);
}
#[cfg(feature = "net")]
#[test]
fn test_reject_if_over_download_cap() {
assert!(reject_if_over_download_cap(0, MAX_DOWNLOAD_BYTES).is_ok());
assert!(reject_if_over_download_cap(0, MAX_DOWNLOAD_BYTES + 1).is_err());
assert!(reject_if_over_download_cap(MAX_DOWNLOAD_BYTES, 1).is_err());
assert!(reject_if_over_download_cap(MAX_DOWNLOAD_BYTES - 10, 10).is_ok());
}
#[test]
fn test_default_model_dir_contains_gigastt() {
let dir = default_model_dir();
assert!(
dir.contains(".gigastt"),
"default_model_dir() should contain \".gigastt\", got: {dir}"
);
}
#[test]
fn test_partial_path_appends_suffix() {
let p = partial_path(Path::new("/tmp/gigastt/encoder.onnx"));
assert_eq!(
p,
std::path::PathBuf::from("/tmp/gigastt/encoder.onnx.partial"),
);
}
#[test]
fn test_download_writes_partial_then_renames() {
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("encoder.onnx");
let payload = b"fake encoder weights";
let expected = sha256_hex(payload);
let partial = stage_partial(&final_path, payload);
assert!(partial.exists(), "precondition: partial is present");
assert!(!final_path.exists(), "precondition: final is absent");
finalize_download(&partial, &final_path, Some(&expected), "encoder.onnx")
.expect("finalize should succeed");
assert!(
!partial.exists(),
"partial must be gone after atomic rename"
);
assert!(
final_path.exists(),
"final path must exist after atomic rename"
);
assert_eq!(std::fs::read(&final_path).unwrap(), payload);
}
#[test]
fn test_download_crash_before_rename_leaves_no_final_file() {
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("encoder.onnx");
let partial = stage_partial(&final_path, b"half-written junk");
assert!(partial.exists(), "partial must exist to simulate crash");
assert!(
!final_path.exists(),
"crash before rename must never leave the final artefact visible"
);
assert!(
!is_model_present(ModelVariant::Rnnt, tmp.path()),
"is_model_present must not accept a staged partial"
);
assert!(
!is_model_present(ModelVariant::E2eRnnt, tmp.path()),
"is_model_present must not accept a staged partial"
);
}
#[test]
fn test_download_rejects_sha256_mismatch() {
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("decoder.onnx");
let payload = b"real bytes";
let wrong_expected = sha256_hex(b"different bytes");
let partial = stage_partial(&final_path, payload);
let err = finalize_download(&partial, &final_path, Some(&wrong_expected), "decoder.onnx")
.expect_err("mismatch must error");
let msg = format!("{err}");
assert!(msg.contains("SHA-256 mismatch"), "unexpected error: {msg}");
assert!(!partial.exists(), "partial must be removed on SHA mismatch");
assert!(
!final_path.exists(),
"final must never appear on SHA mismatch"
);
}
#[test]
fn test_download_atomic_on_success_without_checksum() {
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("vocab.txt");
let payload = b"token0\ntoken1\n";
let partial = stage_partial(&final_path, payload);
finalize_download(&partial, &final_path, None, "vocab.txt")
.expect("no-checksum finalize should succeed");
assert!(!partial.exists(), "partial must be gone after rename");
assert!(final_path.exists(), "final path must exist");
assert_eq!(std::fs::read(&final_path).unwrap(), payload);
}
#[test]
fn test_sha256_file_matches_in_memory_hash() {
let tmp = tempfile::tempdir().expect("tempdir");
let p = tmp.path().join("blob");
let payload = b"gigastt-model-bytes";
std::fs::write(&p, payload).unwrap();
let got = sha256_file(&p).expect("sha256_file");
let want = sha256_hex(payload);
assert_eq!(got, want);
}
#[cfg(feature = "diarization")]
#[test]
fn test_speaker_model_sha256_shape() {
assert_eq!(
SPEAKER_MODEL_SHA256.len(),
64,
"SPEAKER_MODEL_SHA256 must be a 64-char hex digest"
);
assert!(
SPEAKER_MODEL_SHA256
.chars()
.all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)),
"SPEAKER_MODEL_SHA256 must be lowercase hex; got: {SPEAKER_MODEL_SHA256}"
);
}
#[cfg(feature = "diarization")]
#[test]
fn test_speaker_model_rejects_sha256_mismatch() {
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join(SPEAKER_MODEL_FILE);
let partial = stage_partial(&final_path, b"not the real wespeaker weights");
let err = finalize_download(
&partial,
&final_path,
Some(SPEAKER_MODEL_SHA256),
SPEAKER_MODEL_FILE,
)
.expect_err("speaker mismatch must error");
assert!(
format!("{err}").contains("SHA-256 mismatch"),
"unexpected error: {err}"
);
assert!(
!partial.exists(),
"partial speaker model must be removed on mismatch"
);
assert!(
!final_path.exists(),
"final speaker model must never appear on mismatch"
);
}
#[cfg(feature = "diarization")]
#[test]
fn test_speaker_model_partial_promoted_on_match() {
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join(SPEAKER_MODEL_FILE);
let payload = b"wespeaker-surrogate";
let expected = sha256_hex(payload);
let partial = stage_partial(&final_path, payload);
finalize_download(&partial, &final_path, Some(&expected), SPEAKER_MODEL_FILE)
.expect("matching partial must promote");
assert!(!partial.exists());
assert!(final_path.exists());
assert_eq!(std::fs::read(&final_path).unwrap(), payload);
}
#[test]
fn test_partial_path_unique_contains_pid_and_timestamp() {
let p = partial_path_unique(Path::new("/tmp/final.onnx"));
let s = p.to_string_lossy();
assert!(s.contains(".partial."));
assert!(s.contains(&std::process::id().to_string()));
}
#[cfg(unix)]
#[test]
fn test_acquire_download_lock_creates_lock_file() {
let tmp = tempfile::tempdir().expect("tempdir");
let lock = acquire_download_lock(tmp.path()).expect("acquire lock");
assert!(tmp.path().join(".download.lock").exists());
drop(lock);
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_stream_to_partial_then_finalize_success() {
let server = wiremock::MockServer::start().await;
let payload = b"fake model bytes";
wiremock::Mock::given(wiremock::matchers::method("GET"))
.and(wiremock::matchers::path("/model.onnx"))
.respond_with(
wiremock::ResponseTemplate::new(200)
.set_body_bytes(payload.as_slice())
.insert_header("content-length", payload.len().to_string()),
)
.mount(&server)
.await;
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("model.onnx");
let url = format!("{}/model.onnx", server.uri());
stream_to_partial_then_finalize(&url, &final_path, None, "model.onnx")
.await
.expect("download should succeed");
assert!(final_path.exists());
assert_eq!(std::fs::read(&final_path).unwrap(), payload);
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_stream_to_partial_then_finalize_http_error() {
let server = wiremock::MockServer::start().await;
wiremock::Mock::given(wiremock::matchers::method("GET"))
.and(wiremock::matchers::path("/missing.onnx"))
.respond_with(wiremock::ResponseTemplate::new(404))
.mount(&server)
.await;
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("missing.onnx");
let url = format!("{}/missing.onnx", server.uri());
let err = stream_to_partial_then_finalize(&url, &final_path, None, "missing.onnx")
.await
.expect_err("404 should fail");
let msg = format!("{err}");
assert!(msg.contains("404"), "error should mention 404: {msg}");
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_stream_to_partial_then_finalize_checksum_mismatch() {
let server = wiremock::MockServer::start().await;
let payload = b"wrong bytes";
wiremock::Mock::given(wiremock::matchers::method("GET"))
.and(wiremock::matchers::path("/model.onnx"))
.respond_with(wiremock::ResponseTemplate::new(200).set_body_bytes(payload.as_slice()))
.mount(&server)
.await;
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("model.onnx");
let url = format!("{}/model.onnx", server.uri());
let wrong_hash = sha256_hex(b"different bytes");
let err = stream_to_partial_then_finalize(&url, &final_path, Some(&wrong_hash), "model.onnx")
.await
.expect_err("checksum mismatch should fail");
let msg = format!("{err}");
assert!(
msg.contains("SHA-256 mismatch"),
"error should mention mismatch: {msg}"
);
}
#[test]
fn test_partial_file_guard_drop_removes_staged_file() {
let tmp = tempfile::tempdir().expect("tempdir");
let partial = tmp.path().join("model.onnx.partial");
std::fs::write(&partial, b"half-written junk").expect("write partial");
drop(PartialFileGuard::new(partial.clone()));
assert!(!partial.exists(), "guard drop must remove the partial");
std::fs::write(&partial, b"half-written junk").expect("write partial");
PartialFileGuard::new(partial.clone()).disarm();
assert!(partial.exists(), "disarmed guard must keep the partial");
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_stream_to_partial_then_finalize_stream_error_removes_partial() {
use tokio::io::AsyncReadExt as _;
let listener = tokio::net::TcpListener::bind("127.0.0.1:0")
.await
.expect("bind stub server");
let addr = listener.local_addr().expect("local addr");
let server = tokio::spawn(async move {
let (mut socket, _) = listener.accept().await.expect("accept");
let mut buf = [0u8; 1024];
let _ = socket.read(&mut buf).await;
let head = "HTTP/1.1 200 OK\r\ncontent-length: 4096\r\nconnection: close\r\n\r\n";
socket.write_all(head.as_bytes()).await.expect("write head");
socket
.write_all(b"half of the model")
.await
.expect("write body");
});
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("model.onnx");
let url = format!("http://{addr}/model.onnx");
let err = stream_to_partial_then_finalize(&url, &final_path, None, "model.onnx")
.await
.expect_err("truncated body should fail");
let msg = format!("{err:#}");
assert!(
msg.contains("Download stream error"),
"error should come from the stream read: {msg}"
);
server.await.expect("stub server task");
assert!(!final_path.exists(), "final must not appear on failure");
let orphans: Vec<_> = std::fs::read_dir(tmp.path())
.expect("read dir")
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().contains(".partial"))
.collect();
assert!(orphans.is_empty(), "no orphan .partial files: {orphans:?}");
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_stream_to_partial_then_finalize_json_event_sequence() {
let server = wiremock::MockServer::start().await;
let payload = b"fake model bytes";
wiremock::Mock::given(wiremock::matchers::method("GET"))
.and(wiremock::matchers::path("/model.onnx"))
.respond_with(
wiremock::ResponseTemplate::new(200)
.set_body_bytes(payload.as_slice())
.insert_header("content-length", payload.len().to_string()),
)
.mount(&server)
.await;
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("model.onnx");
let url = format!("{}/model.onnx", server.uri());
let expected = sha256_hex(payload);
let (sink, log) = ProgressSink::capturing();
stream_to_partial_then_finalize_with_sink(
&url,
&final_path,
Some(&expected),
"model.onnx",
&sink,
)
.await
.expect("download should succeed");
let events = log.lock().unwrap();
assert_eq!(
events.as_slice(),
[
ProgressEvent::Download {
file: "model.onnx".to_string(),
bytes_done: payload.len() as u64,
bytes_total: payload.len() as u64,
},
ProgressEvent::Verify {
file: "model.onnx".to_string(),
},
]
);
for event in events.iter() {
let parsed: serde_json::Value =
serde_json::from_str(&event.to_ndjson()).expect("event must be NDJSON");
assert!(parsed.get("phase").is_some());
}
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_stream_to_partial_then_finalize_json_no_verify_without_checksum() {
let server = wiremock::MockServer::start().await;
let payload = b"fake model bytes";
wiremock::Mock::given(wiremock::matchers::method("GET"))
.and(wiremock::matchers::path("/model.onnx"))
.respond_with(
wiremock::ResponseTemplate::new(200)
.set_body_bytes(payload.as_slice())
.insert_header("content-length", payload.len().to_string()),
)
.mount(&server)
.await;
let tmp = tempfile::tempdir().expect("tempdir");
let final_path = tmp.path().join("model.onnx");
let url = format!("{}/model.onnx", server.uri());
let (sink, log) = ProgressSink::capturing();
stream_to_partial_then_finalize_with_sink(&url, &final_path, None, "model.onnx", &sink)
.await
.expect("download should succeed");
let events = log.lock().unwrap();
assert!(
events
.iter()
.all(|e| !matches!(e, ProgressEvent::Verify { .. })),
"no checksum -> no verify event: {events:?}"
);
assert!(
events
.iter()
.any(|e| matches!(e, ProgressEvent::Download { .. })),
"download events expected: {events:?}"
);
}
#[test]
fn test_punct_files_checksums_are_pinned() {
assert_eq!(PUNCT_FILES.len(), 3);
for (file, sum) in PUNCT_FILES {
assert_eq!(
sum.len(),
64,
"{file} punct checksum must be 64 hex chars, got: {sum}"
);
assert!(
sum.chars()
.all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)),
"{file} punct checksum must be lowercase hex, got: {sum}"
);
}
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_punct_model_present_no_download() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for (file, _) in PUNCT_FILES {
std::fs::write(dir.join(file), b"stub").unwrap();
}
ensure_punct_model(dir.to_str().unwrap())
.await
.expect("present model must short-circuit");
let partials: Vec<_> = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().contains(".partial"))
.collect();
assert!(partials.is_empty(), "no .partial files: {partials:?}");
for (file, _) in PUNCT_FILES {
assert_eq!(std::fs::read(dir.join(file)).unwrap(), b"stub");
}
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_model_none_respects_existing_e2e_install() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::E2eRnnt.prequantized_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
let variant = ensure_model_variant(None, dir.to_str().unwrap())
.await
.expect("ensure_model_variant should succeed");
assert_eq!(
variant,
ModelVariant::E2eRnnt,
"must use the installed E2eRnnt"
);
let partials: Vec<_> = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().contains(".partial"))
.collect();
assert!(
partials.is_empty(),
"no .partial files must exist: {partials:?}"
);
for f in ModelVariant::E2eRnnt.prequantized_files() {
assert_eq!(
std::fs::read(dir.join(f)).unwrap(),
b"stub",
"{f} must be unchanged"
);
}
}
#[test]
fn test_fp32_only_e2e_is_not_usable() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::E2eRnnt.download_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
assert!(
is_model_present(ModelVariant::E2eRnnt, dir),
"FP32 download set is complete"
);
assert!(
!is_usable_present(ModelVariant::E2eRnnt, dir),
"FP32-only must not count as a usable install"
);
let existing = ModelVariant::detect_in_dir(dir).filter(|&v| is_usable_present(v, dir));
assert_eq!(existing, None, "detect+usable filter must ignore FP32-only");
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_model_wrapper_uses_existing_install() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
ensure_model(dir.to_str().unwrap())
.await
.expect("ensure_model must succeed against an existing install");
let partials: Vec<_> = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().contains(".partial"))
.collect();
assert!(
partials.is_empty(),
"ensure_model must not download when the set is present: {partials:?}"
);
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_model_variant_explicit_match_uses_existing() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
let variant = ensure_model_variant(Some(ModelVariant::Rnnt), dir.to_str().unwrap())
.await
.expect("explicit matching variant must short-circuit");
assert_eq!(variant, ModelVariant::Rnnt);
let has_partial = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.any(|e| e.file_name().to_string_lossy().contains(".partial"));
assert!(!has_partial, "no download for an explicit matching variant");
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_vad_model_present_no_download() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
std::fs::write(dir.join(crate::vad::VAD_MODEL_FILE), b"stub vad").unwrap();
ensure_vad_model(dir.to_str().unwrap())
.await
.expect("present VAD model must short-circuit");
let partials: Vec<_> = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.filter(|e| e.file_name().to_string_lossy().contains(".partial"))
.collect();
assert!(partials.is_empty(), "no .partial files: {partials:?}");
assert_eq!(
std::fs::read(dir.join(crate::vad::VAD_MODEL_FILE)).unwrap(),
b"stub vad",
"existing VAD model must be left untouched"
);
}
#[test]
fn test_default_punct_and_vad_dirs_are_model_siblings() {
let punct = default_punct_model_dir();
let vad = default_vad_model_dir();
assert!(
punct.contains(".gigastt") && punct.ends_with("punct"),
"punct dir should be under .gigastt and end with 'punct', got: {punct}"
);
assert!(
vad.contains(".gigastt") && vad.ends_with("vad"),
"vad dir should be under .gigastt and end with 'vad', got: {vad}"
);
}
#[test]
fn test_vad_model_sha256_shape() {
assert_eq!(
VAD_MODEL_SHA256.len(),
64,
"VAD_MODEL_SHA256 must be a 64-char hex digest"
);
assert!(
VAD_MODEL_SHA256
.chars()
.all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)),
"VAD_MODEL_SHA256 must be lowercase hex; got: {VAD_MODEL_SHA256}"
);
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_model_variant_uses_complete_set_in_nested_path() {
let tmp = tempfile::tempdir().expect("tempdir");
let nested = tmp.path().join("a").join("b").join("models");
std::fs::create_dir_all(&nested).unwrap();
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(nested.join(f), b"stub").unwrap();
}
let variant = ensure_model_variant(None, nested.to_str().unwrap())
.await
.expect("nested complete install must be used as-is");
assert_eq!(variant, ModelVariant::Rnnt);
}
#[test]
fn test_prequantized_files_mapping() {
assert_eq!(
ModelVariant::Rnnt.prequantized_files(),
[
"v3_rnnt_encoder_int8.onnx",
"v3_rnnt_decoder.onnx",
"v3_rnnt_joint.onnx",
"v3_vocab.txt",
]
);
assert_eq!(
ModelVariant::E2eRnnt.prequantized_files(),
[
"v3_e2e_rnnt_encoder_int8.onnx",
"v3_e2e_rnnt_decoder.onnx",
"v3_e2e_rnnt_joint.onnx",
"v3_e2e_rnnt_vocab.txt",
]
);
assert_eq!(
ModelVariant::MlCtc.prequantized_files(),
["multilingual_ctc.int8.onnx", "multilingual_vocab.txt"]
);
assert_eq!(
ModelVariant::MlCtcLarge.prequantized_files(),
["multilingual_large_ctc.int8.onnx", "multilingual_vocab.txt"]
);
}
#[test]
fn test_encoder_int8_checksums_are_pinned() {
for variant in [
ModelVariant::Rnnt,
ModelVariant::E2eRnnt,
ModelVariant::MlCtc,
ModelVariant::MlCtcLarge,
] {
let sum = variant.encoder_int8_checksum();
assert_eq!(
sum.len(),
64,
"{variant:?} INT8 checksum must be 64 hex chars, got: {sum}"
);
assert!(
sum.chars()
.all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)),
"{variant:?} INT8 checksum must be lowercase hex, got: {sum}"
);
}
}
#[test]
fn test_prequantized_checksum_int8_encoder_and_reuses_fp32_for_rest() {
let v = ModelVariant::Rnnt;
assert_eq!(
v.prequantized_checksum(v.encoder_int8_file()),
Some(v.encoder_int8_checksum())
);
for f in [v.decoder_file(), v.joint_file(), v.vocab_file()] {
assert_eq!(v.prequantized_checksum(f), v.checksum(f));
assert!(v.prequantized_checksum(f).is_some(), "{f} must be pinned");
}
}
#[test]
fn test_is_prequantized_present() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
assert!(!is_prequantized_present(ModelVariant::Rnnt, dir));
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(dir.join(f), b"x").unwrap();
}
assert!(is_prequantized_present(ModelVariant::Rnnt, dir));
assert!(!is_model_present(ModelVariant::Rnnt, dir));
}
#[test]
fn test_ensure_filter_accepts_prequantized_only_dir() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
assert_eq!(
ModelVariant::detect_in_dir(dir),
Some(ModelVariant::Rnnt),
"encoder_int8 alone is enough for detect_in_dir"
);
assert!(
is_prequantized_present(ModelVariant::Rnnt, dir),
"prequantized set is complete"
);
assert!(
!is_model_present(ModelVariant::Rnnt, dir),
"FP32 download set must still be absent"
);
assert!(
is_usable_present(ModelVariant::Rnnt, dir),
"INT8-only set is usable without FP32"
);
let existing = ModelVariant::detect_in_dir(dir).filter(|&v| is_usable_present(v, dir));
assert_eq!(
existing,
Some(ModelVariant::Rnnt),
"ensure treats prequantized-only as present"
);
assert_eq!(
resolve_variant(None, existing),
VariantAction::Use(ModelVariant::Rnnt),
"usable existing → no download"
);
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_model_variant_accepts_prequantized_only() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
let variant = ensure_model_variant(None, dir.to_str().unwrap())
.await
.expect("prequantized-only dir must short-circuit without download");
assert_eq!(variant, ModelVariant::Rnnt);
let has_partial = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.any(|e| e.file_name().to_string_lossy().contains(".partial"));
assert!(
!has_partial,
"no download when the prequantized set is present"
);
assert!(!dir.join(ModelVariant::Rnnt.encoder_file()).exists());
}
#[tokio::test]
#[cfg_attr(miri, ignore = "tokio runtime is unsupported under Miri")]
async fn test_ensure_prequantized_present_no_download() {
let tmp = tempfile::tempdir().expect("tempdir");
let dir = tmp.path();
for f in ModelVariant::Rnnt.prequantized_files() {
std::fs::write(dir.join(f), b"stub").unwrap();
}
let variant = ensure_prequantized_model_variant(None, dir.to_str().unwrap())
.await
.expect("present prequantized set must short-circuit");
assert_eq!(variant, ModelVariant::Rnnt);
let has_partial = std::fs::read_dir(dir)
.unwrap()
.filter_map(|e| e.ok())
.any(|e| e.file_name().to_string_lossy().contains(".partial"));
assert!(
!has_partial,
"no download when the prequantized set is present"
);
}