use std::path::{Path, PathBuf};
use sha2::{Digest, Sha256};
use super::*;
const LABELS_SHA256: &str = "f13f0331965a4a402f4308ed80de662f2d55167d77e163406712ba170b92eb35";
#[derive(serde::Deserialize)]
struct AssetEntry {
code: String,
id: usize,
name: String,
upstream_label: String,
}
fn asset_entries() -> Vec<AssetEntry> {
serde_json::from_slice(labels_json_bytes()).expect("the embedded asset must parse")
}
fn asset_path() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("src/audio/lid/assets/voxlingua107_labels.json")
}
#[test]
fn embedded_bytes_match_the_committed_file_on_disk() {
let on_disk = std::fs::read(asset_path()).expect("committed asset must be readable");
assert_eq!(
on_disk,
labels_json_bytes(),
"include_bytes! must reflect the committed asset exactly"
);
}
#[test]
fn asset_length_and_digest_are_pinned() {
assert_eq!(labels_json_bytes().len(), LABELS_JSON_LEN);
assert_eq!(LABELS_JSON_LEN, 10_756);
let digest = Sha256::digest(labels_json_bytes())
.iter()
.fold(String::new(), |mut acc, b| {
use core::fmt::Write;
let _ = write!(acc, "{b:02x}");
acc
});
assert_eq!(digest, LABELS_SHA256);
}
#[test]
fn every_table_row_matches_the_committed_asset() {
let entries = asset_entries();
assert_eq!(entries.len(), NUM_LANGUAGES);
assert_eq!(languages().len(), NUM_LANGUAGES);
for (entry, language) in entries.iter().zip(languages().iter()) {
assert_eq!(language.index(), entry.id, "index drift at {}", entry.code);
assert_eq!(
language.code(),
entry.code,
"code drift at index {}",
entry.id
);
assert_eq!(
language.name(),
entry.name,
"name drift at index {}",
entry.id
);
}
}
#[test]
fn asset_upstream_labels_reconstruct_the_label_encoder_lines() {
for entry in asset_entries() {
assert_eq!(
entry.upstream_label,
format!("{}: {}", entry.code, entry.name),
"entry {} does not reconstruct its upstream label",
entry.id
);
}
}
#[test]
fn indices_are_dense_and_positional() {
for (i, language) in languages().iter().enumerate() {
assert_eq!(language.index(), i);
assert_eq!(Language::from_index(i), Some(language));
}
assert_eq!(Language::from_index(NUM_LANGUAGES), None);
assert_eq!(Language::from_index(usize::MAX), None);
}
#[test]
fn index_pins_are_stable() {
let pins = [
(0, "ab", "Abkhazian"),
(52, "la", "Latin"),
(55, "lo", "Lao"),
(94, "th", "Thai"),
(106, "zh", "Chinese"),
];
for (index, code, name) in pins {
let language = Language::from_index(index).expect("pinned index must resolve");
assert_eq!(language.code(), code, "code at index {index}");
assert_eq!(language.name(), name, "name at index {index}");
}
}
#[test]
fn legacy_hebrew_and_javanese_codes_are_preserved() {
assert_eq!(Language::from_index(44).map(Language::code), Some("iw"));
assert_eq!(Language::from_index(44).map(Language::name), Some("Hebrew"));
assert_eq!(Language::from_index(46).map(Language::code), Some("jw"));
assert_eq!(
Language::from_index(46).map(Language::name),
Some("Javanese")
);
assert_eq!(Language::from_code("he"), None);
assert_eq!(Language::from_code("jv"), None);
assert_eq!(Language::from_code("iw").map(Language::index), Some(44));
assert_eq!(Language::from_code("jw").map(Language::index), Some(46));
assert_eq!(Language::from_code("in"), None);
assert!(Language::from_code("id").is_some());
}
#[test]
fn codes_are_unique_and_sorted() {
let codes: Vec<&str> = languages().iter().map(Language::code).collect();
let mut sorted = codes.clone();
sorted.sort_unstable();
assert_eq!(codes, sorted, "the roster must stay code-sorted");
sorted.dedup();
assert_eq!(sorted.len(), NUM_LANGUAGES, "codes must be unique");
for language in languages() {
assert_eq!(Language::from_code(language.code()), Some(language));
}
for miss in ["", "z", "zzz", "TH", "th ", "xx"] {
assert_eq!(Language::from_code(miss), None, "{miss:?} must not resolve");
}
}
#[test]
fn truncated_asset_is_rejected_by_the_parser() {
let bytes = labels_json_bytes();
let truncated = &bytes[..bytes.len() / 2];
assert!(
core::str::from_utf8(truncated).is_ok(),
"fixture assumption: the halfway point must land on an ASCII byte"
);
assert!(
serde_json::from_slice::<Vec<AssetEntry>>(truncated).is_err(),
"truncated JSON must not parse as a roster"
);
}
#[test]
fn a_swapped_name_is_caught_by_the_row_comparison() {
let text = core::str::from_utf8(labels_json_bytes()).expect("asset is UTF-8");
let needle = "\"name\": \"Hebrew\"";
assert!(
text.contains(needle),
"fixture assumption: `{needle}` must be present for this mutation to corrupt anything"
);
let mutated = text.replacen(needle, "\"name\": \"Hindi\"", 1);
let entries: Vec<AssetEntry> =
serde_json::from_str(&mutated).expect("still structurally valid JSON");
assert_eq!(entries.len(), NUM_LANGUAGES, "the count is unaffected");
let mismatches = entries
.iter()
.zip(languages().iter())
.filter(|(entry, language)| language.name() != entry.name)
.count();
assert_eq!(
mismatches, 1,
"the row comparison must see exactly the one corrupted name"
);
}