mirror of
https://github.com/zarzet/SpotiFLAC-Mobile.git
synced 2026-08-26 21:02:28 +02:00
223 lines
6.8 KiB
Go
223 lines
6.8 KiB
Go
package gobackend
|
|
|
|
import (
|
|
"bufio"
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestLibraryScanFullIncrementalAndMetadataFallbacks(t *testing.T) {
|
|
dir := t.TempDir()
|
|
albumDir := filepath.Join(dir, "Album")
|
|
if err := os.MkdirAll(albumDir, 0755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mp3Path := filepath.Join(albumDir, "Artist - Song.mp3")
|
|
if err := os.WriteFile(mp3Path, []byte("not really mp3"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
numberedPath := filepath.Join(albumDir, "01 - Intro.ogg")
|
|
if err := os.WriteFile(numberedPath, []byte("not really ogg"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
apePath := filepath.Join(albumDir, "tagged.ape")
|
|
if err := os.WriteFile(apePath, []byte("audio"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := WriteAPETags(apePath, &APETag{Items: AudioMetadataToAPEItems(&AudioMetadata{
|
|
Title: "Tagged",
|
|
Artist: "APE Artist",
|
|
Album: "APE Album",
|
|
TrackNumber: 2,
|
|
TotalTracks: 3,
|
|
Date: "2026",
|
|
Genre: "Pop",
|
|
Composer: "Composer",
|
|
})}); err != nil {
|
|
t.Fatalf("write ape tags: %v", err)
|
|
}
|
|
cuePath, _ := writeExportCueFixture(t, albumDir)
|
|
if err := os.WriteFile(filepath.Join(albumDir, "ignored.txt"), []byte("ignore"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
legacyPartialPath := filepath.Join(albumDir, "Artist - Song.partial.flac")
|
|
if err := os.WriteFile(legacyPartialPath, []byte("partial flac"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
newPartialPath := filepath.Join(albumDir, "Artist - Song.flac.partial")
|
|
if err := os.WriteFile(newPartialPath, []byte("partial flac"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
files, err := collectLibraryAudioFiles(dir, make(chan struct{}))
|
|
if err != nil {
|
|
t.Fatalf("collectLibraryAudioFiles: %v", err)
|
|
}
|
|
if len(files) < 4 {
|
|
t.Fatalf("files = %#v", files)
|
|
}
|
|
for _, file := range files {
|
|
if file.path == legacyPartialPath || file.path == newPartialPath {
|
|
t.Fatalf("staging file should be ignored: %#v", files)
|
|
}
|
|
}
|
|
cancelCh := make(chan struct{})
|
|
close(cancelCh)
|
|
if _, err := collectLibraryAudioFiles(dir, cancelCh); err == nil {
|
|
t.Fatal("expected cancelled collect")
|
|
}
|
|
|
|
jsonText, err := ScanLibraryFolder(dir)
|
|
if err != nil {
|
|
t.Fatalf("ScanLibraryFolder: %v", err)
|
|
}
|
|
var results []LibraryScanResult
|
|
if err := json.Unmarshal([]byte(jsonText), &results); err != nil {
|
|
t.Fatalf("decode scan results: %v", err)
|
|
}
|
|
if len(results) < 4 {
|
|
t.Fatalf("scan results = %#v", results)
|
|
}
|
|
foundTagged := false
|
|
for _, result := range results {
|
|
if result.FilePath == apePath {
|
|
foundTagged = result.TrackName == "Tagged" && result.ArtistName == "APE Artist"
|
|
}
|
|
}
|
|
if !foundTagged {
|
|
t.Fatalf("tagged APE not found in %#v", results)
|
|
}
|
|
|
|
ndjsonPath := filepath.Join(t.TempDir(), "library.ndjson")
|
|
streamedCount, err := ScanLibraryFolderToNDJSONFile(dir, ndjsonPath)
|
|
if err != nil {
|
|
t.Fatalf("ScanLibraryFolderToNDJSONFile: %v", err)
|
|
}
|
|
ndjsonFile, err := os.Open(ndjsonPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer ndjsonFile.Close()
|
|
decodedCount := 0
|
|
scanner := bufio.NewScanner(ndjsonFile)
|
|
for scanner.Scan() {
|
|
var result LibraryScanResult
|
|
if err := json.Unmarshal(scanner.Bytes(), &result); err != nil {
|
|
t.Fatalf("decode NDJSON row: %v", err)
|
|
}
|
|
if result.FilePath == "" {
|
|
t.Fatal("NDJSON row has no file path")
|
|
}
|
|
decodedCount++
|
|
}
|
|
if err := scanner.Err(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if decodedCount != streamedCount || decodedCount != len(results) {
|
|
t.Fatalf("NDJSON counts = decoded:%d streamed:%d array:%d", decodedCount, streamedCount, len(results))
|
|
}
|
|
if progress := GetLibraryScanProgress(); !strings.Contains(progress, `"IsComplete":true`) && !strings.Contains(progress, `"is_complete":true`) {
|
|
t.Fatalf("progress = %s", progress)
|
|
}
|
|
|
|
metaJSON, err := ReadAudioMetadataWithDisplayName(mp3Path, "Display Artist - Display Song.mp3")
|
|
if err != nil {
|
|
t.Fatalf("ReadAudioMetadataWithDisplayName: %v", err)
|
|
}
|
|
if !strings.Contains(metaJSON, "Display Song") {
|
|
t.Fatalf("metadata json = %s", metaJSON)
|
|
}
|
|
noExtPath := filepath.Join(albumDir, "noext")
|
|
if err := os.WriteFile(noExtPath, []byte("audio"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
noExtJSON, err := ReadAudioMetadataWithDisplayNameAndCoverCacheKey(noExtPath, "Artist - No Ext.mp3", "cache-key")
|
|
if err != nil {
|
|
t.Fatalf("ReadAudioMetadataWithDisplayNameAndCoverCacheKey: %v", err)
|
|
}
|
|
if !strings.Contains(noExtJSON, "No Ext") {
|
|
t.Fatalf("no ext metadata = %s", noExtJSON)
|
|
}
|
|
|
|
existing := map[string]int64{}
|
|
for _, file := range files {
|
|
existing[file.path] = file.modTime
|
|
}
|
|
if info, err := os.Stat(cuePath); err == nil {
|
|
existing[cuePath+"#track01"] = info.ModTime().UnixMilli()
|
|
}
|
|
incJSON, err := scanLibraryFolderIncrementalWithExistingFiles(dir, existing)
|
|
if err != nil {
|
|
t.Fatalf("incremental existing: %v", err)
|
|
}
|
|
var inc IncrementalScanResult
|
|
if err := json.Unmarshal([]byte(incJSON), &inc); err != nil {
|
|
t.Fatalf("decode incremental: %v", err)
|
|
}
|
|
if inc.SkippedCount == 0 {
|
|
t.Fatalf("incremental = %#v", inc)
|
|
}
|
|
if _, err := ScanLibraryFolderIncremental("", "{}"); err == nil {
|
|
t.Fatal("expected empty incremental folder error")
|
|
}
|
|
if incJSON, err := ScanLibraryFolderIncremental(dir, `not-json`); err != nil || incJSON == "" {
|
|
t.Fatalf("incremental invalid existing JSON = %q/%v", incJSON, err)
|
|
}
|
|
|
|
snapshot := filepath.Join(dir, "snapshot.txt")
|
|
if err := os.WriteFile(snapshot, []byte("bad\n123\t"+mp3Path+"\nnotint\tpath\n999\t"+filepath.Join(dir, "deleted.mp3")+"\n"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
fromSnapshot, err := ScanLibraryFolderIncrementalFromSnapshot(dir, snapshot)
|
|
if err != nil {
|
|
t.Fatalf("snapshot incremental: %v", err)
|
|
}
|
|
if !strings.Contains(fromSnapshot, "deleted.mp3") {
|
|
t.Fatalf("snapshot result = %s", fromSnapshot)
|
|
}
|
|
if _, err := ScanLibraryFolder(""); err == nil {
|
|
t.Fatal("expected empty folder scan error")
|
|
}
|
|
fileInsteadOfFolder := filepath.Join(dir, "file.flac")
|
|
if err := os.WriteFile(fileInsteadOfFolder, []byte("audio"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := ScanLibraryFolder(fileInsteadOfFolder); err == nil {
|
|
t.Fatal("expected not folder error")
|
|
}
|
|
CancelLibraryScan()
|
|
SetLibraryCoverCacheDir("")
|
|
}
|
|
|
|
func TestScanLibraryFolderPreservesFileOrderWithParallelWorkers(t *testing.T) {
|
|
dir := t.TempDir()
|
|
for i := 0; i < 20; i++ {
|
|
name := fmt.Sprintf("%02d - Track.mp3", i)
|
|
if err := os.WriteFile(filepath.Join(dir, name), []byte("not really mp3"), 0600); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
jsonText, err := ScanLibraryFolder(dir)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var results []LibraryScanResult
|
|
if err := json.Unmarshal([]byte(jsonText), &results); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(results) != 20 {
|
|
t.Fatalf("results = %d", len(results))
|
|
}
|
|
for i, result := range results {
|
|
expected := filepath.Join(dir, fmt.Sprintf("%02d - Track.mp3", i))
|
|
if result.FilePath != expected {
|
|
t.Fatalf("result %d path = %q, want %q", i, result.FilePath, expected)
|
|
}
|
|
}
|
|
}
|