#[cfg(test)]
mod subscriber_panic_isolation_tests {
use super::*;
use std::sync::atomic::{AtomicUsize, Ordering};
#[test]
fn panicking_subscriber_does_not_poison_mutex() {
let state = State::new(0i32);
let fired = Arc::new(AtomicUsize::new(0));
let _ = state.subscribe(|_| -> () {
panic!("subscriber 1 explodes");
});
let fired_clone = Arc::clone(&fired);
let _ = state.subscribe(move |v| {
fired_clone.store(*v as usize + 1, Ordering::SeqCst);
});
state.set(42);
assert_eq!(
fired.load(Ordering::SeqCst),
43,
"second subscriber must fire even though first panicked"
);
let fired2 = Arc::new(AtomicUsize::new(0));
let fired2_clone = Arc::clone(&fired2);
let _ = state.subscribe(move |v| {
fired2_clone.store(*v as usize, Ordering::SeqCst);
});
state.set(100);
assert_eq!(
fired2.load(Ordering::SeqCst),
100,
"future updates must work after subscriber panic"
);
}
#[test]
fn all_subscribers_fire_even_if_one_panics() {
let state = State::new(0u32);
let count = Arc::new(AtomicUsize::new(0));
let _ = state.subscribe(|_| panic!("boom 1"));
let c1 = Arc::clone(&count);
let _ = state.subscribe(move |_| {
c1.fetch_add(1, Ordering::SeqCst);
});
let _ = state.subscribe(|_| panic!("boom 2"));
let c2 = Arc::clone(&count);
let _ = state.subscribe(move |_| {
c2.fetch_add(1, Ordering::SeqCst);
});
state.set(1);
assert_eq!(
count.load(Ordering::SeqCst),
2,
"both non-panicking subscribers should fire"
);
}
#[test]
fn invoke_subscribers_safely_returns_count() {
use std::sync::Mutex;
let subs: SubscriberList<u32> = Arc::new(Mutex::new(Vec::new()));
let count1 = Arc::new(AtomicUsize::new(0));
let count1_clone = Arc::clone(&count1);
subs.lock().unwrap().push(Box::new(move |v| {
count1_clone.store(*v as usize, Ordering::SeqCst);
}));
let count2 = Arc::new(AtomicUsize::new(0));
let count2_clone = Arc::clone(&count2);
subs.lock().unwrap().push(Box::new(move |v| {
count2_clone.store(*v as usize + 100, Ordering::SeqCst);
}));
let invoked = invoke_subscribers_safely(&subs, &7);
assert_eq!(invoked, 2, "both subscribers should be invoked");
assert_eq!(count1.load(Ordering::SeqCst), 7);
assert_eq!(count2.load(Ordering::SeqCst), 107);
}
}
#[cfg(test)]
mod p1_17_shared_fallback_runtime_tests {
use super::*;
use std::time::Duration;
#[test]
fn fallback_runtime_is_shared() {
let r1 = fallback_runtime();
let r2 = fallback_runtime();
assert!(
std::ptr::eq(r1 as *const _, r2 as *const _),
"fallback_runtime must return the same instance"
);
}
#[test]
fn fallback_worker_count_is_bounded() {
let n = *FALLBACK_WORKER_COUNT.get_or_init(|| {
let available = std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(2);
available.saturating_sub(1).clamp(1, 8)
});
assert!(n >= 1, "worker count must be at least 1, got {n}");
assert!(n <= 8, "worker count must be at most 8, got {n}");
}
#[test]
fn many_suspense_calls_share_runtime() {
let counter = State::new(0u32);
let mut handles = Vec::new();
for _ in 0..20 {
let s = Suspense::new_async(async { Ok::<u32, String>(1) });
let _ = s; handles.push(s);
}
counter.set(20);
assert_eq!(counter.get(), 20);
}
#[test]
fn p1_14_state_storage_mechanisms() {
use std::mem::size_of;
let state = State::new(42u32);
let size = size_of_val(&state);
assert!(
size >= 4 * std::mem::size_of::<usize>(),
"State<T> should be at least 4 Arcs in size"
);
}
#[test]
fn p1_14_set_direct_updates_value() {
let state = State::new(0u32);
state.set_direct(42);
assert_eq!(state.get(), 42);
}
#[test]
fn p1_14_set_direct_notifies_subscribers() {
let state = State::new(0u32);
let received = Arc::new(Mutex::new(Vec::new()));
let received_clone = Arc::clone(&received);
state.subscribe(move |v| {
received_clone.lock().unwrap().push(*v);
});
state.set_direct(1);
state.set_direct(2);
state.set_direct(3);
std::thread::sleep(std::time::Duration::from_millis(10));
let log = received.lock().unwrap();
assert!(
log.contains(&1) && log.contains(&2) && log.contains(&3),
"set_direct must notify subscribers of all values"
);
}
}
#[derive(Debug, Clone, Default)]
pub struct DirtyRegionManager {
regions: Vec<Rect>,
generation: u64,
}
impl DirtyRegionManager {
pub fn new() -> Self {
Self::default()
}
pub fn mark_dirty(&mut self, region: Rect) {
for existing in self.regions.iter_mut() {
if Self::rects_overlap(*existing, region) {
*existing = Self::union_rect(*existing, region);
return;
}
}
self.regions.push(region);
}
pub fn regions(&self) -> &[Rect] {
&self.regions
}
pub fn is_dirty(&self) -> bool {
!self.regions.is_empty()
}
pub fn clear(&mut self) {
self.regions.clear();
self.generation = self.generation.wrapping_add(1);
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn len(&self) -> usize {
self.regions.len()
}
pub fn is_empty(&self) -> bool {
self.regions.is_empty()
}
fn rects_overlap(a: Rect, b: Rect) -> bool {
a.x < b.x + b.width
&& a.x + a.width > b.x
&& a.y < b.y + b.height
&& a.y + a.height > b.y
}
fn union_rect(a: Rect, b: Rect) -> Rect {
let min_x = a.x.min(b.x);
let min_y = a.y.min(b.y);
let max_x = (a.x + a.width).max(b.x + b.width);
let max_y = (a.y + a.height).max(b.y + b.height);
Rect {
x: min_x,
y: min_y,
width: max_x - min_x,
height: max_y - min_y,
}
}
}
#[cfg(test)]
mod p1_39_dirty_region_tests {
use super::{DirtyRegionManager, Rect};
#[test]
fn new_manager_is_empty() {
let m = DirtyRegionManager::new();
assert!(!m.is_dirty());
assert!(m.is_empty());
assert_eq!(m.len(), 0);
}
#[test]
fn mark_dirty_adds_region() {
let mut m = DirtyRegionManager::new();
m.mark_dirty(Rect { x: 0.0, y: 0.0, width: 10.0, height: 10.0 });
assert!(m.is_dirty());
assert_eq!(m.len(), 1);
}
#[test]
fn overlapping_regions_coalesce() {
let mut m = DirtyRegionManager::new();
m.mark_dirty(Rect { x: 0.0, y: 0.0, width: 10.0, height: 10.0 });
m.mark_dirty(Rect { x: 5.0, y: 5.0, width: 10.0, height: 10.0 });
assert_eq!(m.len(), 1);
let r = &m.regions()[0];
assert_eq!(r.x, 0.0);
assert_eq!(r.y, 0.0);
assert_eq!(r.width, 15.0);
assert_eq!(r.height, 15.0);
}
#[test]
fn non_overlapping_regions_dont_coalesce() {
let mut m = DirtyRegionManager::new();
m.mark_dirty(Rect { x: 0.0, y: 0.0, width: 10.0, height: 10.0 });
m.mark_dirty(Rect { x: 100.0, y: 100.0, width: 10.0, height: 10.0 });
assert_eq!(m.len(), 2);
}
#[test]
fn clear_resets_regions_and_increments_generation() {
let mut m = DirtyRegionManager::new();
m.mark_dirty(Rect { x: 0.0, y: 0.0, width: 10.0, height: 10.0 });
let g1 = m.generation();
m.clear();
assert!(!m.is_dirty());
assert_eq!(m.len(), 0);
assert_eq!(m.generation(), g1 + 1);
}
#[test]
fn many_overlapping_marks_coalesce_to_one() {
let mut m = DirtyRegionManager::new();
for i in 0..100 {
m.mark_dirty(Rect {
x: i as f32,
y: i as f32,
width: 10.0,
height: 10.0,
});
}
assert_eq!(m.len(), 1);
}
}
#[derive(Debug, Clone, PartialEq)]