Files
wehub-resource-sync ead81af521
Deploy to GitHub Pages / deploy (push) Failing after 0s
CI / go (push) Has been cancelled
CI / build (darwin, macos-14) (push) Has been cancelled
CI / build (windows, windows-2025) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

210 lines
4.9 KiB
Go

package resolve
import (
"strings"
"testing"
)
func TestParsePLSBasic(t *testing.T) {
input := `[playlist]
File1=http://radio.example.com/stream1
Title1=Station One
Length1=-1
File2=http://radio.example.com/stream2
Title2=Station Two
Length2=-1
NumberOfEntries=2
Version=2
`
entries, err := parsePLS(strings.NewReader(input))
if err != nil {
t.Fatalf("parsePLS error: %v", err)
}
if len(entries) != 2 {
t.Fatalf("got %d entries, want 2", len(entries))
}
if entries[0].File != "http://radio.example.com/stream1" {
t.Errorf("entry[0].File = %q", entries[0].File)
}
if entries[0].Title != "Station One" {
t.Errorf("entry[0].Title = %q", entries[0].Title)
}
if entries[0].Num != 1 {
t.Errorf("entry[0].Num = %d, want 1", entries[0].Num)
}
if entries[1].File != "http://radio.example.com/stream2" {
t.Errorf("entry[1].File = %q", entries[1].File)
}
if entries[1].Title != "Station Two" {
t.Errorf("entry[1].Title = %q", entries[1].Title)
}
}
func TestParsePLSSortedByNumber(t *testing.T) {
// Entries out of order should be sorted
input := `[playlist]
File3=http://example.com/3
Title3=Third
File1=http://example.com/1
Title1=First
File2=http://example.com/2
Title2=Second
`
entries, err := parsePLS(strings.NewReader(input))
if err != nil {
t.Fatalf("parsePLS error: %v", err)
}
if len(entries) != 3 {
t.Fatalf("got %d entries, want 3", len(entries))
}
if entries[0].Title != "First" {
t.Errorf("entry[0].Title = %q, want First", entries[0].Title)
}
if entries[1].Title != "Second" {
t.Errorf("entry[1].Title = %q, want Second", entries[1].Title)
}
if entries[2].Title != "Third" {
t.Errorf("entry[2].Title = %q, want Third", entries[2].Title)
}
}
func TestParsePLSEmpty(t *testing.T) {
input := `[playlist]
NumberOfEntries=0
Version=2
`
_, err := parsePLS(strings.NewReader(input))
if err == nil {
t.Fatal("parsePLS should return error for empty playlist")
}
}
func TestParsePLSSkipsSectionAndComments(t *testing.T) {
input := `[playlist]
; This is a comment
File1=http://example.com/stream
Title1=Station
`
entries, err := parsePLS(strings.NewReader(input))
if err != nil {
t.Fatalf("parsePLS error: %v", err)
}
if len(entries) != 1 {
t.Fatalf("got %d entries, want 1", len(entries))
}
}
func TestStripMirrorSuffix(t *testing.T) {
tests := []struct {
input string
want string
}{
{"Groove Salad (#3)", "Groove Salad"},
{"Station Name (#1)", "Station Name"},
{"No Mirror Suffix", "No Mirror Suffix"},
{"", ""},
{"Station: (#2)", "Station"},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
if got := stripMirrorSuffix(tt.input); got != tt.want {
t.Errorf("stripMirrorSuffix(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
func TestAllStreams(t *testing.T) {
tests := []struct {
name string
entries []plsEntry
want bool
}{
{
"all URLs",
[]plsEntry{{File: "http://a.com/s"}, {File: "https://b.com/s"}},
true,
},
{
"mixed",
[]plsEntry{{File: "http://a.com/s"}, {File: "/local/file.mp3"}},
false,
},
{
"all local",
[]plsEntry{{File: "/a.mp3"}, {File: "/b.mp3"}},
false,
},
{
"empty",
[]plsEntry{},
false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := allStreams(tt.entries); got != tt.want {
t.Errorf("allStreams() = %v, want %v", got, tt.want)
}
})
}
}
func TestPlsEntriesToTracksCollapsesMirrors(t *testing.T) {
// When all entries are streams, should collapse to a single track
entries := []plsEntry{
{Num: 1, File: "http://a.com/stream", Title: "Station (#1)"},
{Num: 2, File: "http://b.com/stream", Title: "Station (#2)"},
}
tracks := plsEntriesToTracks(entries)
if len(tracks) != 1 {
t.Fatalf("got %d tracks, want 1 (mirrors collapsed)", len(tracks))
}
if tracks[0].Title != "Station" {
t.Errorf("Title = %q, want Station (mirror suffix stripped)", tracks[0].Title)
}
if !tracks[0].Stream || !tracks[0].Realtime {
t.Error("collapsed stream should be Stream=true, Realtime=true")
}
}
func TestPlsEntriesToTracksMultipleTracks(t *testing.T) {
// Mixed local/remote entries should produce individual tracks
entries := []plsEntry{
{Num: 1, File: "/home/user/song.mp3", Title: "Song One"},
{Num: 2, File: "/home/user/song2.mp3", Title: "Song Two"},
}
tracks := plsEntriesToTracks(entries)
if len(tracks) != 2 {
t.Fatalf("got %d tracks, want 2", len(tracks))
}
if tracks[0].Title != "Song One" {
t.Errorf("track[0].Title = %q, want Song One", tracks[0].Title)
}
}
func TestHumanizeBasename(t *testing.T) {
tests := []struct {
input string
want string
}{
{"clr-podcast-467", "clr podcast 467"},
{"no-dashes-here", "no dashes here"},
{"nodashes", "nodashes"},
{"", ""},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
if got := humanizeBasename(tt.input); got != tt.want {
t.Errorf("humanizeBasename(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}