Context
PR #3202 introduced absolute, drift-corrected scheduling in CwxLocalKeyer to fix macOS sidetone stutter under GUI load (#2980). The implementation is correct and was manually verified by @jensenpat on macOS with the canonical "30 S characters + aggressive panadapter drag" methodology.
What's missing is an automated regression test for the drift correction itself. The existing cw_sidetone_test and cwx_panel_test were written against the relative-scheduling path and pass against the new code, but they don't specifically exercise the late-tick-recovery behaviour that the new m_elapsed + m_nextEdgeMs machinery exists to provide.
Proposed test shape
tests/cwx_local_keyer_drift_test.cpp:
- Construct a
CwxLocalKeyer and start a deterministic macro (e.g. 10 × S — pure dit pattern, easy timing).
- Drive scheduling synchronously by intercepting the timer callback rather than letting Qt's event loop fire — call
onTick() manually with a simulated QElapsedTimer offset that lags behind the intended timeline.
- After each
onTick, assert that m_nextEdgeMs continues to track the intended cumulative time (sum of unit durations) and that the requested next-wait shortens when the simulated clock is ahead of the target.
- Add a "sustained overload" case where simulated elapsed time exceeds
m_nextEdgeMs — verify wait clamps to 1 ms and the keyer fires elements back-to-back to catch up.
- Cross-macro continuity case: enqueue two
start() calls back-to-back, drain the first, verify epoch survives into the second and timing continuity holds.
- Symmetric reset cases:
stop() and final drain both invalidate m_elapsed and zero m_nextEdgeMs so the next run starts fresh.
Why this matters
CwxLocalKeyer::scheduleNext is the hot path for local sidetone cadence and the drift logic is subtle (absolute target time vs relative wait, partial-drain keeping epoch alive, recursive scheduleNext re-entry on empty-but-queued state). A future refactor — especially one that touches the m_currentlyDown + CharGap insertion at line 117 or the recursive call at line 122-124 — could quietly regress the drift behaviour and only surface as "macOS contest CWX feels stuttery again."
Scope
Single new test file + CMakeLists.txt entry. No production-code changes. The QElapsedTimer injection point may need a tiny seam in CwxLocalKeyer (or a friend-class hook in the test) — author's call.
References
73, Jeremy KK7GWY & Claude (AI dev partner)
Context
PR #3202 introduced absolute, drift-corrected scheduling in
CwxLocalKeyerto fix macOS sidetone stutter under GUI load (#2980). The implementation is correct and was manually verified by @jensenpat on macOS with the canonical "30 S characters + aggressive panadapter drag" methodology.What's missing is an automated regression test for the drift correction itself. The existing
cw_sidetone_testandcwx_panel_testwere written against the relative-scheduling path and pass against the new code, but they don't specifically exercise the late-tick-recovery behaviour that the newm_elapsed+m_nextEdgeMsmachinery exists to provide.Proposed test shape
tests/cwx_local_keyer_drift_test.cpp:CwxLocalKeyerand start a deterministic macro (e.g. 10 ×S— pure dit pattern, easy timing).onTick()manually with a simulatedQElapsedTimeroffset that lags behind the intended timeline.onTick, assert thatm_nextEdgeMscontinues to track the intended cumulative time (sum of unit durations) and that the requested next-wait shortens when the simulated clock is ahead of the target.m_nextEdgeMs— verifywaitclamps to 1 ms and the keyer fires elements back-to-back to catch up.start()calls back-to-back, drain the first, verify epoch survives into the second and timing continuity holds.stop()and final drain both invalidatem_elapsedand zerom_nextEdgeMsso the next run starts fresh.Why this matters
CwxLocalKeyer::scheduleNextis the hot path for local sidetone cadence and the drift logic is subtle (absolute target time vs relative wait, partial-drain keeping epoch alive, recursivescheduleNextre-entry on empty-but-queued state). A future refactor — especially one that touches them_currentlyDown+ CharGap insertion at line 117 or the recursive call at line 122-124 — could quietly regress the drift behaviour and only surface as "macOS contest CWX feels stuttery again."Scope
Single new test file + CMakeLists.txt entry. No production-code changes. The
QElapsedTimerinjection point may need a tiny seam inCwxLocalKeyer(or a friend-class hook in the test) — author's call.References
73, Jeremy KK7GWY & Claude (AI dev partner)