Skip to content

test(sr-tests): add golden-output regression test for predict() - #383

Open
dhritimandas wants to merge 1 commit into
neuronets:alphafrom
dhritimandas:feat/golden-output-tests
Open

test(sr-tests): add golden-output regression test for predict()#383
dhritimandas wants to merge 1 commit into
neuronets:alphafrom
dhritimandas:feat/golden-output-tests

Conversation

@dhritimandas

@dhritimandas dhritimandas commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

What: We add a golden-output regression test for predict(). A committed fixture pins its numeric output.
Why: No test checked the values that predict() returns. Shape and dtype tests miss a changed pad, block order, or argmax. A refactor could change results silently.

Fixture (nobrainer/sr-tests/golden/)

  • tiny brain-extraction unet (channels=(4,8), strides=(2,)) briefly overfit on an analytic sphere; weights + packed label mask + 4096-voxel probability probe + meta block (versions/platform/seed) in one 26.4 KB .npz
  • input volume regenerated from meta via np.mgrid — no RNG, no stored volume
  • volume shape (40,48,56) deliberately non-multiple of the 16³ block on 2 axes, so _pad_to_multiple is always exercised (asserted)

Test (test_golden_outputs.py)

  • sha256 fast path: default prediction path reassembles by pure block assignment (no float accumulation), so the mask is bit-stable on a fixed platform
  • fallback: voxel-disagreement budget (0.05%) + Dice floor 0.995 — both derived from this fixture's measured object size, not picked in the abstract
  • probability probe via torch.testing.assert_close(rtol=1e-4, atol=1e-5), every tolerance justified inline
  • device="cpu" pinned explicitly; set_determinism(seed=42) before every run
  • non-degeneracy guard on the fixture itself; batch_size invariance pinned
  • no gpu marker — runs on CPU CI, no network, no downloads

Regeneration (generate_golden.py)

  • deliberate two-step: dry run prints the diff (voxels changed, Dice, probe delta, meta delta), --yes to write; refuses a degenerate mask
  • regeneration discipline documented in golden/README.md

Test plan

Nothing pins predict()'s actual output values -- existing tests assert
shapes, dtypes, and finiteness, never numeric content. A refactor to
padding, block splitting, reassembly, or the argmax/threshold logic could
silently change what users get.

- nobrainer/sr-tests/golden/generate_golden.py: builds a tiny
  brain-extraction unet (channels=(4,8), strides=(2,)), briefly overfits
  a synthetic sphere volume, runs predict(), and writes the committed
  fixture. Not test_-prefixed, so pytest never collects it; run it
  deliberately to regenerate.
- nobrainer/sr-tests/golden/golden_brain_extraction.npz: the fixture
  (26.4 KB) -- packed label mask + sha256, a probability probe at 4096
  stride-sampled voxels, the model weights, and a meta block recording
  the generating environment (torch/monai/numpy/platform versions) for
  diagnosability.
- nobrainer/sr-tests/test_golden_outputs.py: sha256 fast path (the
  default, non-strided prediction path reassembles via pure block
  assignment with no float accumulation, so the label mask is genuinely
  bit-stable on a fixed platform), falling back to a voxel-disagreement
  budget + Dice check, then a probability-probe assert_close. Every
  tolerance is derived from this fixture's actual measurements (object
  size, worst-case Dice at the budget ceiling) and commented inline,
  never a bare default. device="cpu" is pinned explicitly rather than
  left to get_device(), which would return CUDA/MPS depending on
  runner. monai.utils.set_determinism(seed=42) is called before every
  model construction and predict() call in both scripts.
- nobrainer/sr-tests/golden/README.md: what the fixture contains, why
  each design choice, and the deliberate regeneration workflow (dry-run
  diff by default, --yes to write, refuses a degenerate mask).

No @pytest.mark.gpu, so `-m "not gpu"` includes these tests -- they run on
CPU CI with no network and no GPU.
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