aboutsummaryrefslogtreecommitdiffstats
path: root/tests/shader_test.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-16 15:37:16 +0200
committeruser <user@clank>2026-08-16 15:37:16 +0200
commita5114a9e923814878cd9c331adfe7a4a3ae5113a (patch)
treef06a3e780969eaadc59508503a06fb1f096eaf55 /tests/shader_test.c
parentsl0ppty: three ways back from a painted pane (diff)
downloadslosh-a5114a9e923814878cd9c331adfe7a4a3ae5113a.tar.gz
slosh-a5114a9e923814878cd9c331adfe7a4a3ae5113a.tar.xz
slosh-a5114a9e923814878cd9c331adfe7a4a3ae5113a.zip
sl0ppty: zero strength is not a pass
`shader_abi.h` has said `0 is identity` from the start, and the pass did not honour it: it materialised a cell's default colours -- resolving "whatever the terminal draws" into our `default_fg`/`default_bg` -- and only then called a shader that was contractually going to change nothing. So a chain that is currently *off* still repainted. `focused * 200` on an unfocused pane, `(since < 500) * 255` half a second later, `(x < 10) * 200` from column ten on: every one of those wrote our idea of the default over the terminal's own. Visible whenever the two differ, which for a real terminal theme is always, and invisible in the test suite because the tests compared against the materialised value. Found by measuring, not by reading: I was checking that each example I was about to hand over actually changed the pane, and one that should have changed nothing changed eight style runs. The fix is an ordering: work out the strength, and if it is zero leave the cell before touching it. A constant-zero shader now skips its whole rect before the loops. Cheaper too -- a pass that is off costs no materialisation and no call -- though that is a side effect and not the reason. Three test changes, all of them the same correction. `tests/shader_test.c`'s traversal probe was built `{.kind, .fn}` with `amount` defaulting to 0, so it was asking to be called for every cell at zero strength; it now asks for 255, which is what those checks are about. A new block pins the rule: a zero-strength pass visits nothing and leaves an unset cell unset. And four checks in `tests/test_shader_expr.py` asserted `== "#ffffff"` for cells outside an effect's band -- the materialised default. They now assert the cell has no colour of its own at all, which is the stronger claim and the one that would have caught this.
Diffstat (limited to 'tests/shader_test.c')
-rw-r--r--tests/shader_test.c31
1 files changed, 28 insertions, 3 deletions
diff --git a/tests/shader_test.c b/tests/shader_test.c
index a8f68d3..872ff99 100644
--- a/tests/shader_test.c
+++ b/tests/shader_test.c
@@ -324,7 +324,9 @@ int main(void) {
for (uint16_t x = 0; x < 8; x++)
put(&s, x, y, rgb(0x11, 0x22, 0x33), rgb(0, 0, 0));
- shader_t sh = {.kind = "probe", .fn = probe_fn};
+ /* Strength 255: the probe ignores it, but a pass at 0 is an identity and
+ * identities are skipped, so a traversal test has to ask for a real one. */
+ shader_t sh = {.kind = "probe", .fn = probe_fn, .amount = 255};
shade_ctx_t base = base_ctx();
base.now_ms = 1234;
base.focused = true;
@@ -338,6 +340,7 @@ int main(void) {
probe.x == 1 && probe.y == 1, "");
ok("and the cell at that position is the right one",
ceq(probe.saw_fg, 0x11, 0x22, 0x33), shown(probe.saw_fg));
+
ok("time is passed through for animation", probe.now_ms == 1234, "");
ok("so is focus", probe.focused, "");
@@ -349,13 +352,33 @@ int main(void) {
screen_free(&s);
}
+ /* ---- zero strength is not a pass at all ---- */
+ {
+ memset(&probe, 0, sizeof probe);
+ screen_init(&s, 4, 2); /* every colour unset: the terminal's own */
+ shader_t idle = {.kind = "probe", .fn = probe_fn}; /* amount 0 */
+ shade_ctx_t base = base_ctx();
+ shade_apply(&s, &idle, 1, (rect_t){0, 0, 4, 2}, NULL, &base);
+
+ ok("a pass at zero strength visits nothing", probe.calls == 0, "");
+ /* `0 is identity` in the ABI, and the pass used to materialise a cell's
+ * default colours before honouring that -- so a chain like `focused * 200`
+ * repainted the frame of the pane it was leaving alone. */
+ ok("and a default-coloured cell is left saying so",
+ screen_at(&s, 0, 0)->fg.set == false && screen_at(&s, 0, 0)->bg.set == false,
+ "an identity pass materialised the terminal's default into ours");
+ screen_free(&s);
+ }
+
/* ---- the hole: a frame is a rect minus its contents ---- */
{
memset(&probe, 0, sizeof probe);
screen_init(&s, 6, 5);
fill(&s, rgb(0xff, 0xff, 0xff), rgb(0xff, 0xff, 0xff));
- shader_t sh = {.kind = "probe", .fn = probe_fn};
+ /* Strength 255: the probe ignores it, but a pass at 0 is an identity and
+ * identities are skipped, so a traversal test has to ask for a real one. */
+ shader_t sh = {.kind = "probe", .fn = probe_fn, .amount = 255};
shade_ctx_t base = base_ctx();
rect_t hole = {1, 1, 4, 3};
shade_apply(&s, &sh, 1, (rect_t){0, 0, 6, 5}, &hole, &base);
@@ -381,7 +404,9 @@ int main(void) {
memset(&probe, 0, sizeof probe);
screen_init(&s, 4, 2);
fill(&s, rgb(0xff, 0xff, 0xff), rgb(0xff, 0xff, 0xff));
- shader_t sh = {.kind = "probe", .fn = probe_fn};
+ /* Strength 255: the probe ignores it, but a pass at 0 is an identity and
+ * identities are skipped, so a traversal test has to ask for a real one. */
+ shader_t sh = {.kind = "probe", .fn = probe_fn, .amount = 255};
shade_ctx_t base = base_ctx();
rect_t empty = {1, 1, 0, 0};
shade_apply(&s, &sh, 1, (rect_t){0, 0, 4, 2}, &empty, &base);