Follow-up to #2480 (merge commit 59c1c378).
PerfTelemetry is the diagnostic singleton @jensenpat just landed, and the public API is deterministic enough to unit-test in isolation:
- Inputs are explicit recording calls (
recordFrameAge, recordPanUpdate, recordUdpBatch, etc.) with caller-supplied numeric values
- Window aggregation runs on a 1-second cadence triggered by
maybeLogSummary(now) — tests can drive now directly
- Output goes through the
lcPerf logging category — capturable via a Qt message handler hook for the duration of the test
Suggested test cases
Disabled hot-path is no-op:
- With
lcPerf disabled, calling recordPanUpdate(50.0) repeatedly emits no log lines, mutates no observable state.
Window aggregation:
- Enable
lcPerf. Feed N pan-update samples at known values. Advance to T+1s. Verify the emitted PerfSummary line contains the correct count, p95, max.
- Window resets after summary: subsequent samples don't carry old state.
Stall thresholds:
recordPanUpdate(9.0) → emits a PerfStall line with kind=panUpdate. Threshold is 8.0 ms.
recordPanUpdate(7.0) → no stall line.
- Same matrix for the other thresholds (frameAge, render, udpDrain, etc.).
Drag-aware UI lag thresholds:
setDragActive(false) then recordUiHeartbeat() with a 40 ms gap → no stall (idle threshold is 50 ms).
setDragActive(true) same input → emits a stall (drag threshold is 33 ms).
Frame-restart counter:
recordFrameRestart(Panadapter) × 3 → counter shows 3 in the next summary.
Percentile-95 boundary cases:
- Empty input → returns 0.0
- Single value → returns that value
- 100 values [1..100] → returns 95
- Already-sorted input → result matches unsorted
Window timing:
- Records spread across 950 ms → no summary yet
- One more record at 1100 ms → triggers summary with full count
Test infrastructure
The Qt message handler hook is the standard way to capture log output:
QString lastLine;
qInstallMessageHandler([](QtMsgType, const QMessageLogContext& ctx, const QString& msg) {
if (QString::fromUtf8(ctx.category) == "aether.perf")
lastLine = msg;
});
Test file at tests/perf_telemetry_test.cpp, registered in CMakeLists.txt as add_executable(perf_telemetry_test ...) per the existing test pattern.
Priority
Low. The class is exercised continuously when telemetry is enabled in production runs, so behavioral regressions surface fast. But unit coverage would catch threshold/percentile/aggregation regressions before a release ships, and the threshold values themselves are likely to be tuned over time — tests prevent silent drift.
73, Jeremy KK7GWY & Claude (AI dev partner)
Follow-up to #2480 (merge commit
59c1c378).PerfTelemetry is the diagnostic singleton @jensenpat just landed, and the public API is deterministic enough to unit-test in isolation:
recordFrameAge,recordPanUpdate,recordUdpBatch, etc.) with caller-supplied numeric valuesmaybeLogSummary(now)— tests can drivenowdirectlylcPerflogging category — capturable via a Qt message handler hook for the duration of the testSuggested test cases
Disabled hot-path is no-op:
lcPerfdisabled, callingrecordPanUpdate(50.0)repeatedly emits no log lines, mutates no observable state.Window aggregation:
lcPerf. Feed N pan-update samples at known values. Advance to T+1s. Verify the emittedPerfSummaryline contains the correct count, p95, max.Stall thresholds:
recordPanUpdate(9.0)→ emits aPerfStallline withkind=panUpdate. Threshold is 8.0 ms.recordPanUpdate(7.0)→ no stall line.Drag-aware UI lag thresholds:
setDragActive(false)thenrecordUiHeartbeat()with a 40 ms gap → no stall (idle threshold is 50 ms).setDragActive(true)same input → emits a stall (drag threshold is 33 ms).Frame-restart counter:
recordFrameRestart(Panadapter)× 3 → counter shows 3 in the next summary.Percentile-95 boundary cases:
Window timing:
Test infrastructure
The Qt message handler hook is the standard way to capture log output:
Test file at
tests/perf_telemetry_test.cpp, registered in CMakeLists.txt asadd_executable(perf_telemetry_test ...)per the existing test pattern.Priority
Low. The class is exercised continuously when telemetry is enabled in production runs, so behavioral regressions surface fast. But unit coverage would catch threshold/percentile/aggregation regressions before a release ships, and the threshold values themselves are likely to be tuned over time — tests prevent silent drift.
73, Jeremy KK7GWY & Claude (AI dev partner)