refactor: update scanner, scanner tests, mockfs
closes #165 closes #163
This commit is contained in:
269
server/mockfs/mockfs.go
Normal file
269
server/mockfs/mockfs.go
Normal file
@@ -0,0 +1,269 @@
|
||||
package mockfs
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"go.senan.xyz/gonic/server/db"
|
||||
"go.senan.xyz/gonic/server/scanner"
|
||||
"go.senan.xyz/gonic/server/scanner/tags"
|
||||
)
|
||||
|
||||
var ErrPathNotFound = errors.New("path not found")
|
||||
|
||||
type MockFS struct {
|
||||
t *testing.T
|
||||
scanner *scanner.Scanner
|
||||
dir string
|
||||
reader *mreader
|
||||
db *db.DB
|
||||
}
|
||||
|
||||
func New(t *testing.T) *MockFS {
|
||||
return new(t, []string{""})
|
||||
}
|
||||
|
||||
func NewWithDirs(t *testing.T, dirs []string) *MockFS {
|
||||
return new(t, dirs)
|
||||
}
|
||||
|
||||
func new(t *testing.T, dirs []string) *MockFS {
|
||||
dbc, err := db.NewMock()
|
||||
if err != nil {
|
||||
t.Fatalf("create db: %v", err)
|
||||
}
|
||||
if err := dbc.Migrate(db.MigrationContext{}); err != nil {
|
||||
t.Fatalf("migrate db db: %v", err)
|
||||
}
|
||||
dbc.LogMode(false)
|
||||
|
||||
tmpDir, err := ioutil.TempDir("", "gonic-test-")
|
||||
if err != nil {
|
||||
t.Fatalf("create tmp dir: %v", err)
|
||||
}
|
||||
|
||||
var absDirs []string
|
||||
for _, dir := range dirs {
|
||||
absDirs = append(absDirs, filepath.Join(tmpDir, dir))
|
||||
}
|
||||
|
||||
parser := &mreader{map[string]*Tags{}}
|
||||
scanner := scanner.New(absDirs, true, dbc, ";", parser)
|
||||
|
||||
return &MockFS{
|
||||
t: t,
|
||||
scanner: scanner,
|
||||
dir: tmpDir,
|
||||
reader: parser,
|
||||
db: dbc,
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) DB() *db.DB { return m.db }
|
||||
func (m *MockFS) TmpDir() string { return m.dir }
|
||||
|
||||
func (m *MockFS) ScanAndClean() {
|
||||
if err := m.scanner.ScanAndClean(scanner.ScanOptions{}); err != nil {
|
||||
m.t.Fatalf("error scan and cleaning: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) ResetDates() {
|
||||
t := time.Date(2020, 0, 0, 0, 0, 0, 0, time.UTC)
|
||||
if err := m.db.Model(db.Album{}).Updates(db.Album{CreatedAt: t, UpdatedAt: t, ModifiedAt: t}).Error; err != nil {
|
||||
m.t.Fatalf("reset album times: %v", err)
|
||||
}
|
||||
if err := m.db.Model(db.Track{}).Updates(db.Track{CreatedAt: t, UpdatedAt: t}).Error; err != nil {
|
||||
m.t.Fatalf("reset track times: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) CleanUp() {
|
||||
if err := m.db.Close(); err != nil {
|
||||
m.t.Fatalf("close db: %v", err)
|
||||
}
|
||||
if err := os.RemoveAll(m.dir); err != nil {
|
||||
m.t.Fatalf("remove all: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) addItems(prefix string, covers bool) {
|
||||
p := func(format string, a ...interface{}) string {
|
||||
return filepath.Join(prefix, fmt.Sprintf(format, a...))
|
||||
}
|
||||
for ar := 0; ar < 3; ar++ {
|
||||
for al := 0; al < 3; al++ {
|
||||
for tr := 0; tr < 3; tr++ {
|
||||
m.AddTrack(p("artist-%d/album-%d/track-%d.flac", ar, al, tr))
|
||||
m.SetTags(p("artist-%d/album-%d/track-%d.flac", ar, al, tr), func(tags *Tags) {
|
||||
tags.RawArtist = fmt.Sprintf("artist-%d", ar)
|
||||
tags.RawAlbumArtist = fmt.Sprintf("artist-%d", ar)
|
||||
tags.RawAlbum = fmt.Sprintf("album-%d", al)
|
||||
tags.RawTitle = fmt.Sprintf("title-%d", tr)
|
||||
})
|
||||
}
|
||||
if covers {
|
||||
m.AddCover(p("artist-%d/album-%d/cover.png", ar, al))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) AddItems() { m.addItems("", false) }
|
||||
func (m *MockFS) AddItemsPrefix(prefix string) { m.addItems(prefix, false) }
|
||||
func (m *MockFS) AddItemsWithCovers() { m.addItems("", true) }
|
||||
func (m *MockFS) AddItemsPrefixWithCovers(prefix string) { m.addItems(prefix, true) }
|
||||
|
||||
func (m *MockFS) RemoveAll(path string) {
|
||||
abspath := filepath.Join(m.dir, path)
|
||||
if err := os.RemoveAll(abspath); err != nil {
|
||||
m.t.Fatalf("remove all: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) LogItems() {
|
||||
var dirs int
|
||||
err := filepath.Walk(m.dir, func(path string, info fs.FileInfo, err error) error {
|
||||
m.t.Logf("item %q", path)
|
||||
if info.IsDir() {
|
||||
dirs++
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
m.t.Fatalf("error logging items: %v", err)
|
||||
}
|
||||
m.t.Logf("dirs: %d", dirs)
|
||||
}
|
||||
|
||||
func (m *MockFS) LogAlbums() {
|
||||
var albums []*db.Album
|
||||
if err := m.db.Find(&albums).Error; err != nil {
|
||||
m.t.Fatalf("error logging items: %v", err)
|
||||
}
|
||||
|
||||
m.t.Logf("\nalbums")
|
||||
for _, album := range albums {
|
||||
m.t.Logf("id %-3d root %-3s %-10s %-10s pid %-3d aid %-3d cov %-10s",
|
||||
album.ID, album.RootDir, album.LeftPath, album.RightPath, album.ParentID, album.TagArtistID, album.Cover)
|
||||
}
|
||||
m.t.Logf("total %d", len(albums))
|
||||
}
|
||||
|
||||
func (m *MockFS) LogArtists() {
|
||||
var artists []*db.Artist
|
||||
if err := m.db.Find(&artists).Error; err != nil {
|
||||
m.t.Fatalf("error logging items: %v", err)
|
||||
}
|
||||
|
||||
m.t.Logf("\nartists")
|
||||
for _, artist := range artists {
|
||||
m.t.Logf("id %-3d %-10s", artist.ID, artist.Name)
|
||||
}
|
||||
m.t.Logf("total %d", len(artists))
|
||||
}
|
||||
|
||||
func (m *MockFS) LogTracks() {
|
||||
var tracks []*db.Track
|
||||
if err := m.db.Find(&tracks).Error; err != nil {
|
||||
m.t.Fatalf("error logging items: %v", err)
|
||||
}
|
||||
|
||||
m.t.Logf("\ntracks")
|
||||
for _, track := range tracks {
|
||||
m.t.Logf("id %-3d aid %-3d filename %-10s tagtitle %-10s",
|
||||
track.ID, track.AlbumID, track.Filename, track.TagTitle)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) LogTrackGenres() {
|
||||
var tgs []*db.TrackGenre
|
||||
if err := m.db.Find(&tgs).Error; err != nil {
|
||||
m.t.Fatalf("error logging items: %v", err)
|
||||
}
|
||||
|
||||
m.t.Logf("\ntrack genres")
|
||||
for _, tg := range tgs {
|
||||
m.t.Logf("tid %-3d gid %-3d", tg.TrackID, tg.GenreID)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) AddTrack(path string) {
|
||||
abspath := filepath.Join(m.dir, path)
|
||||
dir := filepath.Dir(abspath)
|
||||
if err := os.MkdirAll(dir, os.ModePerm); err != nil {
|
||||
m.t.Fatalf("mkdir: %v", err)
|
||||
}
|
||||
if _, err := os.Create(abspath); err != nil {
|
||||
m.t.Fatalf("create track: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) AddCover(path string) {
|
||||
abspath := filepath.Join(m.dir, path)
|
||||
if err := os.MkdirAll(filepath.Dir(abspath), os.ModePerm); err != nil {
|
||||
m.t.Fatalf("mkdir: %v", err)
|
||||
}
|
||||
if _, err := os.Create(abspath); err != nil {
|
||||
m.t.Fatalf("create cover: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *MockFS) SetTags(path string, cb func(*Tags)) {
|
||||
abspath := filepath.Join(m.dir, path)
|
||||
if err := os.Chtimes(abspath, time.Time{}, time.Now()); err != nil {
|
||||
m.t.Fatalf("touch track: %v", err)
|
||||
}
|
||||
if _, ok := m.reader.tags[abspath]; !ok {
|
||||
m.reader.tags[abspath] = &Tags{}
|
||||
}
|
||||
cb(m.reader.tags[abspath])
|
||||
}
|
||||
|
||||
type mreader struct {
|
||||
tags map[string]*Tags
|
||||
}
|
||||
|
||||
func (m *mreader) Read(abspath string) (tags.Parser, error) {
|
||||
parser, ok := m.tags[abspath]
|
||||
if !ok {
|
||||
return nil, ErrPathNotFound
|
||||
}
|
||||
return parser, nil
|
||||
}
|
||||
|
||||
var _ tags.Reader = (*mreader)(nil)
|
||||
|
||||
type Tags struct {
|
||||
RawTitle string
|
||||
RawArtist string
|
||||
RawAlbum string
|
||||
RawAlbumArtist string
|
||||
RawGenre string
|
||||
}
|
||||
|
||||
func (m *Tags) Title() string { return m.RawTitle }
|
||||
func (m *Tags) BrainzID() string { return "" }
|
||||
func (m *Tags) Artist() string { return m.RawArtist }
|
||||
func (m *Tags) Album() string { return m.RawAlbum }
|
||||
func (m *Tags) AlbumArtist() string { return m.RawAlbumArtist }
|
||||
func (m *Tags) AlbumBrainzID() string { return "" }
|
||||
func (m *Tags) Genre() string { return m.RawGenre }
|
||||
func (m *Tags) TrackNumber() int { return 1 }
|
||||
func (m *Tags) DiscNumber() int { return 1 }
|
||||
func (m *Tags) Length() int { return 100 }
|
||||
func (m *Tags) Bitrate() int { return 100 }
|
||||
func (m *Tags) Year() int { return 2021 }
|
||||
|
||||
func (m *Tags) SomeAlbum() string { return m.Album() }
|
||||
func (m *Tags) SomeArtist() string { return m.Artist() }
|
||||
func (m *Tags) SomeAlbumArtist() string { return m.AlbumArtist() }
|
||||
func (m *Tags) SomeGenre() string { return m.Genre() }
|
||||
|
||||
var _ tags.Parser = (*Tags)(nil)
|
||||
Reference in New Issue
Block a user