Files
vibesonic/internal/jukebox/jukebox_test.go
T

217 lines
5.1 KiB
Go

package jukebox
import (
"context"
"os/exec"
"path/filepath"
"testing"
"time"
)
func genFile(t *testing.T, dir, name string) string {
t.Helper()
if _, err := exec.LookPath("ffmpeg"); err != nil {
t.Skip("ffmpeg not installed")
}
out := filepath.Join(dir, name)
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "ffmpeg", "-f", "lavfi", "-i", "sine=frequency=440:duration=3", "-codec:a", "libmp3lame", out)
if combined, err := cmd.CombinedOutput(); err != nil {
t.Fatalf("ffmpeg: %v\n%s", err, combined)
}
return out
}
func requireMPV(t *testing.T) {
t.Helper()
if _, err := exec.LookPath("mpv"); err != nil {
t.Skip("mpv not installed")
}
}
func TestNewUnavailableWithoutMPV(t *testing.T) {
j := New("/nonexistent/mpv/binary")
if j.Available {
t.Fatal("Available = true for a nonexistent mpv binary")
}
if _, err := j.Status(); err != ErrUnavailable {
t.Fatalf("Status() error = %v, want ErrUnavailable", err)
}
}
func TestJukeboxSetAndStatus(t *testing.T) {
requireMPV(t)
dir := t.TempDir()
song1 := genFile(t, dir, "song1.mp3")
song2 := genFile(t, dir, "song2.mp3")
j := New("")
defer j.Close()
if !j.Available {
t.Fatal("expected jukebox to be available with mpv installed")
}
paths := map[string]string{"s1": song1, "s2": song2}
resolve := func(id string) (string, error) { return paths[id], nil }
state, err := j.Set([]string{"s1", "s2"}, resolve)
if err != nil {
t.Fatalf("Set: %v", err)
}
if len(state.SongIDs) != 2 {
t.Fatalf("SongIDs = %v, want 2", state.SongIDs)
}
// Give mpv a beat to actually start playback.
time.Sleep(300 * time.Millisecond)
state, err = j.Status()
if err != nil {
t.Fatalf("Status: %v", err)
}
if !state.Playing {
t.Error("expected jukebox to be playing after Set")
}
}
func TestJukeboxStartStop(t *testing.T) {
requireMPV(t)
dir := t.TempDir()
song1 := genFile(t, dir, "song1.mp3")
j := New("")
defer j.Close()
resolve := func(id string) (string, error) { return song1, nil }
if _, err := j.Set([]string{"s1"}, resolve); err != nil {
t.Fatalf("Set: %v", err)
}
time.Sleep(200 * time.Millisecond)
state, err := j.Stop()
if err != nil {
t.Fatalf("Stop: %v", err)
}
if state.Playing {
t.Error("expected paused after Stop")
}
state, err = j.Start()
if err != nil {
t.Fatalf("Start: %v", err)
}
time.Sleep(200 * time.Millisecond)
state, err = j.Status()
if err != nil {
t.Fatalf("Status: %v", err)
}
if !state.Playing {
t.Error("expected playing after Start")
}
}
func TestJukeboxAddClearRemove(t *testing.T) {
requireMPV(t)
dir := t.TempDir()
song1 := genFile(t, dir, "song1.mp3")
song2 := genFile(t, dir, "song2.mp3")
j := New("")
defer j.Close()
paths := map[string]string{"s1": song1, "s2": song2}
resolve := func(id string) (string, error) { return paths[id], nil }
if _, err := j.Set([]string{"s1"}, resolve); err != nil {
t.Fatalf("Set: %v", err)
}
state, err := j.Add([]string{"s2"}, resolve)
if err != nil {
t.Fatalf("Add: %v", err)
}
if len(state.SongIDs) != 2 {
t.Fatalf("SongIDs after Add = %v, want 2", state.SongIDs)
}
state, err = j.Remove(0)
if err != nil {
t.Fatalf("Remove: %v", err)
}
if len(state.SongIDs) != 1 || state.SongIDs[0] != "s2" {
t.Fatalf("SongIDs after Remove(0) = %v, want [s2]", state.SongIDs)
}
state, err = j.Clear()
if err != nil {
t.Fatalf("Clear: %v", err)
}
if len(state.SongIDs) != 0 {
t.Fatalf("SongIDs after Clear = %v, want empty", state.SongIDs)
}
}
func TestJukeboxSetGain(t *testing.T) {
requireMPV(t)
dir := t.TempDir()
song1 := genFile(t, dir, "song1.mp3")
j := New("")
defer j.Close()
resolve := func(id string) (string, error) { return song1, nil }
if _, err := j.Set([]string{"s1"}, resolve); err != nil {
t.Fatalf("Set: %v", err)
}
state, err := j.SetGain(0.5)
if err != nil {
t.Fatalf("SetGain: %v", err)
}
if state.Gain != 0.5 {
t.Fatalf("Gain = %v, want 0.5", state.Gain)
}
}
func TestJukeboxShuffle(t *testing.T) {
requireMPV(t)
dir := t.TempDir()
song1 := genFile(t, dir, "song1.mp3")
song2 := genFile(t, dir, "song2.mp3")
j := New("")
defer j.Close()
paths := map[string]string{"s1": song1, "s2": song2}
resolve := func(id string) (string, error) { return paths[id], nil }
if _, err := j.Set([]string{"s1", "s2"}, resolve); err != nil {
t.Fatalf("Set: %v", err)
}
state, err := j.Shuffle(resolve)
if err != nil {
t.Fatalf("Shuffle: %v", err)
}
if len(state.SongIDs) != 2 {
t.Fatalf("SongIDs after Shuffle = %v, want 2 entries preserved", state.SongIDs)
}
}
func TestJukeboxSkipTo(t *testing.T) {
requireMPV(t)
dir := t.TempDir()
song1 := genFile(t, dir, "song1.mp3")
song2 := genFile(t, dir, "song2.mp3")
j := New("")
defer j.Close()
paths := map[string]string{"s1": song1, "s2": song2}
resolve := func(id string) (string, error) { return paths[id], nil }
if _, err := j.Set([]string{"s1", "s2"}, resolve); err != nil {
t.Fatalf("Set: %v", err)
}
state, err := j.SkipTo(1, 0)
if err != nil {
t.Fatalf("SkipTo: %v", err)
}
if state.CurrentIndex != 1 {
t.Fatalf("CurrentIndex = %d, want 1", state.CurrentIndex)
}
}