fix(queue): guard OnTrackEnded against direct-play cross-context advance
Only advance when player's CurrentTrack.Id matches queue's Current.Id; direct-play call sites (SessionDetail, StreamNowButton, resume) that supersede the queue no longer spuriously advance the album. Adds regression test covering the scenario.
This commit is contained in:
@@ -335,6 +335,37 @@ public class QueueServiceTests
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Regression guard for the cross-context spurious-advance bug: a direct single-track play
|
||||
/// (Session, StreamNowButton, resume) overwrites the player's CurrentTrack without touching the
|
||||
/// queue. When that external track reaches its natural end, TrackEnded fires — but the queue's
|
||||
/// Current no longer matches the player's CurrentTrack, so the queue must NOT advance.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task TrackEnded_WhenPlayerCurrentTrackIsNotQueueCurrent_DoesNotAdvance()
|
||||
{
|
||||
// Load a 3-track album into the queue (queue.Current → track-1, player.CurrentTrack → track-1).
|
||||
await _queue.PlayRelease(Tracks(3));
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(0));
|
||||
|
||||
// Simulate a direct play (e.g. SessionDetail streams an unrelated track by Id = 99).
|
||||
// The player's CurrentTrack is now the session track, but the queue is still on track-1.
|
||||
var sessionTrack = new TrackDto { Id = 99, EntryKey = "session-track", TrackName = "Session Mix" };
|
||||
_player.SimulateDirectPlay(sessionTrack);
|
||||
|
||||
// The session track finishes naturally — player raises TrackEnded.
|
||||
_player.RaiseTrackEnded();
|
||||
|
||||
// The queue must not have advanced: index still 0, and no additional SelectTrackStreaming
|
||||
// calls beyond the initial PlayRelease selection.
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(_queue.CurrentIndex, Is.EqualTo(0), "Queue must not advance when a direct-play track ends");
|
||||
Assert.That(_player.SelectedTracks, Has.Count.EqualTo(1), "No further streaming must be triggered by the queue");
|
||||
Assert.That(_player.SelectedTracks.Single().EntryKey, Is.EqualTo("track-1"), "Only the original PlayRelease selection must have been streamed");
|
||||
});
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task TrackEnded_PlaysWholeAlbumThroughToTheEnd()
|
||||
{
|
||||
@@ -396,6 +427,13 @@ public class QueueServiceTests
|
||||
|
||||
public void RaiseTrackEnded() => TrackEnded?.Invoke();
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="CurrentTrack"/> to <paramref name="track"/> without recording a
|
||||
/// <see cref="SelectedTracks"/> entry. Models a direct single-track play (SessionDetail,
|
||||
/// StreamNowButton, resume) that overwrites the player state without going through the queue.
|
||||
/// </summary>
|
||||
public void SimulateDirectPlay(TrackDto track) => CurrentTrack = track;
|
||||
|
||||
public Task SelectTrackStreaming(TrackDto track)
|
||||
{
|
||||
SelectedTracks.Add(track);
|
||||
|
||||
Reference in New Issue
Block a user