Tests: a language one ui-egui test sets no longer translates the tests running beside it

The language the UI is drawn in was one process-wide value in unit
tests too, and the tests run on parallel threads.
current_language_is_a_frame_setting sets Traditional Chinese for a
moment, and a test that drew text in that moment read it in Chinese.
chrome::tests::the_zoom_hint_follows_animated_zoom failed that way in
29 of 500 runs together with the i18n tests.

The drawing language is now one per process in the app and one per
thread in unit tests, as ui_mirror! keeps the shortcut and workspace
mirrors. The app keeps the same static. A new test sets the language
on another thread and checks that its own thread still draws in
English.
This commit is contained in:
dotCooCoo
2026-10-17 12:50:38 +07:00
committed by bflatastic
parent 6961bed99d
commit 476d228262
2 changed files with 28 additions and 6 deletions
+18 -4
View File
@@ -457,19 +457,33 @@ fn detect_system_lang() -> Lang {
Lang::EN
}
/// Index into [`LANGUAGES`] of the language the UI is drawn in this frame.
static CURRENT: AtomicUsize = AtomicUsize::new(0);
/// Index into [`LANGUAGES`] of the language the UI is drawn in this frame. The app keeps one
/// index for the whole process. Unit tests keep one per thread, as `ui_mirror!` keeps the
/// shortcut and workspace mirrors, so a language one test sets does not change the language of
/// the tests running beside it on other threads.
fn current_index() -> &'static AtomicUsize {
#[cfg(not(test))]
{
static CURRENT: AtomicUsize = AtomicUsize::new(0);
&CURRENT
}
#[cfg(test)]
{
thread_local!(static CURRENT: &'static AtomicUsize = Box::leak(Box::default()));
CURRENT.with(|c| *c)
}
}
/// Set the UI language for drawing (the shell calls this once per frame from the preference), so
/// widgets can translate without every call site carrying a language around.
pub fn set_current(lang: Lang) {
let i = LANGUAGES.iter().position(|l| l.code == lang.code()).unwrap_or(0);
CURRENT.store(i, Ordering::Relaxed);
current_index().store(i, Ordering::Relaxed);
}
/// The language the UI is drawn in.
pub fn current() -> Lang {
Lang(LANGUAGES.get(CURRENT.load(Ordering::Relaxed)).unwrap_or(&LANGUAGES[0]))
Lang(LANGUAGES.get(current_index().load(Ordering::Relaxed)).unwrap_or(&LANGUAGES[0]))
}
/// Does `lang` have a catalog entry for this plain string? (English never does: it is the source.)
+10 -2
View File
@@ -100,6 +100,15 @@ fn current_language_is_a_frame_setting() {
assert_eq!(t("Layers"), "Layers");
}
/// Unit tests run on parallel threads, and each one draws in its own language: a language set on
/// another thread (by a test running beside this one) leaves this thread's text in English.
#[test]
fn a_language_set_by_another_test_leaves_this_one_in_english() {
std::thread::spawn(|| set_current(ZH())).join().unwrap();
assert_eq!(current(), Lang::EN);
assert_eq!(t("Layers"), "Layers");
}
#[test]
fn lookups_fall_back_to_english() {
assert_eq!(tr(Lang::EN, "Layers"), "Layers");
@@ -197,8 +206,7 @@ fn partial_catalogs_only_translate_strings_the_ui_shows() {
}
/// The engine learns the language the UI is drawn in each frame: new type takes the Japanese
/// defaults while it is Japanese (#432). The frames stay in English: the drawing language is
/// process-wide, and a Japanese frame would translate the tests running beside this one.
/// defaults while it is Japanese (#432). The frames stay in English.
#[test]
fn the_engine_follows_the_interface_language() {
let mut app = crate::VectorcraftApp::new(vectorcraft_engine::Session::new(), crate::Services::default());