1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/usr/bin/env python3
"""Every chrome preset in contrib/chrome/ works in slosh.
The content presets are cross-checked against the toy that previews them
(test_shadertoy.py). A frame has no preview — the toy draws a pane's contents —
so these are checked the only way left that means anything: load the file into a
real session and look at the cells. An example that reads beautifully and does
nothing when you paste it is worse than no example.
Two things are asserted of each, and the second matters as much as the first:
the frame moves, and the pane's own output does not. A chrome pass that leaked
into the contents would be a shader bug wearing a preset's clothes.
"""
import glob
import os
import sys
import tempfile
import time
from harness import Session, check, report
HERE = os.path.dirname(os.path.abspath(__file__))
FILES = sorted(glob.glob(os.path.join(HERE, "..", "contrib", "chrome", "*.kdl")))
# Prints a screenful of known-coloured text, then waits.
SH = [
"/bin/sh",
"-c",
'printf "\\033]2;p\\007"; '
'printf "\\033[38;2;0;255;0mBLOCK\\033[0m\\n"; stty raw -echo; cat',
]
# Announcements stack up from the bottom right and are drawn over everything,
# including a pane's bottom rule -- and this test reloads eight times. A toast
# that outlived its reload would be measured as the frame having changed, so
# they are told to expire immediately.
THEME = 'theme { default_fg "#ffffff" default_bg "#000000" }\ntoast_ms 1\n'
def frame_cells(snap, pane):
"""Every cell of the frame's outer ring, clockwise from the top-left."""
x0, y0, w, h = pane["x"], pane["y"], pane["w"], pane["h"]
out = []
for x in range(w):
out.append(snap.style_at(x0 + x, y0))
for y in range(1, h):
out.append(snap.style_at(x0 + w - 1, y0 + y))
for x in range(w - 2, -1, -1):
out.append(snap.style_at(x0 + x, y0 + h - 1))
for y in range(h - 2, 0, -1):
out.append(snap.style_at(x0, y0 + y))
return [(r or {}).get("fg") for r in out]
def content_cells(snap, pane):
return [
(snap.style_at(pane["content_x"] + c, pane["content_y"] + r) or {}).get("fg")
for r in range(pane["content_h"])
for c in range(pane["content_w"])
]
def test_every_chrome_preset_moves_the_frame():
check("contrib ships chrome presets", len(FILES) >= 5, str(len(FILES)))
cfg = tempfile.NamedTemporaryFile("w", suffix=".kdl", delete=False)
cfg.write(THEME)
cfg.close()
with Session(SH, cols=54, rows=12, config=cfg.name) as s:
s.until_text("BLOCK")
base_snap = s.snapshot()
base_frame = frame_cells(base_snap, s.pane())
base_content = content_cells(base_snap, s.pane())
for path in FILES:
name = os.path.basename(path)
with open(cfg.name, "w") as f:
f.write(THEME + open(path).read())
s.api("reload")
s.settle(40) # past toast_ms, so no announcement is on the frame
# Polled, not sampled a fixed few times. Most of these are animated
# and one is deliberately quiet for a second at a stretch
# (`heartbeat`), so "look five times, 50ms apart" is a guess about
# something observable -- and a guess that fails under load, where
# five snapshots can land inside one rest. The deadline only bounds
# failure: a preset that paints is seen the moment it paints.
seen = set()
content_moved = False
started = time.monotonic()
deadline = started + 3.0
while time.monotonic() < deadline:
snap = s.snapshot()
pane = s.pane()
seen.update(
i
for i, (a, b) in enumerate(zip(base_frame, frame_cells(snap, pane)))
if a != b
)
if content_cells(snap, pane) != base_content:
content_moved = True
# The frame answer can stop the clock; the contents answer is
# "nothing happened", and nothing needs a while to be sure of.
if seen and time.monotonic() - started > 0.3:
break
time.sleep(0.03)
check(f"{name} colours the frame", len(seen) > 0, "no cell changed")
check(
f"{name} leaves the contents alone",
not content_moved,
"content changed",
)
# ...and taking it out again puts the frame back, so one preset cannot
# leave a mark on the next.
with open(cfg.name, "w") as f:
f.write(THEME)
s.api("reload")
s.settle(40)
check(
"removing the last one restores the frame",
frame_cells(s.snapshot(), s.pane()) == base_frame,
"still shaded",
)
os.unlink(cfg.name)
if __name__ == "__main__":
test_every_chrome_preset_moves_the_frame()
sys.exit(report())
|