Skip to content

Pin filter-before-merge invariant on license-class-aware contiguousRegionsForBand (#3060). Principle IV.#3089

Merged
ten9876 merged 2 commits into
mainfrom
aetherclaude/issue-3060
May 25, 2026
Merged

Pin filter-before-merge invariant on license-class-aware contiguousRegionsForBand (#3060). Principle IV.#3089
ten9876 merged 2 commits into
mainfrom
aetherclaude/issue-3060

Conversation

@aethersdr-agent

Copy link
Copy Markdown
Contributor

Summary

Fixes #3060

What was changed

Pin filter-before-merge invariant on license-class-aware contiguousRegionsForBand (#3060). Principle IV.

Files modified

  • CMakeLists.txt
  • src/models/BandPlanManager.cpp
  • src/models/BandPlanManager.h
  • tests/band_plan_license_filter_test.cpp
 CMakeLists.txt                          |  11 ++
 src/models/BandPlanManager.cpp          |   3 +-
 src/models/BandPlanManager.h            |   5 +
 tests/band_plan_license_filter_test.cpp | 212 ++++++++++++++++++++++++++++++++
 4 files changed, 230 insertions(+), 1 deletion(-)

Generated by AetherClaude (automated agent for AetherSDR)


🤖 aethersdr-agent · cost: $10.0004 · model: claude-opus-4-7

…gionsForBand (#3060). Principle IV.

PR #3050 (closing #2649) added a license-class-aware overload of
BandPlanManager::contiguousRegionsForBand that filters segments by the
operator's class before merging contiguous regions. The filter-BEFORE-merge
ordering is load-bearing: if an allowed segment is adjacent to a disallowed
one, they must NOT merge — otherwise the merged region's center could fall
on a disallowed frequency. The implementation comment calls this out, but
no test pinned the invariant. A future refactor (STL-algorithm
consolidation, parallelisation of the segment loop) could silently
re-introduce the bug.

This commit adds tests/band_plan_license_filter_test.cpp covering the five
scenarios enumerated in the issue:

  1. Empty class → no filter (three-arg with "" == two-arg).
  2. Empty-license segments (BCN markers) always pass.
  3. Class filter drops non-matching segments [E]/[G]/[E,G] under T/G/E.
  4. Adjacent allowed/disallowed segments stay separate (THE invariant).
  5. Trailing-whitespace allowedClass is trimmed; "GE" doesn't match
     "G,E" substring-style; lowercase doesn't match uppercase.

To make scenario 5 pass and to keep the comparison symmetric, the
allowedClass argument is now .trimmed() at the top of the filter — the
segment-side code was already trimmed per element. This is a one-line
forgiveness fix; existing callers that pass already-trimmed strings
(MainWindow::startSwrSweep, AtuPreTuneDialog::populateBands) are
unaffected.

A small public test hook BandPlanManager::setSegmentsForTest is added
so the test can inject synthetic segments without going through resource
loading or AppSettings. Matches the *ForTest naming convention already
used in PerfTelemetry::resetForTest and
AetherAx25LibmodemShim::processRecoveredBitsForTest.

Honors Principle IV (region-aware data comes from BandPlanManager, not
BandDefs.h) by hardening the regression-guard around the manager's
license-class filter — exactly the surface a refactor cleanup pass would
be tempted to consolidate.

Blast radius: risk_score=0.198, 4 high-risk affected (top: MainWindow::MainWindow, MainWindow::buildMenuBar, MainWindow::buildUI). Header change is purely additive (new public inline setSegmentsForTest) — no existing API touched; the high-risk callers don't reference the new symbol and aren't behaviorally affected, only a recompile cascade. The .cpp change adds .trimmed() to one argument — forgiveness-only (strings that previously failed strict comparison may now match); no caller currently passes whitespace.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
BandPlanManager.h:4 includes <QColor> for the Segment struct's color
field.  The test target was only linking Qt6::Core, so the test build
failed with `fatal error: QColor: No such file or directory`.

Fix: add Qt6::Gui to target_link_libraries.  Local build clean, all 13
test scenarios pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

@aethersdr-agent aethersdr-agent Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Looks good — focused regression pin for the filter-before-merge invariant.

Strengths

  • Scenario 4 directly captures the bug class the comment in BandPlanManager.cpp:84 warns about, so a future STL-algorithm refactor that hoists the filter past the merge will trip a deterministic test.
  • Test hook follows the existing …ForTest convention used by PerfTelemetry::resetForTest and AetherAx25LibmodemShim::processRecoveredBitsForTest.
  • Tests bypass loadPlans() / AppSettings cleanly — no global state leaked between scenarios.

Small notes (non-blocking)

  • The needle = allowedClass.trimmed() change in BandPlanManager.cpp:88 is a behavior change beyond the stated "pin invariant" scope (previously trailing whitespace on allowedClass would not match; now it does). Worth a one-line mention in the PR body since the title only advertises the tests. Scenario 5 covers the new behavior, so functionally fine.
  • setSegmentsForTest lives in the public header. Naming is clear enough, but if you want to harden it later, wrapping in #ifdef AETHER_TESTING or adding a friend declaration would prevent accidental call from production code. Not required given the codebase's existing convention.

Thanks for the careful comments — especially the inline derivation of why E-class A and C stay separate in scenario 3. Makes the test self-documenting.


🤖 aethersdr-agent · cost: $2.3574 · model: claude-opus-4-7

@ten9876 ten9876 merged commit 2a5d039 into main May 25, 2026
5 checks passed
@ten9876 ten9876 deleted the aetherclaude/issue-3060 branch May 25, 2026 03:21
G6PWY-Chris pushed a commit to G6PWY-Chris/AetherSDR that referenced this pull request Jun 22, 2026
…gionsForBand (aethersdr#3060). Principle IV. (aethersdr#3089)

## Summary

Fixes aethersdr#3060

### What was changed

Pin filter-before-merge invariant on license-class-aware
contiguousRegionsForBand (aethersdr#3060). Principle IV.

### Files modified

- `CMakeLists.txt`
- `src/models/BandPlanManager.cpp`
- `src/models/BandPlanManager.h`
- `tests/band_plan_license_filter_test.cpp`

```
 CMakeLists.txt                          |  11 ++
 src/models/BandPlanManager.cpp          |   3 +-
 src/models/BandPlanManager.h            |   5 +
 tests/band_plan_license_filter_test.cpp | 212 ++++++++++++++++++++++++++++++++
 4 files changed, 230 insertions(+), 1 deletion(-)
```

---
Generated by AetherClaude (automated agent for AetherSDR)

---
<sub>🤖 aethersdr-agent · cost: $10.0004 · model: claude-opus-4-7</sub>

---------

Co-authored-by: aethersdr-agent[bot] <273844287+aethersdr-agent[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Co-authored-by: Jeremy Fielder <kk7gwy@aethersdr.com>
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.

Unit tests for license-class-filtered contiguousRegionsForBand (follow-up to #2649)

1 participant