diff --git a/crates/ui-egui/src/theme.rs b/crates/ui-egui/src/theme.rs index 66c3380..c667e6f 100644 --- a/crates/ui-egui/src/theme.rs +++ b/crates/ui-egui/src/theme.rs @@ -6,6 +6,13 @@ use std::sync::atomic::{AtomicBool, Ordering}; /// Whether the Light palette is active. Dark is the default (the incumbent is dark-only). static LIGHT_THEME: AtomicBool = AtomicBool::new(false); +/// Unit tests that change the process-wide palette must own it until their assertions finish. +#[cfg(test)] +pub(crate) fn lock_test_palette() -> std::sync::MutexGuard<'static, ()> { + static PALETTE: std::sync::Mutex<()> = std::sync::Mutex::new(()); + PALETTE.lock().unwrap_or_else(std::sync::PoisonError::into_inner) +} + /// The Appearance setting (`ui.theme {mode}`). Dark by default; System and Light are opt-in. #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[serde(rename_all = "lowercase")] @@ -443,7 +450,8 @@ mod tests { #[test] fn application_frames_follow_system_appearance_and_preserve_manual_palettes() { - // Keep all frame assertions in one test: custom-painted tokens share LIGHT_THEME. + // Mixer rendering tests also change the process-wide custom-painted palette. + let _palette = lock_test_palette(); fn frame(app: &mut SoundApp, ctx: &egui::Context, system_theme: Option, light: bool) { let mode = app.ui.theme; let colors = |app: &SoundApp| { diff --git a/crates/ui-egui/src/widgets.rs b/crates/ui-egui/src/widgets.rs index d4eab05..1eca07c 100644 --- a/crates/ui-egui/src/widgets.rs +++ b/crates/ui-egui/src/widgets.rs @@ -396,6 +396,7 @@ mod selector_visual_regressions { #[test] fn capture_selector_visual_fixtures() { + let _palette = crate::theme::lock_test_palette(); let Some(directory) = std::env::var_os("CRAFT_UI_VISUAL_FIXTURES").map(std::path::PathBuf::from) else { return }; std::fs::create_dir_all(&directory).unwrap(); for scale in [1.0, 1.5, 2.0] { @@ -451,6 +452,7 @@ mod selector_visual_regressions { #[test] fn actual_mixer_keeps_every_active_automation_mode_legible() { + let _palette = crate::theme::lock_test_palette(); for narrow in [false, true] { for mode in soundcraft_model::AutomationMode::ALL { for scale in [1.0, 1.5, 2.0] { @@ -491,6 +493,7 @@ mod selector_visual_regressions { #[test] fn capture_actual_mixer_visual_fixtures() { + let _palette = crate::theme::lock_test_palette(); let Some(directory) = std::env::var_os("CRAFT_UI_VISUAL_FIXTURES").map(std::path::PathBuf::from) else { return }; std::fs::create_dir_all(&directory).unwrap(); for mode in soundcraft_model::AutomationMode::ALL { @@ -544,6 +547,7 @@ mod selector_visual_regressions { #[test] fn compact_mixer_selectors_keep_labels_on_one_visible_line() { + let _palette = crate::theme::lock_test_palette(); for width in [46.0, 74.0, 110.0] { for label in ["auto read", "no group", "A very long mixer group name"] { for scale in [1.0, 1.5, 2.0] {