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 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 return;
596 }
597 self.clear_player();
598 if let Some(song) = self.get_current_song()
599 && let Err(e) = self.append_song_to_player(&song)
600 {
601 error!("Failed to append song to player: {e}");
602 }
603 }
604
605 #[instrument(skip(self))]
606 fn get_current_song(&self) -> Option<SongBrief> {
607 self.queue.current_song().cloned()
608 }
609
610 #[instrument(skip(self))]
611 fn get_next_song(&mut self) -> Option<SongBrief> {
612 self.queue.next_song().cloned()
613 }
614
615 fn get_time_played(&self) -> Duration {
616 self.player.get_pos()
617 }
618
619 #[instrument(skip(self, source))]
620 fn append_to_player<T>(&self, source: T)
621 where
622 T: Source<Item = f32> + Send + 'static,
623 {
624 self.player.append(source);
625
626 let command_tx = self.command_tx.clone();
628 self.player.append(EmptyCallback::new(Box::new(move || {
629 debug!("Song finished");
630 if let Err(e) = command_tx.send((
631 AudioCommand::Queue(QueueCommand::PlayNextSong),
632 tracing::Span::current(),
633 )) {
634 error!("Failed to send command to audio kernel: {e}");
635 } else {
636 debug!("Sent PlayNextSong command to audio kernel");
637 }
638 })));
639 }
640
641 #[instrument(skip(self))]
642 fn append_song_to_player(&self, song: &SongBrief) -> Result<(), LibraryError> {
643 let file = File::open(&song.path)?;
644 let byte_len = file.metadata()?.len();
645 let decoder = DecoderBuilder::new()
646 .with_data(BufReader::new(file))
647 .with_byte_len(byte_len)
648 .with_seekable(true)
649 .with_coarse_seek(true)
650 .with_gapless(true)
651 .build()?;
652 self.append_to_player(decoder);
653
654 Ok(())
655 }
656
657 #[instrument(skip(self))]
658 fn volume_control(&mut self, command: VolumeCommand) {
659 match command {
660 VolumeCommand::Up(change) => {
661 let volume = self.volume;
662 let updated = (volume + change).clamp(MIN_VOLUME, MAX_VOLUME);
663 if (volume - updated).abs() > 0.0001 {
665 self.volume = updated;
666 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
667 }
668 }
669 VolumeCommand::Down(change) => {
670 let volume = self.volume;
671 let updated = (volume - change).clamp(MIN_VOLUME, MAX_VOLUME);
672 if (volume - updated).abs() > 0.0001 {
673 self.volume = updated;
674 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
675 }
676 }
677 VolumeCommand::Set(updated) => {
678 let volume = self.volume;
679 let updated = updated.clamp(MIN_VOLUME, MAX_VOLUME);
680 if (volume - updated).abs() > 0.0001 {
681 self.volume = updated;
682 let _ = self.event_tx.send(StateChange::VolumeChanged(self.volume));
683 }
684 }
685 VolumeCommand::Mute => {
686 self.muted = true;
687 let _ = self.event_tx.send(StateChange::Muted);
688 }
689 VolumeCommand::Unmute => {
690 self.muted = false;
691 let _ = self.event_tx.send(StateChange::Unmuted);
692 }
693 VolumeCommand::ToggleMute => {
694 self.muted = !self.muted;
695 if self.muted {
696 let _ = self.event_tx.send(StateChange::Muted);
697 } else {
698 let _ = self.event_tx.send(StateChange::Unmuted);
699 }
700 }
701 }
702
703 if self.muted {
704 self.player.set_volume(0.0);
705 } else {
706 self.player.set_volume(self.volume.to_owned());
707 }
708 }
709
710 #[instrument(skip(self))]
711 fn seek(&mut self, seek: SeekType, duration: Duration) {
712 let new_time = match seek {
714 SeekType::Absolute => duration,
715 SeekType::RelativeForwards => self.get_time_played().saturating_add(duration),
716 SeekType::RelativeBackwards => self.get_time_played().saturating_sub(duration),
717 };
718
719 match self.player.try_seek(new_time) {
723 Ok(()) => {
724 debug!("Seek to {} successful", format_duration(&new_time));
725 if new_time > Duration::from_secs(0) && self.status == Status::Stopped {
726 self.status = Status::Paused;
727 }
728 }
729 Err(SeekError::NotSupported { underlying_source }) => {
730 error!("Seek not supported by source: {underlying_source}");
731 }
732 Err(err) => {
733 error!("Seeking failed with error: {err}");
734 }
735 }
736 }
737}
738
739#[cfg(test)]
740mod tests {
741 use pretty_assertions::assert_eq;
742 use rstest::{fixture, rstest};
743
744 use crate::test_utils::init;
745
746 use super::*;
747 use std::sync::mpsc;
748 use std::time::Duration;
749
750 #[fixture]
751 fn audio_kernel() -> AudioKernel {
752 let (tx, _) = mpsc::channel();
754 let (event_tx, _) = mpsc::channel();
756 AudioKernel::new(tx, event_tx)
757 }
758
759 #[fixture]
760 fn audio_kernel_sender() -> Arc<AudioKernelSender> {
761 let (tx, _) = mpsc::channel();
763 AudioKernelSender::start(tx)
764 }
765
766 async fn get_state(sender: Arc<AudioKernelSender>) -> StateAudio {
767 let (tx, rx) = tokio::sync::oneshot::channel::<StateAudio>();
768 sender.send(AudioCommand::ReportStatus(tx));
769 rx.await.unwrap()
770 }
771
772 #[fixture]
773 fn sound() -> impl Source<Item = f32> + Send + 'static {
774 rodio::source::SineWave::new(440.0)
775 }
776
777 #[test]
778 fn test_audio_kernel_sender_send() {
779 let (tx, rx) = mpsc::channel();
780 let sender = AudioKernelSender::new(tx);
781 sender.send(AudioCommand::Play);
782 let (recv, _) = rx.recv().unwrap();
783 assert_eq!(recv, AudioCommand::Play);
784 }
785
786 #[test]
787 #[should_panic = "Failed to send command to audio kernel: sending on a closed channel"]
788 fn test_audio_kernel_send_closed_channel() {
789 let (tx, _) = mpsc::channel();
790 let sender = AudioKernelSender::new(tx);
791 sender.send(AudioCommand::Play);
792 }
793
794 #[test]
795 fn test_audio_kernel_try_send_closed_channel() {
796 let (tx, _) = mpsc::channel();
797 let sender = AudioKernelSender::new(tx);
798 assert!(sender.try_send(AudioCommand::Play).is_err());
799 }
800
801 #[rstest]
802 #[timeout(Duration::from_secs(3))] fn test_audio_player_kernel_spawn_and_exit(
804 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
805 ) {
806 init();
807
808 sender.send(AudioCommand::Exit);
809 }
810
811 #[rstest]
812 fn test_volume_control(mut audio_kernel: AudioKernel) {
813 audio_kernel.volume_control(VolumeCommand::Up(0.1));
814 let volume = audio_kernel.volume;
815 assert!(f32::EPSILON > (volume - 1.1).abs(), "{volume} != 1.1");
816
817 audio_kernel.volume_control(VolumeCommand::Down(0.1));
818 let volume = audio_kernel.volume;
819 assert!(f32::EPSILON > (volume - 1.0).abs(), "{volume} != 1.0");
820
821 audio_kernel.volume_control(VolumeCommand::Set(0.5));
822 let volume = audio_kernel.volume;
823 assert!(f32::EPSILON > (volume - 0.5).abs(), "{volume} != 0.5");
824
825 audio_kernel.volume_control(VolumeCommand::Mute);
826 assert_eq!(audio_kernel.muted, true);
827
828 audio_kernel.volume_control(VolumeCommand::Unmute);
829 assert_eq!(audio_kernel.muted, false);
830
831 audio_kernel.volume_control(VolumeCommand::ToggleMute);
832 assert_eq!(audio_kernel.muted, true);
833
834 audio_kernel.volume_control(VolumeCommand::ToggleMute);
835 assert_eq!(audio_kernel.muted, false);
836 }
837
838 mod playback_tests {
839 use mecomp_storage::{
844 db::schemas::song::Song,
845 test_utils::{arb_song_case, create_song_metadata, init_test_database},
846 };
847 use pretty_assertions::assert_eq;
848 use rstest::rstest;
849
850 use crate::test_utils::init;
851
852 use super::{super::*, audio_kernel, audio_kernel_sender, get_state, sound};
853
854 #[rstest]
855 fn test_audio_kernel_play_pause(
856 mut audio_kernel: AudioKernel,
857 sound: impl Source<Item = f32> + Send + 'static,
858 ) {
859 init();
860 audio_kernel.player.append(sound);
861 audio_kernel.play();
862 assert!(!audio_kernel.player.is_paused());
863 audio_kernel.pause();
864 assert!(audio_kernel.player.is_paused());
865 }
866
867 #[rstest]
868 fn test_audio_kernel_toggle_playback(
869 mut audio_kernel: AudioKernel,
870 sound: impl Source<Item = f32> + Send + 'static,
871 ) {
872 init();
873 audio_kernel.player.append(sound);
874 audio_kernel.play();
875 assert!(!audio_kernel.player.is_paused());
876 audio_kernel.toggle_playback();
877 assert!(audio_kernel.player.is_paused());
878 audio_kernel.toggle_playback();
879 assert!(!audio_kernel.player.is_paused());
880 }
881
882 #[rstest]
883 #[timeout(Duration::from_secs(10))] #[tokio::test]
885 async fn test_play_pause_toggle_restart(
886 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
887 ) {
888 init();
889 let db = init_test_database().await.unwrap();
890 let tempdir = tempfile::tempdir().unwrap();
891
892 let song = Song::try_load_into_db(
893 &db,
894 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
895 )
896 .await
897 .unwrap();
898
899 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
900 song.brief().into(),
901 )));
902
903 let state = get_state(sender.clone()).await;
904 assert_eq!(state.queue_position, Some(0));
905 assert_eq!(state.status, Status::Playing);
906
907 sender.send(AudioCommand::Pause);
908 let state = get_state(sender.clone()).await;
909 assert_eq!(state.status, Status::Paused);
910
911 sender.send(AudioCommand::Play);
912 let state = get_state(sender.clone()).await;
913 assert_eq!(state.status, Status::Playing);
914
915 sender.send(AudioCommand::RestartSong);
916 let state = get_state(sender.clone()).await;
917 assert_eq!(state.status, Status::Playing); sender.send(AudioCommand::TogglePlayback);
920 let state = get_state(sender.clone()).await;
921 assert_eq!(state.status, Status::Paused);
922
923 sender.send(AudioCommand::RestartSong);
924 let state = get_state(sender.clone()).await;
925 assert_eq!(state.status, Status::Paused); sender.send(AudioCommand::Exit);
928 }
929
930 #[rstest]
931 fn test_audio_kernel_stop(mut audio_kernel: AudioKernel) {
932 init();
933 audio_kernel.player.append(sound());
934 audio_kernel.play();
935 assert!(!audio_kernel.player.is_paused());
936 audio_kernel.stop();
937 assert!(audio_kernel.player.is_paused());
938 assert_eq!(audio_kernel.player.get_pos(), Duration::from_secs(0));
939 assert_eq!(audio_kernel.status, Status::Stopped);
940 }
941
942 #[rstest]
943 #[timeout(Duration::from_secs(10))] #[tokio::test]
945 async fn test_audio_kernel_skip_forward(mut audio_kernel: AudioKernel) {
946 init();
947 let db = init_test_database().await.unwrap();
948 let tempdir = tempfile::tempdir().unwrap();
949
950 let state = audio_kernel.state();
951 assert_eq!(state.queue_position, None);
952 assert!(state.paused());
953 assert_eq!(state.status, Status::Stopped);
954
955 audio_kernel.queue_control(QueueCommand::AddToQueue(OneOrMany::Many(vec![
956 Song::try_load_into_db(
957 &db,
958 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
959 )
960 .await
961 .unwrap()
962 .into(),
963 Song::try_load_into_db(
964 &db,
965 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
966 )
967 .await
968 .unwrap()
969 .into(),
970 Song::try_load_into_db(
971 &db,
972 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
973 )
974 .await
975 .unwrap()
976 .into(),
977 ])));
978
979 let state = audio_kernel.state();
981 assert_eq!(state.queue_position, Some(0));
982 assert!(!state.paused());
983 assert_eq!(state.status, Status::Playing);
984
985 audio_kernel.queue_control(QueueCommand::SkipForward(1));
986
987 let state = audio_kernel.state();
989 assert_eq!(state.queue_position, Some(1));
990 assert!(!state.paused());
991 assert_eq!(state.status, Status::Playing);
992
993 audio_kernel.queue_control(QueueCommand::SkipForward(1));
994
995 let state = audio_kernel.state();
997 assert_eq!(state.queue_position, Some(2));
998 assert!(!state.paused());
999 assert_eq!(state.status, Status::Playing);
1000
1001 audio_kernel.queue_control(QueueCommand::SkipForward(1));
1002
1003 let state = audio_kernel.state();
1006 assert_eq!(state.queue_position, None);
1007 assert!(state.paused());
1008 assert_eq!(state.status, Status::Stopped);
1009 }
1010
1011 #[rstest]
1012 #[timeout(Duration::from_secs(6))] #[tokio::test]
1014 async fn test_audio_kernel_skip_forward_sender(
1015 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1016 ) {
1017 init();
1019
1020 let db = init_test_database().await.unwrap();
1021 let tempdir = tempfile::tempdir().unwrap();
1022
1023 let state = get_state(sender.clone()).await;
1024 assert_eq!(state.queue_position, None);
1025 assert!(state.paused());
1026 assert_eq!(state.status, Status::Stopped);
1027
1028 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1029 OneOrMany::Many(vec![
1030 Song::try_load_into_db(
1031 &db,
1032 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1033 )
1034 .await
1035 .unwrap()
1036 .into(),
1037 Song::try_load_into_db(
1038 &db,
1039 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1040 )
1041 .await
1042 .unwrap()
1043 .into(),
1044 Song::try_load_into_db(
1045 &db,
1046 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1047 )
1048 .await
1049 .unwrap()
1050 .into(),
1051 ]),
1052 )));
1053 let state = get_state(sender.clone()).await;
1055 assert_eq!(state.queue_position, Some(0));
1056 assert!(!state.paused());
1057 assert_eq!(state.status, Status::Playing);
1058
1059 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1060 let state = get_state(sender.clone()).await;
1062 assert_eq!(state.queue_position, Some(1));
1063 assert!(!state.paused());
1064 assert_eq!(state.status, Status::Playing);
1065
1066 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1067 let state = get_state(sender.clone()).await;
1069 assert_eq!(state.queue_position, Some(2));
1070 assert!(!state.paused());
1071 assert_eq!(state.status, Status::Playing);
1072
1073 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1074 let state = get_state(sender.clone()).await;
1076 assert_eq!(state.queue_position, None);
1077 assert!(state.paused());
1078 assert_eq!(state.status, Status::Stopped);
1079
1080 sender.send(AudioCommand::Exit);
1081 }
1082
1083 #[rstest]
1084 #[timeout(Duration::from_secs(6))] #[tokio::test]
1086 async fn test_remove_range_from_queue(
1087 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1088 ) {
1089 init();
1090 let db = init_test_database().await.unwrap();
1091 let tempdir = tempfile::tempdir().unwrap();
1092 let song1 = Song::try_load_into_db(
1093 &db,
1094 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1095 )
1096 .await
1097 .unwrap();
1098 let song2 = Song::try_load_into_db(
1099 &db,
1100 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1101 )
1102 .await
1103 .unwrap();
1104
1105 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1107 OneOrMany::Many(vec![song1.clone().into(), song2.clone().into()]),
1108 )));
1109 let state = get_state(sender.clone()).await;
1110 assert_eq!(state.queue_position, Some(0));
1111 assert!(!state.paused());
1112 assert_eq!(state.status, Status::Playing);
1113
1114 sender.send(AudioCommand::Pause);
1116
1117 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(0..1)));
1119 let state = get_state(sender.clone()).await;
1120 assert_eq!(state.queue_position, Some(0));
1121 assert!(state.paused());
1122 assert_eq!(state.status, Status::Stopped);
1123 assert_eq!(state.queue.len(), 1);
1124 assert_eq!(state.queue[0], song2.clone().into());
1125
1126 sender.send(AudioCommand::Play);
1128
1129 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1131 song1.clone().brief().into(),
1132 )));
1133 let state = get_state(sender.clone()).await;
1134 assert_eq!(state.queue_position, Some(0));
1135 assert!(!state.paused());
1136 assert_eq!(state.status, Status::Playing);
1137 assert_eq!(state.queue.len(), 2);
1138 assert_eq!(state.queue[0], song2.clone().into());
1139 assert_eq!(state.queue[1], song1.into());
1140
1141 sender.send(AudioCommand::Queue(QueueCommand::RemoveRange(1..2)));
1143 let state = get_state(sender.clone()).await;
1144 assert_eq!(state.queue_position, Some(0));
1145 assert!(!state.paused());
1146 assert_eq!(state.status, Status::Playing);
1147 assert_eq!(state.queue.len(), 1);
1148 assert_eq!(state.queue[0], song2.into());
1149
1150 sender.send(AudioCommand::Exit);
1151 }
1152
1153 #[rstest]
1154 #[timeout(Duration::from_secs(10))] #[tokio::test]
1156 async fn test_audio_kernel_skip_backward(
1157 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1158 ) {
1159 init();
1160 let db = init_test_database().await.unwrap();
1161 let tempdir = tempfile::tempdir().unwrap();
1162
1163 let state = get_state(sender.clone()).await;
1164 assert_eq!(state.queue_position, None);
1165 assert!(state.paused());
1166 assert_eq!(state.status, Status::Stopped);
1167
1168 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1169 OneOrMany::Many(vec![
1170 Song::try_load_into_db(
1171 &db,
1172 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1173 )
1174 .await
1175 .unwrap()
1176 .into(),
1177 Song::try_load_into_db(
1178 &db,
1179 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1180 )
1181 .await
1182 .unwrap()
1183 .into(),
1184 Song::try_load_into_db(
1185 &db,
1186 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1187 )
1188 .await
1189 .unwrap()
1190 .into(),
1191 ]),
1192 )));
1193
1194 let state = get_state(sender.clone()).await;
1196 assert_eq!(state.queue_position, Some(0));
1197 assert!(!state.paused());
1198 assert_eq!(state.status, Status::Playing);
1199
1200 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(2)));
1201
1202 let state = get_state(sender.clone()).await;
1204 assert_eq!(state.queue_position, Some(2));
1205 assert!(!state.paused());
1206 assert_eq!(state.status, Status::Playing);
1207
1208 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1209
1210 let state = get_state(sender.clone()).await;
1212 assert_eq!(state.queue_position, Some(1));
1213 assert!(!state.paused());
1214 assert_eq!(state.status, Status::Playing);
1215
1216 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1217
1218 let state = get_state(sender.clone()).await;
1220 assert_eq!(state.queue_position, Some(0));
1221 assert!(!state.paused());
1222
1223 sender.send(AudioCommand::Queue(QueueCommand::SkipBackward(1)));
1224
1225 let state = get_state(sender.clone()).await;
1227 assert_eq!(state.queue_position, None);
1228 assert!(state.paused());
1229 assert_eq!(state.status, Status::Stopped);
1230
1231 sender.send(AudioCommand::Exit);
1232 }
1233
1234 #[rstest]
1235 #[timeout(Duration::from_secs(10))] #[tokio::test]
1237 async fn test_audio_kernel_set_position(
1238 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1239 ) {
1240 init();
1241 let db = init_test_database().await.unwrap();
1242 let tempdir = tempfile::tempdir().unwrap();
1243
1244 let state = get_state(sender.clone()).await;
1245 assert_eq!(state.queue_position, None);
1246 assert!(state.paused());
1247 assert_eq!(state.status, Status::Stopped);
1248
1249 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1250 OneOrMany::Many(vec![
1251 Song::try_load_into_db(
1252 &db,
1253 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1254 )
1255 .await
1256 .unwrap()
1257 .into(),
1258 Song::try_load_into_db(
1259 &db,
1260 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1261 )
1262 .await
1263 .unwrap()
1264 .into(),
1265 Song::try_load_into_db(
1266 &db,
1267 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1268 )
1269 .await
1270 .unwrap()
1271 .into(),
1272 ]),
1273 )));
1274 let state = get_state(sender.clone()).await;
1276 assert_eq!(state.queue_position, Some(0));
1277 assert!(!state.paused());
1278 assert_eq!(state.status, Status::Playing);
1279
1280 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(1)));
1281 let state = get_state(sender.clone()).await;
1283 assert_eq!(state.queue_position, Some(1));
1284 assert!(!state.paused());
1285 assert_eq!(state.status, Status::Playing);
1286
1287 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(2)));
1288 let state = get_state(sender.clone()).await;
1290 assert_eq!(state.queue_position, Some(2));
1291 assert!(!state.paused());
1292 assert_eq!(state.status, Status::Playing);
1293
1294 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(0)));
1295 let state = get_state(sender.clone()).await;
1297 assert_eq!(state.queue_position, Some(0));
1298 assert!(!state.paused());
1299 assert_eq!(state.status, Status::Playing);
1300
1301 sender.send(AudioCommand::Queue(QueueCommand::SetPosition(3)));
1302 let state = get_state(sender.clone()).await;
1304 assert_eq!(state.queue_position, Some(2));
1305 assert!(!state.paused());
1306 assert_eq!(state.status, Status::Playing);
1307
1308 sender.send(AudioCommand::Exit);
1309 }
1310
1311 #[rstest]
1312 #[timeout(Duration::from_secs(6))] #[tokio::test]
1314 async fn test_audio_kernel_clear(
1315 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1316 ) {
1317 init();
1318 let db = init_test_database().await.unwrap();
1319 let tempdir = tempfile::tempdir().unwrap();
1320
1321 let state = get_state(sender.clone()).await;
1322 assert_eq!(state.queue_position, None);
1323 assert!(state.paused());
1324 assert_eq!(state.status, Status::Stopped);
1325
1326 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1327 OneOrMany::Many(vec![
1328 Song::try_load_into_db(
1329 &db,
1330 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1331 )
1332 .await
1333 .unwrap()
1334 .into(),
1335 Song::try_load_into_db(
1336 &db,
1337 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1338 )
1339 .await
1340 .unwrap()
1341 .into(),
1342 Song::try_load_into_db(
1343 &db,
1344 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1345 )
1346 .await
1347 .unwrap()
1348 .into(),
1349 ]),
1350 )));
1351 let state = get_state(sender.clone()).await;
1353 assert_eq!(state.queue_position, Some(0));
1354 assert_eq!(state.queue.len(), 3);
1355 assert!(!state.paused());
1356 assert_eq!(state.status, Status::Playing);
1357
1358 sender.send(AudioCommand::ClearPlayer);
1359 let state = get_state(sender.clone()).await;
1361 assert_eq!(state.queue_position, Some(0));
1362 assert_eq!(state.queue.len(), 3);
1363 assert!(state.paused());
1364 assert_eq!(state.status, Status::Stopped);
1365
1366 sender.send(AudioCommand::Queue(QueueCommand::Clear));
1367 let state = get_state(sender.clone()).await;
1369 assert_eq!(state.queue_position, None);
1370 assert_eq!(state.queue.len(), 0);
1371 assert!(state.paused());
1372 assert_eq!(state.status, Status::Stopped);
1373
1374 sender.send(AudioCommand::Exit);
1375 }
1376
1377 #[rstest]
1378 #[timeout(Duration::from_secs(6))] #[tokio::test]
1380 async fn test_audio_kernel_shuffle(
1381 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1382 ) {
1383 init();
1384 let db = init_test_database().await.unwrap();
1385 let tempdir = tempfile::tempdir().unwrap();
1386
1387 let state = get_state(sender.clone()).await;
1388 assert_eq!(state.queue_position, None);
1389 assert!(state.paused());
1390 assert_eq!(state.status, Status::Stopped);
1391
1392 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1393 OneOrMany::Many(vec![
1394 Song::try_load_into_db(
1395 &db,
1396 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1397 )
1398 .await
1399 .unwrap()
1400 .into(),
1401 Song::try_load_into_db(
1402 &db,
1403 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1404 )
1405 .await
1406 .unwrap()
1407 .into(),
1408 Song::try_load_into_db(
1409 &db,
1410 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1411 )
1412 .await
1413 .unwrap()
1414 .into(),
1415 ]),
1416 )));
1417 let state = get_state(sender.clone()).await;
1419 assert_eq!(state.queue_position, Some(0));
1420 assert_eq!(state.queue.len(), 3);
1421 assert!(!state.paused());
1422 assert_eq!(state.status, Status::Playing);
1423
1424 sender.send(AudioCommand::Queue(QueueCommand::SkipForward(1)));
1426 let state = get_state(sender.clone()).await;
1427 assert_eq!(state.queue_position, Some(1));
1428 assert_eq!(state.queue.len(), 3);
1429 assert!(!state.paused());
1430 assert_eq!(state.status, Status::Playing);
1431
1432 sender.send(AudioCommand::Queue(QueueCommand::Shuffle));
1434 let state = get_state(sender.clone()).await;
1436 assert_eq!(state.queue_position, Some(0));
1437 assert_eq!(state.queue.len(), 3);
1438 assert!(!state.paused());
1439 assert_eq!(state.status, Status::Playing);
1440
1441 sender.send(AudioCommand::Exit);
1442 }
1443
1444 #[rstest]
1445 #[timeout(Duration::from_secs(5))] #[tokio::test]
1447 async fn test_volume_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1448 init();
1449
1450 let state = get_state(sender.clone()).await;
1451 assert!(
1452 f32::EPSILON > (state.volume - 1.0).abs(),
1453 "{} != 1.0",
1454 state.volume
1455 );
1456 assert!(!state.muted);
1457
1458 sender.send(AudioCommand::Volume(VolumeCommand::Up(0.1)));
1459 let state = get_state(sender.clone()).await;
1460 assert!(
1461 f32::EPSILON > (state.volume - 1.1).abs(),
1462 "{} != 1.1",
1463 state.volume
1464 );
1465 assert!(!state.muted);
1466
1467 sender.send(AudioCommand::Volume(VolumeCommand::Down(0.1)));
1468 let state = get_state(sender.clone()).await;
1469 assert!(
1470 f32::EPSILON > (state.volume - 1.0).abs(),
1471 "{} != 1.0",
1472 state.volume
1473 );
1474 assert!(!state.muted);
1475
1476 sender.send(AudioCommand::Volume(VolumeCommand::Set(0.5)));
1477 let state = get_state(sender.clone()).await;
1478 assert!(
1479 f32::EPSILON > (state.volume - 0.5).abs(),
1480 "{} != 0.5",
1481 state.volume
1482 );
1483 assert!(!state.muted);
1484
1485 sender.send(AudioCommand::Volume(VolumeCommand::Mute));
1486 let state = get_state(sender.clone()).await;
1487 assert!(
1488 f32::EPSILON > (state.volume - 0.5).abs(),
1489 "{} != 0.5",
1490 state.volume
1491 ); assert!(state.muted);
1493
1494 sender.send(AudioCommand::Volume(VolumeCommand::Unmute));
1495 let state = get_state(sender.clone()).await;
1496 assert!(
1497 f32::EPSILON > (state.volume - 0.5).abs(),
1498 "{} != 0.5",
1499 state.volume
1500 );
1501 assert!(!state.muted);
1502
1503 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1504 let state = get_state(sender.clone()).await;
1505 assert!(
1506 f32::EPSILON > (state.volume - 0.5).abs(),
1507 "{} != 0.5",
1508 state.volume
1509 );
1510 assert!(state.muted);
1511
1512 sender.send(AudioCommand::Volume(VolumeCommand::ToggleMute));
1513 let state = get_state(sender.clone()).await;
1514 assert!(
1515 f32::EPSILON > (state.volume - 0.5).abs(),
1516 "{} != 0.5",
1517 state.volume
1518 );
1519 assert!(!state.muted);
1520
1521 sender.send(AudioCommand::Exit);
1522 }
1523
1524 #[rstest]
1525 #[timeout(Duration::from_secs(5))] #[tokio::test]
1527 async fn test_volume_out_of_bounds(
1528 #[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>,
1529 ) {
1530 init();
1531
1532 sender.send(AudioCommand::Volume(VolumeCommand::Up(MAX_VOLUME + 0.5)));
1534 let state = get_state(sender.clone()).await;
1535 assert!(
1536 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1537 "{} != {}",
1538 state.volume,
1539 MAX_VOLUME
1540 );
1541 assert!(!state.muted);
1542 sender.send(AudioCommand::Volume(VolumeCommand::Down(
1543 MAX_VOLUME + 0.5 - MIN_VOLUME,
1544 )));
1545 let state = get_state(sender.clone()).await;
1546 assert!(
1547 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1548 "{} != {}",
1549 state.volume,
1550 MIN_VOLUME
1551 );
1552 assert!(!state.muted);
1553
1554 sender.send(AudioCommand::Volume(VolumeCommand::Set(MAX_VOLUME + 0.5)));
1556 let state = get_state(sender.clone()).await;
1557 assert!(
1558 f32::EPSILON > (state.volume - MAX_VOLUME).abs(),
1559 "{} != {}",
1560 state.volume,
1561 MAX_VOLUME
1562 );
1563 assert!(!state.muted);
1564 sender.send(AudioCommand::Volume(VolumeCommand::Set(MIN_VOLUME - 0.5)));
1565 let state = get_state(sender.clone()).await;
1566 assert!(
1567 f32::EPSILON > (state.volume - MIN_VOLUME).abs(),
1568 "{} != {}",
1569 state.volume,
1570 MIN_VOLUME
1571 );
1572 assert!(!state.muted);
1573
1574 sender.send(AudioCommand::Exit);
1575 }
1576
1577 #[rstest]
1578 #[timeout(Duration::from_secs(9))] #[tokio::test]
1580 async fn test_seek_commands(#[from(audio_kernel_sender)] sender: Arc<AudioKernelSender>) {
1581 init();
1582 let db = init_test_database().await.unwrap();
1583 let tempdir = tempfile::tempdir().unwrap();
1584
1585 let song = Song::try_load_into_db(
1586 &db,
1587 create_song_metadata(&tempdir, arb_song_case()()).unwrap(),
1588 )
1589 .await
1590 .unwrap();
1591
1592 sender.send(AudioCommand::Queue(QueueCommand::AddToQueue(
1595 song.clone().brief().into(),
1596 )));
1597 sender.send(AudioCommand::Stop);
1598 sender.send(AudioCommand::Seek(
1599 SeekType::Absolute,
1600 Duration::from_secs(0),
1601 ));
1602 let state: StateAudio = get_state(sender.clone()).await;
1603 assert_eq!(state.queue_position, Some(0));
1604 assert_eq!(state.status, Status::Stopped);
1605 assert_eq!(
1606 state.runtime.unwrap().duration,
1607 Duration::from_secs(10) + Duration::from_millis(188)
1608 );
1609 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(0));
1610
1611 sender.send(AudioCommand::Seek(
1613 SeekType::RelativeForwards,
1614 Duration::from_secs(2),
1615 ));
1616 let state = get_state(sender.clone()).await;
1617 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(2));
1618 assert_eq!(state.current_song, Some(song.clone().into()));
1619 assert_eq!(state.status, Status::Paused);
1620
1621 sender.send(AudioCommand::Seek(
1623 SeekType::RelativeBackwards,
1624 Duration::from_secs(1),
1625 ));
1626 let state = get_state(sender.clone()).await;
1627 assert_eq!(state.runtime.unwrap().seek_position, Duration::from_secs(1));
1628 assert_eq!(state.current_song, Some(song.clone().into()));
1629 assert_eq!(state.status, Status::Paused);
1630
1631 sender.send(AudioCommand::Seek(
1633 SeekType::Absolute,
1634 Duration::from_secs(10),
1635 ));
1636 let state = get_state(sender.clone()).await;
1637 assert_eq!(
1638 state.runtime.unwrap().seek_position,
1639 Duration::from_secs(10)
1640 );
1641 assert_eq!(state.current_song, Some(song.clone().into()));
1642 assert_eq!(state.status, Status::Paused);
1643
1644 sender.send(AudioCommand::Play);
1646 sender.send(AudioCommand::Seek(
1647 SeekType::RelativeForwards,
1648 Duration::from_secs(1),
1649 ));
1650 tokio::time::sleep(Duration::from_millis(500)).await;
1651 let state = get_state(sender.clone()).await;
1652 assert_eq!(state.queue_position, None);
1653 assert_eq!(state.status, Status::Stopped);
1654
1655 sender.send(AudioCommand::Exit);
1656 }
1657 }
1658}