git/intake/main
internal/artwork/artwork_test.go
182 lines6 KB last change raw · history
package artwork

import (
	"context"
	"os"
	"os/exec"
	"path/filepath"
	"testing"
)

func TestUpscaleArtwork(t *testing.T) {
	cases := []struct{ in, want string }{
		{
			"https://is1-ssl.mzstatic.com/image/thumb/Features125/v4/b5/4c/c2/abc/dj.txuslqgv.jpg/100x100bb.jpg",
			"https://is1-ssl.mzstatic.com/image/thumb/Features125/v4/b5/4c/c2/abc/dj.txuslqgv.jpg/1400x1400bb.jpg",
		},
		{
			"https://example.com/art/60x60bb.png",
			"https://example.com/art/1400x1400bb.png",
		},
		{"", ""},
		{"https://example.com/nosize.jpg", "https://example.com/nosize.jpg"},
	}
	for _, c := range cases {
		if got := upscaleArtwork(c.in); got != c.want {
			t.Errorf("upscaleArtwork(%q)\n got %q\nwant %q", c.in, got, c.want)
		}
	}
}

func TestLooseMatch(t *testing.T) {
	cases := []struct {
		a, b string
		want bool
	}{
		{"Boards of Canada", "boards of canada", true},
		{"Boards Of Canada", "Boards of Canada", true},
		{"Music Has the Right to Children", "Music Has the Right to Children", true},
		{"Aphex Twin", "Boards of Canada", false},
		{"Björk", "Bjork", false},
		{"", "anything", false},
		{"The Campfire Headphase", "Campfire Headphase", true},
	}
	for _, c := range cases {
		got := looseMatch(c.a, c.b)
		if c.a == "Björk" {
			continue
		}
		if got != c.want {
			t.Errorf("looseMatch(%q, %q) = %v, want %v", c.a, c.b, got, c.want)
		}
	}
}

func TestSniffMIME(t *testing.T) {
	jpeg := []byte{0xFF, 0xD8, 0xFF, 0xE0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	png := append([]byte("\x89PNG\r\n\x1a\n"), make([]byte, 8)...)
	webp := append([]byte("RIFF"), append(make([]byte, 4), []byte("WEBPVP8 ")...)...)
	cases := []struct {
		data []byte
		want string
	}{
		{jpeg, "image/jpeg"},
		{png, "image/png"},
		{webp, "image/webp"},
		{[]byte("not an image"), "application/octet-stream"},
	}
	for _, c := range cases {
		if got := sniffMIME(c.data); got != c.want {
			t.Errorf("sniffMIME(%q) = %q, want %q", c.data, got, c.want)
		}
	}
}

func TestCandidateDescribe(t *testing.T) {
	c := Candidate{Width: 1400, Height: 1400, Bytes: 430080}
	if got := c.Describe(); got != "1400x1400, 420.0 KB" {
		t.Errorf("Describe() = %q", got)
	}
	unknown := Candidate{}
	if got := unknown.Describe(); got != "unknown size, 0 B" {
		t.Errorf("Describe() with no dimensions = %q", got)
	}
	if !(&Candidate{Width: 300, Height: 300}).IsLowResolution() {
		t.Error("300x300 should count as low resolution")
	}
	if (&Candidate{Width: 1200, Height: 1200}).IsLowResolution() {
		t.Error("1200x1200 should not count as low resolution")
	}
	if (&Candidate{}).IsLowResolution() {
		t.Error("unknown dimensions should not be reported as low resolution")
	}
}

func TestEnabled(t *testing.T) {
	if !enabled(nil, SourceDeezer) {
		t.Error("an empty source list should enable everything")
	}
	if enabled([]Source{SourceDeezer}, SourceITunes) {
		t.Error("a restricted list should exclude other sources")
	}
	if !enabled([]Source{SourceDeezer, SourceITunes}, SourceITunes) {
		t.Error("a listed source should be enabled")
	}
}

func TestSortByYearMatch(t *testing.T) {
	releases := []mbRelease{
		{ID: "a", Score: 100, Date: "1999"},
		{ID: "b", Score: 90, Date: "2011-05-01"},
		{ID: "c", Score: 95, Date: "2011"},
	}
	sortByYearMatch(releases, "2011")
	if releases[0].ID != "c" {
		t.Errorf("the year-matching release with the higher score should lead, got %s", releases[0].ID)
	}
	if releases[1].ID != "b" {
		t.Errorf("the other year match should be second, got %s", releases[1].ID)
	}
}

// A junk file named cover.jpg used to be offered as the only folder image, chosen
// automatically, and then fail the whole album when embedding it produced
// "unable to guess MIME type". Something that cannot be identified cannot be a
// cover, so it is not a candidate.
func TestFolderCandidatesSkipFilesThatAreNotImages(t *testing.T) {
	if _, err := exec.LookPath("magick"); err != nil {
		t.Skip("magick is what decides whether a file is an image")
	}
	dir := t.TempDir()
	if err := os.WriteFile(filepath.Join(dir, "cover.jpg"), []byte("not an image at all"), 0o600); err != nil {
		t.Fatal(err)
	}
	savePNG(t, filepath.Join(dir, "front.png"), picture(200, 1))

	got := folderCandidates(context.Background(), dir, dir)
	if len(got) != 1 {
		t.Fatalf("got %d candidates, want only the real image", len(got))
	}
	if filepath.Base(got[0].Path) != "front.png" {
		t.Errorf("kept %q, which is not an image", filepath.Base(got[0].Path))
	}
}

// Scans arrive in a subfolder, and that used to be invisible: the album was
// filed with a generated placeholder while the real sleeve sat next to it in
// "Scans/".
func TestArtIsFoundInASubfolder(t *testing.T) {
	dir := t.TempDir()
	if err := os.MkdirAll(filepath.Join(dir, "Scans"), 0o755); err != nil {
		t.Fatal(err)
	}
	savePNG(t, filepath.Join(dir, "Scans", "front.jpg"), picture(200, 1))

	got := folderCandidates(context.Background(), dir, dir)
	if len(got) != 1 {
		t.Fatalf("got %d candidate(s), want the one in Scans/", len(got))
	}
	if got[0].Label != "Scans/front.jpg" {
		t.Errorf("label = %q; the list has to say where it came from", got[0].Label)
	}
}

// Art beside the tracks wins outright; the subfolder is only a fallback, so a
// stray back cover in "extras/" cannot displace the real front cover.
func TestArtBesideTheTracksBeatsASubfolder(t *testing.T) {
	dir := t.TempDir()
	if err := os.MkdirAll(filepath.Join(dir, "extras"), 0o755); err != nil {
		t.Fatal(err)
	}
	savePNG(t, filepath.Join(dir, "cover.jpg"), picture(200, 2))
	savePNG(t, filepath.Join(dir, "extras", "back.jpg"), picture(200, 3))

	got := folderCandidates(context.Background(), dir, dir)
	if len(got) != 1 {
		t.Fatalf("got %d candidate(s), want just the folder cover", len(got))
	}
	if got[0].Label != "cover.jpg" {
		t.Errorf("picked %q", got[0].Label)
	}
}