Skip to content

icons::USE_NERD_FONTS is unguarded process-global render state — one future test away from nondeterministic snapshot failures #618

Description

@JDonaghy

src/icons.rs:12 holds render state in a process-global mutable:

static USE_NERD_FONTS: AtomicBool = AtomicBool::new(true);

pub fn set_nerd_fonts(val: bool) { USE_NERD_FONTS.store(val, Ordering::Relaxed); }

Icon::s() / Icon::c() read it on every call to choose between the nerd glyph and the ASCII fallback, so it directly determines rendered output — and rendered width, since the two variants are not the same width.

It is written from several production paths, all driven by settings.use_nerd_fonts:

  • src/tui_main/mod.rs:675
  • src/tui_main/shell_app.rs:187
  • src/core/settings.rs:1087, :1599 (the :set nerdfonts / :set nonerdfonts command path)
  • src/gtk/mod.rs:1395

Why this matters now

No test currently calls set_nerd_fonts, so this is latent, not live — verified during the #615 investigation. But a Rust test binary runs its tests in parallel threads in one process, so a single future test that flips this flag would non-deterministically corrupt every snapshot test running concurrently, with failures that depend on scheduling and therefore on core count. That is a nightmare to diagnose.

We just spent a multi-round investigation on #615, whose failure mode was exactly this shape: a render that depends on process-wide ambient state, passing everywhere locally and failing only in CI. #615 turned out to be a different mechanism (~/.config/vimcode/session.json leaking into Engine::new()), and USE_NERD_FONTS was audited and cleared as a candidate — but the audit is what surfaced it as a landmine.

Proposal

Move the flag off process-global state and onto something scoped — per-Engine, per-Backend, or thread-local. src/render.rs:788 already does b.set_nerd_fonts(engine.settings.use_nerd_fonts) on the backend, and src/render.rs:16653 has a comment asserting that resolving icons is "the backend's job (Backend::set_nerd_fonts)". So the intended design may already be backend-scoped and the global is vestigial — worth checking whether the icons:: global can simply be deleted in favour of the backend path.

If the global must stay for now, at minimum add a test-only guard (a mutex or #[serial]) so no test can flip it while snapshot tests run.

Not urgent

Nothing is broken today. This is a "fix it before it costs a week" issue — file it, size it, schedule it. Found during the #615 investigation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    coordTracked by coord-tui pipelinestatus:readyRefined and ready to enter the work pipeline

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions