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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
|
#!/usr/bin/env python3
"""Shaders loaded from shared libraries at runtime.
The built-ins are compiled in; these are not. What matters is that a plugin's
shader is indistinguishable from a built-in once loaded (same config syntax,
same parameters, same pass), and that everything which can be wrong with a
library fails safely: a wrong ABI, a name that already exists, a directory
that is not there.
The example in contrib/ is built and loaded here too, so it cannot rot.
"""
import os
import shutil
import sys
import tempfile
from harness import Session, check, report
HERE = os.path.dirname(os.path.abspath(__file__))
BUILD = os.path.join(HERE, "..", "build")
GOOD = os.path.join(BUILD, "testshader.so")
BAD = os.path.join(BUILD, "badshader.so")
EXAMPLE = os.path.join(BUILD, "exampleshader.so")
# A pane that paints one known cell and then sits still.
SH = ["/bin/sh", "-c", 'printf "\\033]2;p\\007"; printf "BLOCK\\n"; read x']
def session_dir(plugins, config, shaders_subdir=False):
"""A config file, and the plugins it should find beside it."""
d = tempfile.mkdtemp(prefix="slosh-plug-")
dest = os.path.join(d, "shaders") if shaders_subdir else d
os.makedirs(dest, exist_ok=True)
for p in plugins:
shutil.copy(p, dest)
path = os.path.join(d, "config.kdl")
with open(path, "w") as f:
f.write(config)
return path, dest
def bg_at(snap, pane, row=0, col=0):
run = snap.style_at(pane["content_x"] + col, pane["content_y"] + row)
return (run or {}).get("bg")
def fg_at(snap, pane, row=0, col=0):
run = snap.style_at(pane["content_x"] + col, pane["content_y"] + row)
return (run or {}).get("fg")
def test_a_plugin_shader_is_used_like_a_builtin():
cfg, d = session_dir(
[GOOD],
'shader_dir "%s"\nshaders {\n testfill amount=255 color="#00ff00"\n}\n',
)
# the directory is known only after it exists, so write the config now
with open(cfg, "w") as f:
f.write(
'shader_dir "%s"\nshaders {\n'
' testfill amount=255 color="#00ff00"\n}\n' % d
)
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
pane = s.pane()
check(
"the loaded shader ran",
bg_at(snap, pane) == "#00ff00",
str(snap.style_at(pane["content_x"], pane["content_y"])),
)
def test_it_is_found_beside_the_config_by_default():
cfg, d = session_dir([GOOD], "", shaders_subdir=True)
with open(cfg, "w") as f:
f.write('shaders {\n testfill amount=255 color="#0000ff"\n}\n')
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
check("no shader_dir needed", bg_at(snap, s.pane()) == "#0000ff", snap.screen())
def test_the_config_number_reaches_the_plugin():
cfg, d = session_dir([GOOD], "")
with open(cfg, "w") as f:
f.write(
'shader_dir "%s"\nshaders {\n'
' teststripe amount=255 band=3 color="#ff0000"\n}\n' % d
)
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
pane = s.pane()
got = [bg_at(snap, pane, 0, x) for x in range(6)]
check(
"every third column, as asked",
got[0] == "#ff0000"
and got[3] == "#ff0000"
and got[1] != "#ff0000"
and got[2] != "#ff0000",
str(got),
)
def test_a_stale_plugin_is_refused_and_the_rest_still_load():
cfg, d = session_dir([GOOD, BAD], "")
with open(cfg, "w") as f:
f.write(
'shader_dir "%s"\nshaders {\n'
' testfill amount=255 color="#00ff00"\n}\n' % d
)
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
check("the session is alive", s.alive())
check(
"the good plugin still loaded",
bg_at(snap, s.pane()) == "#00ff00",
snap.screen(),
)
def test_an_unknown_shader_is_skipped_not_fatal():
cfg, d = session_dir([], "")
with open(cfg, "w") as f:
f.write("shaders {\n nosuchshader amount=255\n dim amount=0\n}\n")
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
check("the session survives a name it does not know", s.alive())
check("and still draws", "BLOCK" in snap.screen(), snap.screen())
def test_no_shader_directory_at_all_is_normal():
cfg, d = session_dir([], "")
with open(cfg, "w") as f:
f.write(
'shader_dir "/nonexistent/slosh/shaders"\n'
"shaders {\n dim amount=100\n}\n"
)
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
check("a missing directory is not an error", s.alive())
check("and the built-ins are unaffected", "BLOCK" in snap.screen())
def test_the_directory_may_be_written_with_a_tilde():
cfg, d = session_dir([GOOD], "", shaders_subdir=True)
home = os.path.dirname(d)
with open(cfg, "w") as f:
f.write(
'shader_dir "~/shaders"\nshaders {\n'
' testfill amount=255 color="#00ff00"\n}\n'
)
with Session(SH, cols=40, rows=10, config=cfg, env={"HOME": home}) as s:
snap = s.until_text("BLOCK")
check(
"~ in shader_dir is the home directory",
bg_at(snap, s.pane()) == "#00ff00",
snap.screen(),
)
def test_the_contrib_example_builds_and_loads():
cfg, d = session_dir([EXAMPLE], "")
with open(cfg, "w") as f:
f.write('shader_dir "%s"\nshaders {\n checker amount=255 band=2\n}\n' % d)
with Session(SH, cols=40, rows=10, config=cfg) as s:
snap = s.until_text("BLOCK")
pane = s.pane()
# Asserted on the foreground: the checkerboard darkens towards black
# and the default background already *is* black, so two squares that
# differ would still read the same there. Reading the wrong channel
# would make a working effect look like a broken one.
a = fg_at(snap, pane, 0, 0)
b = fg_at(snap, pane, 0, 2)
check("the shipped example draws something", a != b, f"{a} {b}")
for name, fn in sorted(list(globals().items())):
if name.startswith("test_"):
fn()
sys.exit(report())
|