package engine import ( "fmt" "strings" "testing" "github.com/cdnutter/playlink/internal/store" ) func scanWarnings(t *testing.T, ev *env) []store.Warning { t.Helper() ev.scan() p, err := ev.st.Pair(ctx, ev.pair.ID) if err != nil { t.Fatal(err) } var out []store.Warning for _, w := range p.LastScanWarnings { if !strings.Contains(w.Title, "ffprobe") { out = append(out, w) } } return out } // The user's situation: a singles library with far more artists and songs // than the albums library. Slave-only content must not trigger warnings. func TestSlaveOnlyContentIsNotALayoutProblem(t *testing.T) { ev := setup(t, false) for i := 0; i < 40; i++ { ev.write(ev.slave, fmt.Sprintf("Singles Artist %02d/Some Single/Song.mp3", i), "s") } for i := 0; i < 10; i++ { rel := fmt.Sprintf("Shared Artist/Album/%02d Track.flac", i) ev.write(ev.master, rel, "same") ev.write(ev.slave, rel, "same") } ev.write(ev.master, "Master Only/Album/01 X.flac", "m") if w := scanWarnings(t, ev); len(w) != 0 { t.Fatalf("slave-only content should not produce warnings, got %+v", w) } } func TestRenamedFoldersAreReported(t *testing.T) { ev := setup(t, false) ev.write(ev.master, "AC_DC/Back in Black/01 Hells Bells.flac", "a") ev.write(ev.slave, "AC-DC/Back in Black/01 Hells Bells.mp3", "b") ev.write(ev.master, "The Beatles/Abbey Road/01 Come Together.flac", "a") ev.write(ev.slave, "Beatles, The/Abbey Road/01 Come Together.mp3", "b") ev.write(ev.master, "Avenged Sevenfold/City of Evil/03 Bat Country.flac", "a") ev.write(ev.slave, "Avenged Sevenfold/City Of Evil/03 Bat Country.mp3", "b") ev.write(ev.slave, "Beyoncé/Lemonade/Formation.mp3", "b") ev.write(ev.master, "Beyonce/Lemonade/Formation.flac", "a") w := scanWarnings(t, ev) if len(w) != 1 || w[0].Title != "4 folders are named differently in the two libraries" || w[0].Total != 4 { t.Fatalf("want one renamed-folders warning, got %+v", w) } want := map[store.WarningItem]bool{ {Slave: "AC-DC", Master: "AC_DC"}: true, {Slave: "Beatles, The", Master: "The Beatles"}: true, {Slave: "Beyoncé", Master: "Beyonce"}: true, {Slave: "Avenged Sevenfold/City Of Evil", Master: "Avenged Sevenfold/City of Evil"}: true, } for _, it := range w[0].Items { if !want[it] { t.Errorf("unexpected item %+v", it) } delete(want, it) } if len(want) != 0 { t.Errorf("missing items %v", want) } } // A slave library without album folders is a genuine layout difference. func TestDifferentLayoutIsReported(t *testing.T) { ev := setup(t, false) for i := 0; i < 24; i++ { ev.write(ev.master, fmt.Sprintf("Band/Album/%02d Song Number %d.flac", i+1, i), "m") ev.write(ev.slave, fmt.Sprintf("Band/Song Number %d.mp3", i), "s") } w := scanWarnings(t, ev) if len(w) != 1 || w[0].Title != "24 of 24 songs found in both libraries are in a different folder in the master library" { t.Fatalf("want a different-layout warning, got %+v", w) } if len(w[0].Items) != 1 || w[0].Items[0] != (store.WarningItem{Slave: "Band", Master: "Band/Album", Count: 24}) { t.Fatalf("songs should be grouped by folder pair, got %+v", w[0].Items) } } // Singles matched to their album version are intended, not a layout problem. func TestSinglesMatchedToAlbumsAreNotALayoutProblem(t *testing.T) { ev := setup(t, false) enableApprox(t, ev) for i := 0; i < 24; i++ { ev.write(ev.master, fmt.Sprintf("Band/Album/%02d Song Number %d.flac", i+1, i), "m") ev.write(ev.slave, fmt.Sprintf("Band/Song Number %d - Single/Song Number %d.mp3", i, i), "s") } if w := scanWarnings(t, ev); len(w) != 0 { t.Fatalf("singles matched to album versions should not warn, got %+v", w) } } func TestFolderKey(t *testing.T) { same := [][2]string{{"AC/DC", "AC-DC"}, {"The Beatles", "Beatles, The"}, {"Simon & Garfunkel", "Simon and Garfunkel"}, {"Beyoncé", "Beyonce"}, {"City Of Evil", "City of Evil"}, {"Sigur Rós", "Sigur Ros"}} for _, p := range same { if folderKey(p[0]) != folderKey(p[1]) { t.Errorf("%q and %q should share a key (%q vs %q)", p[0], p[1], folderKey(p[0]), folderKey(p[1])) } } if folderKey("Theory of a Deadman") == folderKey("ory of a Deadman") { t.Error(`"The" must only be stripped as a whole word`) } if folderKey("Nightmare") == folderKey("Nightmare (Deluxe)") { t.Error("different folders must keep different keys") } } // A renamed folder is reported once, as a renamed folder, not again as songs // living in a different folder. func TestRenamedFolderNotCountedAsMoved(t *testing.T) { var ls layoutStats for i := 0; i < 30; i++ { ls.add(fmt.Sprintf("ABBA/Voulez-Vous/%02d x.flac", i), fmt.Sprintf("ABBA/Voulez‐vous/%02d x.flac", i), store.KindElsewhere, "") } if ls.shared != 30 || ls.moved != 0 || ls.warning() != nil { t.Fatalf("renamed folder counted as moved: shared=%d moved=%d", ls.shared, ls.moved) } }