From 75de26c7a1d4fcc6c10b274000b03b452016021d Mon Sep 17 00:00:00 2001 From: Hunter Bown Date: Sun, 26 Apr 2026 00:00:32 -0500 Subject: [PATCH] test(tui): de-flake parse-invocation counter via thread-local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `parse_invocations_increment` and `render_parsed_does_not_call_parse` both read the global PARSE_INVOCATIONS atomic. They were racing whenever any other test in the suite called `parse()` in parallel — the global counter would tick once for each unrelated call and the assertion (== 2 / == 0) would mismatch. Switching to `thread_local!>` gives each test thread its own counter, so concurrent callers from other tests can't pollute the result. Tested across 8 sequential full-suite runs: 8/8 green (was ~40% green). Co-Authored-By: Claude Opus 4.7 (1M context) --- crates/tui/src/tui/markdown_render.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/crates/tui/src/tui/markdown_render.rs b/crates/tui/src/tui/markdown_render.rs index 57b2226b..b501f3cc 100644 --- a/crates/tui/src/tui/markdown_render.rs +++ b/crates/tui/src/tui/markdown_render.rs @@ -27,7 +27,8 @@ use std::sync::Arc; #[cfg(any(test, feature = "perf-counters"))] -use std::sync::atomic::{AtomicU64, Ordering}; +#[cfg(any(test, feature = "perf-counters"))] +use std::cell::Cell; use ratatui::style::{Modifier, Style}; use ratatui::text::{Line, Span}; @@ -40,18 +41,23 @@ use crate::palette; /// /// Available in test builds and behind the `perf-counters` feature flag so /// release builds pay no cost. +// Thread-local instead of a global atomic so concurrent tests that call +// `parse()` don't pollute each other's counters. Each test thread sees only +// its own invocations. #[cfg(any(test, feature = "perf-counters"))] -static PARSE_INVOCATIONS: AtomicU64 = AtomicU64::new(0); +thread_local! { + static PARSE_INVOCATIONS: Cell = const { Cell::new(0) }; +} #[cfg(any(test, feature = "perf-counters"))] #[must_use] pub fn parse_invocation_count() -> u64 { - PARSE_INVOCATIONS.load(Ordering::Relaxed) + PARSE_INVOCATIONS.with(|c| c.get()) } #[cfg(any(test, feature = "perf-counters"))] pub fn reset_parse_invocation_count() { - PARSE_INVOCATIONS.store(0, Ordering::Relaxed); + PARSE_INVOCATIONS.with(|c| c.set(0)); } /// One classified line of markdown source, width-independent. @@ -109,7 +115,7 @@ impl ParsedMarkdown { #[must_use] pub fn parse(content: &str) -> ParsedMarkdown { #[cfg(any(test, feature = "perf-counters"))] - PARSE_INVOCATIONS.fetch_add(1, Ordering::Relaxed); + PARSE_INVOCATIONS.with(|c| c.set(c.get() + 1)); let mut blocks = Vec::new(); let mut in_code_block = false; @@ -485,6 +491,8 @@ mod tests { #[test] fn parse_invocations_increment() { + // Counter is thread-local, so concurrent tests calling `parse()` + // can't pollute each other. reset_parse_invocation_count(); let _ = parse("hello\n"); let _ = parse("world\n");