package playback import ( "errors" "testing" "time" ) func TestApplyPlaylistSelectionFirstNext(t *testing.T) { retry := validPlaylistRetryPolicy() next, command, apply, err := ApplyPlaylistSelection( navigationPlaylist(false), PlaylistState{}, PlaylistCommand{Kind: PlaylistNext}, retry, ) if err != nil { t.Fatalf("ApplyPlaylistSelection() error = %v", err) } if !apply { t.Fatal("ApplyPlaylistSelection() apply = false, want true") } wantState := PlaylistState{CurrentIndex: 0, HasSelection: true} if next != wantState { t.Fatalf("ApplyPlaylistSelection() state = %#v, want %#v", next, wantState) } wantCommand := SessionCommand{ Kind: CommandSetSession, Session: SessionConfig{ Video: FeedConfig{Domain: "domain", UUID: "video-1", Active: true}, Retry: retry, }, } if command != wantCommand { t.Fatalf("ApplyPlaylistSelection() command = %#v, want %#v", command, wantCommand) } } func TestApplyPlaylistSelectionUsesCompleteEntrySession(t *testing.T) { retry := validPlaylistRetryPolicy() playlist := Playlist{Entries: []PlaylistEntry{ { Video: PlaylistFeed{Domain: "video-domain", UUID: "video"}, Audio: PlaylistFeed{Domain: "audio-domain", UUID: "audio"}, SyncRequested: true, }, {Video: PlaylistFeed{Domain: "next-domain", UUID: "next-video"}}, }} _, command, apply, err := ApplyPlaylistSelection( playlist, PlaylistState{CurrentIndex: 1, HasSelection: true}, PlaylistCommand{Kind: PlaylistSelect, Index: 0}, retry, ) if err != nil { t.Fatalf("ApplyPlaylistSelection() error = %v", err) } if !apply { t.Fatal("ApplyPlaylistSelection() apply = false, want true") } want := SessionConfig{ Video: FeedConfig{Domain: "video-domain", UUID: "video", Active: true}, Audio: FeedConfig{Domain: "audio-domain", UUID: "audio", Active: true}, SyncRequested: true, Retry: retry, } if command.Kind != CommandSetSession || command.Session != want { t.Fatalf("ApplyPlaylistSelection() command = %#v, want session %#v", command, want) } } func TestApplyPlaylistSelectionVideoOnlyClearsAudio(t *testing.T) { _, command, apply, err := ApplyPlaylistSelection( navigationPlaylist(false), PlaylistState{CurrentIndex: 1, HasSelection: true}, PlaylistCommand{Kind: PlaylistSelect, Index: 0}, validPlaylistRetryPolicy(), ) if err != nil { t.Fatalf("ApplyPlaylistSelection() error = %v", err) } if !apply { t.Fatal("ApplyPlaylistSelection() apply = false, want true") } if command.Session.Audio != (FeedConfig{}) { t.Fatalf("ApplyPlaylistSelection() audio = %#v, want zero value", command.Session.Audio) } } func TestApplyPlaylistSelectionApplicationDecision(t *testing.T) { tests := []struct { name string loop bool current int command PlaylistCommand want int apply bool }{ {name: "reselect current", current: 1, command: PlaylistCommand{Kind: PlaylistSelect, Index: 1}, want: 1, apply: true}, {name: "move next", current: 1, command: PlaylistCommand{Kind: PlaylistNext}, want: 2, apply: true}, {name: "next stops at end", current: 2, command: PlaylistCommand{Kind: PlaylistNext}, want: 2}, {name: "previous stops at beginning", current: 0, command: PlaylistCommand{Kind: PlaylistPrevious}, want: 0}, {name: "next wraps", loop: true, current: 2, command: PlaylistCommand{Kind: PlaylistNext}, want: 0, apply: true}, {name: "previous wraps", loop: true, current: 0, command: PlaylistCommand{Kind: PlaylistPrevious}, want: 2, apply: true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { next, command, apply, err := ApplyPlaylistSelection( navigationPlaylist(test.loop), PlaylistState{CurrentIndex: test.current, HasSelection: true}, test.command, validPlaylistRetryPolicy(), ) if err != nil { t.Fatalf("ApplyPlaylistSelection() error = %v", err) } wantState := PlaylistState{CurrentIndex: test.want, HasSelection: true} if next != wantState { t.Fatalf("ApplyPlaylistSelection() state = %#v, want %#v", next, wantState) } if apply != test.apply { t.Fatalf("ApplyPlaylistSelection() apply = %v, want %v", apply, test.apply) } if apply && command.Kind != CommandSetSession { t.Fatalf("ApplyPlaylistSelection() command kind = %v, want %v", command.Kind, CommandSetSession) } if !apply && command != (SessionCommand{}) { t.Fatalf("ApplyPlaylistSelection() command = %#v, want zero value", command) } }) } } func TestApplyPlaylistSelectionErrorsDoNotApply(t *testing.T) { current := PlaylistState{CurrentIndex: 1, HasSelection: true} tests := []struct { name string command PlaylistCommand retry RetryPolicy wantErr error }{ { name: "invalid command", command: PlaylistCommand{}, retry: validPlaylistRetryPolicy(), wantErr: ErrUnknownPlaylistCommand, }, { name: "invalid retry", command: PlaylistCommand{Kind: PlaylistNext}, retry: RetryPolicy{}, wantErr: ErrInvalidRetryDelay, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { next, command, apply, err := ApplyPlaylistSelection( navigationPlaylist(false), current, test.command, test.retry, ) if !errors.Is(err, test.wantErr) { t.Fatalf("ApplyPlaylistSelection() error = %v, want %v", err, test.wantErr) } if next != current { t.Fatalf("ApplyPlaylistSelection() state = %#v, want unchanged %#v", next, current) } if apply { t.Fatal("ApplyPlaylistSelection() apply = true, want false") } if command != (SessionCommand{}) { t.Fatalf("ApplyPlaylistSelection() command = %#v, want zero value", command) } }) } } func validPlaylistRetryPolicy() RetryPolicy { return RetryPolicy{ MaxAttempts: 3, InitialDelay: time.Millisecond, MaxDelay: time.Second, } }