fe4087d471
v1.32-v1.36 audit (shipped 5 features in one batch) had 4
real bugs the test suite missed, plus 2 htop/btop-parity
gaps. This release fixes all 4 bugs and adds the 2 parity
features, with regression tests for each.
P0-1 (CRITICAL) Bug 1: Sparkline type confusion
The v1.32 algorithm stored raw f64 bits in u64 cells, then
normalize_history overwrote them with integer-as-u64
(0..=255 cast to u64). The renderer's
f64::from_bits() interpreted these as f64 bits, producing
subnormal ~6e-324, then .max(0.0).min(255.0) as u8 -> 0.
Every sparkline cell rendered as blank after the first
refresh. The v1.31 IO-RATE sparkline worked ONCE then
degraded; the v1.32 CPU% and RSS sparklines never worked.
Fix: change the three history maps from VecDeque<u64> to
VecDeque<u8>. update_io_history now uses a two-phase
algorithm: (1) collect raw f64 samples into per-PID
pending Vecs, (2) write normalized u8 to the public
history. The renderer reads u8 directly with no f64
round-trip. Side effect: the per-refresh overwrite
replaces the v1.31 sliding window. 12 samples of
in-flight history is the same time window in practice
(12 * 6.5s = 78s) as 12 refreshes of windowed history
under the same refresh cadence.
P0-2 Bug 1 regression test: tests assert the io_history
cells are non-zero after update with a non-zero rate.
v1.32 would have left them at 0 (subnormal); v1.37
produces true u8 1..=255.
P0-3 (HIGH) Bug 2: Tree prefix bar continuation
The v1.34 ancestor_active_in_next check walked the next
row's chain only 0..=d steps. For a tree 1->{2->3, 4}
at row pid=2, the bar at depth 0 (above pid=2) checked
only one step of the next row's chain and missed
ancestor=1 in the chain. Result: bar showed ' ' instead
of '| ' for trees deeper than 1. v1.37 walks the full
chain to the root.
P0-4 Bug 2 regression test: 8 tree_prefix tests covering
single root, two-level fan-out, three-level chain,
sibling-after-deep, fold marker on/off, and the leaf
case. None existed before; would have caught Bug 2.
P0-5 (MEDIUM) Bug 3: Mouse y-offset
The Process tab render layout is: line 0 = title,
line 1 = blank, line 2 = column header, lines 3+ = rows.
The mouse handler subtracted table.y + 2 (one too high).
Clicking the column header mapped to process_cursor = 0
(first data row), and every row was off by one. v1.37:
table.y + 3.
P0-6 (LOW) Bug 4: Right-click filter
Re-verified: selected_pid() already uses
visible_processes() (post-filter list), so the audit's
bug report was incorrect. Added a regression test
instead that asserts selected_pid returns the right PID
with an active filter.
P1-1 htop parity: Re-click to expand
Mirrors htop's KEY_RECLICK handling. A second left-click
on the same CPU row toggles expand; a click on a different
row just selects. New App::last_clicked_cpu: Option<u32>
tracks the last click.
P1-2 htop parity: PageUp/PageDown
Already wired in v1.35 via page_selection(tab-aware).
Added a regression test that asserts the Process-tab
PageDown moves the cursor by ROWS_PER_PAGE (8) rows and
clamps at the last visible row.
Test count 127 -> 140 (+13):
- 2 new update_io_history tests (Bug 1 regression)
- 1 new io_history no-rate test
- 8 new tree_prefix tests (Bug 2 regression)
- 1 new selected_pid filter test (Bug 4 regression)
- 1 new page_selection test (P1-2)
Redox stripped binary: 4,242,280 bytes (-8 KiB from v1.36;
the new methods' bodies are tiny and the linker dedup'd
shared code).
Compile warnings: 56 (unchanged; pre-existing).