From 3c054ed1ec22b1cf9f1dea5d0a2403e6407fbdfa Mon Sep 17 00:00:00 2001 From: salmon Date: Wed, 12 Aug 2026 02:17:02 +0200 Subject: queue logic fixes --- tests/queue.rs | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) (limited to 'tests') diff --git a/tests/queue.rs b/tests/queue.rs index 2eaa6f3..d338f2a 100644 --- a/tests/queue.rs +++ b/tests/queue.rs @@ -474,6 +474,99 @@ fn test_clear_resets_shuffle_indices() { assert!(q.shuffled_indices().is_empty()); } +// ---------- Regression tests ---------- +// These encode invariants the queue must uphold across shuffle transitions. +// They previously failed and now pin the fixed behavior. + +#[test] +fn test_add_during_shuffle_survives_shuffle_off() { + // A track added while Shuffle::All is active must survive turning shuffle + // off: `restore_from_original` rebuilds the queue from the live track + // list (entries, not paths), so late additions are kept. + // (src/queue.rs: add_track 223, restore_from_original 266) + let mut q = Queue::new(); + add_track(&mut q, "/music/a.mp3"); + add_track(&mut q, "/music/b.mp3"); + add_track(&mut q, "/music/c.mp3"); + q.set_shuffle(ShuffleMode::All); + + // User adds a track while shuffle is active (e.g. `rsp play --next`). + add_track(&mut q, "/music/d.mp3"); + assert_eq!(q.len(), 4); + + q.set_shuffle(ShuffleMode::None); + + // d.mp3 must still be in the queue. + assert_eq!( + q.len(), + 4, + "queue lost a track after shuffle-off; len = {}", + q.len() + ); + assert!( + q.tracks().iter().any(|t| t.path == PathBuf::from("/music/d.mp3")), + "track added during shuffle was dropped when shuffle was turned off" + ); +} + +#[test] +fn test_clear_then_shuffle_off_does_not_resurrect_queue() { + // A cleared queue must stay empty across a later shuffle-off: `clear()` + // wipes tracks/position/shuffled_indices, and `restore_from_original` + // must not resurrect entries from any lingering state. + // (src/queue.rs: clear 546 / restore_from_original 266; + // src/daemon.rs: stop 815, queue-end 353-354) + let mut q = Queue::new(); + add_track(&mut q, "/music/a.mp3"); + add_track(&mut q, "/music/b.mp3"); + add_track(&mut q, "/music/c.mp3"); + q.set_shuffle(ShuffleMode::All); + + q.clear(); + assert!(q.is_empty()); + assert_eq!(q.len(), 0); + + // Later the user toggles shuffle off: the queue must stay empty. + q.set_shuffle(ShuffleMode::None); + assert!( + q.is_empty(), + "cleared queue was resurrected from the stale shuffle snapshot (len = {})", + q.len() + ); + assert_eq!(q.len(), 0); +} + +#[test] +fn test_shuffle_off_keeps_unplayed_duplicate_entries() { + // Duplicates are distinct queue entries: `restore_from_original` rebuilds + // by entry identity (played indices vs. the rest), never by path, so an + // unplayed duplicate of an already-played path must survive shuffle-off. + // (src/queue.rs: restore_from_original 266) + let mut q = Queue::new(); + add_track(&mut q, "/music/a.mp3"); + add_track(&mut q, "/music/b.mp3"); + add_track(&mut q, "/music/a.mp3"); // duplicate entry (same path) + + // Play the first two entries (position 1). The third entry is unplayed. + q.next(); + assert_eq!(q.position(), 1); + + // Position is 1 with len 3: the shuffled tail is a single element, so the + // shuffled indices stay identity and the restore is deterministic. + q.set_shuffle(ShuffleMode::All); + q.set_shuffle(ShuffleMode::None); + + // All three entries must survive shuffle-off (duplicates are distinct + // queue entries, not a single path). + assert_eq!( + q.len(), + 3, + "unplayed duplicate entry was dropped on shuffle-off (len = {})", + q.len() + ); + assert_eq!(q.position(), 1); +} + // ---------- Track struct ---------- #[test] -- cgit v1.2.3