#![cfg(not(target_arch = "wasm32"))]
use fxtranslate::engine::Engine;
use fxtranslate::shortlist::Shortlist;
const MODEL: &str = "../../../data/models/enfr/model.enfr.intgemm.alphas.bin";
const VOCAB: &str = "../../../data/models/enfr/vocab.enfr.spm";
const SHORTLIST: &str = "../../../data/models/enfr/lex.50.50.enfr.s2t.bin";
fn engine() -> Option<Engine> {
if !std::path::Path::new(MODEL).exists() || !std::path::Path::new(VOCAB).exists() {
eprintln!("skipping translate: model or vocab absent");
return None;
}
let engine = Engine::load(MODEL, VOCAB, VOCAB).expect("engine loads");
let engine = engine.with_shortlist(Shortlist::load(SHORTLIST).expect("shortlist loads"));
Some(engine)
}
#[test]
fn translates_hello_world() {
let Some(engine) = engine() else { return };
let src = engine_src_ids();
let out = engine.greedy(&src);
eprintln!("greedy ids: {out:?}");
assert_eq!(out, vec![16060, 280, 514, 264], "greedy token ids");
let text = engine.translate("Hello world.");
eprintln!("translation: {text:?}");
assert_eq!(text, "Bonjour le monde.");
}
#[test]
fn matches_reference_translations() {
let Some(engine) = engine() else { return };
let cases = [
("Hello world.", "Bonjour le monde."),
(
"The cat sat on the mat.",
"Le chat était assis sur le tapis.",
),
("I love programming.", "J'adore la programmation."),
];
for (src, want) in cases {
assert_eq!(engine.translate(src), want, "translating {src:?}");
}
}
#[test]
fn near_tie_casing_matches_apart_from_case() {
let Some(engine) = engine() else { return };
let got = engine.translate("Good morning, how are you?");
assert_eq!(got.to_lowercase(), "bonjour, comment allez-vous ?");
}
#[test]
fn over_long_input_is_silently_truncated() {
let Some(engine) = engine() else { return };
let sentences = [
"The weather today is remarkably pleasant and the sky is a brilliant blue.",
"My brother traveled to Japan last summer and visited many ancient temples.",
"Scientists have discovered a new species of frog deep in the rainforest.",
"The old library on the corner has thousands of rare and valuable books.",
"She carefully planted tomatoes, peppers, and herbs in her small garden.",
"The orchestra performed a beautiful symphony for the enthusiastic audience.",
"Engineers are building a longer bridge across the wide and rushing river.",
"The children laughed and played in the park until the sun went down.",
"A gentle rain fell over the quiet village throughout the entire night.",
"The chef prepared a delicious meal with fresh vegetables and local fish.",
];
assert_eq!(
engine.translate(sentences[9]),
"Le chef a préparé un délicieux repas avec des légumes frais et du poisson local."
);
assert_eq!(
engine.translate(&sentences.join(" ")),
"Le temps aujourd'hui est remarquablement agréable et le ciel est un bleu \
brillant. Mon frère a voyagé au Japon l'été dernier et a visité de nombreux \
temples anciens. Les scientifiques ont découvert une nouvelle espèce \
d'amphibiens au fond de la forêt tropicale. L'ancienne bibliothèque au coin a \
des milliers de livres rares et précieux. Elle a soigneusement planté des \
tomates, des poivrons et des herbes dans son petit jardin. L'orchestre a joué \
une belle symphonie pour le public enthousiaste. Les ingénieurs construisent \
un plus long pont"
);
}
#[test]
fn segmented_translates_full_paragraph() {
use fxtranslate::segment::BasicSegmenter;
let Some(engine) = engine() else { return };
let paragraph = "The weather today is remarkably pleasant and the sky is a brilliant blue. \
My brother traveled to Japan last summer and visited many ancient temples. \
Scientists have discovered a new species of frog deep in the rainforest. \
The old library on the corner has thousands of rare and valuable books. \
She carefully planted tomatoes, peppers, and herbs in her small garden. \
The orchestra performed a beautiful symphony for the enthusiastic audience. \
Engineers are building a longer bridge across the wide and rushing river. \
The children laughed and played in the park until the sun went down. \
A gentle rain fell over the quiet village throughout the entire night. \
The chef prepared a delicious meal with fresh vegetables and local fish.";
let out = engine.translate_segmented(paragraph, &BasicSegmenter);
eprintln!("segmented: {out:?}");
assert!(out.contains("bleu brillant"), "opening present: {out:?}");
assert!(
out.contains("Une pluie douce"),
"9th sentence (rain) present: {out:?}"
);
assert!(
out.contains("du poisson local"),
"10th sentence (fish) present: {out:?}"
);
}
#[cfg(feature = "icu-segmenter")]
#[test]
fn translate_long_with_icu_is_complete() {
let Some(engine) = engine() else { return };
let paragraph = "The children laughed and played in the park until the sun went down. \
A gentle rain fell over the quiet village throughout the entire night. \
The chef prepared a delicious meal with fresh vegetables and local fish.";
let out = engine.translate_long(paragraph);
assert!(out.contains("du poisson local"), "tail present: {out:?}");
}
#[test]
fn segmented_single_sentence_matches_raw() {
use fxtranslate::segment::BasicSegmenter;
let Some(engine) = engine() else { return };
for s in [
"Hello world.",
"The cat sat on the mat.",
"I love programming.",
] {
assert_eq!(
engine.translate_segmented(s, &BasicSegmenter),
engine.translate(s),
"segmented diverged from raw on {s:?}"
);
}
}
#[cfg(feature = "mmap")]
#[test]
fn mmap_matches_owned() {
if !std::path::Path::new(MODEL).exists() || !std::path::Path::new(VOCAB).exists() {
eprintln!("skipping mmap parity: model or vocab absent");
return;
}
let owned = Engine::load(MODEL, VOCAB, VOCAB).expect("owned engine loads");
let mapped = Engine::load_mmapped(MODEL, VOCAB, VOCAB).expect("mmapped engine loads");
for src in [
"Hello world.",
"The cat sat on the mat.",
"I love programming.",
] {
assert_eq!(
owned.translate(src),
mapped.translate(src),
"mmap vs owned diverged on {src:?}"
);
}
}
#[test]
fn from_bytes_matches_load() {
if !std::path::Path::new(MODEL).exists() || !std::path::Path::new(VOCAB).exists() {
eprintln!("skipping from_bytes parity: model or vocab absent");
return;
}
let path_engine = Engine::load(MODEL, VOCAB, VOCAB).expect("path engine loads");
let path_engine =
path_engine.with_shortlist(Shortlist::load(SHORTLIST).expect("shortlist loads"));
let model_bytes = std::fs::read(MODEL).expect("read model");
let vocab_bytes = std::fs::read(VOCAB).expect("read vocab");
let shortlist_bytes = std::fs::read(SHORTLIST).expect("read shortlist");
let byte_engine =
Engine::from_bytes(&model_bytes, &vocab_bytes, &vocab_bytes).expect("byte engine builds");
let byte_engine = byte_engine.with_shortlist_bytes(&shortlist_bytes);
for src in [
"Hello world.",
"The cat sat on the mat.",
"I love programming.",
] {
assert_eq!(
path_engine.translate(src),
byte_engine.translate(src),
"from_bytes vs load diverged on {src:?}"
);
}
}
fn engine_src_ids() -> Vec<u32> {
vec![17169, 564, 264, 0]
}