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.into(),
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 match command {
388 QueueCommand::Clear => self.clear(),
389 QueueCommand::PlayNextSong => self.start_next_song(),
390 QueueCommand::SkipForward(n) => self.skip_forward(n),
391 QueueCommand::SkipBackward(n) => self.skip_backward(n),
392 QueueCommand::SetPosition(n) => self.set_position(n),
393 QueueCommand::Shuffle => self.queue.shuffle(),
394 QueueCommand::AddToQueue(song_box) => match song_box {
395 OneOrMany::None => {}
396 OneOrMany::One(song) => self.add_song_to_queue(*song),
397 OneOrMany::Many(songs) => self.add_songs_to_queue(songs),
398 },
399 QueueCommand::RemoveRange(range) => self.remove_range_from_queue(range),
400 QueueCommand::SetRepeatMode(mode) => {
401 self.queue.set_repeat_mode(mode);
402 let _ = self.event_tx.send(StateChange::RepeatModeChanged(mode));
403 }
404 }
405
406 let new_song = self.queue.current_song().cloned();
407
408 if prev_song != new_song {
409 let _ = self
410 .event_tx
411 .send(StateChange::TrackChanged(new_song.map(|s| s.id.into())));
412 }
413 }
414
415 #[instrument(skip(self))]
416 fn state(&self) -> StateAudio {
417 let queue_position = self.queue.current_index();
418 let current_song = self.queue.current_song().cloned();
419 let repeat_mode = self.queue.get_repeat_mode();
420 let runtime = current_song.as_ref().map(|song| {
421 let duration = song.runtime;
422 let seek_position = self.get_time_played();
423 let seek_percent =
424 Percent::new(seek_position.as_secs_f32() / duration.as_secs_f32() * 100.0);
425 StateRuntime {
426 seek_position,
427 seek_percent,
428 duration,
429 }
430 });
431 let status = self.status;
432 let status = if self.player.is_paused() {
433 debug_assert!(matches!(status, Status::Paused | Status::Stopped));
434 status
435 } else {
436 debug_assert_eq!(status, Status::Playing);
437 Status::Playing
438 };
439
440 let muted = self.muted;
441 let volume = self.volume;
442
443 let queued_songs = self.queue.queued_songs();
444
445 StateAudio {
446 queue: queued_songs,
447 queue_position,
448 current_song,
449 repeat_mode,
450 runtime,
451 status,
452 muted,
453 volume,
454 }
455 }
456
457 #[instrument(skip(self))]
458 fn start_next_song(&mut self) {
459 self.status = Status::Stopped;
460 self.player.pause();
464
465 let next_song = self.queue.next_song().cloned();
466 let repeat_mode = self.queue.get_repeat_mode();
467 let current_index = self.queue.current_index();
468
469 if let Some(song) = next_song {
470 if let Err(e) = self.append_song_to_player(&song) {
471 error!("Failed to append song to player: {e}");
472 }
473
474 if current_index.is_some() || repeat_mode.is_all() {
477 self.play();
478 }
479 }
480 }
481
482 #[instrument(skip(self))]
483 fn skip_forward(&mut self, n: usize) {
484 let status = self.status;
485 self.clear_player();
486
487 let next_song = self.queue.skip_forward(n).cloned();
488
489 if let Some(song) = next_song {
490 if let Err(e) = self.append_song_to_player(&song) {
491 error!("Failed to append song to player: {e}");
492 }
493
494 match status {
495 Status::Paused => self.pause(),
496 Status::Playing
499 if self.queue.get_repeat_mode().is_all()
500 || self.queue.current_index().is_some() =>
501 {
502 self.play();
503 }
504 _ => {}
505 }
506 }
507 }
508
509 #[instrument(skip(self))]
510 fn skip_backward(&mut self, n: usize) {
511 let status = self.status;
512 self.clear_player();
513
514 let next_song = self.queue.skip_backward(n).cloned();
515
516 if let Some(song) = next_song {
517 if let Err(e) = self.append_song_to_player(&song) {
518 error!("Failed to append song to player: {e}");
519 }
520 match status {
521 Status::Stopped => {}
522 Status::Paused => self.pause(),
523 Status::Playing => self.play(),
524 }
525 }
526 }
527
528 #[instrument(skip(self))]
529 fn set_position(&mut self, n: usize) {
530 let status = self.status;
531 self.clear_player();
532
533 self.queue.set_current_index(n);
534 let next_song = self.queue.current_song().cloned();
535
536 if let Some(song) = next_song {
537 if let Err(e) = self.append_song_to_player(&song) {
538 error!("Failed to append song to player: {e}");
539 }
540
541 match status {
542 Status::Stopped => {}
543 Status::Paused => self.pause(),
544 Status::Playing => self.play(),
545 }
546 }
547 }
548
549 #[instrument(skip(self))]
550 fn add_song_to_queue(&mut self, song: SongBrief) {
551 self.queue.add_song(song);
552
553 if self.player.empty() {
555 let current_song = self.get_current_song();
556
557 if let Some(song) = current_song.or_else(|| self.get_next_song()) {
558 if let Err(e) = self.append_song_to_player(&song) {
559 error!("Failed to append song to player: {e}");
560 }
561 self.play();
562 }
563 }
564 }
565
566 #[instrument(skip(self))]
567 fn add_songs_to_queue(&mut self, songs: Vec<SongBrief>) {
568 self.queue.add_songs(songs);
569
570 if self.player.empty() {
572 let current_song = self.get_current_song();
573
574 if let Some(song) = current_song.or_else(|| self.get_next_song()) {
575 if let Err(e) = self.append_song_to_player(&song) {
576 error!("Failed to append song to player: {e}");
577 }
578 self.play();
579 }
580 }
581 }
582
583 #[instrument(skip(self))]
584 fn remove_range_from_queue(&mut self, range: Range<usize>) {
585 let current_to_be_removed = self
587 .queue
588 .current_index()
589 .is_some_and(|current_index| range.contains(¤t_index));
590
591 self.queue.remove_range(range);
592
593 if current_to_be_removed {
595 self.clear_player();
596 if let Some(song) = self.get_current_song() {
597 if let Err(e) = self.append_song_to_player(&song) {
598 error!("Failed to append song to player: {e}");
599 }
600 }
601 }
602 }
603
604 #[instrument(skip(self))]
605 fn get_current_song(&self) -> Option<SongBrief> {
606 self.queue.current_song().cloned()
607 }
608
609 #[instrument(skip(self))]
610 fn get_next_song(&mut self) -> Option<SongBrief> {
611 self.queue.next_song().cloned()
612 }
613
614 fn get_time_played(&self) -> Duration {
615 self.player.get_pos()
616 }
617
618 #[instrument(skip(self, source))]
619 fn append_to_player<T>(&self, source: T)
620 where
621 T: Source<Item = f32> + Send + 'static,
622 {
623 self.player.append(source);
624
625 let command_tx = self.command_tx.clone();
627 self.player.append(EmptyCallback::new(Box::new(move || {
628 debug!("Song finished");
629 if let Err(e) = command_tx.send((
630 AudioCommand::Queue(QueueCommand::PlayNextSong),
631 tracing::Span::current(),
632 )) {
633 error!("Failed to send command to audio kernel: {e}");
634 } else {
635 debug!("Sent PlayNextSong command to audio kernel");
636 }
637 })));
638 }
639
640 #[instrument(skip(self))]
641 fn append_song_to_player(&self, song: &SongBrief) -> Result<(), LibraryError> {
642 let file = File::open(&song.path)?;
643 let byte_len = file.metadata()?.len();
644 let decoder = DecoderBuilder::new()
645 .with_data(BufReader::new(file))
646 .with_byte_len(byte_len)
647 .with_seekable(true)
648 .with_coarse_seek(true)
649 .with_gapless(true)
650 .build()?;
651 self.append_to_player(decoder);
652
653 Ok(())
654 }
655
656 #[instrument(skip(self))]
657 fn volume_control(&mut self, command: VolumeCommand) {
658 match command {
659 VolumeCommand::Up(change) => {
660 let volume = self.volume;
661 let updated = (volume + change).clamp(MIN_VOLUME, MAX_VOLUME);
662 if (volume - updated).abs() > 0.0001 {
664 self.volume = updated;
665 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
666 }
667 }
668 VolumeCommand::Down(change) => {
669 let volume = self.volume;
670 let updated = (volume - change).clamp(MIN_VOLUME, MAX_VOLUME);
671 if (volume - updated).abs() > 0.0001 {
672 self.volume = updated;
673 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
674 }
675 }
676 VolumeCommand::Set(updated) => {
677 let volume = self.volume;
678 let updated = updated.clamp(MIN_VOLUME, MAX_VOLUME);
679 if (volume - updated).abs() > 0.0001 {
680 self.volume = updated;
681 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
682 }
683 }
684 VolumeCommand::Mute => {
685 self.muted = true;
686 let _ = self.event_tx.send(StateChange::Muted);
687 }
688 VolumeCommand::Unmute => {
689 self.muted = false;
690 let _ = self.event_tx.send(StateChange::Unmuted);
691 }
692 VolumeCommand::ToggleMute => {
693 self.muted = !self.muted;
694 if self.muted {
695 let _ = self.event_tx.send(StateChange::Muted);
696 } else {
697 let _ = self.event_tx.send(StateChange::Unmuted);
698 }
699 }
700 }
701
702 if self.muted {
703 self.player.set_volume(0.0);
704 } else {
705 self.player.set_volume(self.volume.to_owned());
706 }
707 }
708
709 #[instrument(skip(self))]
710 fn seek(&mut self, seek: SeekType, duration: Duration) {
711 let new_time = match seek {
713 SeekType::Absolute => duration,
714 SeekType::RelativeForwards => self.get_time_played().saturating_add(duration),
715 SeekType::RelativeBackwards => self.get_time_played().saturating_sub(duration),
716 };
717
718 match self.player.try_seek(new_time) {
722 Ok(()) => {
723 debug!("Seek to {} successful", format_duration(&new_time));
724 if new_time > Duration::from_secs(0) && self.status == Status::Stopped {
725 self.status = Status::Paused;
726 }
727 }
728 Err(SeekError::NotSupported { underlying_source }) => {
729 error!("Seek not supported by source: {underlying_source}");
730 }
731 Err(err) => {
732 error!("Seeking failed with error: {err}");
733 }
734 }
735 }
736}
737
738#[cfg(test)]
739mod tests {
740 use pretty_assertions::assert_eq;
741 use rstest::{fixture, rstest};
742
743 use crate::test_utils::init;
744
745 use super::*;
746 use std::sync::mpsc;
747 use std::time::Duration;
748
749 #[fixture]
750 fn audio_kernel() -> AudioKernel {
751 let (tx, _) = mpsc::channel();
753 let (event_tx, _) = mpsc::channel();
755 AudioKernel::new(tx, event_tx)
756 }
757
758 #[fixture]
759 fn audio_kernel_sender() -> Arc<AudioKernelSender> {
760 let (tx, _) = mpsc::channel();
762 AudioKernelSender::start(tx)
763 }
764
765 async fn get_state(sender: Arc<AudioKernelSender>) -> StateAudio {
766 let (tx, rx) = tokio::sync::oneshot::channel::<StateAudio>();
767 sender.send(AudioCommand::ReportStatus(tx));
768 rx.await.unwrap()
769 }
770
771 #[fixture]
772 fn sound() -> impl Source<Item = f32> + Send + 'static {
773 rodio::source::SineWave::new(440.0)
774 }
775
776 #[test]
777 fn test_audio_kernel_sender_send() {
778 let (tx, rx) = mpsc::channel();
779 let sender = AudioKernelSender::new(tx);
780 sender.send(AudioCommand::Play);
781 let (recv, _) = rx.recv().unwrap();
782 assert_eq!(recv, AudioCommand::Play);
783 }
784
785 #[test]
786 #[should_panic = "Failed to send command to audio kernel: sending on a closed channel"]
787 fn test_audio_kernel_send_closed_channel() {
788 let (tx, _) = mpsc::channel();
789 let sender = AudioKernelSender::new(tx);
790 sender.send(AudioCommand::Play);
791 }
792
793 #[test]
794 fn test_audio_kernel_try_send_closed_channel() {
795 let (tx, _) = mpsc::channel();
796 let sender = AudioKernelSender::new(tx);
797 assert!(sender.try_send(AudioCommand::Play).is_err());
798 }
799
800 #[rstest]
801 #[timeout(Duration::from_secs(3))] fn test_audio_player_kernel_spawn_and_exit(
803 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
804 ) {
805 init();
806
807 sender.send(AudioCommand::Exit);
808 }
809
810 #[rstest]
811 fn test_volume_control(mut audio_kernel: AudioKernel) {
812 audio_kernel.volume_control(VolumeCommand::Up(0.1));
813 let volume = audio_kernel.volume;
814 assert!(f32::EPSILON > (volume - 1.1).abs(), "{volume} != 1.1");
815
816 audio_kernel.volume_control(VolumeCommand::Down(0.1));
817 let volume = audio_kernel.volume;
818 assert!(f32::EPSILON > (volume - 1.0).abs(), "{volume} != 1.0");
819
820 audio_kernel.volume_control(VolumeCommand::Set(0.5));
821 let volume = audio_kernel.volume;
822 assert!(f32::EPSILON > (volume - 0.5).abs(), "{volume} != 0.5");
823
824 audio_kernel.volume_control(VolumeCommand::Mute);
825 assert_eq!(audio_kernel.muted, true);
826
827 audio_kernel.volume_control(VolumeCommand::Unmute);
828 assert_eq!(audio_kernel.muted, false);
829
830 audio_kernel.volume_control(VolumeCommand::ToggleMute);
831 assert_eq!(audio_kernel.muted, true);
832
833 audio_kernel.volume_control(VolumeCommand::ToggleMute);
834 assert_eq!(audio_kernel.muted, false);
835 }
836
837 mod playback_tests {
838 use mecomp_storage::{
843 db::schemas::song::Song,
844 test_utils::{arb_song_case, create_song_metadata, init_test_database},
845 };
846 use pretty_assertions::assert_eq;
847 use rstest::rstest;
848
849 use crate::test_utils::init;
850
851 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
852
853 #[rstest]
854 fn test_audio_kernel_play_pause(
855 mut audio_kernel: AudioKernel,
856 sound: impl Source<Item = f32> + Send + 'static,
857 ) {
858 init();
859 audio_kernel.player.append(sound);
860 audio_kernel.play();
861 assert!(!audio_kernel.player.is_paused());
862 audio_kernel.pause();
863 assert!(audio_kernel.player.is_paused());
864 }
865
866 #[rstest]
867 fn test_audio_kernel_toggle_playback(
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.toggle_playback();
876 assert!(audio_kernel.player.is_paused());
877 audio_kernel.toggle_playback();
878 assert!(!audio_kernel.player.is_paused());
879 }
880
881 #[rstest]
882 #[timeout(Duration::from_secs(10))] #[tokio::test]
884 async fn test_play_pause_toggle_restart(
885 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
886 ) {
887 init();
888 let db = init_test_database().await.unwrap();
889 let tempdir = tempfile::tempdir().unwrap();
890
891 let song = Song::try_load_into_db(
892 &db,
893 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
894 )
895 .await
896 .unwrap();
897
898 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
899 song.brief().into(),
900 )));
901
902 let state = get_state(sender.clone()).await;
903 assert_eq!(state.queue_position, Some(0));
904 assert_eq!(state.status, Status::Playing);
905
906 sender.send(AudioCommand::Pause);
907 let state = get_state(sender.clone()).await;
908 assert_eq!(state.status, Status::Paused);
909
910 sender.send(AudioCommand::Play);
911 let state = get_state(sender.clone()).await;
912 assert_eq!(state.status, Status::Playing);
913
914 sender.send(AudioCommand::RestartSong);
915 let state = get_state(sender.clone()).await;
916 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
919 let state = get_state(sender.clone()).await;
920 assert_eq!(state.status, Status::Paused);
921
922 sender.send(AudioCommand::RestartSong);
923 let state = get_state(sender.clone()).await;
924 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
927 }
928
929 #[rstest]
930 fn test_audio_kernel_stop(mut audio_kernel: AudioKernel) {
931 init();
932 audio_kernel.player.append(sound());
933 audio_kernel.play();
934 assert!(!audio_kernel.player.is_paused());
935 audio_kernel.stop();
936 assert!(audio_kernel.player.is_paused());
937 assert_eq!(audio_kernel.player.get_pos(), Duration::from_secs(0));
938 assert_eq!(audio_kernel.status, Status::Stopped);
939 }
940
941 #[rstest]
942 #[timeout(Duration::from_secs(10))] #[tokio::test]
944 async fn test_audio_kernel_skip_forward(mut audio_kernel: AudioKernel) {
945 init();
946 let db = init_test_database().await.unwrap();
947 let tempdir = tempfile::tempdir().unwrap();
948
949 let state = audio_kernel.state();
950 assert_eq!(state.queue_position, None);
951 assert!(state.paused());
952 assert_eq!(state.status, Status::Stopped);
953
954 audio_kernel.queue_control(QueueCommand::AddToQueue(OneOrMany::Many(vec![
955 Song::try_load_into_db(
956 &db,
957 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
958 )
959 .await
960 .unwrap()
961 .into(),
962 Song::try_load_into_db(
963 &db,
964 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
965 )
966 .await
967 .unwrap()
968 .into(),
969 Song::try_load_into_db(
970 &db,
971 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
972 )
973 .await
974 .unwrap()
975 .into(),
976 ])));
977
978 let state = audio_kernel.state();
980 assert_eq!(state.queue_position, Some(0));
981 assert!(!state.paused());
982 assert_eq!(state.status, Status::Playing);
983
984 audio_kernel.queue_control(QueueCommand::SkipForward(1));
985
986 let state = audio_kernel.state();
988 assert_eq!(state.queue_position, Some(1));
989 assert!(!state.paused());
990 assert_eq!(state.status, Status::Playing);
991
992 audio_kernel.queue_control(QueueCommand::SkipForward(1));
993
994 let state = audio_kernel.state();
996 assert_eq!(state.queue_position, Some(2));
997 assert!(!state.paused());
998 assert_eq!(state.status, Status::Playing);
999
1000 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1001
1002 let state = audio_kernel.state();
1005 assert_eq!(state.queue_position, None);
1006 assert!(state.paused());
1007 assert_eq!(state.status, Status::Stopped);
1008 }
1009
1010 #[rstest]
1011 #[timeout(Duration::from_secs(6))] #[tokio::test]
1013 async fn test_audio_kernel_skip_forward_sender(
1014 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1015 ) {
1016 init();
1018
1019 let db = init_test_database().await.unwrap();
1020 let tempdir = tempfile::tempdir().unwrap();
1021
1022 let state = get_state(sender.clone()).await;
1023 assert_eq!(state.queue_position, None);
1024 assert!(state.paused());
1025 assert_eq!(state.status, Status::Stopped);
1026
1027 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1028 OneOrMany::Many(vec![
1029 Song::try_load_into_db(
1030 &db,
1031 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1032 )
1033 .await
1034 .unwrap()
1035 .into(),
1036 Song::try_load_into_db(
1037 &db,
1038 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1039 )
1040 .await
1041 .unwrap()
1042 .into(),
1043 Song::try_load_into_db(
1044 &db,
1045 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1046 )
1047 .await
1048 .unwrap()
1049 .into(),
1050 ]),
1051 )));
1052 let state = get_state(sender.clone()).await;
1054 assert_eq!(state.queue_position, Some(0));
1055 assert!(!state.paused());
1056 assert_eq!(state.status, Status::Playing);
1057
1058 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1059 let state = get_state(sender.clone()).await;
1061 assert_eq!(state.queue_position, Some(1));
1062 assert!(!state.paused());
1063 assert_eq!(state.status, Status::Playing);
1064
1065 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1066 let state = get_state(sender.clone()).await;
1068 assert_eq!(state.queue_position, Some(2));
1069 assert!(!state.paused());
1070 assert_eq!(state.status, Status::Playing);
1071
1072 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1073 let state = get_state(sender.clone()).await;
1075 assert_eq!(state.queue_position, None);
1076 assert!(state.paused());
1077 assert_eq!(state.status, Status::Stopped);
1078
1079 sender.send(AudioCommand::Exit);
1080 }
1081
1082 #[rstest]
1083 #[timeout(Duration::from_secs(6))] #[tokio::test]
1085 async fn test_remove_range_from_queue(
1086 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1087 ) {
1088 init();
1089 let db = init_test_database().await.unwrap();
1090 let tempdir = tempfile::tempdir().unwrap();
1091 let song1 = Song::try_load_into_db(
1092 &db,
1093 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1094 )
1095 .await
1096 .unwrap();
1097 let song2 = Song::try_load_into_db(
1098 &db,
1099 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1100 )
1101 .await
1102 .unwrap();
1103
1104 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1106 OneOrMany::Many(vec![song1.clone().into(), song2.clone().into()]),
1107 )));
1108 let state = get_state(sender.clone()).await;
1109 assert_eq!(state.queue_position, Some(0));
1110 assert!(!state.paused());
1111 assert_eq!(state.status, Status::Playing);
1112
1113 sender.send(AudioCommand::Pause);
1115
1116 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1118 let state = get_state(sender.clone()).await;
1119 assert_eq!(state.queue_position, Some(0));
1120 assert!(state.paused());
1121 assert_eq!(state.status, Status::Stopped);
1122 assert_eq!(state.queue.len(), 1);
1123 assert_eq!(state.queue[0], song2.clone().into());
1124
1125 sender.send(AudioCommand::Play);
1127
1128 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1130 song1.clone().brief().into(),
1131 )));
1132 let state = get_state(sender.clone()).await;
1133 assert_eq!(state.queue_position, Some(0));
1134 assert!(!state.paused());
1135 assert_eq!(state.status, Status::Playing);
1136 assert_eq!(state.queue.len(), 2);
1137 assert_eq!(state.queue[0], song2.clone().into());
1138 assert_eq!(state.queue[1], song1.into());
1139
1140 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1142 let state = get_state(sender.clone()).await;
1143 assert_eq!(state.queue_position, Some(0));
1144 assert!(!state.paused());
1145 assert_eq!(state.status, Status::Playing);
1146 assert_eq!(state.queue.len(), 1);
1147 assert_eq!(state.queue[0], song2.into());
1148
1149 sender.send(AudioCommand::Exit);
1150 }
1151
1152 #[rstest]
1153 #[timeout(Duration::from_secs(10))] #[tokio::test]
1155 async fn test_audio_kernel_skip_backward(
1156 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1157 ) {
1158 init();
1159 let db = init_test_database().await.unwrap();
1160 let tempdir = tempfile::tempdir().unwrap();
1161
1162 let state = get_state(sender.clone()).await;
1163 assert_eq!(state.queue_position, None);
1164 assert!(state.paused());
1165 assert_eq!(state.status, Status::Stopped);
1166
1167 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1168 OneOrMany::Many(vec![
1169 Song::try_load_into_db(
1170 &db,
1171 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1172 )
1173 .await
1174 .unwrap()
1175 .into(),
1176 Song::try_load_into_db(
1177 &db,
1178 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1179 )
1180 .await
1181 .unwrap()
1182 .into(),
1183 Song::try_load_into_db(
1184 &db,
1185 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1186 )
1187 .await
1188 .unwrap()
1189 .into(),
1190 ]),
1191 )));
1192
1193 let state = get_state(sender.clone()).await;
1195 assert_eq!(state.queue_position, Some(0));
1196 assert!(!state.paused());
1197 assert_eq!(state.status, Status::Playing);
1198
1199 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1200
1201 let state = get_state(sender.clone()).await;
1203 assert_eq!(state.queue_position, Some(2));
1204 assert!(!state.paused());
1205 assert_eq!(state.status, Status::Playing);
1206
1207 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1208
1209 let state = get_state(sender.clone()).await;
1211 assert_eq!(state.queue_position, Some(1));
1212 assert!(!state.paused());
1213 assert_eq!(state.status, Status::Playing);
1214
1215 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1216
1217 let state = get_state(sender.clone()).await;
1219 assert_eq!(state.queue_position, Some(0));
1220 assert!(!state.paused());
1221
1222 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1223
1224 let state = get_state(sender.clone()).await;
1226 assert_eq!(state.queue_position, None);
1227 assert!(state.paused());
1228 assert_eq!(state.status, Status::Stopped);
1229
1230 sender.send(AudioCommand::Exit);
1231 }
1232
1233 #[rstest]
1234 #[timeout(Duration::from_secs(10))] #[tokio::test]
1236 async fn test_audio_kernel_set_position(
1237 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1238 ) {
1239 init();
1240 let db = init_test_database().await.unwrap();
1241 let tempdir = tempfile::tempdir().unwrap();
1242
1243 let state = get_state(sender.clone()).await;
1244 assert_eq!(state.queue_position, None);
1245 assert!(state.paused());
1246 assert_eq!(state.status, Status::Stopped);
1247
1248 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1249 OneOrMany::Many(vec![
1250 Song::try_load_into_db(
1251 &db,
1252 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1253 )
1254 .await
1255 .unwrap()
1256 .into(),
1257 Song::try_load_into_db(
1258 &db,
1259 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1260 )
1261 .await
1262 .unwrap()
1263 .into(),
1264 Song::try_load_into_db(
1265 &db,
1266 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1267 )
1268 .await
1269 .unwrap()
1270 .into(),
1271 ]),
1272 )));
1273 let state = get_state(sender.clone()).await;
1275 assert_eq!(state.queue_position, Some(0));
1276 assert!(!state.paused());
1277 assert_eq!(state.status, Status::Playing);
1278
1279 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1280 let state = get_state(sender.clone()).await;
1282 assert_eq!(state.queue_position, Some(1));
1283 assert!(!state.paused());
1284 assert_eq!(state.status, Status::Playing);
1285
1286 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1287 let state = get_state(sender.clone()).await;
1289 assert_eq!(state.queue_position, Some(2));
1290 assert!(!state.paused());
1291 assert_eq!(state.status, Status::Playing);
1292
1293 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1294 let state = get_state(sender.clone()).await;
1296 assert_eq!(state.queue_position, Some(0));
1297 assert!(!state.paused());
1298 assert_eq!(state.status, Status::Playing);
1299
1300 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1301 let state = get_state(sender.clone()).await;
1303 assert_eq!(state.queue_position, Some(2));
1304 assert!(!state.paused());
1305 assert_eq!(state.status, Status::Playing);
1306
1307 sender.send(AudioCommand::Exit);
1308 }
1309
1310 #[rstest]
1311 #[timeout(Duration::from_secs(6))] #[tokio::test]
1313 async fn test_audio_kernel_clear(
1314 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1315 ) {
1316 init();
1317 let db = init_test_database().await.unwrap();
1318 let tempdir = tempfile::tempdir().unwrap();
1319
1320 let state = get_state(sender.clone()).await;
1321 assert_eq!(state.queue_position, None);
1322 assert!(state.paused());
1323 assert_eq!(state.status, Status::Stopped);
1324
1325 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1326 OneOrMany::Many(vec![
1327 Song::try_load_into_db(
1328 &db,
1329 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1330 )
1331 .await
1332 .unwrap()
1333 .into(),
1334 Song::try_load_into_db(
1335 &db,
1336 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1337 )
1338 .await
1339 .unwrap()
1340 .into(),
1341 Song::try_load_into_db(
1342 &db,
1343 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1344 )
1345 .await
1346 .unwrap()
1347 .into(),
1348 ]),
1349 )));
1350 let state = get_state(sender.clone()).await;
1352 assert_eq!(state.queue_position, Some(0));
1353 assert_eq!(state.queue.len(), 3);
1354 assert!(!state.paused());
1355 assert_eq!(state.status, Status::Playing);
1356
1357 sender.send(AudioCommand::ClearPlayer);
1358 let state = get_state(sender.clone()).await;
1360 assert_eq!(state.queue_position, Some(0));
1361 assert_eq!(state.queue.len(), 3);
1362 assert!(state.paused());
1363 assert_eq!(state.status, Status::Stopped);
1364
1365 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1366 let state = get_state(sender.clone()).await;
1368 assert_eq!(state.queue_position, None);
1369 assert_eq!(state.queue.len(), 0);
1370 assert!(state.paused());
1371 assert_eq!(state.status, Status::Stopped);
1372
1373 sender.send(AudioCommand::Exit);
1374 }
1375
1376 #[rstest]
1377 #[timeout(Duration::from_secs(6))] #[tokio::test]
1379 async fn test_audio_kernel_shuffle(
1380 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1381 ) {
1382 init();
1383 let db = init_test_database().await.unwrap();
1384 let tempdir = tempfile::tempdir().unwrap();
1385
1386 let state = get_state(sender.clone()).await;
1387 assert_eq!(state.queue_position, None);
1388 assert!(state.paused());
1389 assert_eq!(state.status, Status::Stopped);
1390
1391 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1392 OneOrMany::Many(vec![
1393 Song::try_load_into_db(
1394 &db,
1395 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1396 )
1397 .await
1398 .unwrap()
1399 .into(),
1400 Song::try_load_into_db(
1401 &db,
1402 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1403 )
1404 .await
1405 .unwrap()
1406 .into(),
1407 Song::try_load_into_db(
1408 &db,
1409 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1410 )
1411 .await
1412 .unwrap()
1413 .into(),
1414 ]),
1415 )));
1416 let state = get_state(sender.clone()).await;
1418 assert_eq!(state.queue_position, Some(0));
1419 assert_eq!(state.queue.len(), 3);
1420 assert!(!state.paused());
1421 assert_eq!(state.status, Status::Playing);
1422
1423 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1425 let state = get_state(sender.clone()).await;
1426 assert_eq!(state.queue_position, Some(1));
1427 assert_eq!(state.queue.len(), 3);
1428 assert!(!state.paused());
1429 assert_eq!(state.status, Status::Playing);
1430
1431 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1433 let state = get_state(sender.clone()).await;
1435 assert_eq!(state.queue_position, Some(0));
1436 assert_eq!(state.queue.len(), 3);
1437 assert!(!state.paused());
1438 assert_eq!(state.status, Status::Playing);
1439
1440 sender.send(AudioCommand::Exit);
1441 }
1442
1443 #[rstest]
1444 #[timeout(Duration::from_secs(5))] #[tokio::test]
1446 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1447 init();
1448
1449 let state = get_state(sender.clone()).await;
1450 assert!(
1451 f32::EPSILON > (state.volume - 1.0).abs(),
1452 "{} != 1.0",
1453 state.volume
1454 );
1455 assert!(!state.muted);
1456
1457 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1458 let state = get_state(sender.clone()).await;
1459 assert!(
1460 f32::EPSILON > (state.volume - 1.1).abs(),
1461 "{} != 1.1",
1462 state.volume
1463 );
1464 assert!(!state.muted);
1465
1466 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1467 let state = get_state(sender.clone()).await;
1468 assert!(
1469 f32::EPSILON > (state.volume - 1.0).abs(),
1470 "{} != 1.0",
1471 state.volume
1472 );
1473 assert!(!state.muted);
1474
1475 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
1476 let state = get_state(sender.clone()).await;
1477 assert!(
1478 f32::EPSILON > (state.volume - 0.5).abs(),
1479 "{} != 0.5",
1480 state.volume
1481 );
1482 assert!(!state.muted);
1483
1484 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
1485 let state = get_state(sender.clone()).await;
1486 assert!(
1487 f32::EPSILON > (state.volume - 0.5).abs(),
1488 "{} != 0.5",
1489 state.volume
1490 ); assert!(state.muted);
1492
1493 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
1494 let state = get_state(sender.clone()).await;
1495 assert!(
1496 f32::EPSILON > (state.volume - 0.5).abs(),
1497 "{} != 0.5",
1498 state.volume
1499 );
1500 assert!(!state.muted);
1501
1502 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1503 let state = get_state(sender.clone()).await;
1504 assert!(
1505 f32::EPSILON > (state.volume - 0.5).abs(),
1506 "{} != 0.5",
1507 state.volume
1508 );
1509 assert!(state.muted);
1510
1511 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1512 let state = get_state(sender.clone()).await;
1513 assert!(
1514 f32::EPSILON > (state.volume - 0.5).abs(),
1515 "{} != 0.5",
1516 state.volume
1517 );
1518 assert!(!state.muted);
1519
1520 sender.send(AudioCommand::Exit);
1521 }
1522
1523 #[rstest]
1524 #[timeout(Duration::from_secs(5))] #[tokio::test]
1526 async fn test_volume_out_of_bounds(
1527 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1528 ) {
1529 init();
1530
1531 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1533 let state = get_state(sender.clone()).await;
1534 assert!(
1535 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1536 "{} != {}",
1537 state.volume,
1538 MAX_VOLUME
1539 );
1540 assert!(!state.muted);
1541 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1542 MAX_VOLUME + 0.5 - MIN_VOLUME,
1543 )));
1544 let state = get_state(sender.clone()).await;
1545 assert!(
1546 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1547 "{} != {}",
1548 state.volume,
1549 MIN_VOLUME
1550 );
1551 assert!(!state.muted);
1552
1553 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1555 let state = get_state(sender.clone()).await;
1556 assert!(
1557 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1558 "{} != {}",
1559 state.volume,
1560 MAX_VOLUME
1561 );
1562 assert!(!state.muted);
1563 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1564 let state = get_state(sender.clone()).await;
1565 assert!(
1566 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1567 "{} != {}",
1568 state.volume,
1569 MIN_VOLUME
1570 );
1571 assert!(!state.muted);
1572
1573 sender.send(AudioCommand::Exit);
1574 }
1575
1576 #[rstest]
1577 #[timeout(Duration::from_secs(9))] #[tokio::test]
1579 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1580 init();
1581 let db = init_test_database().await.unwrap();
1582 let tempdir = tempfile::tempdir().unwrap();
1583
1584 let song = Song::try_load_into_db(
1585 &db,
1586 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1587 )
1588 .await
1589 .unwrap();
1590
1591 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1594 song.clone().brief().into(),
1595 )));
1596 sender.send(AudioCommand::Stop);
1597 sender.send(AudioCommand::Seek(
1598 SeekType::Absolute,
1599 Duration::from_secs(0),
1600 ));
1601 let state: StateAudio = get_state(sender.clone()).await;
1602 assert_eq!(state.queue_position, Some(0));
1603 assert_eq!(state.status, Status::Stopped);
1604 assert_eq!(
1605 state.runtime.unwrap().duration,
1606 Duration::from_secs(10) + Duration::from_millis(188)
1607 );
1608 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(0));
1609
1610 sender.send(AudioCommand::Seek(
1612 SeekType::RelativeForwards,
1613 Duration::from_secs(2),
1614 ));
1615 let state = get_state(sender.clone()).await;
1616 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1617 assert_eq!(state.current_song, Some(song.clone().into()));
1618 assert_eq!(state.status, Status::Paused);
1619
1620 sender.send(AudioCommand::Seek(
1622 SeekType::RelativeBackwards,
1623 Duration::from_secs(1),
1624 ));
1625 let state = get_state(sender.clone()).await;
1626 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1627 assert_eq!(state.current_song, Some(song.clone().into()));
1628 assert_eq!(state.status, Status::Paused);
1629
1630 sender.send(AudioCommand::Seek(
1632 SeekType::Absolute,
1633 Duration::from_secs(10),
1634 ));
1635 let state = get_state(sender.clone()).await;
1636 assert_eq!(
1637 state.runtime.unwrap().seek_position,
1638 Duration::from_secs(10)
1639 );
1640 assert_eq!(state.current_song, Some(song.clone().into()));
1641 assert_eq!(state.status, Status::Paused);
1642
1643 sender.send(AudioCommand::Play);
1645 sender.send(AudioCommand::Seek(
1646 SeekType::RelativeForwards,
1647 Duration::from_secs(1),
1648 ));
1649 tokio::time::sleep(Duration::from_millis(500)).await;
1650 let state = get_state(sender.clone()).await;
1651 assert_eq!(state.queue_position, None);
1652 assert_eq!(state.status, Status::Stopped);
1653
1654 sender.send(AudioCommand::Exit);
1655 }
1656 }
1657}