scan_test now checks title and face groups explicitly instead of only lengths. (#1180)

This commit is contained in:
Googol Lee
2025-03-11 20:20:07 +01:00
committed by GitHub
parent d1636fb7ea
commit e4d5345d56
3 changed files with 174 additions and 157 deletions

View File

@@ -117,132 +117,13 @@ linters:
#disable-all: true
# Enable specific linter
# https://golangci-lint.run/usage/linters/#enabled-by-default
enable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- containedctx
- contextcheck
- copyloopvar
- cyclop
- decorder
- depguard
- dogsled
- dupl
- dupword
- durationcheck
- err113
- errcheck
- errchkjson
- errname
- errorlint
- execinquery
- exhaustive
- exhaustruct
- exportloopref
- fatcontext
- forbidigo
- forcetypeassert
- funlen
- gci
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gochecksumtype
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- gofmt
- gofumpt
- goheader
- goimports
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosimple
- gosmopolitan
- govet
- grouper
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- mirror
- misspell
- mnd
- musttag
- nakedret
- nestif
- nilerr
- nilnil
- nlreturn
- noctx
- nolintlint
- nonamedreturns
- nosprintfhostport
- paralleltest
- perfsprint
- prealloc
- predeclared
- promlinter
- protogetter
- reassign
- revive
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- stylecheck
- tagalign
- tagliatelle
- tenv
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- typecheck
- unconvert
- unparam
- unused
- usestdlibvars
- varnamelen
- wastedassign
- whitespace
- wrapcheck
- wsl
- zerologlint
#enable: {}
# Enable all available linters.
# Default: false
#enable-all: true
# Disable specific linter
# https://golangci-lint.run/usage/linters/#disabled-by-default
disable:
- deadcode # Deprecated
- exhaustivestruct # Deprecated
- golint # Deprecated
- ifshort # Deprecated
- interfacer # Deprecated
- maligned # Deprecated
- gomnd # Deprecated
- nosnakecase # Deprecated
- scopelint # Deprecated
- structcheck # Deprecated
- varcheck # Deprecated
#disable: {}
# Enable presets.
# https://golangci-lint.run/usage/linters
# Default: []

View File

@@ -9,6 +9,7 @@ require (
github.com/buckket/go-blurhash v1.1.0
github.com/disintegration/imaging v1.6.2
github.com/go-sql-driver/mysql v1.9.0
github.com/google/go-cmp v0.6.0
github.com/gorilla/handlers v1.5.2
github.com/gorilla/mux v1.8.1
github.com/gorilla/websocket v1.5.3

View File

@@ -1,15 +1,20 @@
package scanner_test
import (
"context"
"fmt"
"os"
"path/filepath"
"slices"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/photoview/photoview/api/graphql/models"
"github.com/photoview/photoview/api/scanner/face_detection"
"github.com/photoview/photoview/api/test_utils"
scanner_utils "github.com/photoview/photoview/api/test_utils/scanner"
"github.com/stretchr/testify/assert"
)
func TestMain(m *testing.M) {
@@ -22,8 +27,8 @@ func TestFullScan(t *testing.T) {
pass := "1234"
user, err := models.RegisterUser(db, "test_user", &pass, true)
if !assert.NoError(t, err) {
return
if err != nil {
t.Fatal("register user error:", err)
}
rootAlbum := models.Album{
@@ -31,52 +36,182 @@ func TestFullScan(t *testing.T) {
Path: "./test_media/library",
}
if !assert.NoError(t, db.Save(&rootAlbum).Error) {
return
wantImages := []string{
"buttercup_close_summer_yellow.jpg",
"lilac_lilac_bush_lilac.jpg",
"mount_merapi_volcano_indonesia.jpg",
"boy1.jpg",
"boy2.jpg",
"girl_black_hair2.jpg",
"girl_blond1.jpg",
"girl_blond2.jpg",
"girl_blond3.jpg",
}
wantFaceGroups := [][]string{
{"boy1.jpg", "boy2.jpg"},
{"girl_black_hair2.jpg"},
{"girl_blond1.jpg", "girl_blond2.jpg", "girl_blond3.jpg"},
}
err = db.Model(user).Association("Albums").Append(&rootAlbum)
if !assert.NoError(t, err) {
return
for i := range wantFaceGroups {
slices.Sort(wantFaceGroups[i])
}
slices.SortFunc(wantFaceGroups, func(a, b []string) int {
return strings.Compare(fmt.Sprint(a), fmt.Sprint(b))
})
if err := db.Save(&rootAlbum).Error; err != nil {
t.Fatal("create root album error:", err)
}
if !assert.NoError(t, face_detection.InitializeFaceDetector(db)) {
return
if err := db.Model(user).Association("Albums").Append(&rootAlbum); err != nil {
t.Fatal("bind root album error:", err)
}
if err := face_detection.InitializeFaceDetector(db); err != nil {
t.Fatal("initalize face detector error:", err)
}
scanner_utils.RunScannerOnUser(t, db, user)
t.Run("CheckMedia", func(t *testing.T) {
var allMedia []*models.Media
if !assert.NoError(t, db.Find(&allMedia).Error) {
return
if err := db.Find(&allMedia).Error; err != nil {
t.Fatal("get all media error:", err)
}
assert.Equal(t, 9, len(allMedia))
want := slices.Clone(wantImages)
slices.Sort(want)
got := make([]string, len(allMedia))
for i, media := range allMedia {
got[i] = media.Title
}
slices.Sort(got)
if diff := cmp.Diff(got, want); diff != "" {
t.Errorf("all media diff:\n%s", diff)
}
})
t.Run("CheckMediaURL", func(t *testing.T) {
var allMediaURL []*models.MediaURL
if !assert.NoError(t, db.Find(&allMediaURL).Error) {
if err := db.Find(&allMediaURL).Error; err != nil {
t.Fatal("get all media url error:", err)
}
if got, want := len(allMediaURL), 18; got != want {
t.Errorf("got = %d, want: %v", got, want)
}
want := slices.Clone(wantImages)
wantThumbs := slices.Clone(wantImages)
for _, thumb := range wantThumbs {
want = append(want, "thumbnail_"+thumb)
}
slices.Sort(want)
got := make([]string, len(allMediaURL))
for i, media := range allMediaURL {
got[i] = media.MediaName
}
slices.Sort(got)
if diff := cmp.Diff(got, want, cmp.Comparer(equalNameWithoutSuffix)); diff != "" {
t.Errorf("all media diff:\n%s", diff)
}
})
t.Run("CheckFaceGroup", func(t *testing.T) {
ctx, done := context.WithTimeout(t.Context(), time.Second*5)
defer done()
waitFor(ctx, t, time.Second/2, func() bool {
var allFaceGroups []*models.FaceGroup
if err := db.Find(&allFaceGroups).Error; err != nil {
t.Fatal("get face groups error:", err)
return false
}
return len(allFaceGroups) == len(wantFaceGroups)
})
})
t.Run("CheckFaces", func(t *testing.T) {
var allImageFaces []*models.ImageFace
if err := db.Find(&allImageFaces).Error; err != nil {
t.Fatal("get face images error:", err)
}
for _, face := range allImageFaces {
if err := face.FillMedia(db); err != nil {
t.Fatalf("fill media for face %v error: %v", face, err)
}
}
got := groupMediaWithFaces(allImageFaces)
if diff := cmp.Diff(got, wantFaceGroups); diff != "" {
t.Errorf("all media diff:\n%s", diff)
}
})
}
func equalNameWithoutSuffix(a, b string) bool {
extA := filepath.Ext(a)
mainA := strings.TrimRight(a, extA)
extB := filepath.Ext(b)
mainB := strings.TrimRight(b, extB)
// ext names are not same
if extA != extB {
return false
}
// a is not part of b and b is not part of a
if strings.Index(mainA, mainB) < 0 && strings.Index(mainB, mainA) < 0 {
return false
}
return true
}
func waitFor(ctx context.Context, t *testing.T, interval time.Duration, checkFn func() bool) {
t.Helper()
ticker := time.NewTicker(interval)
for {
select {
case <-ctx.Done():
t.Fatal("check timeout")
return
case <-ticker.C:
}
if checkFn() {
return
}
assert.Equal(t, 18, len(allMediaURL))
// Verify that faces was recognized
assert.Eventually(t, func() bool {
var allFaceGroups []*models.FaceGroup
if !assert.NoError(t, db.Find(&allFaceGroups).Error) {
return false
}
}
return len(allFaceGroups) == 3
}, time.Second*5, time.Millisecond*500)
func groupMediaWithFaces(medias []*models.ImageFace) [][]string {
grouped := make(map[int][]string)
assert.Eventually(t, func() bool {
var allImageFaces []*models.ImageFace
if !assert.NoError(t, db.Find(&allImageFaces).Error) {
return false
for _, media := range medias {
group := grouped[media.FaceGroupID]
group = append(group, media.Media.Title)
grouped[media.FaceGroupID] = group
}
return len(allImageFaces) == 6
}, time.Second*5, time.Millisecond*500)
ret := make([][]string, 0, len(grouped))
for _, medias := range grouped {
slices.Sort(medias)
ret = append(ret, medias)
}
slices.SortFunc(ret, func(a, b []string) int {
return strings.Compare(fmt.Sprint(a), fmt.Sprint(b))
})
return ret
}