package download import ( "context" "errors" "os" "path/filepath" "testing" ) // Filling in the tracks an almost-complete album is missing (#276). func fiveTrackTitles() []string { return append(allTitles(), "Let Down") } func fiveTrackDownload() Download { dl := fourTrackDownload() dl.Expected = append(dl.Expected, ExpectedTrack{Position: 5, Title: "Let Down"}) return dl } func TestMissingTracks(t *testing.T) { t.Parallel() dl := fiveTrackDownload() got := func(positions ...int) []ExpectedTrack { out := make([]ExpectedTrack, 0, len(positions)) for _, p := range positions { out = append(out, dl.Expected[p-1]) } return out } cases := []struct { name string dl Download matched []ExpectedTrack want int }{ {"complete", dl, got(1, 2, 3, 4, 5), 0}, {"one short", dl, got(1, 2, 3, 4), 1}, {"two short", dl, got(1, 2, 3), 2}, {"half gone is a poor copy", dl, got(1, 2), 0}, {"a recording is not an album", func() Download { d := dl d.RecordingMBID = "rec" return d }(), got(1, 2, 3, 4), 0}, } for _, tc := range cases { if n := len(missingTracks(tc.dl, tc.matched)); n != tc.want { t.Errorf("%s: %d missing, want %d", tc.name, n, tc.want) } } big := fiveTrackDownload() for i := 6; i <= 20; i++ { big.Expected = append(big.Expected, ExpectedTrack{Position: i, Title: "T" + itoa(i)}) } if n := len(missingTracks(big, big.Expected[:16])); n != 0 { t.Errorf("four of twenty missing: %d filled in, want none past %d", n, maxFillInTracks) } } // A fill-in import takes only the file that is the missing track, and // places it in the album with the album's tags; anything else the grab // brought is left out. func TestImportOnlyTakesTheMissingTrack(t *testing.T) { t.Parallel() f := newImportFixture(t, "05 - Let Down.flac", "02 - Paranoid Android.flac", ) dl := fiveTrackDownload() got, err := f.importer.Import( context.Background(), dl, Result{Dir: f.dir, Files: f.files}, ImportOptions{LibraryRoot: f.root, WriteTags: true, Only: dl.Expected[4:]}, ) if err != nil { t.Fatalf("Import: %v", err) } want := filepath.Join(f.root, "Radiohead", "OK Computer", "05 Let Down.flac") if len(got.Paths) != 1 || got.Paths[0] != want { t.Errorf("imported %q, want only %s", got.Paths, want) } // A file that is not the missing track is not imported at all. g := newImportFixture(t, "02 - Paranoid Android.flac") if _, err := g.importer.Import( context.Background(), dl, Result{Dir: g.dir, Files: g.files}, ImportOptions{LibraryRoot: g.root, WriteTags: true, Only: dl.Expected[4:]}, ); !errors.Is(err, ErrTooIncomplete) { t.Errorf("Import = %v, want nothing matched", err) } } // The album comes from one source missing its fifth track; the fifth is // then found on its own at another and lands in the same album. func TestManagerFillsInAMissingTrack(t *testing.T) { t.Parallel() f := newManagerFixture(t) titles := fiveTrackTitles() album := NewFakeProvider(1, "album", Caps{CanSearch: true, CanTransport: true}) ac := candidateFor("album-cand", titles, ".flac", 30_000_000) ac.ProviderID = 1 album.Candidates = []Candidate{ac} for i, tt := range titles[:4] { album.Written[trackToken(i+1)+" - "+tt+".flac"] = []byte("audio-data") } single := NewFakeProvider(2, "single", Caps{CanSearch: true, CanTransport: true}) sc := Candidate{ ID: "single-cand", Protocol: ProtocolDirect, Title: "Radiohead - OK Computer", Artist: "Radiohead", Files: []CandidateFile{{ Path: "Radiohead - OK Computer/05 - Let Down.flac", Size: 30_000_000, }}, Health: 0.5, ProviderID: 2, } single.Candidates = []Candidate{sc} single.Written["05 - Let Down.flac"] = []byte("audio-data") f.manager.installProvider(Config{ID: 1, Priority: 90}, album) f.manager.installProvider(Config{ID: 2, Priority: 10}, single) dl := fiveTrackDownload() if _, err := f.manager.Start(context.Background(), dl); err != nil { t.Fatalf("Start: %v", err) } waitForDownloadState(t, f.store, dl.ID, StateComplete) if album.GrabCallCount() != 1 || single.GrabCallCount() != 1 { t.Errorf( "grabs: album=%d single=%d, want 1 and 1", album.GrabCallCount(), single.GrabCallCount(), ) } for i, tt := range titles { p := filepath.Join(f.root, "Radiohead", "OK Computer", trackToken(i+1)+" "+tt+".flac") if _, err := os.Stat(p); err != nil { t.Errorf("track %d not in the library: %v", i+1, err) } } }