Skip to main content

thedes_tui_core/runtime/device/
mock.rs

1use std::sync::{
2    Arc,
3    atomic::{AtomicBool, Ordering::*},
4};
5
6use crate::{
7    audio::device::{AudioDevice, mock::AudioDeviceMock},
8    geometry::CoordPair,
9    input::device::{InputDevice, mock::InputDeviceMock},
10    panic::restore::{PanicRestoreGuard, mock::PanicRestoreMock},
11    screen::device::{ScreenDevice, mock::ScreenDeviceMock},
12};
13
14use super::{Error, RuntimeDevice};
15
16#[derive(Debug)]
17struct Shared {
18    open: AtomicBool,
19    initialized: AtomicBool,
20}
21
22impl Shared {
23    pub fn new() -> Self {
24        Self {
25            open: AtomicBool::new(false),
26            initialized: AtomicBool::new(false),
27        }
28    }
29
30    pub fn initialized(&self) -> bool {
31        self.initialized.load(Acquire)
32    }
33
34    pub fn initialize(&self) -> bool {
35        !self.initialized.swap(true, AcqRel)
36    }
37
38    pub fn shutdown(&self) -> bool {
39        self.initialized.swap(false, AcqRel)
40    }
41
42    pub fn mark_device_open(&self) -> bool {
43        !self.open.swap(true, AcqRel)
44    }
45}
46
47#[derive(Debug, Clone)]
48pub struct RuntimeDeviceMock {
49    screen: ScreenDeviceMock,
50    input: InputDeviceMock,
51    audio: AudioDeviceMock,
52    panic_restore: PanicRestoreMock,
53    shared: Arc<Shared>,
54}
55
56impl RuntimeDeviceMock {
57    pub fn new(term_size: CoordPair) -> Self {
58        Self {
59            screen: ScreenDeviceMock::new(term_size),
60            input: InputDeviceMock::new(),
61            audio: AudioDeviceMock::new(),
62            panic_restore: PanicRestoreMock::new(),
63            shared: Arc::new(Shared::new()),
64        }
65    }
66
67    pub fn initialized(&self) -> bool {
68        self.shared.initialized()
69    }
70
71    fn initialize(&self) -> bool {
72        self.shared.initialize()
73    }
74
75    fn shutdown(&self) -> bool {
76        self.shared.shutdown()
77    }
78
79    pub fn screen(&self) -> &ScreenDeviceMock {
80        &self.screen
81    }
82
83    pub fn input(&self) -> &InputDeviceMock {
84        &self.input
85    }
86
87    pub fn audio(&self) -> &AudioDeviceMock {
88        &self.audio
89    }
90
91    pub fn panic_restore(&self) -> &PanicRestoreMock {
92        &self.panic_restore
93    }
94
95    pub fn open(&self) -> Box<dyn RuntimeDevice> {
96        if !self.shared.mark_device_open() {
97            panic!("Mocked runtime device was already open for this mock");
98        }
99        Box::new(MockedRuntimeDevice::new(self.clone()))
100    }
101}
102
103#[derive(Debug)]
104struct MockedRuntimeDevice {
105    mock: RuntimeDeviceMock,
106}
107
108impl MockedRuntimeDevice {
109    pub fn new(mock: RuntimeDeviceMock) -> Self {
110        Self { mock }
111    }
112}
113
114impl RuntimeDevice for MockedRuntimeDevice {
115    fn blocking_init(&mut self) -> Result<(), Error> {
116        if self.mock.initialize() { Ok(()) } else { Err(Error::AlreadyInit) }
117    }
118
119    fn blocking_shutdown(&mut self) -> Result<(), Error> {
120        if self.mock.shutdown() { Ok(()) } else { Err(Error::NotInit) }
121    }
122
123    fn open_input_device(&mut self) -> Box<dyn InputDevice> {
124        self.mock.input().open()
125    }
126
127    fn open_screen_device(&mut self) -> Box<dyn ScreenDevice> {
128        self.mock.screen().open()
129    }
130
131    fn open_audio_device(&mut self) -> Box<dyn AudioDevice> {
132        self.mock.audio().open()
133    }
134
135    fn open_panic_restore_guard(&mut self) -> Box<dyn PanicRestoreGuard> {
136        self.mock.panic_restore().open()
137    }
138}