A coding agent could develop this repo's Go packages and could not develop the application: every path to running YellowJacket ended in a blocking GTK window, so 265 bound methods, 46 events, 33 component directories and 13 stores had exactly one form of verification available — `tsc --noEmit`. The unlock is that `wails dev`'s dev server on :34115 serves the real frontend with the real generated bindings against the same Go backend a desktop window attaches to, so a plain Chromium under Xvfb gets a fully functional app. Four test tiers now exist, cheapest first: - `make ui-test` — 313 Vitest tests in a real browser in ~2 s, no app, no backend, no display. Works because `frontend/wailsjs/` is a pure passthrough to `window.go`/`window.runtime`, so faking just those two globals runs the real bindings and the real store code. - `make test` — services in-process, asserting on the payload the frontend would receive, via a new `events.Emit` wrapper. - `make dev-headless` + `playwright-cli` — the real app, driven interactively, with an event bridge on `window.__yjEvents` and a dev-only control surface at `/__test/`. - `make e2e` — 19 of those flows frozen as Playwright specs. `events.Emit(ctx, …)` replaces all 35 direct `runtime.EventsEmit` call sites: wails' `getEvents` `log.Fatalf`s on any context without its runtime, so those paths could not run under test and a background worker could take the app down. Four packages had each hand-rolled the same guard; nine more guarded on `ctx != nil`, which does not help. `TestNoDirectRuntimeEmits` fails the build on a new one. Fixtures are generated, not committed (`make testdata`), and seeds are built by *running the app* — never by hand-writing config and DB rows, which would be a second description of a valid YJ_HOME. `.gitea/workflows/ci.yml` is the first workflow here that tests anything; the other three only package, so `gitea_ci` reported only packaging jobs and misled anyone asking whether a push was healthy. Both jobs were prototyped to green in a bare ubuntu:24.04 container before the YAML was written, which immediately caught `make lint` linting three configurations that nothing builds: all three passes omitted `webkit2_41`, so wails resolved webkit2gtk-4.0 — which Arch still ships and Ubuntu 24.04 dropped. Operational instructions live in `.pi/skills/yellowjacket-dev/`, measured discoveries in `.planning/NOTES.md`, and architecture in `CLAUDE.md` — split by tense, not by topic, because a topical split gives every new fact two plausible homes. `make skill-check` fails a commit if the skill cites a make target that does not exist.
498 lines
16 KiB
Go
498 lines
16 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"time"
|
|
|
|
"yellowjacket/backend/tagwriter"
|
|
)
|
|
|
|
// Case names group fixtures by the application behaviour they exist to
|
|
// exercise. Tests select fixtures by case rather than by path, so a
|
|
// path can be renamed without breaking them.
|
|
const (
|
|
caseCoverDedup = "cover-dedup"
|
|
caseMultiDisc = "multi-disc"
|
|
caseVariousArtist = "various-artists"
|
|
caseFLACAlbum = "flac-album"
|
|
caseOGGAlbum = "ogg-album"
|
|
caseWAVTracks = "wav-tracks"
|
|
casePartialTags = "partial-tags"
|
|
caseUnicode = "unicode"
|
|
caseDuplicates = "duplicates"
|
|
caseEdgeLengths = "edge-lengths"
|
|
caseBroken = "broken"
|
|
)
|
|
|
|
var errUnknownFormat = errors.New("gentestdata: unknown audio format")
|
|
|
|
// tags mirrors the subset of tagwriter fields a fixture can set. A
|
|
// struct rather than a bare map so the spec table stays readable and
|
|
// the manifest can record exactly what was written.
|
|
type tags struct {
|
|
Title string
|
|
Artist string
|
|
Album string
|
|
AlbumArtist string
|
|
Genre string
|
|
Composer string
|
|
Year int
|
|
TrackNumber int
|
|
DiscNumber int
|
|
}
|
|
|
|
// changes converts a fixture's tags into a tagwriter diff map,
|
|
// omitting zero values so "no tag at all" is expressible.
|
|
func (t tags) changes() tagwriter.TagChanges {
|
|
c := tagwriter.TagChanges{}
|
|
|
|
set := func(field, value string) {
|
|
if value != "" {
|
|
c[field] = value
|
|
}
|
|
}
|
|
|
|
set(tagwriter.FieldTitle, t.Title)
|
|
set(tagwriter.FieldArtist, t.Artist)
|
|
set(tagwriter.FieldAlbum, t.Album)
|
|
set(tagwriter.FieldAlbumArtist, t.AlbumArtist)
|
|
set(tagwriter.FieldGenre, t.Genre)
|
|
set(tagwriter.FieldComposer, t.Composer)
|
|
|
|
if t.Year != 0 {
|
|
c[tagwriter.FieldYear] = t.Year
|
|
}
|
|
|
|
if t.TrackNumber != 0 {
|
|
c[tagwriter.FieldTrackNumber] = t.TrackNumber
|
|
}
|
|
|
|
if t.DiscNumber != 0 {
|
|
c[tagwriter.FieldDiscNumber] = t.DiscNumber
|
|
}
|
|
|
|
return c
|
|
}
|
|
|
|
// fixture is one generated audio file.
|
|
type fixture struct {
|
|
// Rel is the path relative to the library root, with '/'
|
|
// separators regardless of platform.
|
|
Rel string
|
|
// Case is the behaviour group this fixture belongs to.
|
|
Case string
|
|
// Format decides both the container and the tag writer used.
|
|
Format tagwriter.AudioFormat
|
|
// Duration is the nominal length of the synthesized tone.
|
|
Duration time.Duration
|
|
// FreqHz identifies the track audibly and in a decoded sample.
|
|
FreqHz float64
|
|
// Cover is a cover-art key; fixtures sharing a key get
|
|
// byte-identical images, which is what dedup must collapse.
|
|
Cover string
|
|
// Tags is what gets written after encoding.
|
|
Tags tags
|
|
}
|
|
|
|
// Durations kept short deliberately; one long track exists so the
|
|
// progress bar and seeking have something to work against.
|
|
const (
|
|
durShort = 2 * time.Second
|
|
durNormal = 4 * time.Second
|
|
durMedium = 6 * time.Second
|
|
durLong = 90 * time.Second
|
|
)
|
|
|
|
// longTitle is long enough to force truncation in every list view.
|
|
const longTitle = "An Exhaustively Overlong Track Title That Exists " +
|
|
"Solely To Find Out Whether The Track List Truncates Or Overflows"
|
|
|
|
const longArtist = "The Orchestra Of Very Considerable And " +
|
|
"Deliberately Unreasonable Length"
|
|
|
|
// duplicateTags is shared by the deliberate duplicate pair so the
|
|
// duplicate-tracks dialog has an unambiguous match to find.
|
|
var duplicateTags = tags{
|
|
Title: "Tideline",
|
|
Artist: "Aurora Fields",
|
|
Album: "Glass Harbour",
|
|
AlbumArtist: "Aurora Fields",
|
|
Genre: "Dream Pop",
|
|
Year: 2019,
|
|
TrackNumber: 2,
|
|
}
|
|
|
|
// libraryFixtures is the full contents of the clean fixture library.
|
|
//
|
|
// Everything here is scannable audio: a seeded sandbox built from this
|
|
// root must produce a stable track count, so deliberately broken files
|
|
// live in a separate root (see brokenFiles).
|
|
//
|
|
//nolint:gochecknoglobals // the fixture spec is the point of this cmd.
|
|
var libraryFixtures = []fixture{
|
|
// 1. A plain album whose four tracks carry the same embedded
|
|
// cover: the dedup path should store one blob, not four.
|
|
{
|
|
Rel: "Aurora Fields/Glass Harbour/01 Salt Air.mp3",
|
|
Case: caseCoverDedup, Format: tagwriter.FormatMP3,
|
|
Duration: durNormal, FreqHz: 220, Cover: "glass-harbour",
|
|
Tags: tags{
|
|
Title: "Salt Air", Artist: "Aurora Fields",
|
|
Album: "Glass Harbour", AlbumArtist: "Aurora Fields",
|
|
Genre: "Dream Pop", Year: 2019, TrackNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Aurora Fields/Glass Harbour/02 Tideline.mp3",
|
|
Case: caseCoverDedup, Format: tagwriter.FormatMP3,
|
|
Duration: durMedium, FreqHz: 247, Cover: "glass-harbour",
|
|
Tags: duplicateTags,
|
|
},
|
|
{
|
|
Rel: "Aurora Fields/Glass Harbour/03 Harbour Lights.mp3",
|
|
Case: caseCoverDedup, Format: tagwriter.FormatMP3,
|
|
Duration: durNormal, FreqHz: 262, Cover: "glass-harbour",
|
|
Tags: tags{
|
|
Title: "Harbour Lights", Artist: "Aurora Fields",
|
|
Album: "Glass Harbour", AlbumArtist: "Aurora Fields",
|
|
Genre: "Dream Pop", Year: 2019, TrackNumber: 3,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Aurora Fields/Glass Harbour/04 Low Water.mp3",
|
|
Case: caseCoverDedup, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 294, Cover: "glass-harbour",
|
|
Tags: tags{
|
|
Title: "Low Water", Artist: "Aurora Fields",
|
|
Album: "Glass Harbour", AlbumArtist: "Aurora Fields",
|
|
Genre: "Dream Pop", Year: 2019, TrackNumber: 4,
|
|
},
|
|
},
|
|
|
|
// 2. Multi-disc, with the disc split reflected both in the
|
|
// directory layout and in the disc number tag. The
|
|
// semicolon-separated genre also covers metadata.ParseGenres.
|
|
{
|
|
Rel: "Aurora Fields/Long Way Round/Disc 1/01 Departure.mp3",
|
|
Case: caseMultiDisc, Format: tagwriter.FormatMP3,
|
|
Duration: durNormal, FreqHz: 330, Cover: "long-way-round",
|
|
Tags: tags{
|
|
Title: "Departure", Artist: "Aurora Fields",
|
|
Album: "Long Way Round", AlbumArtist: "Aurora Fields",
|
|
Genre: "Dream Pop; Ambient", Year: 2021,
|
|
TrackNumber: 1, DiscNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Aurora Fields/Long Way Round/Disc 1/02 Waystation.mp3",
|
|
Case: caseMultiDisc, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 349, Cover: "long-way-round",
|
|
Tags: tags{
|
|
Title: "Waystation", Artist: "Aurora Fields",
|
|
Album: "Long Way Round", AlbumArtist: "Aurora Fields",
|
|
Genre: "Dream Pop; Ambient", Year: 2021,
|
|
TrackNumber: 2, DiscNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Aurora Fields/Long Way Round/Disc 2/01 Return.mp3",
|
|
Case: caseMultiDisc, Format: tagwriter.FormatMP3,
|
|
Duration: durNormal, FreqHz: 392, Cover: "long-way-round",
|
|
Tags: tags{
|
|
Title: "Return", Artist: "Aurora Fields",
|
|
Album: "Long Way Round", AlbumArtist: "Aurora Fields",
|
|
Genre: "Ambient", Year: 2021,
|
|
TrackNumber: 1, DiscNumber: 2,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Aurora Fields/Long Way Round/Disc 2/02 Homing.mp3",
|
|
Case: caseMultiDisc, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 440, Cover: "long-way-round",
|
|
Tags: tags{
|
|
Title: "Homing", Artist: "Aurora Fields",
|
|
Album: "Long Way Round", AlbumArtist: "Aurora Fields",
|
|
Genre: "Ambient", Year: 2021,
|
|
TrackNumber: 2, DiscNumber: 2,
|
|
},
|
|
},
|
|
|
|
// 3. Compilation: per-track artists under a Various Artists
|
|
// album artist, which groups differently from everything else.
|
|
{
|
|
Rel: "Various Artists/Night Shift Vol. 1/01 Blue Hour.mp3",
|
|
Case: caseVariousArtist, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 466, Cover: "night-shift",
|
|
Tags: tags{
|
|
Title: "Blue Hour", Artist: "Kilowatt",
|
|
Album: "Night Shift Vol. 1", AlbumArtist: "Various Artists",
|
|
Genre: "Electronic", Year: 2003, TrackNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Various Artists/Night Shift Vol. 1/02 Concrete Sun.mp3",
|
|
Case: caseVariousArtist, Format: tagwriter.FormatMP3,
|
|
Duration: durNormal, FreqHz: 494, Cover: "night-shift",
|
|
Tags: tags{
|
|
Title: "Concrete Sun", Artist: "Marisol Vega",
|
|
Album: "Night Shift Vol. 1", AlbumArtist: "Various Artists",
|
|
Genre: "Electronic", Year: 2003, TrackNumber: 2,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Various Artists/Night Shift Vol. 1/03 Dry Season.mp3",
|
|
Case: caseVariousArtist, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 523, Cover: "night-shift",
|
|
Tags: tags{
|
|
Title: "Dry Season", Artist: "The Hollow Coast",
|
|
Album: "Night Shift Vol. 1", AlbumArtist: "Various Artists",
|
|
Genre: "Jazz", Year: 2003, TrackNumber: 3,
|
|
},
|
|
},
|
|
|
|
// 4. FLAC, whose cover art rides in a METADATA_BLOCK_PICTURE and
|
|
// whose reader/writer share no code with the ID3 path.
|
|
{
|
|
Rel: "Pale Circuit/Static Bloom/01 Static Bloom.flac",
|
|
Case: caseFLACAlbum, Format: tagwriter.FormatFLAC,
|
|
Duration: durShort, FreqHz: 262, Cover: "static-bloom",
|
|
Tags: tags{
|
|
Title: "Static Bloom", Artist: "Pale Circuit",
|
|
Album: "Static Bloom", AlbumArtist: "Pale Circuit",
|
|
Genre: "Electronic", Year: 1998, TrackNumber: 1,
|
|
Composer: "P. Circuit",
|
|
},
|
|
},
|
|
{
|
|
Rel: "Pale Circuit/Static Bloom/02 Cold Cathode.flac",
|
|
Case: caseFLACAlbum, Format: tagwriter.FormatFLAC,
|
|
Duration: durNormal, FreqHz: 277, Cover: "static-bloom",
|
|
Tags: tags{
|
|
Title: "Cold Cathode", Artist: "Pale Circuit",
|
|
Album: "Static Bloom", AlbumArtist: "Pale Circuit",
|
|
Genre: "Electronic", Year: 1998, TrackNumber: 2,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Pale Circuit/Static Bloom/03 Dust Loop.flac",
|
|
Case: caseFLACAlbum, Format: tagwriter.FormatFLAC,
|
|
Duration: durShort, FreqHz: 311, Cover: "static-bloom",
|
|
Tags: tags{
|
|
Title: "Dust Loop", Artist: "Pale Circuit",
|
|
Album: "Static Bloom", AlbumArtist: "Pale Circuit",
|
|
Genre: "Electronic", Year: 1998, TrackNumber: 3,
|
|
},
|
|
},
|
|
|
|
// 5. Ogg Vorbis, whose writer rebuilds the page structure by hand
|
|
// and is the most fragile of the four.
|
|
{
|
|
Rel: "Pale Circuit/Ribbon Road/01 Ribbon Road.ogg",
|
|
Case: caseOGGAlbum, Format: tagwriter.FormatOGG,
|
|
Duration: durShort, FreqHz: 349, Cover: "ribbon-road",
|
|
Tags: tags{
|
|
Title: "Ribbon Road", Artist: "Pale Circuit",
|
|
Album: "Ribbon Road", AlbumArtist: "Pale Circuit",
|
|
Genre: "Ambient", Year: 2015, TrackNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Pale Circuit/Ribbon Road/02 Verge.ogg",
|
|
Case: caseOGGAlbum, Format: tagwriter.FormatOGG,
|
|
Duration: durNormal, FreqHz: 370, Cover: "ribbon-road",
|
|
Tags: tags{
|
|
Title: "Verge", Artist: "Pale Circuit",
|
|
Album: "Ribbon Road", AlbumArtist: "Pale Circuit",
|
|
Genre: "Ambient", Year: 2015, TrackNumber: 2,
|
|
},
|
|
},
|
|
|
|
// 6. WAV, where tags live in a RIFF ID3 chunk. One with cover
|
|
// art, one without, since the chunk layouts differ.
|
|
{
|
|
Rel: "Field Recordings/Test Tones/01 Tone A.wav",
|
|
Case: caseWAVTracks, Format: tagwriter.FormatWAV,
|
|
Duration: durShort, FreqHz: 400, Cover: "test-tones",
|
|
Tags: tags{
|
|
Title: "Tone A", Artist: "Field Recordings",
|
|
Album: "Test Tones", AlbumArtist: "Field Recordings",
|
|
Genre: "Field Recording", Year: 2024, TrackNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Field Recordings/Test Tones/02 Tone B.wav",
|
|
Case: caseWAVTracks, Format: tagwriter.FormatWAV,
|
|
Duration: durShort, FreqHz: 800,
|
|
Tags: tags{
|
|
Title: "Tone B", Artist: "Field Recordings",
|
|
Album: "Test Tones", AlbumArtist: "Field Recordings",
|
|
Genre: "Field Recording", Year: 2024, TrackNumber: 2,
|
|
},
|
|
},
|
|
|
|
// 7. Degrees of missing metadata, which is what the "Unknown
|
|
// Artist" fallbacks and the autotag candidate list are for.
|
|
{
|
|
Rel: "unsorted/no-tags-at-all.mp3",
|
|
Case: casePartialTags, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 200,
|
|
},
|
|
{
|
|
Rel: "unsorted/title-only.mp3",
|
|
Case: casePartialTags, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 210,
|
|
Tags: tags{Title: "Title Only"},
|
|
},
|
|
{
|
|
Rel: "unsorted/no-track-number.mp3",
|
|
Case: casePartialTags, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 230,
|
|
Tags: tags{
|
|
Title: "No Track Number", Artist: "Loose Ends",
|
|
Album: "Odds And Sods",
|
|
},
|
|
},
|
|
|
|
// 8. Scripts the layout engine handles differently, plus
|
|
// filenames with characters that break naive URL building.
|
|
{
|
|
Rel: "Unicode Tests/多言語アルバム/01 さくら.mp3",
|
|
Case: caseUnicode, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 261, Cover: "unicode",
|
|
Tags: tags{
|
|
Title: "さくら", Artist: "サンプル・アーティスト",
|
|
Album: "多言語アルバム", AlbumArtist: "サンプル・アーティスト",
|
|
Genre: "J-Pop", Year: 2020, TrackNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Unicode Tests/多言語アルバム/02 Привет мир.mp3",
|
|
Case: caseUnicode, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 293, Cover: "unicode",
|
|
Tags: tags{
|
|
Title: "Привет мир", Artist: "Тестовый исполнитель",
|
|
Album: "多言語アルバム", AlbumArtist: "サンプル・アーティスト",
|
|
Genre: "J-Pop", Year: 2020, TrackNumber: 2,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Unicode Tests/多言語アルバム/03 مرحبا بالعالم.mp3",
|
|
Case: caseUnicode, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 329, Cover: "unicode",
|
|
Tags: tags{
|
|
Title: "مرحبا بالعالم", Artist: "فنان تجريبي",
|
|
Album: "多言語アルバム", AlbumArtist: "サンプル・アーティスト",
|
|
Genre: "J-Pop", Year: 2020, TrackNumber: 3,
|
|
},
|
|
},
|
|
{
|
|
// Precomposed é in the filename, decomposed e+U+0301 in the
|
|
// title: a genuine source of "the same track twice" bugs.
|
|
Rel: "Unicode Tests/多言語アルバム/04 Café ☕ Über #1's.mp3",
|
|
Case: caseUnicode, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 415, Cover: "unicode",
|
|
Tags: tags{
|
|
Title: "Cafe\u0301 ☕ Über #1's", Artist: "サンプル・アーティスト",
|
|
Album: "多言語アルバム", AlbumArtist: "サンプル・アーティスト",
|
|
Genre: "J-Pop", Year: 2020, TrackNumber: 4,
|
|
},
|
|
},
|
|
|
|
// 9. The deliberate duplicate pair: identical tags and length as
|
|
// "02 Tideline.mp3" above, in another directory and another
|
|
// format, for the duplicate-tracks dialog to match on.
|
|
{
|
|
Rel: "unsorted/dupes/Tideline (copy).mp3",
|
|
Case: caseDuplicates, Format: tagwriter.FormatMP3,
|
|
Duration: durMedium, FreqHz: 247, Cover: "glass-harbour",
|
|
Tags: duplicateTags,
|
|
},
|
|
{
|
|
Rel: "unsorted/dupes/Tideline.flac",
|
|
Case: caseDuplicates, Format: tagwriter.FormatFLAC,
|
|
Duration: durMedium, FreqHz: 247, Cover: "glass-harbour",
|
|
Tags: duplicateTags,
|
|
},
|
|
|
|
// 10. Extremes of text length and track length, plus a track with
|
|
// no year at all for smart-playlist range rules to exclude.
|
|
{
|
|
Rel: "Edge Cases/Extremes/01 Long Title.mp3",
|
|
Case: caseEdgeLengths, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 180,
|
|
Tags: tags{
|
|
Title: longTitle, Artist: longArtist,
|
|
Album: "Extremes", AlbumArtist: longArtist,
|
|
Genre: "Jazz", Year: 1975, TrackNumber: 1,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Edge Cases/Extremes/02 Brief.mp3",
|
|
Case: caseEdgeLengths, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 190,
|
|
Tags: tags{
|
|
Title: "Brief", Artist: longArtist,
|
|
Album: "Extremes", AlbumArtist: longArtist,
|
|
Genre: "Jazz", Year: 1975, TrackNumber: 2,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Edge Cases/Extremes/03 Long Player.mp3",
|
|
Case: caseEdgeLengths, Format: tagwriter.FormatMP3,
|
|
Duration: durLong, FreqHz: 165,
|
|
Tags: tags{
|
|
Title: "Long Player", Artist: longArtist,
|
|
Album: "Extremes", AlbumArtist: longArtist,
|
|
Genre: "Jazz", Year: 1975, TrackNumber: 3,
|
|
},
|
|
},
|
|
{
|
|
Rel: "Edge Cases/Extremes/04 Undated.mp3",
|
|
Case: caseEdgeLengths, Format: tagwriter.FormatMP3,
|
|
Duration: durShort, FreqHz: 175,
|
|
Tags: tags{
|
|
Title: "Undated", Artist: longArtist,
|
|
Album: "Extremes", AlbumArtist: longArtist,
|
|
Genre: "Jazz", TrackNumber: 4,
|
|
},
|
|
},
|
|
}
|
|
|
|
// auxFile is a non-audio or malformed file: either debris that
|
|
// legitimately sits inside a music library, or a deliberately broken
|
|
// file used to exercise scanner error handling.
|
|
type auxFile struct {
|
|
Rel string
|
|
// Source, when set, names a library fixture whose encoded bytes
|
|
// get truncated to Bytes; otherwise Literal is written verbatim.
|
|
Source string
|
|
Bytes int
|
|
Literal string
|
|
}
|
|
|
|
// brokenFiles live in their own root so the clean library's track count
|
|
// stays deterministic. A test that wants the error paths adds this
|
|
// root as a second library on purpose.
|
|
//
|
|
//nolint:gochecknoglobals // the fixture spec is the point of this cmd.
|
|
var brokenFiles = []auxFile{
|
|
{Rel: "notes.txt", Literal: "not audio\n"},
|
|
{Rel: "empty.flac"},
|
|
{
|
|
Rel: "truncated.mp3",
|
|
Source: "Aurora Fields/Glass Harbour/01 Salt Air.mp3",
|
|
Bytes: 512,
|
|
},
|
|
}
|
|
|
|
// libraryExtras are the non-audio files a real library is full of.
|
|
// They belong inside the clean root because ignoring them is itself
|
|
// behaviour worth testing — folder art in particular, which is a
|
|
// separate cover source from embedded art.
|
|
//
|
|
//nolint:gochecknoglobals // the fixture spec is the point of this cmd.
|
|
var libraryExtras = []auxFile{
|
|
{Rel: "Pale Circuit/Ribbon Road/cover.jpg"},
|
|
{Rel: "Pale Circuit/Ribbon Road/ripping notes.txt", Literal: "EAC log\n"},
|
|
}
|