oxigdal_gpu/device_lost_test.rs
1//! CPU-only unit tests for GPU device-lost recovery machinery.
2//!
3//! These tests exercise the `Arc<AtomicBool>` flag mechanism and the
4//! `GpuError::DeviceLost` variant without requiring a real GPU device.
5//! They are designed to always pass on any host (including CI without a GPU).
6
7#[cfg(test)]
8mod tests {
9 use crate::error::GpuError;
10 use std::sync::{
11 Arc,
12 atomic::{AtomicBool, Ordering},
13 };
14
15 /// Verifies that a freshly-created `AtomicBool` flag starts as `false`.
16 ///
17 /// This mirrors the initial state of `GpuContext::device_lost` before any
18 /// device-lost callback fires.
19 #[test]
20 fn test_device_lost_flag_default_is_false() {
21 let flag = Arc::new(AtomicBool::new(false));
22 assert!(!flag.load(Ordering::SeqCst));
23 }
24
25 /// Verifies that the flag can be atomically set to `true`.
26 ///
27 /// This replicates what the wgpu device-lost callback does internally when
28 /// the GPU hardware is reset or removed.
29 #[test]
30 fn test_device_lost_flag_set_to_true() {
31 let flag = Arc::new(AtomicBool::new(false));
32 flag.store(true, Ordering::SeqCst);
33 assert!(flag.load(Ordering::SeqCst));
34 }
35
36 /// Verifies that `Arc::clone` shares ownership of the same underlying bool.
37 ///
38 /// The `GpuContext` registers a clone of `device_lost` with the wgpu
39 /// callback closure. This test confirms that a write through the clone is
40 /// visible through the original, which is the invariant that makes the
41 /// callback mechanism correct.
42 #[test]
43 fn test_device_lost_flag_clone_shares_state() {
44 let flag = Arc::new(AtomicBool::new(false));
45 let cloned = Arc::clone(&flag);
46 // Simulate callback writing through its cloned handle.
47 cloned.store(true, Ordering::SeqCst);
48 // The original — held by GpuContext — must observe the update.
49 assert!(flag.load(Ordering::SeqCst));
50 }
51
52 /// Verifies that `GpuError::device_lost` constructs the `DeviceLost` variant.
53 ///
54 /// `check_device_lost` returns `Err(GpuError::device_lost(...))` when the
55 /// flag is set. This test ensures the constructor produces the expected
56 /// enum variant independently of any GPU context.
57 #[test]
58 fn test_check_device_lost_returns_err_when_flag_set() {
59 let err = GpuError::device_lost("hardware reset");
60 assert!(matches!(err, GpuError::DeviceLost { .. }));
61 }
62
63 /// Verifies that the `Display` impl for `GpuError::DeviceLost` includes the
64 /// reason string in its output.
65 ///
66 /// The formatted error message is what end-users and log subscribers see.
67 /// Ensuring the reason is not silently dropped is a basic sanity check.
68 #[test]
69 fn test_device_lost_error_formats_as_string() {
70 let err = GpuError::device_lost("TDR timeout");
71 let s = format!("{err}");
72 // The formatted message must be non-empty and contain the reason.
73 assert!(!s.is_empty());
74 assert!(s.contains("TDR timeout"), "expected reason in '{s}'");
75 }
76}