package playback import ( "errors" "testing" ) func navigationPlaylist(loop bool) Playlist { return Playlist{ Entries: []PlaylistEntry{ {Name: "first", Video: PlaylistFeed{Domain: "domain", UUID: "video-1"}}, {Name: "second", Audio: PlaylistFeed{Domain: "domain", UUID: "audio-2"}}, {Name: "third", Video: PlaylistFeed{Domain: "domain", UUID: "video-3"}}, }, Loop: loop, } } func TestApplyPlaylistCommandSelect(t *testing.T) { current := PlaylistState{CurrentIndex: 1, HasSelection: true} got, err := ApplyPlaylistCommand( navigationPlaylist(false), current, PlaylistCommand{Kind: PlaylistSelect, Index: 2}, ) if err != nil { t.Fatalf("ApplyPlaylistCommand() error = %v", err) } want := PlaylistState{CurrentIndex: 2, HasSelection: true} if got != want { t.Fatalf("ApplyPlaylistCommand() = %#v, want %#v", got, want) } } func TestApplyPlaylistCommandWithoutSelection(t *testing.T) { tests := []struct { name string loop bool command PlaylistCommandKind want int }{ {name: "next", command: PlaylistNext, want: 0}, {name: "previous without loop", command: PlaylistPrevious, want: 0}, {name: "previous with loop", loop: true, command: PlaylistPrevious, want: 2}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { got, err := ApplyPlaylistCommand( navigationPlaylist(test.loop), PlaylistState{}, PlaylistCommand{Kind: test.command}, ) if err != nil { t.Fatalf("ApplyPlaylistCommand() error = %v", err) } want := PlaylistState{CurrentIndex: test.want, HasSelection: true} if got != want { t.Fatalf("ApplyPlaylistCommand() = %#v, want %#v", got, want) } }) } } func TestApplyPlaylistCommandNavigation(t *testing.T) { tests := []struct { name string loop bool current int command PlaylistCommandKind want int }{ {name: "next", current: 1, command: PlaylistNext, want: 2}, {name: "previous", current: 1, command: PlaylistPrevious, want: 0}, {name: "next stops at end", current: 2, command: PlaylistNext, want: 2}, {name: "previous stops at beginning", current: 0, command: PlaylistPrevious, want: 0}, {name: "next wraps", loop: true, current: 2, command: PlaylistNext, want: 0}, {name: "previous wraps", loop: true, current: 0, command: PlaylistPrevious, want: 2}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { current := PlaylistState{CurrentIndex: test.current, HasSelection: true} got, err := ApplyPlaylistCommand( navigationPlaylist(test.loop), current, PlaylistCommand{Kind: test.command}, ) if err != nil { t.Fatalf("ApplyPlaylistCommand() error = %v", err) } want := PlaylistState{CurrentIndex: test.want, HasSelection: true} if got != want { t.Fatalf("ApplyPlaylistCommand() = %#v, want %#v", got, want) } }) } } func TestApplyPlaylistCommandErrorsLeaveStateUnchanged(t *testing.T) { current := PlaylistState{CurrentIndex: 1, HasSelection: true} tests := []struct { name string playlist Playlist command PlaylistCommand wantErr error }{ { name: "empty playlist", playlist: Playlist{}, command: PlaylistCommand{Kind: PlaylistNext}, wantErr: ErrPlaylistEmpty, }, { name: "negative selection", playlist: navigationPlaylist(false), command: PlaylistCommand{Kind: PlaylistSelect, Index: -1}, wantErr: ErrPlaylistIndexOutOfRange, }, { name: "selection past end", playlist: navigationPlaylist(false), command: PlaylistCommand{Kind: PlaylistSelect, Index: 3}, wantErr: ErrPlaylistIndexOutOfRange, }, { name: "stale current index", playlist: navigationPlaylist(false), command: PlaylistCommand{Kind: PlaylistNext}, wantErr: ErrPlaylistIndexOutOfRange, }, { name: "unknown command", playlist: navigationPlaylist(false), command: PlaylistCommand{}, wantErr: ErrUnknownPlaylistCommand, }, { name: "invalid playlist", playlist: Playlist{Entries: []PlaylistEntry{ {}, }}, command: PlaylistCommand{Kind: PlaylistNext}, wantErr: ErrPlaylistEntryEmpty, }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { state := current if test.name == "stale current index" { state.CurrentIndex = 4 } got, err := ApplyPlaylistCommand(test.playlist, state, test.command) if !errors.Is(err, test.wantErr) { t.Fatalf("ApplyPlaylistCommand() error = %v, want %v", err, test.wantErr) } if got != state { t.Fatalf("ApplyPlaylistCommand() = %#v, want unchanged %#v", got, state) } }) } } func TestPlaylistStateEntry(t *testing.T) { playlist := navigationPlaylist(false) tests := []struct { name string state PlaylistState want string ok bool }{ {name: "no selection", state: PlaylistState{}}, {name: "selected", state: PlaylistState{CurrentIndex: 1, HasSelection: true}, want: "second", ok: true}, {name: "negative stale index", state: PlaylistState{CurrentIndex: -1, HasSelection: true}}, {name: "stale index", state: PlaylistState{CurrentIndex: 3, HasSelection: true}}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { entry, ok := test.state.Entry(playlist) if ok != test.ok { t.Fatalf("Entry() ok = %v, want %v", ok, test.ok) } if entry.Name != test.want { t.Fatalf("Entry() name = %q, want %q", entry.Name, test.want) } }) } }