1#![allow(clippy::module_name_repetitions)]
2
3#[cfg(feature = "mock_playback")]
4use std::sync::atomic::AtomicBool;
5use std::{
6 fs::File,
7 io::BufReader,
8 ops::Range,
9 sync::{
10 Arc,
11 mpsc::{Receiver, Sender},
12 },
13 time::Duration,
14};
15
16use log::{debug, error};
17use rodio::{
18 Source,
19 decoder::DecoderBuilder,
20 source::{EmptyCallback, SeekError},
21};
22use tracing::instrument;
23
24use crate::{
25 errors::LibraryError,
26 format_duration,
27 state::{Percent, SeekType, StateAudio, StateRuntime, Status},
28 udp::StateChange,
29};
30use mecomp_storage::db::schemas::song::SongBrief;
31use one_or_many::OneOrMany;
32
33pub mod commands;
34pub mod queue;
35
36use commands::{AudioCommand, QueueCommand, VolumeCommand};
37use queue::Queue;
38
39const MIN_VOLUME: f32 = 0.0;
41const MAX_VOLUME: f32 = 10.0;
43
44#[derive(Debug, Clone)]
45pub struct AudioKernelSender {
46 tx: Sender<(AudioCommand, tracing::Span)>,
47}
48
49impl AudioKernelSender {
50 #[must_use]
61 #[inline]
62 pub fn start(event_tx: Sender<StateChange>) -> Arc<Self> {
63 let (command_tx, command_rx) = std::sync::mpsc::channel();
64 let tx_clone = command_tx.clone();
65 std::thread::Builder::new()
66 .name(String::from("Audio Kernel"))
67 .spawn(move || {
68 let kernel = AudioKernel::new(tx_clone, event_tx);
69 kernel.init(command_rx);
70 })
71 .unwrap();
72 Arc::new(Self::new(command_tx))
73 }
74
75 #[must_use]
76 #[inline]
77 pub(crate) const fn new(tx: Sender<(AudioCommand, tracing::Span)>) -> Self {
78 Self { tx }
79 }
80
81 #[instrument(skip(self))]
92 #[inline]
93 pub fn send(&self, command: AudioCommand) {
94 let ctx =
95 tracing::info_span!("Sending Audio Command to Kernel", command = ?command).or_current();
96
97 if let Err(e) = self.tx.send((command, ctx)) {
98 error!("Failed to send command to audio kernel: {e}");
99 panic!("Failed to send command to audio kernel: {e}");
100 }
101 }
102
103 #[instrument(skip(self))]
115 #[inline]
116 pub fn try_send(
117 &self,
118 command: AudioCommand,
119 ) -> Result<(), std::sync::mpsc::SendError<(AudioCommand, tracing::Span)>> {
120 let ctx =
121 tracing::info_span!("Sending Audio Command to Kernel", command = ?command).or_current();
122
123 self.tx.send((command, ctx))
124 }
125}
126
127impl Drop for AudioKernelSender {
128 #[allow(clippy::missing_inline_in_public_items)]
129 fn drop(&mut self) {
130 let _ = self.try_send(AudioCommand::Exit);
133 }
134}
135
136pub(crate) struct AudioKernel {
144 #[cfg(not(feature = "mock_playback"))]
146 _music_output: rodio::OutputStream,
147 #[cfg(feature = "mock_playback")]
148 queue_rx_stop: Arc<AtomicBool>,
149 player: rodio::Sink,
153 queue: Queue,
155 volume: f32,
158 muted: bool,
160 status: Status,
162 command_tx: Sender<(AudioCommand, tracing::Span)>,
165 event_tx: Sender<StateChange>,
167}
168
169#[cfg(feature = "mock_playback")]
170impl Drop for AudioKernel {
171 fn drop(&mut self) {
172 self.queue_rx_stop
173 .store(true, std::sync::atomic::Ordering::Relaxed);
174 }
175}
176
177impl AudioKernel {
178 #[must_use]
184 #[cfg(not(feature = "mock_playback"))]
185 pub fn new(
186 command_tx: Sender<(AudioCommand, tracing::Span)>,
187 event_tx: Sender<StateChange>,
188 ) -> Self {
189 let stream = rodio::OutputStreamBuilder::open_default_stream().unwrap();
190
191 let sink = rodio::Sink::connect_new(stream.mixer());
192 sink.pause();
193
194 Self {
195 _music_output: stream,
196 player: sink,
197 queue: Queue::new(),
198 volume: 1.0,
199 muted: false,
200 status: Status::Stopped,
201 command_tx,
202 event_tx,
203 }
204 }
205
206 #[must_use]
214 #[cfg(feature = "mock_playback")]
215 pub fn new(
216 command_tx: Sender<(AudioCommand, tracing::Span)>,
217 event_tx: Sender<StateChange>,
218 ) -> Self {
219 const QUEUE_POLLING_INTERVAL: Duration = Duration::from_micros(22);
222
223 let (sink, mut queue_rx) = rodio::Sink::new();
224
225 let queue_stop = Arc::new(AtomicBool::new(false));
226 let queue_stop_clone = queue_stop.clone();
227
228 std::thread::spawn(move || {
229 loop {
231 if queue_stop_clone.load(std::sync::atomic::Ordering::Relaxed) {
232 break;
233 }
234 queue_rx.next();
235 std::thread::sleep(QUEUE_POLLING_INTERVAL);
236 }
237 });
238
239 sink.pause();
240
241 Self {
242 player: sink,
243 queue_rx_stop: queue_stop,
244 queue: Queue::new(),
245 volume: 1.0,
246 muted: false,
247 status: Status::Stopped,
248 command_tx,
249 event_tx,
250 }
251 }
252
253 pub fn init(mut self, command_rx: Receiver<(AudioCommand, tracing::Span)>) {
264 for (command, ctx) in command_rx {
265 let _guard = ctx.enter();
266
267 let prev_status = self.status;
268
269 match command {
270 AudioCommand::Play => self.play(),
271 AudioCommand::Pause => self.pause(),
272 AudioCommand::TogglePlayback => self.toggle_playback(),
273 AudioCommand::RestartSong => {
274 self.restart_song();
275 let _ = self
276 .event_tx
277 .send(StateChange::Seeked(Duration::from_secs(0)));
278 }
279 AudioCommand::ClearPlayer => self.clear_player(),
280 AudioCommand::Queue(command) => self.queue_control(command),
281 AudioCommand::Exit => break,
282 AudioCommand::ReportStatus(tx) => {
283 let state = self.state();
284
285 if let Err(e) = tx.send(state) {
286 error!(
291 "Audio Kernel failed to send state to the receiver, state receiver likely has been dropped. State: {e}"
292 );
293 break;
294 }
295 }
296 AudioCommand::Volume(command) => self.volume_control(command),
297 AudioCommand::Seek(seek, duration) => {
298 self.seek(seek, duration);
299 let _ = self
300 .event_tx
301 .send(StateChange::Seeked(self.get_time_played()));
302 }
303 AudioCommand::Stop if prev_status != Status::Stopped => {
304 self.stop();
305 let _ = self
306 .event_tx
307 .send(StateChange::Seeked(Duration::from_secs(0)));
308 }
309 AudioCommand::Stop => {}
310 }
311
312 let new_status = self.status;
313
314 if prev_status != new_status {
315 let _ = self.event_tx.send(StateChange::StatusChanged(new_status));
316 }
317 }
318 }
319
320 #[instrument(skip(self))]
321 fn play(&mut self) {
322 if self.player.empty() {
323 return;
324 }
325 self.player.play();
326 self.status = Status::Playing;
327 }
328
329 #[instrument(skip(self))]
330 fn pause(&mut self) {
331 self.player.pause();
332 self.status = Status::Paused;
333 }
334
335 #[instrument(skip(self))]
336 fn stop(&mut self) {
337 self.player.pause();
338 self.seek(SeekType::Absolute, Duration::from_secs(0));
339 self.status = Status::Stopped;
340 }
341
342 #[instrument(skip(self))]
343 fn toggle_playback(&mut self) {
344 if self.player.is_paused() {
345 self.play();
346 } else {
347 self.pause();
348 }
349 }
350
351 #[instrument(skip(self))]
352 fn restart_song(&mut self) {
353 let status = self.status;
354 self.clear_player();
355
356 if let Some(song) = self.queue.current_song() {
357 if let Err(e) = self.append_song_to_player(song) {
358 error!("Failed to append song to player: {e}");
359 }
360
361 match status {
362 Status::Stopped => {}
364 Status::Paused => self.pause(),
366 Status::Playing => self.play(),
368 }
369 }
370 }
371
372 #[instrument(skip(self))]
373 fn clear(&mut self) {
374 self.clear_player();
375 self.queue.clear();
376 }
377
378 #[instrument(skip(self))]
379 fn clear_player(&mut self) {
380 self.player.clear();
381 self.status = Status::Stopped;
382 }
383
384 #[instrument(skip(self))]
385 fn queue_control(&mut self, command: QueueCommand) {
386 let prev_song = self.queue.current_song().cloned();
387 let prev_len = self.queue.len();
388 match command {
389 QueueCommand::Clear => self.clear(),
390 QueueCommand::PlayNextSong => self.start_next_song(),
391 QueueCommand::SkipForward(n) => self.skip_forward(n),
392 QueueCommand::SkipBackward(n) => self.skip_backward(n),
393 QueueCommand::SetPosition(n) => self.set_position(n),
394 QueueCommand::Shuffle => {
395 self.queue.shuffle();
396 let _ = self.event_tx.send(StateChange::QueueChanged);
399 }
400 QueueCommand::AddToQueue(song_box) => match song_box {
401 OneOrMany::None => {}
402 OneOrMany::One(song) => self.add_song_to_queue(*song),
403 OneOrMany::Many(songs) => self.add_songs_to_queue(songs),
404 },
405 QueueCommand::RemoveRange(range) => self.remove_range_from_queue(range),
406 QueueCommand::SetRepeatMode(mode) => {
407 self.queue.set_repeat_mode(mode);
408 let _ = self.event_tx.send(StateChange::RepeatModeChanged(mode));
409 }
410 }
411
412 let new_song = self.queue.current_song().cloned();
413 let new_len = self.queue.len();
414
415 if prev_len != new_len {
417 let _ = self.event_tx.send(StateChange::QueueChanged);
418 }
419 if prev_song != new_song {
421 let _ = self
422 .event_tx
423 .send(StateChange::TrackChanged(new_song.map(|s| s.id.into())));
424 }
425 }
426
427 #[instrument(skip(self))]
428 fn state(&self) -> StateAudio {
429 let queue_position = self.queue.current_index();
430 let current_song = self.queue.current_song().cloned();
431 let repeat_mode = self.queue.get_repeat_mode();
432 let runtime = current_song.as_ref().map(|song| {
433 let duration = song.runtime;
434 let seek_position = self.get_time_played();
435 let seek_percent =
436 Percent::new(seek_position.as_secs_f32() / duration.as_secs_f32() * 100.0);
437 StateRuntime {
438 seek_position,
439 seek_percent,
440 duration,
441 }
442 });
443 let status = self.status;
444 let status = if self.player.is_paused() {
445 debug_assert!(matches!(status, Status::Paused | Status::Stopped));
446 status
447 } else {
448 debug_assert_eq!(status, Status::Playing);
449 Status::Playing
450 };
451
452 let muted = self.muted;
453 let volume = self.volume;
454
455 let queued_songs = self.queue.queued_songs();
456
457 StateAudio {
458 queue: queued_songs,
459 queue_position,
460 current_song,
461 repeat_mode,
462 runtime,
463 status,
464 muted,
465 volume,
466 }
467 }
468
469 #[instrument(skip(self))]
470 fn start_next_song(&mut self) {
471 self.status = Status::Stopped;
472 self.player.pause();
476
477 let next_song = self.queue.next_song().cloned();
478 let repeat_mode = self.queue.get_repeat_mode();
479 let current_index = self.queue.current_index();
480
481 if let Some(song) = next_song {
482 if let Err(e) = self.append_song_to_player(&song) {
483 error!("Failed to append song to player: {e}");
484 }
485
486 if current_index.is_some() || repeat_mode.is_all() {
489 self.play();
490 }
491 }
492 }
493
494 #[instrument(skip(self))]
495 fn skip_forward(&mut self, n: usize) {
496 let status = self.status;
497 self.clear_player();
498
499 let next_song = self.queue.skip_forward(n).cloned();
500
501 if let Some(song) = next_song {
502 if let Err(e) = self.append_song_to_player(&song) {
503 error!("Failed to append song to player: {e}");
504 }
505
506 match status {
507 Status::Paused => self.pause(),
508 Status::Playing
511 if self.queue.get_repeat_mode().is_all()
512 || self.queue.current_index().is_some() =>
513 {
514 self.play();
515 }
516 _ => {}
517 }
518 }
519 }
520
521 #[instrument(skip(self))]
522 fn skip_backward(&mut self, n: usize) {
523 let status = self.status;
524 self.clear_player();
525
526 let next_song = self.queue.skip_backward(n).cloned();
527
528 if let Some(song) = next_song {
529 if let Err(e) = self.append_song_to_player(&song) {
530 error!("Failed to append song to player: {e}");
531 }
532 match status {
533 Status::Stopped => {}
534 Status::Paused => self.pause(),
535 Status::Playing => self.play(),
536 }
537 }
538 }
539
540 #[instrument(skip(self))]
541 fn set_position(&mut self, n: usize) {
542 let status = self.status;
543 self.clear_player();
544
545 self.queue.set_current_index(n);
546 let next_song = self.queue.current_song().cloned();
547
548 if let Some(song) = next_song {
549 if let Err(e) = self.append_song_to_player(&song) {
550 error!("Failed to append song to player: {e}");
551 }
552
553 match status {
554 Status::Stopped => {}
555 Status::Paused => self.pause(),
556 Status::Playing => self.play(),
557 }
558 }
559 }
560
561 #[instrument(skip(self))]
562 fn add_song_to_queue(&mut self, song: SongBrief) {
563 self.queue.add_song(song);
564
565 if self.player.empty() {
567 let current_song = self.get_current_song();
568
569 if let Some(song) = current_song.or_else(|| self.get_next_song()) {
570 if let Err(e) = self.append_song_to_player(&song) {
571 error!("Failed to append song to player: {e}");
572 }
573 self.play();
574 }
575 }
576 }
577
578 #[instrument(skip(self))]
579 fn add_songs_to_queue(&mut self, songs: Vec<SongBrief>) {
580 self.queue.add_songs(songs);
581
582 if self.player.empty() {
584 let current_song = self.get_current_song();
585
586 if let Some(song) = current_song.or_else(|| self.get_next_song()) {
587 if let Err(e) = self.append_song_to_player(&song) {
588 error!("Failed to append song to player: {e}");
589 }
590 self.play();
591 }
592 }
593 }
594
595 #[instrument(skip(self))]
596 fn remove_range_from_queue(&mut self, range: Range<usize>) {
597 let current_to_be_removed = self
599 .queue
600 .current_index()
601 .is_some_and(|current_index| range.contains(¤t_index));
602
603 self.queue.remove_range(range);
604
605 if !current_to_be_removed {
607 return;
608 }
609 self.clear_player();
610 if let Some(song) = self.get_current_song()
611 && let Err(e) = self.append_song_to_player(&song)
612 {
613 error!("Failed to append song to player: {e}");
614 }
615 }
616
617 #[instrument(skip(self))]
618 fn get_current_song(&self) -> Option<SongBrief> {
619 self.queue.current_song().cloned()
620 }
621
622 #[instrument(skip(self))]
623 fn get_next_song(&mut self) -> Option<SongBrief> {
624 self.queue.next_song().cloned()
625 }
626
627 fn get_time_played(&self) -> Duration {
628 self.player.get_pos()
629 }
630
631 #[instrument(skip(self, source))]
632 fn append_to_player<T>(&self, source: T)
633 where
634 T: Source<Item = f32> + Send + 'static,
635 {
636 self.player.append(source);
637
638 let command_tx = self.command_tx.clone();
640 self.player.append(EmptyCallback::new(Box::new(move || {
641 debug!("Song finished");
642 if let Err(e) = command_tx.send((
643 AudioCommand::Queue(QueueCommand::PlayNextSong),
644 tracing::Span::current(),
645 )) {
646 error!("Failed to send command to audio kernel: {e}");
647 } else {
648 debug!("Sent PlayNextSong command to audio kernel");
649 }
650 })));
651 }
652
653 #[instrument(skip(self))]
654 fn append_song_to_player(&self, song: &SongBrief) -> Result<(), LibraryError> {
655 let file = File::open(&song.path)?;
656 let byte_len = file.metadata()?.len();
657 let decoder = DecoderBuilder::new()
658 .with_data(BufReader::new(file))
659 .with_byte_len(byte_len)
660 .with_seekable(true)
661 .with_coarse_seek(true)
662 .with_gapless(true)
663 .build()?;
664 self.append_to_player(decoder);
665
666 Ok(())
667 }
668
669 #[instrument(skip(self))]
670 fn volume_control(&mut self, command: VolumeCommand) {
671 match command {
672 VolumeCommand::Up(change) => {
673 let volume = self.volume;
674 let updated = (volume + change).clamp(MIN_VOLUME, MAX_VOLUME);
675 if (volume - updated).abs() > 0.0001 {
677 self.volume = updated;
678 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
679 }
680 }
681 VolumeCommand::Down(change) => {
682 let volume = self.volume;
683 let updated = (volume - change).clamp(MIN_VOLUME, MAX_VOLUME);
684 if (volume - updated).abs() > 0.0001 {
685 self.volume = updated;
686 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
687 }
688 }
689 VolumeCommand::Set(updated) => {
690 let volume = self.volume;
691 let updated = updated.clamp(MIN_VOLUME, MAX_VOLUME);
692 if (volume - updated).abs() > 0.0001 {
693 self.volume = updated;
694 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
695 }
696 }
697 VolumeCommand::Mute => {
698 self.muted = true;
699 let _ = self.event_tx.send(StateChange::Muted);
700 }
701 VolumeCommand::Unmute => {
702 self.muted = false;
703 let _ = self.event_tx.send(StateChange::Unmuted);
704 }
705 VolumeCommand::ToggleMute => {
706 self.muted = !self.muted;
707 if self.muted {
708 let _ = self.event_tx.send(StateChange::Muted);
709 } else {
710 let _ = self.event_tx.send(StateChange::Unmuted);
711 }
712 }
713 }
714
715 if self.muted {
716 self.player.set_volume(0.0);
717 } else {
718 self.player.set_volume(self.volume.to_owned());
719 }
720 }
721
722 #[instrument(skip(self))]
723 fn seek(&mut self, seek: SeekType, duration: Duration) {
724 let new_time = match seek {
726 SeekType::Absolute => duration,
727 SeekType::RelativeForwards => self.get_time_played().saturating_add(duration),
728 SeekType::RelativeBackwards => self.get_time_played().saturating_sub(duration),
729 };
730
731 match self.player.try_seek(new_time) {
735 Ok(()) => {
736 debug!("Seek to {} successful", format_duration(&new_time));
737 if new_time > Duration::from_secs(0) && self.status == Status::Stopped {
738 self.status = Status::Paused;
739 }
740 }
741 Err(SeekError::NotSupported { underlying_source }) => {
742 error!("Seek not supported by source: {underlying_source}");
743 }
744 Err(err) => {
745 error!("Seeking failed with error: {err}");
746 }
747 }
748 }
749}
750
751#[cfg(test)]
752mod tests {
753 use pretty_assertions::assert_eq;
754 use rstest::{fixture, rstest};
755
756 use crate::test_utils::init;
757
758 use super::*;
759 use std::sync::mpsc;
760 use std::time::Duration;
761
762 #[fixture]
763 fn audio_kernel() -> AudioKernel {
764 let (tx, _) = mpsc::channel();
766 let (event_tx, _) = mpsc::channel();
768 AudioKernel::new(tx, event_tx)
769 }
770
771 #[fixture]
772 fn audio_kernel_sender() -> Arc<AudioKernelSender> {
773 let (tx, _) = mpsc::channel();
775 AudioKernelSender::start(tx)
776 }
777
778 async fn get_state(sender: Arc<AudioKernelSender>) -> StateAudio {
779 let (tx, rx) = tokio::sync::oneshot::channel::<StateAudio>();
780 sender.send(AudioCommand::ReportStatus(tx));
781 rx.await.unwrap()
782 }
783
784 #[fixture]
785 fn sound() -> impl Source<Item = f32> + Send + 'static {
786 rodio::source::SineWave::new(440.0)
787 }
788
789 #[test]
790 fn test_audio_kernel_sender_send() {
791 let (tx, rx) = mpsc::channel();
792 let sender = AudioKernelSender::new(tx);
793 sender.send(AudioCommand::Play);
794 let (recv, _) = rx.recv().unwrap();
795 assert_eq!(recv, AudioCommand::Play);
796 }
797
798 #[test]
799 #[should_panic = "Failed to send command to audio kernel: sending on a closed channel"]
800 fn test_audio_kernel_send_closed_channel() {
801 let (tx, _) = mpsc::channel();
802 let sender = AudioKernelSender::new(tx);
803 sender.send(AudioCommand::Play);
804 }
805
806 #[test]
807 fn test_audio_kernel_try_send_closed_channel() {
808 let (tx, _) = mpsc::channel();
809 let sender = AudioKernelSender::new(tx);
810 assert!(sender.try_send(AudioCommand::Play).is_err());
811 }
812
813 #[rstest]
814 #[timeout(Duration::from_secs(3))] fn test_audio_player_kernel_spawn_and_exit(
816 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
817 ) {
818 init();
819
820 sender.send(AudioCommand::Exit);
821 }
822
823 #[rstest]
824 fn test_volume_control(mut audio_kernel: AudioKernel) {
825 audio_kernel.volume_control(VolumeCommand::Up(0.1));
826 let volume = audio_kernel.volume;
827 assert!(f32::EPSILON > (volume - 1.1).abs(), "{volume} != 1.1");
828
829 audio_kernel.volume_control(VolumeCommand::Down(0.1));
830 let volume = audio_kernel.volume;
831 assert!(f32::EPSILON > (volume - 1.0).abs(), "{volume} != 1.0");
832
833 audio_kernel.volume_control(VolumeCommand::Set(0.5));
834 let volume = audio_kernel.volume;
835 assert!(f32::EPSILON > (volume - 0.5).abs(), "{volume} != 0.5");
836
837 audio_kernel.volume_control(VolumeCommand::Mute);
838 assert_eq!(audio_kernel.muted, true);
839
840 audio_kernel.volume_control(VolumeCommand::Unmute);
841 assert_eq!(audio_kernel.muted, false);
842
843 audio_kernel.volume_control(VolumeCommand::ToggleMute);
844 assert_eq!(audio_kernel.muted, true);
845
846 audio_kernel.volume_control(VolumeCommand::ToggleMute);
847 assert_eq!(audio_kernel.muted, false);
848 }
849
850 mod playback_tests {
851 use mecomp_storage::{
856 db::schemas::song::Song,
857 test_utils::{arb_song_case, create_song_metadata, init_test_database},
858 };
859 use pretty_assertions::assert_eq;
860 use rstest::rstest;
861
862 use crate::test_utils::init;
863
864 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
865
866 #[rstest]
867 fn test_audio_kernel_play_pause(
868 mut audio_kernel: AudioKernel,
869 sound: impl Source<Item = f32> + Send + 'static,
870 ) {
871 init();
872 audio_kernel.player.append(sound);
873 audio_kernel.play();
874 assert!(!audio_kernel.player.is_paused());
875 audio_kernel.pause();
876 assert!(audio_kernel.player.is_paused());
877 }
878
879 #[rstest]
880 fn test_audio_kernel_toggle_playback(
881 mut audio_kernel: AudioKernel,
882 sound: impl Source<Item = f32> + Send + 'static,
883 ) {
884 init();
885 audio_kernel.player.append(sound);
886 audio_kernel.play();
887 assert!(!audio_kernel.player.is_paused());
888 audio_kernel.toggle_playback();
889 assert!(audio_kernel.player.is_paused());
890 audio_kernel.toggle_playback();
891 assert!(!audio_kernel.player.is_paused());
892 }
893
894 #[rstest]
895 #[timeout(Duration::from_secs(10))] #[tokio::test]
897 async fn test_play_pause_toggle_restart(
898 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
899 ) {
900 init();
901 let db = init_test_database().await.unwrap();
902 let tempdir = tempfile::tempdir().unwrap();
903
904 let song = Song::try_load_into_db(
905 &db,
906 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
907 )
908 .await
909 .unwrap();
910
911 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
912 song.brief().into(),
913 )));
914
915 let state = get_state(sender.clone()).await;
916 assert_eq!(state.queue_position, Some(0));
917 assert_eq!(state.status, Status::Playing);
918
919 sender.send(AudioCommand::Pause);
920 let state = get_state(sender.clone()).await;
921 assert_eq!(state.status, Status::Paused);
922
923 sender.send(AudioCommand::Play);
924 let state = get_state(sender.clone()).await;
925 assert_eq!(state.status, Status::Playing);
926
927 sender.send(AudioCommand::RestartSong);
928 let state = get_state(sender.clone()).await;
929 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
932 let state = get_state(sender.clone()).await;
933 assert_eq!(state.status, Status::Paused);
934
935 sender.send(AudioCommand::RestartSong);
936 let state = get_state(sender.clone()).await;
937 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
940 }
941
942 #[rstest]
943 fn test_audio_kernel_stop(mut audio_kernel: AudioKernel) {
944 init();
945 audio_kernel.player.append(sound());
946 audio_kernel.play();
947 assert!(!audio_kernel.player.is_paused());
948 audio_kernel.stop();
949 assert!(audio_kernel.player.is_paused());
950 assert_eq!(audio_kernel.player.get_pos(), Duration::from_secs(0));
951 assert_eq!(audio_kernel.status, Status::Stopped);
952 }
953
954 #[rstest]
955 #[timeout(Duration::from_secs(10))] #[tokio::test]
957 async fn test_audio_kernel_skip_forward(mut audio_kernel: AudioKernel) {
958 init();
959 let db = init_test_database().await.unwrap();
960 let tempdir = tempfile::tempdir().unwrap();
961
962 let state = audio_kernel.state();
963 assert_eq!(state.queue_position, None);
964 assert!(state.paused());
965 assert_eq!(state.status, Status::Stopped);
966
967 audio_kernel.queue_control(QueueCommand::AddToQueue(OneOrMany::Many(vec![
968 Song::try_load_into_db(
969 &db,
970 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
971 )
972 .await
973 .unwrap()
974 .into(),
975 Song::try_load_into_db(
976 &db,
977 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
978 )
979 .await
980 .unwrap()
981 .into(),
982 Song::try_load_into_db(
983 &db,
984 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
985 )
986 .await
987 .unwrap()
988 .into(),
989 ])));
990
991 let state = audio_kernel.state();
993 assert_eq!(state.queue_position, Some(0));
994 assert!(!state.paused());
995 assert_eq!(state.status, Status::Playing);
996
997 audio_kernel.queue_control(QueueCommand::SkipForward(1));
998
999 let state = audio_kernel.state();
1001 assert_eq!(state.queue_position, Some(1));
1002 assert!(!state.paused());
1003 assert_eq!(state.status, Status::Playing);
1004
1005 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1006
1007 let state = audio_kernel.state();
1009 assert_eq!(state.queue_position, Some(2));
1010 assert!(!state.paused());
1011 assert_eq!(state.status, Status::Playing);
1012
1013 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1014
1015 let state = audio_kernel.state();
1018 assert_eq!(state.queue_position, None);
1019 assert!(state.paused());
1020 assert_eq!(state.status, Status::Stopped);
1021 }
1022
1023 #[rstest]
1024 #[timeout(Duration::from_secs(6))] #[tokio::test]
1026 async fn test_audio_kernel_skip_forward_sender(
1027 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1028 ) {
1029 init();
1031
1032 let db = init_test_database().await.unwrap();
1033 let tempdir = tempfile::tempdir().unwrap();
1034
1035 let state = get_state(sender.clone()).await;
1036 assert_eq!(state.queue_position, None);
1037 assert!(state.paused());
1038 assert_eq!(state.status, Status::Stopped);
1039
1040 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1041 OneOrMany::Many(vec![
1042 Song::try_load_into_db(
1043 &db,
1044 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1045 )
1046 .await
1047 .unwrap()
1048 .into(),
1049 Song::try_load_into_db(
1050 &db,
1051 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1052 )
1053 .await
1054 .unwrap()
1055 .into(),
1056 Song::try_load_into_db(
1057 &db,
1058 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1059 )
1060 .await
1061 .unwrap()
1062 .into(),
1063 ]),
1064 )));
1065 let state = get_state(sender.clone()).await;
1067 assert_eq!(state.queue_position, Some(0));
1068 assert!(!state.paused());
1069 assert_eq!(state.status, Status::Playing);
1070
1071 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1072 let state = get_state(sender.clone()).await;
1074 assert_eq!(state.queue_position, Some(1));
1075 assert!(!state.paused());
1076 assert_eq!(state.status, Status::Playing);
1077
1078 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1079 let state = get_state(sender.clone()).await;
1081 assert_eq!(state.queue_position, Some(2));
1082 assert!(!state.paused());
1083 assert_eq!(state.status, Status::Playing);
1084
1085 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1086 let state = get_state(sender.clone()).await;
1088 assert_eq!(state.queue_position, None);
1089 assert!(state.paused());
1090 assert_eq!(state.status, Status::Stopped);
1091
1092 sender.send(AudioCommand::Exit);
1093 }
1094
1095 #[rstest]
1096 #[timeout(Duration::from_secs(6))] #[tokio::test]
1098 async fn test_remove_range_from_queue(
1099 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1100 ) {
1101 init();
1102 let db = init_test_database().await.unwrap();
1103 let tempdir = tempfile::tempdir().unwrap();
1104 let song1 = Song::try_load_into_db(
1105 &db,
1106 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1107 )
1108 .await
1109 .unwrap();
1110 let song2 = Song::try_load_into_db(
1111 &db,
1112 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1113 )
1114 .await
1115 .unwrap();
1116
1117 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1119 OneOrMany::Many(vec![song1.clone().into(), song2.clone().into()]),
1120 )));
1121 let state = get_state(sender.clone()).await;
1122 assert_eq!(state.queue_position, Some(0));
1123 assert!(!state.paused());
1124 assert_eq!(state.status, Status::Playing);
1125
1126 sender.send(AudioCommand::Pause);
1128
1129 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1131 let state = get_state(sender.clone()).await;
1132 assert_eq!(state.queue_position, Some(0));
1133 assert!(state.paused());
1134 assert_eq!(state.status, Status::Stopped);
1135 assert_eq!(state.queue.len(), 1);
1136 assert_eq!(state.queue[0], song2.clone().into());
1137
1138 sender.send(AudioCommand::Play);
1140
1141 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1143 song1.clone().brief().into(),
1144 )));
1145 let state = get_state(sender.clone()).await;
1146 assert_eq!(state.queue_position, Some(0));
1147 assert!(!state.paused());
1148 assert_eq!(state.status, Status::Playing);
1149 assert_eq!(state.queue.len(), 2);
1150 assert_eq!(state.queue[0], song2.clone().into());
1151 assert_eq!(state.queue[1], song1.into());
1152
1153 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1155 let state = get_state(sender.clone()).await;
1156 assert_eq!(state.queue_position, Some(0));
1157 assert!(!state.paused());
1158 assert_eq!(state.status, Status::Playing);
1159 assert_eq!(state.queue.len(), 1);
1160 assert_eq!(state.queue[0], song2.into());
1161
1162 sender.send(AudioCommand::Exit);
1163 }
1164
1165 #[rstest]
1166 #[timeout(Duration::from_secs(10))] #[tokio::test]
1168 async fn test_audio_kernel_skip_backward(
1169 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1170 ) {
1171 init();
1172 let db = init_test_database().await.unwrap();
1173 let tempdir = tempfile::tempdir().unwrap();
1174
1175 let state = get_state(sender.clone()).await;
1176 assert_eq!(state.queue_position, None);
1177 assert!(state.paused());
1178 assert_eq!(state.status, Status::Stopped);
1179
1180 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1181 OneOrMany::Many(vec![
1182 Song::try_load_into_db(
1183 &db,
1184 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1185 )
1186 .await
1187 .unwrap()
1188 .into(),
1189 Song::try_load_into_db(
1190 &db,
1191 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1192 )
1193 .await
1194 .unwrap()
1195 .into(),
1196 Song::try_load_into_db(
1197 &db,
1198 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1199 )
1200 .await
1201 .unwrap()
1202 .into(),
1203 ]),
1204 )));
1205
1206 let state = get_state(sender.clone()).await;
1208 assert_eq!(state.queue_position, Some(0));
1209 assert!(!state.paused());
1210 assert_eq!(state.status, Status::Playing);
1211
1212 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1213
1214 let state = get_state(sender.clone()).await;
1216 assert_eq!(state.queue_position, Some(2));
1217 assert!(!state.paused());
1218 assert_eq!(state.status, Status::Playing);
1219
1220 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1221
1222 let state = get_state(sender.clone()).await;
1224 assert_eq!(state.queue_position, Some(1));
1225 assert!(!state.paused());
1226 assert_eq!(state.status, Status::Playing);
1227
1228 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1229
1230 let state = get_state(sender.clone()).await;
1232 assert_eq!(state.queue_position, Some(0));
1233 assert!(!state.paused());
1234
1235 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1236
1237 let state = get_state(sender.clone()).await;
1239 assert_eq!(state.queue_position, None);
1240 assert!(state.paused());
1241 assert_eq!(state.status, Status::Stopped);
1242
1243 sender.send(AudioCommand::Exit);
1244 }
1245
1246 #[rstest]
1247 #[timeout(Duration::from_secs(10))] #[tokio::test]
1249 async fn test_audio_kernel_set_position(
1250 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1251 ) {
1252 init();
1253 let db = init_test_database().await.unwrap();
1254 let tempdir = tempfile::tempdir().unwrap();
1255
1256 let state = get_state(sender.clone()).await;
1257 assert_eq!(state.queue_position, None);
1258 assert!(state.paused());
1259 assert_eq!(state.status, Status::Stopped);
1260
1261 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1262 OneOrMany::Many(vec![
1263 Song::try_load_into_db(
1264 &db,
1265 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1266 )
1267 .await
1268 .unwrap()
1269 .into(),
1270 Song::try_load_into_db(
1271 &db,
1272 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1273 )
1274 .await
1275 .unwrap()
1276 .into(),
1277 Song::try_load_into_db(
1278 &db,
1279 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1280 )
1281 .await
1282 .unwrap()
1283 .into(),
1284 ]),
1285 )));
1286 let state = get_state(sender.clone()).await;
1288 assert_eq!(state.queue_position, Some(0));
1289 assert!(!state.paused());
1290 assert_eq!(state.status, Status::Playing);
1291
1292 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1293 let state = get_state(sender.clone()).await;
1295 assert_eq!(state.queue_position, Some(1));
1296 assert!(!state.paused());
1297 assert_eq!(state.status, Status::Playing);
1298
1299 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1300 let state = get_state(sender.clone()).await;
1302 assert_eq!(state.queue_position, Some(2));
1303 assert!(!state.paused());
1304 assert_eq!(state.status, Status::Playing);
1305
1306 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1307 let state = get_state(sender.clone()).await;
1309 assert_eq!(state.queue_position, Some(0));
1310 assert!(!state.paused());
1311 assert_eq!(state.status, Status::Playing);
1312
1313 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1314 let state = get_state(sender.clone()).await;
1316 assert_eq!(state.queue_position, Some(2));
1317 assert!(!state.paused());
1318 assert_eq!(state.status, Status::Playing);
1319
1320 sender.send(AudioCommand::Exit);
1321 }
1322
1323 #[rstest]
1324 #[timeout(Duration::from_secs(6))] #[tokio::test]
1326 async fn test_audio_kernel_clear(
1327 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1328 ) {
1329 init();
1330 let db = init_test_database().await.unwrap();
1331 let tempdir = tempfile::tempdir().unwrap();
1332
1333 let state = get_state(sender.clone()).await;
1334 assert_eq!(state.queue_position, None);
1335 assert!(state.paused());
1336 assert_eq!(state.status, Status::Stopped);
1337
1338 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1339 OneOrMany::Many(vec![
1340 Song::try_load_into_db(
1341 &db,
1342 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1343 )
1344 .await
1345 .unwrap()
1346 .into(),
1347 Song::try_load_into_db(
1348 &db,
1349 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1350 )
1351 .await
1352 .unwrap()
1353 .into(),
1354 Song::try_load_into_db(
1355 &db,
1356 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1357 )
1358 .await
1359 .unwrap()
1360 .into(),
1361 ]),
1362 )));
1363 let state = get_state(sender.clone()).await;
1365 assert_eq!(state.queue_position, Some(0));
1366 assert_eq!(state.queue.len(), 3);
1367 assert!(!state.paused());
1368 assert_eq!(state.status, Status::Playing);
1369
1370 sender.send(AudioCommand::ClearPlayer);
1371 let state = get_state(sender.clone()).await;
1373 assert_eq!(state.queue_position, Some(0));
1374 assert_eq!(state.queue.len(), 3);
1375 assert!(state.paused());
1376 assert_eq!(state.status, Status::Stopped);
1377
1378 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1379 let state = get_state(sender.clone()).await;
1381 assert_eq!(state.queue_position, None);
1382 assert_eq!(state.queue.len(), 0);
1383 assert!(state.paused());
1384 assert_eq!(state.status, Status::Stopped);
1385
1386 sender.send(AudioCommand::Exit);
1387 }
1388
1389 #[rstest]
1390 #[timeout(Duration::from_secs(6))] #[tokio::test]
1392 async fn test_audio_kernel_shuffle(
1393 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1394 ) {
1395 init();
1396 let db = init_test_database().await.unwrap();
1397 let tempdir = tempfile::tempdir().unwrap();
1398
1399 let state = get_state(sender.clone()).await;
1400 assert_eq!(state.queue_position, None);
1401 assert!(state.paused());
1402 assert_eq!(state.status, Status::Stopped);
1403
1404 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1405 OneOrMany::Many(vec![
1406 Song::try_load_into_db(
1407 &db,
1408 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1409 )
1410 .await
1411 .unwrap()
1412 .into(),
1413 Song::try_load_into_db(
1414 &db,
1415 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1416 )
1417 .await
1418 .unwrap()
1419 .into(),
1420 Song::try_load_into_db(
1421 &db,
1422 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1423 )
1424 .await
1425 .unwrap()
1426 .into(),
1427 ]),
1428 )));
1429 let state = get_state(sender.clone()).await;
1431 assert_eq!(state.queue_position, Some(0));
1432 assert_eq!(state.queue.len(), 3);
1433 assert!(!state.paused());
1434 assert_eq!(state.status, Status::Playing);
1435
1436 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1438 let state = get_state(sender.clone()).await;
1439 assert_eq!(state.queue_position, Some(1));
1440 assert_eq!(state.queue.len(), 3);
1441 assert!(!state.paused());
1442 assert_eq!(state.status, Status::Playing);
1443
1444 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1446 let state = get_state(sender.clone()).await;
1448 assert_eq!(state.queue_position, Some(0));
1449 assert_eq!(state.queue.len(), 3);
1450 assert!(!state.paused());
1451 assert_eq!(state.status, Status::Playing);
1452
1453 sender.send(AudioCommand::Exit);
1454 }
1455
1456 #[rstest]
1457 #[timeout(Duration::from_secs(5))] #[tokio::test]
1459 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1460 init();
1461
1462 let state = get_state(sender.clone()).await;
1463 assert!(
1464 f32::EPSILON > (state.volume - 1.0).abs(),
1465 "{} != 1.0",
1466 state.volume
1467 );
1468 assert!(!state.muted);
1469
1470 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1471 let state = get_state(sender.clone()).await;
1472 assert!(
1473 f32::EPSILON > (state.volume - 1.1).abs(),
1474 "{} != 1.1",
1475 state.volume
1476 );
1477 assert!(!state.muted);
1478
1479 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1480 let state = get_state(sender.clone()).await;
1481 assert!(
1482 f32::EPSILON > (state.volume - 1.0).abs(),
1483 "{} != 1.0",
1484 state.volume
1485 );
1486 assert!(!state.muted);
1487
1488 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
1489 let state = get_state(sender.clone()).await;
1490 assert!(
1491 f32::EPSILON > (state.volume - 0.5).abs(),
1492 "{} != 0.5",
1493 state.volume
1494 );
1495 assert!(!state.muted);
1496
1497 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
1498 let state = get_state(sender.clone()).await;
1499 assert!(
1500 f32::EPSILON > (state.volume - 0.5).abs(),
1501 "{} != 0.5",
1502 state.volume
1503 ); assert!(state.muted);
1505
1506 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
1507 let state = get_state(sender.clone()).await;
1508 assert!(
1509 f32::EPSILON > (state.volume - 0.5).abs(),
1510 "{} != 0.5",
1511 state.volume
1512 );
1513 assert!(!state.muted);
1514
1515 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1516 let state = get_state(sender.clone()).await;
1517 assert!(
1518 f32::EPSILON > (state.volume - 0.5).abs(),
1519 "{} != 0.5",
1520 state.volume
1521 );
1522 assert!(state.muted);
1523
1524 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1525 let state = get_state(sender.clone()).await;
1526 assert!(
1527 f32::EPSILON > (state.volume - 0.5).abs(),
1528 "{} != 0.5",
1529 state.volume
1530 );
1531 assert!(!state.muted);
1532
1533 sender.send(AudioCommand::Exit);
1534 }
1535
1536 #[rstest]
1537 #[timeout(Duration::from_secs(5))] #[tokio::test]
1539 async fn test_volume_out_of_bounds(
1540 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1541 ) {
1542 init();
1543
1544 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1546 let state = get_state(sender.clone()).await;
1547 assert!(
1548 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1549 "{} != {}",
1550 state.volume,
1551 MAX_VOLUME
1552 );
1553 assert!(!state.muted);
1554 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1555 MAX_VOLUME + 0.5 - MIN_VOLUME,
1556 )));
1557 let state = get_state(sender.clone()).await;
1558 assert!(
1559 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1560 "{} != {}",
1561 state.volume,
1562 MIN_VOLUME
1563 );
1564 assert!(!state.muted);
1565
1566 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1568 let state = get_state(sender.clone()).await;
1569 assert!(
1570 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1571 "{} != {}",
1572 state.volume,
1573 MAX_VOLUME
1574 );
1575 assert!(!state.muted);
1576 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1577 let state = get_state(sender.clone()).await;
1578 assert!(
1579 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1580 "{} != {}",
1581 state.volume,
1582 MIN_VOLUME
1583 );
1584 assert!(!state.muted);
1585
1586 sender.send(AudioCommand::Exit);
1587 }
1588
1589 #[rstest]
1590 #[timeout(Duration::from_secs(9))] #[tokio::test]
1592 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1593 init();
1594 let db = init_test_database().await.unwrap();
1595 let tempdir = tempfile::tempdir().unwrap();
1596
1597 let song = Song::try_load_into_db(
1598 &db,
1599 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1600 )
1601 .await
1602 .unwrap();
1603
1604 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1607 song.clone().brief().into(),
1608 )));
1609 sender.send(AudioCommand::Stop);
1610 sender.send(AudioCommand::Seek(
1611 SeekType::Absolute,
1612 Duration::from_secs(0),
1613 ));
1614 let state: StateAudio = get_state(sender.clone()).await;
1615 assert_eq!(state.queue_position, Some(0));
1616 assert_eq!(state.status, Status::Stopped);
1617 assert_eq!(
1618 state.runtime.unwrap().duration,
1619 Duration::from_secs(10) + Duration::from_millis(188)
1620 );
1621 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(0));
1622
1623 sender.send(AudioCommand::Seek(
1625 SeekType::RelativeForwards,
1626 Duration::from_secs(2),
1627 ));
1628 let state = get_state(sender.clone()).await;
1629 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1630 assert_eq!(state.current_song, Some(song.clone().into()));
1631 assert_eq!(state.status, Status::Paused);
1632
1633 sender.send(AudioCommand::Seek(
1635 SeekType::RelativeBackwards,
1636 Duration::from_secs(1),
1637 ));
1638 let state = get_state(sender.clone()).await;
1639 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1640 assert_eq!(state.current_song, Some(song.clone().into()));
1641 assert_eq!(state.status, Status::Paused);
1642
1643 sender.send(AudioCommand::Seek(
1645 SeekType::Absolute,
1646 Duration::from_secs(10),
1647 ));
1648 let state = get_state(sender.clone()).await;
1649 assert_eq!(
1650 state.runtime.unwrap().seek_position,
1651 Duration::from_secs(10)
1652 );
1653 assert_eq!(state.current_song, Some(song.clone().into()));
1654 assert_eq!(state.status, Status::Paused);
1655
1656 sender.send(AudioCommand::Play);
1658 sender.send(AudioCommand::Seek(
1659 SeekType::RelativeForwards,
1660 Duration::from_secs(1),
1661 ));
1662 tokio::time::sleep(Duration::from_millis(500)).await;
1663 let state = get_state(sender.clone()).await;
1664 assert_eq!(state.queue_position, None);
1665 assert_eq!(state.status, Status::Stopped);
1666
1667 sender.send(AudioCommand::Exit);
1668 }
1669 }
1670}