Skip to content

ci: real-data fixture + matlab tests on github actions#30

Merged
neuromechanist merged 1 commit into
mainfrom
feature/issue-11-matlab-ci-fixture
May 13, 2026
Merged

ci: real-data fixture + matlab tests on github actions#30
neuromechanist merged 1 commit into
mainfrom
feature/issue-11-matlab-ci-fixture

Conversation

@neuromechanist

Copy link
Copy Markdown
Member

Summary

Adds a small (2.3 MB) BIDS-EEG fixture and a GitHub Actions workflow so the MATLAB test suite runs on every push and PR, not only locally on the maintainer's /Volumes mount. Implements issue #11 plus the practicum brief's Week-4 CI scope that we had been deferring.

Why now: PR #29 just landed the Phase 1 derivatives without CI coverage. Before Phase 2 (AMICA), the test infrastructure should be in place so new code is covered from day one.

Fixture

tests/fixtures/bids_mini/ — real 60 s slice of sub-NDARAA948VFH's ThePresent recording from the HBN R3 100 Hz local dataset. No synthesis, no resampling.

  • Built by scripts/build_test_fixture.m (deterministic, idempotent; re-runs from the same /Volumes source produce byte-identical output for the JSON/TSV sidecars).
  • Carries both subject-level and root-level BIDS sidecars. The root-level task-ThePresent_eeg.json is the key one: it has Manufacturer / ManufacturersModelName that EEGLAB uses to auto-look-up the GSN HydroCel 129 channel locations. Without it, clean_rawdata falls into a location-free path that errors at srate < 110 Hz on our 100 Hz data.
  • License CC-BY-SA 4.0 inherited from HBN-EEG (attribution and license preserved via dataset_description.json + README).

CI workflow

.github/workflows/matlab-tests.yml:

  • matlab-actions/setup-matlab@v2 on R2024a (same release as the existing checkcode job).
  • EEGLAB pinned to tagged release 2026.0.0, cloned with --recurse-submodules so EEG-BIDS, clean_rawdata, ICLabel, dipfit, firfilt come along automatically. Cache keyed on the tag so a version bump invalidates cleanly.
  • Biosig auto-installed via EEGLAB's plugin_askinstall on first run (not a submodule; needed for BDF I/O).
  • Runs tests/matlab/run_all_tests.m against the fixture via HBN_BIDS_ROOT.

Test refactor

  • tests/matlab/test_bids_root.m: new resolver helper. Tries HBN_BIDS_ROOT, then tests/fixtures/bids_mini, then /Volumes/.... Errors with a precise message if none resolve, so a CI failure surfaces "no fixture" rather than disguising itself as a phase1 error.
  • test_phase1_no_subjects.m, test_phase1_smoke.m: drop the hardcoded /Volumes path; use the resolver.

Local CI

  • scripts/local_ci.sh falls back to the fixture when BIDS_ROOT is unset, so a clean checkout works without the maintainer's /Volumes mount.
  • Exports HBN_BIDS_ROOT so the shell and the in-MATLAB resolver agree.

Pre-commit changes

The fixture mirrors upstream HBN sidecars byte-for-byte. Several hooks were rewriting them or flagging upstream artifacts:

  • mixed-line-ending: was treating the binary BDF as text and corrupting it. Now skips BDF/EDF/SET/FDT/MAT.
  • end-of-file-fixer, trailing-whitespace: now skip tests/fixtures/.
  • typos: now skips tests/fixtures/ so upstream spelling artifacts in participants.json don't fail CI.

Misc

  • .github/workflows/lint.yml: drops the stale develop branch trigger (main-only now).
  • logs/ added to .gitignore (transient MATLAB batch logs from local runs).

Local verification

All 4 tests pass against the fixture:

                name                 passed
    _____________________________    ______
    "test_list_eligible_subjects"    true
    "test_write_qa_channels_csv"     true
    "test_phase1_no_subjects"        true
    "test_phase1_smoke"              true

clean_rawdata on the 60 s slice removes 11 channels (vs 6 on the full 210 s recording for the same subject); different rejection statistics on different-length windows, both correct. The smoke test asserts that at least one channel is rejected and that the checkpoint round-trips with matching nbchan / srate, so a no-op rejector cannot silently pass.

Test plan

  • Local: run_all_tests green against HBN_BIDS_ROOT=tests/fixtures/bids_mini.
  • Local: run_all_tests green against the full /Volumes dataset (regression check).
  • Fixture rebuild is deterministic and idempotent.
  • Pre-commit hooks all pass without corrupting binaries.
  • CI workflow runs to green on this PR (first invocation will be a cache miss for EEGLAB; expect ~10-15 min on first run, ~3-5 min thereafter).

Closes #11.
Refs #1.

Adds a small (2.3 MB) BIDS-EEG fixture and a GitHub Actions workflow so
the MATLAB test suite runs on every push/PR, not only locally on the
maintainer's /Volumes mount. Implements issue #11 plus the practicum
brief's Week-4 CI scope.

Fixture (tests/fixtures/bids_mini/)
- Real 60 s slice of sub-NDARAA948VFH ThePresent from the HBN R3 100 Hz
  local dataset. No synthesis, no resampling.
- Built by scripts/build_test_fixture.m (deterministic, idempotent).
- Includes both subject-level and root-level BIDS sidecars; the latter
  carries Manufacturer / ManufacturersModelName that EEGLAB needs to
  auto-look-up GSN HydroCel 129 channel locations. Without it,
  clean_rawdata falls into a location-free path that errors at
  srate < 110 Hz.
- License CC-BY-SA 4.0 inherited from HBN-EEG.

CI workflow (.github/workflows/matlab-tests.yml)
- matlab-actions/setup-matlab @ R2024a.
- EEGLAB pinned to tagged release 2026.0.0, cloned with
  --recurse-submodules so EEG-BIDS, clean_rawdata, ICLabel, dipfit,
  firfilt come along. Cache keyed on the tag.
- Biosig auto-installed via EEGLAB plugin_askinstall on first run.
- Runs tests/matlab/run_all_tests.m against the fixture.

Test refactor
- tests/matlab/test_bids_root.m: resolver helper. Tries HBN_BIDS_ROOT,
  then tests/fixtures/bids_mini, then /Volumes/...
- test_phase1_no_subjects.m, test_phase1_smoke.m: use the resolver.

Pre-commit: end-of-file-fixer, trailing-whitespace, mixed-line-ending,
typos all skip tests/fixtures/. mixed-line-ending also skips binary EEG.

Misc: lint.yml drops the develop branch trigger; logs/ added to
.gitignore; local_ci.sh falls back to the fixture when BIDS_ROOT unset.

All 4 tests pass locally against the fixture (clean_rawdata removes
11 channels on the 60 s slice vs 6 on the full 210 s recording for
the same subject; different statistics, both correct).

Closes #11. Refs #1.
@neuromechanist
neuromechanist merged commit 387af54 into main May 13, 2026
6 checks passed
@neuromechanist
neuromechanist deleted the feature/issue-11-matlab-ci-fixture branch May 13, 2026 06:10
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.

Test fixture: 30s real EEG segment for runtests smoke tests

1 participant