refactor: update scanner, scanner tests, mockfs
closes #165 closes #163
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package scanner
|
||||
package scanner_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -6,62 +6,319 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/matryer/is"
|
||||
|
||||
"go.senan.xyz/gonic/server/db"
|
||||
"go.senan.xyz/gonic/server/mockfs"
|
||||
)
|
||||
|
||||
var testScanner *Scanner
|
||||
|
||||
func resetTables(db *db.DB) {
|
||||
tx := db.Begin()
|
||||
defer tx.Commit()
|
||||
tx.Exec("delete from tracks")
|
||||
tx.Exec("delete from artists")
|
||||
tx.Exec("delete from albums")
|
||||
}
|
||||
|
||||
func resetTablesPause(db *db.DB, b *testing.B) {
|
||||
b.StopTimer()
|
||||
defer b.StartTimer()
|
||||
resetTables(db)
|
||||
}
|
||||
|
||||
func BenchmarkScanFresh(b *testing.B) {
|
||||
for n := 0; n < b.N; n++ {
|
||||
resetTablesPause(testScanner.db, b)
|
||||
_ = testScanner.Start(ScanOptions{})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkScanIncremental(b *testing.B) {
|
||||
// do a full scan and reset
|
||||
_ = testScanner.Start(ScanOptions{})
|
||||
b.ResetTimer()
|
||||
// do the inc scans
|
||||
for n := 0; n < b.N; n++ {
|
||||
_ = testScanner.Start(ScanOptions{})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
db, err := db.NewMock()
|
||||
if err != nil {
|
||||
log.Fatalf("error opening database: %v\n", err)
|
||||
}
|
||||
// benchmarks aren't real code are they? >:)
|
||||
// here is an absolute path to my music directory
|
||||
testScanner = New("/home/senan/music", db, "\n")
|
||||
log.SetOutput(ioutil.Discard)
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
|
||||
// RESULTS fresh
|
||||
// 20 times / 1.436
|
||||
// 20 times / 1.39
|
||||
func TestTableCounts(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.NewRelaxed(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
// RESULTS inc
|
||||
// 100 times / 1.86
|
||||
// 100 times / 1.9
|
||||
// 100 times / 1.5
|
||||
// 100 times / 1.48
|
||||
m.AddItems()
|
||||
m.ScanAndClean()
|
||||
|
||||
var tracks int
|
||||
is.NoErr(m.DB().Model(&db.Track{}).Count(&tracks).Error) // not all tracks
|
||||
is.Equal(tracks, 3*3*3) // not all tracks
|
||||
|
||||
var albums int
|
||||
is.NoErr(m.DB().Model(&db.Album{}).Count(&albums).Error) // not all albums
|
||||
is.Equal(albums, 13) // not all albums
|
||||
|
||||
var artists int
|
||||
is.NoErr(m.DB().Model(&db.Artist{}).Count(&artists).Error) // not all artists
|
||||
is.Equal(artists, 3) // not all artists
|
||||
}
|
||||
|
||||
func TestParentID(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.New(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddItems()
|
||||
m.ScanAndClean()
|
||||
|
||||
var nullParentAlbums []*db.Album
|
||||
is.NoErr(m.DB().Where("parent_id IS NULL").Find(&nullParentAlbums).Error) // one parent_id=NULL which is root folder
|
||||
is.Equal(len(nullParentAlbums), 1) // one parent_id=NULL which is root folder
|
||||
is.Equal(nullParentAlbums[0].LeftPath, "")
|
||||
is.Equal(nullParentAlbums[0].RightPath, ".")
|
||||
|
||||
is.Equal(m.DB().Where("id=parent_id").Find(&db.Album{}).Error, gorm.ErrRecordNotFound) // no self-referencing albums
|
||||
|
||||
var album db.Album
|
||||
var parent db.Album
|
||||
is.NoErr(m.DB().Find(&album, "left_path=? AND right_path=?", "artist-0/", "album-0").Error) // album has parent ID
|
||||
is.NoErr(m.DB().Find(&parent, "right_path=?", "artist-0").Error) // album has parent ID
|
||||
is.Equal(album.ParentID, parent.ID) // album has parent ID
|
||||
}
|
||||
|
||||
func TestUpdatedCover(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.NewRelaxed(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddItems()
|
||||
m.ScanAndClean()
|
||||
m.AddCover("artist-0/album-0/cover.jpg")
|
||||
m.ScanAndClean()
|
||||
|
||||
var album db.Album
|
||||
is.NoErr(m.DB().Where("left_path=? AND right_path=?", "artist-0/", "album-0").Find(&album).Error) // album has cover
|
||||
is.Equal(album.Cover, "cover.jpg") // album has cover
|
||||
}
|
||||
|
||||
func TestCoverBeforeTracks(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.New(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddCover("artist-2/album-2/cover.jpg")
|
||||
m.ScanAndClean()
|
||||
m.AddItems()
|
||||
m.ScanAndClean()
|
||||
|
||||
var album db.Album
|
||||
is.NoErr(m.DB().Preload("TagArtist").Where("left_path=? AND right_path=?", "artist-2/", "album-2").Find(&album).Error) // album has cover
|
||||
is.Equal(album.Cover, "cover.jpg") // album has cover
|
||||
is.Equal(album.TagArtist.Name, "artist-2") // album artist
|
||||
|
||||
var tracks []*db.Track
|
||||
is.NoErr(m.DB().Where("album_id=?", album.ID).Find(&tracks).Error) // album has tracks
|
||||
is.Equal(len(tracks), 3) // album has tracks
|
||||
}
|
||||
|
||||
func TestUpdatedTags(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.New(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddTrack("artist-10/album-10/track-10.flac")
|
||||
m.SetTags("artist-10/album-10/track-10.flac", func(tags *mockfs.Tags) {
|
||||
tags.RawArtist = "artist"
|
||||
tags.RawAlbumArtist = "album-artist"
|
||||
tags.RawAlbum = "album"
|
||||
tags.RawTitle = "title"
|
||||
})
|
||||
|
||||
m.ScanAndClean()
|
||||
|
||||
var track db.Track
|
||||
is.NoErr(m.DB().Preload("Album").Preload("Artist").Where("filename=?", "track-10.flac").Find(&track).Error) // track has tags
|
||||
is.Equal(track.TagTrackArtist, "artist") // track has tags
|
||||
is.Equal(track.Artist.Name, "album-artist") // track has tags
|
||||
is.Equal(track.Album.TagTitle, "album") // track has tags
|
||||
is.Equal(track.TagTitle, "title") // track has tags
|
||||
|
||||
m.SetTags("artist-10/album-10/track-10.flac", func(tags *mockfs.Tags) {
|
||||
tags.RawArtist = "artist-upd"
|
||||
tags.RawAlbumArtist = "album-artist-upd"
|
||||
tags.RawAlbum = "album-upd"
|
||||
tags.RawTitle = "title-upd"
|
||||
})
|
||||
|
||||
m.ScanAndClean()
|
||||
|
||||
var updated db.Track
|
||||
is.NoErr(m.DB().Preload("Album").Preload("Artist").Where("filename=?", "track-10.flac").Find(&updated).Error) // updated has tags
|
||||
is.Equal(updated.ID, track.ID) // updated has tags
|
||||
is.Equal(updated.TagTrackArtist, "artist-upd") // updated has tags
|
||||
is.Equal(updated.Artist.Name, "album-artist-upd") // updated has tags
|
||||
is.Equal(updated.Album.TagTitle, "album-upd") // updated has tags
|
||||
is.Equal(updated.TagTitle, "title-upd") // updated has tags
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.NewRelaxed(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddItems()
|
||||
m.ScanAndClean()
|
||||
|
||||
var album db.Album
|
||||
is.NoErr(m.DB().Where("left_path=? AND right_path=?", "artist-2/", "album-2").Find(&album).Error) // album exists
|
||||
|
||||
m.RemoveAll("artist-2/album-2")
|
||||
m.ScanAndClean()
|
||||
|
||||
is.Equal(m.DB().Where("left_path=? AND right_path=?", "artist-2/", "album-2").Find(&album).Error, gorm.ErrRecordNotFound) // album doesn't exist
|
||||
}
|
||||
|
||||
func TestGenres(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.New(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
albumGenre := func(artist, album, genre string) error {
|
||||
return m.DB().
|
||||
Where("albums.left_path=? AND albums.right_path=? AND genres.name=?", artist, album, genre).
|
||||
Joins("JOIN albums ON albums.id=album_genres.album_id").
|
||||
Joins("JOIN genres ON genres.id=album_genres.genre_id").
|
||||
Find(&db.AlbumGenre{}).
|
||||
Error
|
||||
}
|
||||
isAlbumGenre := func(artist, album, genreName string) {
|
||||
is.Helper()
|
||||
is.NoErr(albumGenre(artist, album, genreName))
|
||||
}
|
||||
isAlbumGenreMissing := func(artist, album, genreName string) {
|
||||
is.Helper()
|
||||
is.Equal(albumGenre(artist, album, genreName), gorm.ErrRecordNotFound)
|
||||
}
|
||||
|
||||
trackGenre := func(artist, album, filename, genreName string) error {
|
||||
return m.DB().
|
||||
Where("albums.left_path=? AND albums.right_path=? AND tracks.filename=? AND genres.name=?", artist, album, filename, genreName).
|
||||
Joins("JOIN tracks ON tracks.id=track_genres.track_id").
|
||||
Joins("JOIN genres ON genres.id=track_genres.genre_id").
|
||||
Joins("JOIN albums ON albums.id=tracks.album_id").
|
||||
Find(&db.TrackGenre{}).
|
||||
Error
|
||||
}
|
||||
isTrackGenre := func(artist, album, filename, genreName string) {
|
||||
is.Helper()
|
||||
is.NoErr(trackGenre(artist, album, filename, genreName))
|
||||
}
|
||||
isTrackGenreMissing := func(artist, album, filename, genreName string) {
|
||||
is.Helper()
|
||||
is.Equal(trackGenre(artist, album, filename, genreName), gorm.ErrRecordNotFound)
|
||||
}
|
||||
|
||||
genre := func(genre string) error {
|
||||
return m.DB().Where("name=?", genre).Find(&db.Genre{}).Error
|
||||
}
|
||||
isGenre := func(genreName string) {
|
||||
is.Helper()
|
||||
is.NoErr(genre(genreName))
|
||||
}
|
||||
isGenreMissing := func(genreName string) {
|
||||
is.Helper()
|
||||
is.Equal(genre(genreName), gorm.ErrRecordNotFound)
|
||||
}
|
||||
|
||||
m.AddItems()
|
||||
m.SetTags("artist-0/album-0/track-0.flac", func(tags *mockfs.Tags) { tags.RawGenre = "genre-a;genre-b" })
|
||||
m.SetTags("artist-0/album-0/track-1.flac", func(tags *mockfs.Tags) { tags.RawGenre = "genre-c;genre-d" })
|
||||
m.SetTags("artist-1/album-2/track-0.flac", func(tags *mockfs.Tags) { tags.RawGenre = "genre-e;genre-f" })
|
||||
m.SetTags("artist-1/album-2/track-1.flac", func(tags *mockfs.Tags) { tags.RawGenre = "genre-g;genre-h" })
|
||||
m.ScanAndClean()
|
||||
|
||||
isGenre("genre-a") // genre exists
|
||||
isGenre("genre-b") // genre exists
|
||||
isGenre("genre-c") // genre exists
|
||||
isGenre("genre-d") // genre exists
|
||||
|
||||
isTrackGenre("artist-0/", "album-0", "track-0.flac", "genre-a") // track genre exists
|
||||
isTrackGenre("artist-0/", "album-0", "track-0.flac", "genre-b") // track genre exists
|
||||
isTrackGenre("artist-0/", "album-0", "track-1.flac", "genre-c") // track genre exists
|
||||
isTrackGenre("artist-0/", "album-0", "track-1.flac", "genre-d") // track genre exists
|
||||
isTrackGenre("artist-1/", "album-2", "track-0.flac", "genre-e") // track genre exists
|
||||
isTrackGenre("artist-1/", "album-2", "track-0.flac", "genre-f") // track genre exists
|
||||
isTrackGenre("artist-1/", "album-2", "track-1.flac", "genre-g") // track genre exists
|
||||
isTrackGenre("artist-1/", "album-2", "track-1.flac", "genre-h") // track genre exists
|
||||
|
||||
isAlbumGenre("artist-0/", "album-0", "genre-a") // album genre exists
|
||||
isAlbumGenre("artist-0/", "album-0", "genre-b") // album genre exists
|
||||
|
||||
m.SetTags("artist-0/album-0/track-0.flac", func(tags *mockfs.Tags) { tags.RawGenre = "genre-aa;genre-bb" })
|
||||
m.ScanAndClean()
|
||||
|
||||
isTrackGenre("artist-0/", "album-0", "track-0.flac", "genre-aa") // updated track genre exists
|
||||
isTrackGenre("artist-0/", "album-0", "track-0.flac", "genre-bb") // updated track genre exists
|
||||
isTrackGenreMissing("artist-0/", "album-0", "track-0.flac", "genre-a") // old track genre missing
|
||||
isTrackGenreMissing("artist-0/", "album-0", "track-0.flac", "genre-b") // old track genre missing
|
||||
|
||||
isAlbumGenreMissing("artist-0/", "album-0", "genre-a") // old album genre missing
|
||||
isAlbumGenreMissing("artist-0/", "album-0", "genre-b") // old album genre missing
|
||||
|
||||
isGenreMissing("genre-a") // old genre missing
|
||||
isGenreMissing("genre-b") // old genre missing
|
||||
}
|
||||
|
||||
func TestMultiFolders(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.New(t)
|
||||
m := mockfs.NewWithDirs(t, []string{"m-1", "m-2", "m-3"})
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddItemsPrefix("m-1")
|
||||
m.AddItemsPrefix("m-2")
|
||||
m.AddItemsPrefix("m-3")
|
||||
m.ScanAndClean()
|
||||
|
||||
var rootDirs []*db.Album
|
||||
is.NoErr(m.DB().Where("parent_id IS NULL").Find(&rootDirs).Error)
|
||||
is.Equal(len(rootDirs), 3)
|
||||
for i, r := range rootDirs {
|
||||
is.Equal(r.RootDir, filepath.Join(m.TmpDir(), fmt.Sprintf("m-%d", i+1)))
|
||||
is.Equal(r.ParentID, 0)
|
||||
is.Equal(r.LeftPath, "")
|
||||
is.Equal(r.RightPath, ".")
|
||||
}
|
||||
|
||||
m.AddCover("m-3/artist-0/album-0/cover.jpg")
|
||||
m.ScanAndClean()
|
||||
m.LogItems()
|
||||
|
||||
checkCover := func(root string, q string) {
|
||||
is.Helper()
|
||||
is.NoErr(m.DB().Where(q, filepath.Join(m.TmpDir(), root)).Find(&db.Album{}).Error)
|
||||
}
|
||||
|
||||
checkCover("m-1", "root_dir=? AND cover IS NULL") // mf 1 no cover
|
||||
checkCover("m-2", "root_dir=? AND cover IS NULL") // mf 2 no cover
|
||||
checkCover("m-3", "root_dir=? AND cover='cover.jpg'") // mf 3 has cover
|
||||
}
|
||||
|
||||
func TestNewAlbumForExistingArtist(t *testing.T) {
|
||||
t.Parallel()
|
||||
is := is.New(t)
|
||||
m := mockfs.New(t)
|
||||
defer m.CleanUp()
|
||||
|
||||
m.AddItems()
|
||||
m.ScanAndClean()
|
||||
|
||||
m.LogAlbums()
|
||||
m.LogArtists()
|
||||
|
||||
var artist db.Artist
|
||||
is.NoErr(m.DB().Where("name=?", "artist-2").Find(&artist).Error) // find orig artist
|
||||
is.True(artist.ID > 0)
|
||||
|
||||
for tr := 0; tr < 3; tr++ {
|
||||
m.AddTrack(fmt.Sprintf("artist-2/new-album/track-%d.mp3", tr))
|
||||
m.SetTags(fmt.Sprintf("artist-2/new-album/track-%d.mp3", tr), func(tags *mockfs.Tags) {
|
||||
tags.RawArtist = "artist-2"
|
||||
tags.RawAlbumArtist = "artist-2"
|
||||
tags.RawAlbum = "new-album"
|
||||
tags.RawTitle = fmt.Sprintf("title-%d", tr)
|
||||
})
|
||||
}
|
||||
|
||||
var updated db.Artist
|
||||
is.NoErr(m.DB().Where("name=?", "artist-2").Find(&updated).Error) // find updated artist
|
||||
is.Equal(artist.ID, updated.ID) // find updated artist
|
||||
|
||||
var all []*db.Artist
|
||||
is.NoErr(m.DB().Find(&all).Error) // still only 3?
|
||||
is.Equal(len(all), 3) // still only 3?
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user