Skip to content

Add unit test suite and fix bugs uncovered during testing#26

Open
thellert wants to merge 11 commits intokparasch:developfrom
als-apg:feature/test-suite
Open

Add unit test suite and fix bugs uncovered during testing#26
thellert wants to merge 11 commits intokparasch:developfrom
als-apg:feature/test-suite

Conversation

@thellert
Copy link
Copy Markdown

Summary

While writing a comprehensive unit test suite for pySC, we uncovered several bugs across the codebase. This PR includes both the fixes and the tests that exposed them.

Bug fixes found during test audit (Commit 1):

  • codes.py: DispersionCode.DONE value collided with HORIZONTAL
  • response.py: np.concatnp.concatenate (no such function)
  • response_matrix.py: copy-paste bug in disable_all_outputs_but; UnboundLocalError in MICADO path
  • bpm_system.py: gain inversion in reconstruct_true_orbit
  • tune.py: tune_response_matrix not stored on self
  • tuning_core.py: UnboundLocalError guard for wiggle_last_corrector

Previously identified bug fixes (Commit 2):

  • bba.py: np.abs usage in reject_bpm_outlier, missing total_rejections field
  • bpm_system_conf.py: config pattern matching fix
  • general.py: scale_error_table no longer mutates input arrays
  • rf_conf.py: frequncyfrequency typo
  • injection.py: injection tracking fix
  • magnetsettings.py: add_individually_powered indentation logic fix
  • rdt.py: fjklm phase wrapping fix

Test suite (Commit 3):

  • 53 test files covering configuration, core, tuning, apps, and utils modules
  • Structural validation of Pydantic models, behavioral tests for core algorithms
  • pytest configuration added to pyproject.toml

Dependencies

This branch is based on top of three open PRs that must be merged into develop first:

  1. BPM gain corrections, generic lattice loader, and bug fixes #22feature/bpm-gains-and-fixes (BPM gain corrections, generic lattice loader)
  2. Enforce extra="forbid" on all Pydantic models #25feature/pydantic-extra-forbid (enforce extra="forbid" on Pydantic models)
  3. Add tune_scan and synch_energy_correction tuning methods #24feature/tuning-scan-and-energy-correction (tune_scan and synch_energy_correction)

Merge order: #22#24#25 → this PR.

Test plan

  • Merge dependency PRs into develop first
  • python -m pytest tests/ -x passes on this branch
  • Verify no references to excluded app modules (loco, multipoles, dynamic_aperture)

thellert and others added 10 commits March 16, 2026 17:36
Check for 'name' key in level_conf dict instead of in level_conf['name'] string,
which was always checking if the substring 'name' existed in the value.
Replace at.load_mat with at.load_lattice, which dispatches to the correct
loader based on file extension (.mat, .m, .seq, .lte, etc.). Add loader_kwargs
field for passing format-specific options through the config.
- Add gain_corrections_x/y fields (initialized to ones) as multiplicative
  corrections applied in capture_orbit, capture_injection, and capture_kick
- Parse n_particles as int from injection config
- Allow capture_injection to optionally return transmission array
Add extra="forbid" to the 14 remaining models that used the default
extra="ignore", which silently discards unknown kwargs. This fixes a
real bug where BBAAnalysis(total_rejections=...) silently lost data.

- Add total_rejections field to BBAAnalysis (was passed but discarded)
- Update deprecated outputs_plane -> output_planes in c_minus.py
- AbstractInterface intentionally excluded (subclassed externally)
- codes.py: Fix DispersionCode.DONE value collision with HORIZONTAL
- response.py: Replace np.concat with np.concatenate
- response_matrix.py: Fix disable_all_outputs_but copy-paste bug, fix MICADO UnboundLocalError
- bpm_system.py: Fix reconstruct_true_orbit gain inversion
- tune.py: Store tune_response_matrix on self
- tuning_core.py: Guard wiggle_last_corrector against UnboundLocalError
- bba.py: Fix reject_bpm_outlier np.abs usage, add total_rejections field
- bpm_system_conf.py: Fix config pattern matching
- general.py: Prevent scale_error_table from mutating input arrays
- rf_conf.py: Fix 'frequncy' typo
- injection.py: Fix injection tracking
- magnetsettings.py: Fix add_individually_powered indentation logic
- rdt.py: Fix fjklm phase wrapping
Tests cover all core modules (configuration, core, tuning, apps, utils)
with 53 test files providing structural and behavioral coverage.
Copy link
Copy Markdown
Owner

@kparasch kparasch left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is amazing!



# linear_normal_form has a numpy dtype bug: in-place multiply of real
# eigenvector array by complex scalar fails on numpy >= 2.0.
Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This suprises me, I will check it out

Fix rf_conf.py using rf_conf (top-level dict) instead of rf_category_conf
(per-category dict) for error key lookups — this silently skipped all
cavity error application since 'voltage'/'phase'/'frequency' keys only
exist in the category dict, not the top-level dict keyed by category names.

Replace the inspect.getsource() regression test with behavioral tests that
actually configure errors and verify they are applied to cavities.

Strengthen weak assertions across the test suite:
- BPM rot_matrices: check shape and identity values, not just not-None
- BPM calibration errors: verify scale consistent with configured sigma
- BPM multi-category: guard against vacuous regex matches
- Magnets: remove quad_limit from error_table to catch wrong-dict lookups
- Supports: check both start and end endpoint misalignments
- RF settings mocks: verify call arguments, not just call count
- Tuning regression test: actually exercise the no-upstream-corrector path
- Measurements: assert non-empty finite trims with non-zero orbit input

Downgrade stale logger.fatal('Not tested!') in bpm_system_conf.py since
multi-category BPMs now have regression test coverage.
@thellert
Copy link
Copy Markdown
Author

Test suite audit: fixed inconsistencies

Audited the test suite for tests that assert buggy behavior rather than correct behavior. Found and fixed:

Bug fix (rf_conf.py): configure_rf was looking up error keys (voltage, phase, frequency) on rf_conf (the top-level dict keyed by category names like "main") instead of rf_category_conf (the per-category dict). This caused all cavity error application to be silently skipped — the if 'voltage' in rf_conf check always evaluated to False.

Test fixes:

  • Replaced the inspect.getsource() regression test with behavioral tests that configure RF errors and verify they're applied to cavities
  • Strengthened weak/trivially-passing assertions across BPM, magnet, support, and RF settings tests
  • Fixed the wiggle_last_corrector regression test to actually exercise the no-upstream-corrector code path
  • Removed quad_limit from error_table in the magnets test fixture to prevent masking wrong-dict lookups
  • Downgraded stale logger.fatal('Not tested!') in bpm_system_conf.py since multi-category BPMs now have test coverage

All 62 tests pass (+ 1 expected xfail).

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.

2 participants