test(plot): pin the hot-handle re-index on region removal, refresh coverage

`remove_region()` now clears or decrements the published `hot_.region` with the
cache it re-indexes, so a frame after a removal highlights the region the handle
actually names. The new case covers both branches (a handle above the hole
follows it down, the removed region's handle is cleared with its zone), and the
coverage document is regenerated by the command that measured it: 95.2% of src/,
every file at or above 80%.
This commit is contained in:
Martino Ferrari committed 2026-09-28 22:42:56 +02:00
1 parent 1f5ce0d4a5
commit f132a5ec92
2 files changed
+41 -6

No files matched your search

+6 -6
View File
@@ -43,7 +43,7 @@ when one is missing.
<!-- BEGIN GENERATED: tools/coverage.sh -->
```
coverage_floor: ok -- 23 files under src/, aggregate 95.3% lines (3284/3447), every file at or above 80%, aggregate target 90%
coverage_floor: ok -- 23 files under src/, aggregate 95.2% lines (3302/3469), every file at or above 80%, aggregate target 90%
```
| File | Lines | Line rate | Functions |
@@ -54,24 +54,24 @@ coverage_floor: ok -- 23 files under src/, aggregate 95.3% lines (3284/3447), ev
| `src/data/stream_series.cpp` | 39/39 | 100.0% | 100.0% |
| `src/gl/handle.cpp` | 256/256 | 100.0% | 100.0% |
| `src/gl/loader.cpp` | 81/81 | 100.0% | 100.0% |
| `src/input/controller.cpp` | 327/341 | 95.9% | 100.0% |
| `src/input/controller.cpp` | 329/343 | 95.9% | 100.0% |
| `src/math/transform.cpp` | 28/28 | 100.0% | 100.0% |
| `src/plot/axis.cpp` | 12/12 | 100.0% | 100.0% |
| `src/plot/figure.cpp` | 213/218 | 97.7% | 95.5% |
| `src/plot/figure.cpp` | 218/223 | 97.8% | 95.5% |
| `src/plot/grid.cpp` | 15/16 | 93.8% | 100.0% |
| `src/plot/measure.cpp` | 21/21 | 100.0% | 100.0% |
| `src/plot/plot.cpp` | 733/753 | 97.3% | 98.9% |
| `src/plot/plot.cpp` | 743/763 | 97.4% | 98.9% |
| `src/plot/ticks.cpp` | 93/99 | 93.9% | 100.0% |
| `src/render/buffer.cpp` | 88/98 | 89.8% | 100.0% |
| `src/render/embedded_mono_font.cpp` | 45/45 | 100.0% | 100.0% |
| `src/render/primitives.cpp` | 71/75 | 94.7% | 100.0% |
| `src/render/program.cpp` | 26/27 | 96.3% | 100.0% |
| `src/render/renderer.cpp` | 529/604 | 87.6% | 93.7% |
| `src/render/renderer.cpp` | 530/609 | 87.0% | 93.7% |
| `src/render/text.cpp` | 27/30 | 90.0% | 100.0% |
| `src/render/text_atlas.cpp` | 40/41 | 97.6% | 100.0% |
| `src/render/vertex_array.cpp` | 59/59 | 100.0% | 100.0% |
| `src/text/true_type_font.cpp` | 270/291 | 92.8% | 96.8% |
| **src/ aggregate** | **3284/3447** | **95.3%** | **98.3%** |
| **src/ aggregate** | **3302/3469** | **95.2%** | **98.3%** |
<!-- END GENERATED: tools/coverage.sh -->
+35
View File
@@ -1787,6 +1787,41 @@ PLOT_TEST(removing_a_region_shifts_the_ids_and_keeps_the_statistics) {
PLOT_CHECK(!p.region_stats(9u, tr).valid);
}
/// The published hot handles name a region by the same index the vector does, so a
/// removal has to re-index them with the rest: a frame drawn before the next pointer event
/// reads `hot_handles()`, and one left naming the old index would highlight the wrong
/// region's bound -- and an open drag would edit a different region. The handle on the
/// removed region is cleared with its zone; one naming a region above the hole follows it
/// down.
PLOT_TEST(removing_a_region_reindexes_the_published_hot_handles) {
plot::Plot p;
p.set_viewport({0.0f, 0.0f, 800.0f, 400.0f});
p.add_x_axis({"t", {0.0, 99.0}, false, plot::AxisPlace::Bottom, false});
p.add_y_axis({"v", {0.0, 99.0}, false, plot::AxisPlace::Left, false});
plot::Region bounds;
bounds.snap = plot::RegionSnap::Free;
PLOT_CHECK(p.add_region(bounds) == 0u);
PLOT_CHECK(p.add_region(bounds) == 1u);
PLOT_CHECK(p.add_region(bounds) == 2u);
// A handle on the last region, with the hole below it: it follows the region down.
plot::HotHandles hot;
hot.region = 2u;
hot.zone = plot::RegionGrab::Right;
p.set_hot_handles(hot);
p.remove_region(0u);
PLOT_CHECK(p.hot_handles().region == 1u);
PLOT_CHECK(p.hot_handles().zone == plot::RegionGrab::Right);
// A handle on the region that is removed: it is cleared, zone and all.
hot.region = 0u;
hot.zone = plot::RegionGrab::Left;
p.set_hot_handles(hot);
p.remove_region(0u);
PLOT_CHECK(p.hot_handles().region == plot::kInvalid);
PLOT_CHECK(p.hot_handles().zone == plot::RegionGrab::None);
}
/// A bound that is not a number is not a bound. `std::min`/`std::max` propagate a NaN
/// through *both* ends of the pair, so accepting one would not merely invert the interval:
/// it would replace the finite partner too, and a `Free` region stored that way could never