Skip to content

e2e: drive the real binary against synthesized media - #26

Merged
vynulldev merged 2 commits into
mainfrom
release-e2e
Jul 26, 2026
Merged

e2e: drive the real binary against synthesized media#26
vynulldev merged 2 commits into
mainfrom
release-e2e

Conversation

@vynulldev

Copy link
Copy Markdown
Owner

What & why

Two commits adding automated end-to-end coverage for flows that until now could only be verified by hand before a release.

The first covers the one recent fix that could not be verified live: the 0x30 mixer-status merge. An idle DJM never emits 0x30 packets, so the state-clobbering flip only reproduces with a full rig. The two mixer-status write paths move out of their socket loops into recordMixerChannels / recordMixerStatus (no behaviour change), and a test replays the newer-DJM interleaving with synthesized wire packets fed through the real parser: 0x03 channel state, then twenty interleaved stripped 0x30s that previously wiped ChannelStateKnown each time, plus a channel change mid-stream. Reverting the merge makes the test fail on the first 0x30, so it pins the actual bug, not the implementation. Also covered: a rich legacy 0x29 replaces the entry wholesale, a lone 0x30 before any 0x03 leaves channel state honestly unknown, and the first-packet log flag fires exactly once.

The second is the e2e package, run with make e2e (gated on VYNULL_E2E=1 so go test ./... stays fast). The harness builds the real binary once, launches one server per test on a free port with an isolated temp data-dir, skips itself when the DJ-Link UDP ports are held by a running instance, and synthesizes all of its own media: deterministic kick-train WAVs whose ground-truth BPM makes the integer-snap assertion exact, plus ffmpeg-produced FLAC (the lossless path) and an MP3 with an embedded JPEG cover. No fixtures, no dependence on a local music library.

Five scenarios covering the main user-facing flows: the analysis pipeline (rows fill, BPM lands exactly on the known integers including FLAC, waveform PNGs serve); lazy artwork (embedded art on first request, artless tracks 404 once and stay probed, directory cover.jpg fallback); playlist membership (wholesale set-tracks removal leaves library rows intact, smart rules resolve, delete leaves the library alone); the analysis-cache upgrade path (a manufactured stale-cacheVersion cache entry with a stale fractional row BPM re-analyzes on first touch and the row follows, while a manual BPM override survives — reproducing what a cacheVersion bump looks like on disk without binary fixtures that rot); and a bulk-add pass whose polling loop is itself the responsiveness assertion. Full suite runs in about 15 seconds.

Writing the artwork scenario surfaced that ExtractArtwork's directory fallback applies to any artless file beside a cover image; that is now an explicit assertion rather than an accident.

What deliberately stays out of scope: web UI wiring (no browser toolchain committed), real-music breadth (a genuinely fractional track keeping its fraction is the one thing synthetic media cannot cover), large-library upgrade runs, and everything deck-facing — hardware behaviour still needs hardware.

Hardware testing

  • Tested on: N/A: test-only change plus a no-behaviour-change refactor (the two mixer-status write paths extracted into named methods; the listeners call them with identical logic). Nothing a deck reacts to changed. Keep-alive cadence figures in comments were measured against a live DJM-A9.

Checklist

  • go build ./..., go vet ./..., and go test ./... pass
  • gofmt -l . is clean
  • New source files carry an SPDX header (GPL-3.0-or-later)
  • Tested on real hardware (deck + firmware noted above), or this change doesn't affect deck behaviour
  • I agree my contribution is licensed under the project's GPLv3

The 0x30 clobber fix could not be verified live: the idle DJM-A9 never
emits 0x30 status packets, so the flip only reproduces with the full
rig. Cover it deterministically instead - the two mixer-status write
paths move out of their socket loops into recordMixerChannels /
recordMixerStatus (no behaviour change; recordMixerStatus returns the
first-packet flag the listener logs on), and the test replays the
newer-DJM interleaving with synthesized wire packets fed through the
real parser: 0x03 channel state, then twenty interleaved stripped
0x30s that previously wiped ChannelStateKnown each time, plus a
channel change mid-stream. Reverting the merge makes the test fail on
the first 0x30, so it pins the actual bug. Also covered: a rich legacy
0x29 replaces the entry wholesale, a lone 0x30 before any 0x03 leaves
channel state honestly unknown, and the first-packet log flag fires
exactly once.
An end-to-end suite for the flows that until now could only be
verified by hand before a release. Run with make e2e (gated on
VYNULL_E2E=1 so go test ./... stays fast); the suite skips itself when
the DJ-Link UDP ports are held by a running instance.

The harness builds the binary once, launches one server per test on a
free port with an isolated temp data-dir, and synthesizes all of its
own media: deterministic kick-train WAVs whose ground-truth BPM makes
the integer-snap assertion exact, an ffmpeg-produced FLAC for the
lossless path, and an MP3 with an embedded JPEG cover for the artwork
path. No fixtures, no dependence on a local music library.

Five scenarios covering the main user-facing flows: the analysis
pipeline (rows fill, detected BPM lands exactly on the known integers
including FLAC, waveform PNGs serve), lazy artwork (embedded art on
first request, artless tracks 404 once and stay probed, directory
cover.jpg fallback), playlist membership (wholesale set-tracks removal
leaves library rows intact, smart rules resolve, delete leaves the
library alone), the analysis-cache upgrade path (a manufactured
stale-cacheVersion cache entry with a stale fractional row BPM
re-analyzes on first touch and the row follows, while a manual BPM
override survives), and a bulk-add pass whose polling loop is itself
the responsiveness assertion.

Writing the artwork scenario surfaced that ExtractArtwork's directory
fallback applies to any artless file beside a cover image - now an
explicit assertion rather than an accident. Full suite runs in ~15s.
@vynulldev
vynulldev merged commit fed6c11 into main Jul 26, 2026
1 check passed
@vynulldev
vynulldev deleted the release-e2e branch July 26, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant