thedes_tui_core/audio/device/
mock.rs1use std::{borrow::Cow, collections::VecDeque, mem, sync::Arc};
2
3use crate::audio::device::{
4 AudioDevice,
5 AudioSinkDevice,
6 CheckPlayStatusError,
7 ClearSinkError,
8 OpenSinkError,
9 PauseSinkError,
10 PlayNowError,
11 ResumeSinkError,
12 SetVolumeError,
13};
14
15#[derive(Debug)]
16struct State {
17 open_sink_results:
18 VecDeque<Result<Box<dyn AudioSinkDevice>, OpenSinkError>>,
19 open_sink_log: Option<u32>,
20 device_open: bool,
21}
22
23impl State {
24 pub fn new() -> Self {
25 Self {
26 open_sink_results: VecDeque::new(),
27 device_open: false,
28 open_sink_log: None,
29 }
30 }
31
32 pub fn register_sink(&mut self) -> AudioSinkDeviceMock {
33 let sink_mock = AudioSinkDeviceMock::new();
34 self.register_open_sink_results([Ok(sink_mock.open())]);
35 sink_mock
36 }
37
38 pub fn register_open_sink_results(
39 &mut self,
40 results: impl IntoIterator<
41 Item = Result<Box<dyn AudioSinkDevice>, OpenSinkError>,
42 >,
43 ) {
44 self.open_sink_results.extend(results);
45 }
46
47 pub fn enable_open_sink_log(&mut self) {
48 self.open_sink_log = Some(0);
49 }
50
51 pub fn disable_open_sink_log(&mut self) {
52 self.open_sink_log = None;
53 }
54
55 pub fn take_open_sink_log(&mut self) -> Option<u32> {
56 self.open_sink_log.as_mut().map(mem::take)
57 }
58
59 pub fn open_sink(
60 &mut self,
61 ) -> Result<Box<dyn AudioSinkDevice>, OpenSinkError> {
62 if let Some(log) = self.open_sink_log.as_mut() {
63 *log += 1;
64 }
65 self.open_sink_results
66 .pop_front()
67 .unwrap_or_else(|| Ok(AudioSinkDeviceMock::new().open()))
68 }
69
70 pub fn mark_device_open(&mut self) -> bool {
71 mem::replace(&mut self.device_open, true)
72 }
73}
74
75#[derive(Debug)]
76struct SinkState {
77 play_results: VecDeque<Result<(), PlayNowError>>,
78 set_volume_results: VecDeque<Result<(), SetVolumeError>>,
79 pause_results: VecDeque<Result<(), PauseSinkError>>,
80 resume_results: VecDeque<Result<(), ResumeSinkError>>,
81 clear_results: VecDeque<Result<(), ClearSinkError>>,
82 is_playing_results: VecDeque<Result<(), CheckPlayStatusError>>,
83 play_log: Option<Vec<Cow<'static, [u8]>>>,
84 set_volume_log: Option<Vec<f32>>,
85 sink_open: bool,
86 playing: bool,
87}
88
89impl SinkState {
90 pub fn new() -> Self {
91 Self {
92 play_results: VecDeque::new(),
93 set_volume_results: VecDeque::new(),
94 is_playing_results: VecDeque::new(),
95 pause_results: VecDeque::new(),
96 resume_results: VecDeque::new(),
97 clear_results: VecDeque::new(),
98 play_log: None,
99 set_volume_log: None,
100 sink_open: false,
101 playing: false,
102 }
103 }
104
105 pub fn register_play_results(
106 &mut self,
107 results: impl IntoIterator<Item = Result<(), PlayNowError>>,
108 ) {
109 self.play_results.extend(results);
110 }
111
112 pub fn register_set_volume_results(
113 &mut self,
114 results: impl IntoIterator<Item = Result<(), SetVolumeError>>,
115 ) {
116 self.set_volume_results.extend(results);
117 }
118
119 pub fn register_pause_results(
120 &mut self,
121 results: impl IntoIterator<Item = Result<(), PauseSinkError>>,
122 ) {
123 self.pause_results.extend(results);
124 }
125
126 pub fn register_resume_results(
127 &mut self,
128 results: impl IntoIterator<Item = Result<(), ResumeSinkError>>,
129 ) {
130 self.resume_results.extend(results);
131 }
132
133 pub fn register_clear_results(
134 &mut self,
135 results: impl IntoIterator<Item = Result<(), ClearSinkError>>,
136 ) {
137 self.clear_results.extend(results);
138 }
139
140 pub fn play_now(
141 &mut self,
142 bytes: Cow<'static, [u8]>,
143 ) -> Result<(), PlayNowError> {
144 if let Some(play_log) = self.play_log.as_mut() {
145 play_log.push(bytes);
146 }
147 let result = self.play_results.pop_front().unwrap_or(Ok(()));
148 if result.is_ok() {
149 self.playing = true;
150 }
151 result
152 }
153
154 pub fn pause(&mut self) -> Result<(), PauseSinkError> {
155 self.pause_results.pop_front().unwrap_or(Ok(()))?;
156 self.playing = false;
157 Ok(())
158 }
159
160 pub fn resume(&mut self) -> Result<(), ResumeSinkError> {
161 self.resume_results.pop_front().unwrap_or(Ok(()))?;
162 self.playing = true;
163 Ok(())
164 }
165
166 pub fn clear(&mut self) -> Result<(), ClearSinkError> {
167 self.clear_results.pop_front().unwrap_or(Ok(()))?;
168 self.playing = false;
169 Ok(())
170 }
171
172 pub fn force_pause(&mut self) {
173 self.playing = false;
174 }
175
176 pub fn force_resume(&mut self) {
177 self.playing = true;
178 }
179
180 pub fn is_playing(&mut self) -> Result<bool, CheckPlayStatusError> {
181 match self.is_playing_results.pop_front() {
182 Some(Ok(())) | None => Ok(self.playing),
183 Some(Err(e)) => Err(e),
184 }
185 }
186
187 pub fn set_volume(&mut self, volume: f32) -> Result<(), SetVolumeError> {
188 if let Some(set_volume_log) = self.set_volume_log.as_mut() {
189 set_volume_log.push(volume);
190 }
191 self.set_volume_results.pop_front().unwrap_or(Ok(()))
192 }
193
194 pub fn enable_play_log(&mut self) {
195 if self.play_log.is_none() {
196 self.play_log = Some(Vec::new());
197 }
198 }
199
200 pub fn disable_play_log(&mut self) -> Option<Vec<Cow<'static, [u8]>>> {
201 self.play_log.take()
202 }
203
204 pub fn take_play_log(&mut self) -> Option<Vec<Cow<'static, [u8]>>> {
205 self.play_log.as_mut().map(mem::take)
206 }
207
208 pub fn enable_set_volume_log(&mut self) {
209 if self.set_volume_log.is_none() {
210 self.set_volume_log = Some(Vec::new());
211 }
212 }
213
214 pub fn disable_set_volume_log(&mut self) -> Option<Vec<f32>> {
215 self.set_volume_log.take()
216 }
217
218 pub fn take_set_volume_log(&mut self) -> Option<Vec<f32>> {
219 self.set_volume_log.as_mut().map(mem::take)
220 }
221
222 pub fn mark_sink_open(&mut self) -> bool {
223 mem::replace(&mut self.sink_open, true)
224 }
225}
226
227#[derive(Debug, Clone)]
228pub struct AudioDeviceMock {
229 state: Arc<std::sync::Mutex<State>>,
230}
231
232impl AudioDeviceMock {
233 pub fn new() -> Self {
234 Self { state: Arc::new(std::sync::Mutex::new(State::new())) }
235 }
236
237 pub fn open(&self) -> Box<dyn AudioDevice> {
238 if self.with_state(State::mark_device_open) {
239 panic!("Mocked audio sink was already open for this mock");
240 }
241 Box::new(MockedAudioDevice::new(self.clone()))
242 }
243
244 pub fn open_sink(&self) -> Result<Box<dyn AudioSinkDevice>, OpenSinkError> {
245 self.with_state(State::open_sink)
246 }
247
248 pub fn register_sink(&self) -> AudioSinkDeviceMock {
249 self.with_state(State::register_sink)
250 }
251
252 pub fn register_open_sink_results(
253 &self,
254 results: impl IntoIterator<
255 Item = Result<Box<dyn AudioSinkDevice>, OpenSinkError>,
256 >,
257 ) {
258 self.with_state(|state| state.register_open_sink_results(results))
259 }
260
261 pub fn enable_open_sink_log(&self) {
262 self.with_state(State::enable_open_sink_log)
263 }
264
265 pub fn disable_open_sink_log(&self) {
266 self.with_state(State::disable_open_sink_log)
267 }
268
269 pub fn take_open_sink_log(&self) -> Option<u32> {
270 self.with_state(State::take_open_sink_log)
271 }
272
273 fn with_state<F, T>(&self, scope: F) -> T
274 where
275 F: FnOnce(&mut State) -> T,
276 {
277 let mut state = self.state.lock().expect("poisoned lock");
278 scope(&mut state)
279 }
280}
281
282#[derive(Debug, Clone)]
283pub struct AudioSinkDeviceMock {
284 state: Arc<std::sync::Mutex<SinkState>>,
285}
286
287impl AudioSinkDeviceMock {
288 pub fn new() -> Self {
289 Self { state: Arc::new(std::sync::Mutex::new(SinkState::new())) }
290 }
291
292 pub fn open(&self) -> Box<dyn AudioSinkDevice> {
293 if self.with_state(SinkState::mark_sink_open) {
294 panic!("Mocked audio sink was already open for this mock");
295 }
296 Box::new(MockedAudioSinkDevice::new(self.clone()))
297 }
298
299 pub fn enable_play_log(&self) {
300 self.with_state(SinkState::enable_play_log)
301 }
302
303 pub fn disable_play_log(&self) -> Option<Vec<Cow<'static, [u8]>>> {
304 self.with_state(SinkState::disable_play_log)
305 }
306
307 pub fn take_play_log(&self) -> Option<Vec<Cow<'static, [u8]>>> {
308 self.with_state(SinkState::take_play_log)
309 }
310
311 pub fn enable_set_volume_log(&self) {
312 self.with_state(SinkState::enable_set_volume_log)
313 }
314
315 pub fn disable_set_volume_log(&self) -> Option<Vec<f32>> {
316 self.with_state(SinkState::disable_set_volume_log)
317 }
318
319 pub fn take_set_volume_log(&self) -> Option<Vec<f32>> {
320 self.with_state(SinkState::take_set_volume_log)
321 }
322
323 pub fn force_pause(&self) {
324 self.with_state(SinkState::force_pause);
325 }
326
327 pub fn force_resume(&self) {
328 self.with_state(SinkState::force_resume);
329 }
330
331 pub fn is_playing(&self) -> Result<bool, CheckPlayStatusError> {
332 self.with_state(|state| state.is_playing())
333 }
334
335 pub fn register_play_results(
336 &self,
337 results: impl IntoIterator<Item = Result<(), PlayNowError>>,
338 ) {
339 self.with_state(|state| state.register_play_results(results))
340 }
341
342 pub fn register_set_volume_results(
343 &self,
344 results: impl IntoIterator<Item = Result<(), SetVolumeError>>,
345 ) {
346 self.with_state(|state| state.register_set_volume_results(results))
347 }
348
349 pub fn register_pause_results(
350 &self,
351 results: impl IntoIterator<Item = Result<(), PauseSinkError>>,
352 ) {
353 self.with_state(|state| state.register_pause_results(results))
354 }
355
356 pub fn register_resume_results(
357 &self,
358 results: impl IntoIterator<Item = Result<(), ResumeSinkError>>,
359 ) {
360 self.with_state(|state| state.register_resume_results(results))
361 }
362
363 pub fn register_clear_results(
364 &self,
365 results: impl IntoIterator<Item = Result<(), ClearSinkError>>,
366 ) {
367 self.with_state(|state| state.register_clear_results(results))
368 }
369
370 fn play_now(
371 &mut self,
372 bytes: Cow<'static, [u8]>,
373 ) -> Result<(), PlayNowError> {
374 self.with_state(|state| state.play_now(bytes))
375 }
376
377 fn set_volume(&mut self, volume: f32) -> Result<(), SetVolumeError> {
378 self.with_state(|state| state.set_volume(volume))
379 }
380
381 fn pause(&mut self) -> Result<(), PauseSinkError> {
382 self.with_state(SinkState::pause)
383 }
384
385 fn resume(&mut self) -> Result<(), ResumeSinkError> {
386 self.with_state(SinkState::resume)
387 }
388
389 fn clear(&mut self) -> Result<(), ClearSinkError> {
390 self.with_state(SinkState::clear)
391 }
392
393 fn with_state<F, T>(&self, scope: F) -> T
394 where
395 F: FnOnce(&mut SinkState) -> T,
396 {
397 let mut state = self.state.lock().expect("poisoned lock");
398 scope(&mut state)
399 }
400}
401
402#[derive(Debug)]
403struct MockedAudioDevice {
404 mock: AudioDeviceMock,
405}
406
407impl MockedAudioDevice {
408 pub fn new(mock: AudioDeviceMock) -> Self {
409 Self { mock }
410 }
411}
412
413impl AudioDevice for MockedAudioDevice {
414 fn open_sink(&mut self) -> Result<Box<dyn AudioSinkDevice>, OpenSinkError> {
415 self.mock.open_sink()
416 }
417}
418
419#[derive(Debug)]
420struct MockedAudioSinkDevice {
421 mock: AudioSinkDeviceMock,
422}
423
424impl MockedAudioSinkDevice {
425 pub fn new(mock: AudioSinkDeviceMock) -> Self {
426 Self { mock }
427 }
428}
429
430impl AudioSinkDevice for MockedAudioSinkDevice {
431 fn play_now(
432 &mut self,
433 bytes: Cow<'static, [u8]>,
434 ) -> Result<(), PlayNowError> {
435 self.mock.play_now(bytes)
436 }
437
438 fn set_volume(&mut self, volume: f32) -> Result<(), SetVolumeError> {
439 self.mock.set_volume(volume)
440 }
441
442 fn pause(&mut self) -> Result<(), PauseSinkError> {
443 self.mock.pause()
444 }
445
446 fn resume(&mut self) -> Result<(), ResumeSinkError> {
447 self.mock.resume()
448 }
449
450 fn clear(&mut self) -> Result<(), ClearSinkError> {
451 self.mock.clear()
452 }
453
454 fn is_playing(&self) -> Result<bool, CheckPlayStatusError> {
455 self.mock.is_playing()
456 }
457}