Status date: 2026-07-05. This document is the living plan for building a proper
testing infrastructure for the decoder/ and pkg/ packages
(measureAlgos/ and _singularity/ are explicitly out of scope).
It is updated as work progresses; each item links to the commit that closed it.
- 2 test files, 5+2 test functions, ~140 lines of tests for ~3900 lines of code:
pkg/dateReader_test.go—flipWords(even/odd word counts). Regression tests for the crash fixed ine645a06.pkg/sipms_test.go—buildSipmData(link merging, length-mismatch panic). Same commit.
- No test runner target:
mageonly builds; tests are run by hand. - No CI, no coverage tracking, no testdata fixtures in the repo.
- Everything else — DATE header parsing, NEXT header parsing, Huffman decompression, PMT/SiPM/fiber payload decoding, trigger parsing, channel-map logic, HDF5 writing, configuration loading, the event loop — is untested.
-
Tests must run inside a container with Go + CGO + libhdf5 (the host has hdf5 runtime libs but no dev headers, and
pkgneeds CGO + libhdf5 throughgithub.com/next-exp/hdf5-go). Originally verified against the local-onlyduck-backend-test-base:latesttag;test.shand CI now use the public equivalent,nextmgmt/next-decoder:lateston Docker Hub (same image,docker inspectconfirms identical image ID), whichtest.shpulls automatically if not already present locally:./test.sh # pulls nextmgmt/next-decoder:latest if missing, then mage test(container Go: 1.24.13; host Go 1.25.6 exists but cannot link hdf5).
-
The internal detector-control MySQL DB
HDDEMODBis reachable from the lab network and containsChannelMapping,ChannelPosition,FecElecIDBase,HuffmanCodesPmt,HuffmanCodesSipm. DB-dependent tests are possible but must be optional (skippable) so the suite does not require the network, and must not hardcode the real host or credentials since this repo is public. -
Sample raw data available:
/daq/demo/run_15022...rd(3 MB) andrun_14988...rd(45 MB) — DEMO++, PMTs + SiPMs, DBDEMOPPDBatnext.ific.uv.es./daq/hddemo/run_616...rd(353 MB),run_672...rd(58 MB) — HDDEMO, fibers + SiPMs, DBHDDEMODB. Known-good outputs exist (run_616_db.h5,run_616_nodb.h5,run_672_*.h5).
- Package-level globals:
configuration,logger,sensorsMap,huffmanCodesPmts,huffmanCodesSipms,fecElecIDBase(inpkg) are set by thedecodermain and byLoadDatabase. Tests must set them explicitly. A shared test helper (testmain_test.gowith a no-op logger and a defaultConfiguration) is needed; without it, most code paths panic on nillogger. Long term the globals should become parameters/struct fields, but the tests come first — refactoring untested decoding code is how detectors lose data. - DB coupling: Huffman trees and channel maps come from MySQL. But the
structures themselves (
HuffmanNode,SensorsMap,fecElecIDBasemap) are trivial to construct in tests, so decoding logic is testable without DB. Onlydatabase.goitself needs a real (or skipped) DB. - HDF5 output:
writer.go/hdf5.gocall the C library and log errors instead of returning them in several paths. Unit-testable parts (sorting, ordering, trigger-channel matrix layout) are pure; full writer tests must create real files in a temp dir inside the container (cheap, no mocking). decoder/ispackage mainbut is testable directly (go test ./decoder) forLoadConfiguration,countEvents,numberOfEventsToProcess,FileReader.getNextEvent.- Panics as error handling in hot decode paths (
buildSipmData, slice indexing on truncated payloads).processEventrecovers, so tests for malformed input should assert panics/recovery behaviour explicitly.
All five were confirmed and fixed (each with a regression test, own commit):
| # | Location | Defect | Status |
|---|---|---|---|
| B1 | pkg/nextHeaders.go readSeqCounter |
Read data[position+1] twice, never data[position]: continuation fragments with only the high counter half set were parsed as fresh headers. |
Fixed acca5e2 |
| B2 | pkg/writer.go WriteEvent |
DB mode never populated blrSorted: DataBLR written empty and pmtblr all zeros whenever dual-mode/HG PMT data was decoded with a DB; BLR baselines also used a different ordering than BLR waveforms. DB mode now reuses the PMT sensorID ordering; waveforms/baselines share order and row count. |
Fixed bed500b |
| B3 | decoder/main.go numberOfEventsToProcess |
Subtracted skip before capping at the file event count: with skip > 0 and large max_events the parallel-mode result loop waited for events that never arrive (hang). |
Fixed f94d49e |
| B4 | decoder/workers.go sendEventsToWorkers |
Dead err == io.EOF branch; per-event print ran on failed reads. |
Fixed 15448b6 |
| B5 | pkg/sipms.go ReadSipmFEC |
Processed-payload deletes ran on every time bin instead of once per FEC pair. | Fixed 15448b6 |
After all fixes, the rebuilt decoder binary reproduces the pre-change
known-good run_616_db.h5 byte-for-byte (all datasets compared equal),
confirming no unintended behaviour change on the normal decoding path.
- Pure unit tests (fast, no container-external deps, run on every change): bit/word manipulation, header field extraction, elecID/position arithmetic, Huffman tree construction + decode, channel-mask expansion, ID post-processing (dual mode, HG/LG, X17/X19 fiber swap, ext-trigger/PMT-sum extraction), pedestal mapping, raw + compressed charge decoding against hand-built payloads, writer ordering functions, JSON config marshalling.
- Fixture-based tests (real binary data, small, committed to repo):
pkg/testdata/holding individual DATE events (~a few hundred KB) extracted fromrun_15022(PMTs+SiPMs) andrun_672(fibers+SiPMs), plus the channel map / Huffman codes / FecElecIDBase for those runs dumped to JSON so decoding tests do not need MySQL. Tests:ReadEvent,ReadGDCend-to-end intoEventType, asserting channel counts, waveform lengths, spot-checked sample values against the known-good.h5outputs. - Writer/round-trip tests (container, temp files):
NewWriter→WriteEvent→Close→ reopen with hdf5-go and verify groups, dataset shapes, mapping tables, trigger tables. - Optional integration test (skipped unless enabled):
DECODER_TEST_DB=1→database.goagainst a live MySQL instance (a disposable local/CI container by default). No external raw-data dependency: the individual events any test needs are extracted and committed instead (see 2.1.2). - Runner:
mage Test(unit+fixture),mage TestDB(+ DB integration),test.shwrapper that runs them inside the container so a single host command runs the suite. CI-ready.
- Tests live next to the code (
*_test.go, same package, to reach unexported functions — most of the API surface is unexported). - A single
testmain_test.goper package installs a silent logger and a defaultConfigurationinTestMain, so individual tests only override what they need. - Table-driven tests; fixture provenance documented in a comment (run number, event ID, extraction command).
- Every bug fix ships with the regression test in the same commit.
Phases are ordered so that decoding logic gets locked down by tests before any refactoring or bug-fixing that could change output.
- P0 — This assessment document.
- P1 — Infrastructure:
mage Testtarget,test.shcontainer wrapper (with persistent Go cache; repeat runs ~1 s),testmain_test.gohelper inpkg. (decoderpackage needs no helper: itsinit()installs the logger.) - P2 — Pure unit tests, decoding side:
CheckBit,ReadTriggerFEC, Huffman (parse_huffman_line,decode_huffman,decode_compressed_valueincl. control-code escape),ReadCommonHeaderand everyread*sub-header (FormatID flags, Juliett event conf, baselines bit-packing, FecID, timestamp/FTBit, TriggerFT),computePmtElecID/Position,computeFiberElecID/Position,computeSipmPosition/IDFromPosition,sipmChannelMask,pmtsChannelMask/fibersChannelMask(incl. missing-FEC error),computeNextFThm,computeSipmTime(ZS ring-buffer wrap). - P3 — Pure unit tests, event post-processing:
processPmtIds(ext-trigger, PMT-sum, dual-mode remap, HG/LG),processFiberIds(HG remap incl. X17/X19 hardware swap),writePmtPedestals/writeFiberPedestals,decodeCharge(4-channel 12-bit packing),decodeChargeIndiaSipmCompressed/...PmtCompressedwith a synthetic Huffman tree,initializeWaveforms. - P4 — Writer + config unit tests (pure parts; the hdf5-backed
trigger-channel writers moved to P6):
sortSensorsBySensorID,sortSensorsByElecID,sortSensorsBySensorIDForWaveforms(0xFFFF fallback),buildSortedElecIDs/buildSortedSensorIDs,writeTriggerChannels/writeTriggerChannelsNoDBordering (via real temp-file datasets),BloscAlgorithm/BloscShuffleJSON round-trip,convertToHdf5String,LoadConfigurationdefaults + overrides,numberOfEventsToProcess. - P5 — Fixtures + real-data tests:
scripts/extract_rd_events.py+scripts/dump_db_fixture.sh; committed fixtures for DEMO++ run 15022 (2 events, PMTs+SiPMs compressed) and HDDEMO run 616 (2 events, fibers+SiPMs raw mode) with DB snapshots for both runs;ReadEventFromFile/ReadGDCgolden tests against known-good.h5values, unconditional (no external file or env var needed);countEvents/getNextEventtests. - P6 — Writer round-trip + end-to-end NoDB golden test in container;
optional
DECODER_TEST_DB-gated live-DB test, against a disposable local container by default. Found along the way: upstreamhdf5-gobug —Dataset.Close()panics for datasets returned byOpenDataset(nil stored datatype); production code is unaffected (only closes datasets it created), test helpers work around it. - P7 — Bug verification & fixes (B1–B5 above), one commit each, regression test included; production output verified unchanged against the known-good run 616 DB-mode file.
- P9 — CI:
.github/workflows/tests.yml, two jobs, on push tomasterand on every pull request: -test: runs./test.sh(unit + fixture tests).test.shswitched to the publicnextmgmt/next-decoder:latestimage (pulled automatically if missing, same locally and in CI); no external data mount needed since every fixture a test uses is committed underpkg/testdata/. -test-db: runs the live-DB integration test (TestLoadDatabaseHddemo, previously always skipped in CI) against a disposablemysql:8.0service container instead of the real internal DB, which isn't reachable from GitHub-hosted runners and whose real credentials must not be hardcoded in a public test file anyway.pkg/testdata/hddemo_616_seed.sql(schema + run-616-filtered data for the 5 tablesdatabase.goreads, generated byscripts/dump_db_seed.sh) seeds the container for the already env-var-configurablemage testdb/./test.sh dbtarget. Whole job topology (job container + service container + seeding) reproduced and verified manually before trusting the YAML, including a negative control (wrong DB password fails the test rather than silently skipping). - P8 — Nice-to-have (later): dependency-inject globals, error returns
instead of logged-and-ignored hdf5 errors, coverage target
(aim: >70% of
pkg).
| Date | Commit | What |
|---|---|---|
| 2026-07-05 | fa4cd3c | P0: assessment and plan. |
| 2026-07-05 | 9dc23e9 | P1: mage test targets, test.sh container wrapper, pkg TestMain helper. |
| 2026-07-05 | 6c4fb2f | P2: 30 unit tests for trigger, huffman, NEXT headers, elecID/position math, channel masks, FT computations. |
| 2026-07-05 | cb646ec | P3: 13 tests for processPmtIds/processFiberIds (incl. X17/X19 swap), pedestal mapping, raw + compressed charge decoding. |
| 2026-07-05 | 22b6043 | P4: writer sort/ordering, blosc JSON, LoadConfiguration, numberOfEventsToProcess tests. |
| 2026-07-05 | 1f1381f | P5: real-data fixtures + golden ReadGDC tests (DEMO++ 15022 and HDDEMO 616 both committed), file-reader tests. |
| 2026-07-05 | d2b0b8e | P6: writer round-trip (NoDB + DB), end-to-end fixture-to-HDF5 golden test, gated live-DB test against a disposable container. |
| 2026-07-05 | 50347ef | B1 fix: readSeqCounter read the same word twice. |
| 2026-07-05 | 378d9e9 | B2 fix: DB-mode BLR waveforms written with empty channel ordering. |
| 2026-07-05 | c446a52 | B3 fix: parallel-mode hang when skip > 0 with large max_events. |
| 2026-07-05 | db9d337 | B4+B5 cleanups: dead EOF branch, per-time-bin payload deletes. Binary output verified byte-identical to known-good run_616_db.h5. |
| 2026-07-09 | (this commit) | P9: GitHub Actions workflow running ./test.sh on push/PR; switched to public nextmgmt/next-decoder:latest image with auto-pull. |
| 2026-07-09 | (this commit) | P9: added test-db CI job -- seeded disposable mysql:8.0 service container replaces the unreachable real DB for TestLoadDatabaseHddemo. |