| 1 | //! Shared test-only helpers. |
| 2 | |
| 3 | use std::sync::{Mutex, MutexGuard, OnceLock}; |
| 4 | |
| 5 | fn env_lock() -> &'static Mutex<()> { |
| 6 | static LOCK: OnceLock<Mutex<()>> = OnceLock::new(); |
| 7 | LOCK.get_or_init(|| Mutex::new(())) |
| 8 | } |
| 9 | |
| 10 | /// Acquire the process-wide env-var mutex. |
| 11 | /// |
| 12 | /// If a prior test panicked while holding the lock, recover the guard instead |
| 13 | /// of cascading failures across unrelated tests. |
| 14 | pub(crate) fn lock_test_env() -> MutexGuard<'static, ()> { |
| 15 | match env_lock().lock() { |
| 16 | Ok(guard) => guard, |
| 17 | Err(poisoned) => poisoned.into_inner(), |
| 18 | } |
| 19 | } |
| 20 | |
| 21 | /// Find the byte position of the first divergence between two strings, |
| 22 | /// returning a windowed view (`±32 bytes` around the divergence) so failures |
| 23 | /// in cache-prefix-stability tests show *which* bytes drifted, not just that |
| 24 | /// they did. Returns `None` when the strings are byte-identical. |
| 25 | pub(crate) fn first_divergence(a: &str, b: &str) -> Option<(usize, String, String)> { |
| 26 | let a_bytes = a.as_bytes(); |
| 27 | let b_bytes = b.as_bytes(); |
| 28 | let max = a_bytes.len().min(b_bytes.len()); |
| 29 | for i in 0..max { |
| 30 | if a_bytes[i] != b_bytes[i] { |
| 31 | let lo = i.saturating_sub(32); |
| 32 | let a_hi = (i + 32).min(a_bytes.len()); |
| 33 | let b_hi = (i + 32).min(b_bytes.len()); |
| 34 | let a_ctx = String::from_utf8_lossy(&a_bytes[lo..a_hi]).into_owned(); |
| 35 | let b_ctx = String::from_utf8_lossy(&b_bytes[lo..b_hi]).into_owned(); |
| 36 | return Some((i, a_ctx, b_ctx)); |
| 37 | } |
| 38 | } |
| 39 | if a_bytes.len() != b_bytes.len() { |
| 40 | return Some(( |
| 41 | max, |
| 42 | format!("(len={})", a_bytes.len()), |
| 43 | format!("(len={})", b_bytes.len()), |
| 44 | )); |
| 45 | } |
| 46 | None |
| 47 | } |
| 48 | |
| 49 | /// Assert two strings are byte-identical, panicking with a windowed diff |
| 50 | /// around the first divergence when they aren't. Used by the prefix-cache |
| 51 | /// stability harness (#263, #280) to pin construction surfaces that land in |
| 52 | /// DeepSeek's KV cache prefix. |
| 53 | #[track_caller] |
| 54 | pub(crate) fn assert_byte_identical(label: &str, a: &str, b: &str) { |
| 55 | if let Some((pos, a_ctx, b_ctx)) = first_divergence(a, b) { |
| 56 | panic!( |
| 57 | "{label}: prompt construction is non-deterministic — first diff at byte {pos}\n\ |
| 58 | ── side A (±32B) ──\n{a_ctx:?}\n── side B (±32B) ──\n{b_ctx:?}", |
| 59 | ); |
| 60 | } |
| 61 | } |
| 62 |