1# NOTE: Tests related to isDeclaredMap go here.
2{
3 isDeclaredMap,
4 lib,
5 runCommand,
6 testers,
7}:
8let
9 inherit (lib.attrsets) recurseIntoAttrs;
10 inherit (testers) shellcheck shfmt testBuildFailure';
11
12 commonArgs = {
13 __structuredAttrs = true;
14 strictDeps = true;
15 preferLocalBuild = true;
16 nativeBuildInputs = [ isDeclaredMap ];
17 };
18
19 check =
20 let
21 mkLine =
22 intro: values:
23 "${if intro == null then "" else intro + " "}check${if values == null then "" else "=" + values}";
24 mkScope =
25 scope: line:
26 if scope == null then
27 line
28 else if scope == "function" then
29 ''
30 foo() {
31 ${line}
32 }
33 foo
34 ''
35 else
36 builtins.throw "Invalid scope: ${scope}";
37 in
38 {
39 name,
40 scope,
41 intro,
42 values,
43 }:
44 runCommand name commonArgs ''
45 set -eu
46
47 ${mkScope scope (mkLine intro values)}
48
49 if isDeclaredMap check; then
50 nixLog "test passed"
51 touch "$out"
52 else
53 nixErrorLog "test failed"
54 exit 1
55 fi
56 '';
57in
58recurseIntoAttrs {
59 shellcheck = shellcheck {
60 name = "isDeclaredMap";
61 src = ./isDeclaredMap.bash;
62 };
63
64 shfmt = shfmt {
65 name = "isDeclaredMap";
66 src = ./isDeclaredMap.bash;
67 };
68
69 undeclaredFails = testBuildFailure' {
70 name = "undeclaredFails";
71 drv = runCommand "undeclared" commonArgs ''
72 set -eu
73 if isDeclaredMap undeclared; then
74 nixLog "test passed"
75 touch "$out"
76 else
77 nixErrorLog "test failed"
78 exit 1
79 fi
80 '';
81 expectedBuilderLogEntries = [
82 "test failed"
83 ];
84 };
85
86 arrayFails = testBuildFailure' {
87 name = "arrayFails";
88 drv = runCommand "array" commonArgs ''
89 set -eu
90 local -a array
91 if isDeclaredMap array; then
92 nixLog "test passed"
93 touch "$out"
94 else
95 nixErrorLog "test failed"
96 exit 1
97 fi
98 '';
99 expectedBuilderLogEntries = [
100 "test failed"
101 ];
102 };
103
104 emptyStringNamerefFails = testBuildFailure' {
105 name = "emptyStringNamerefFails";
106 drv = runCommand "emptyStringNameref" commonArgs ''
107 set -eu
108 if isDeclaredMap ""; then
109 nixLog "test passed"
110 touch "$out"
111 else
112 nixErrorLog "test failed"
113 exit 1
114 fi
115 '';
116 expectedBuilderLogEntries = [
117 "local: `': not a valid identifier"
118 "test failed"
119 ];
120 };
121
122 namerefToEmptyStringFails = testBuildFailure' {
123 name = "namerefToEmptyStringFails";
124 drv = check {
125 name = "namerefToEmptyString";
126 scope = null;
127 intro = "local -n";
128 values = "";
129 };
130 expectedBuilderLogEntries = [
131 "local: `': not a valid identifier"
132 # The test fails in such a way that it exits immediately, without returning to the else branch.
133 ];
134 };
135
136 sameScopeEmptyStringFails = testBuildFailure' {
137 name = "sameScopeEmptyStringFails";
138 drv = check {
139 name = "sameScopeEmptyString";
140 scope = null;
141 intro = null;
142 values = "";
143 };
144 expectedBuilderLogEntries = [
145 "test failed"
146 ];
147 };
148
149 sameScopeEmptyMapFails = testBuildFailure' {
150 name = "sameScopeEmptyMapFails";
151 drv = check {
152 name = "sameScopeEmptyMap";
153 scope = null;
154 intro = null;
155 values = "()";
156 };
157 expectedBuilderLogEntries = [
158 "test failed"
159 ];
160 };
161
162 # Fails because maps must be declared with the -A flag.
163 sameScopeSingletonMapFails = testBuildFailure' {
164 name = "sameScopeSingletonMapFails";
165 drv = check {
166 name = "sameScopeSingletonMap";
167 scope = null;
168 intro = null;
169 values = ''([greeting]="hello!")'';
170 };
171 expectedBuilderLogEntries = [
172 "greeting: unbound variable"
173 ];
174 };
175
176 sameScopeLocalUnsetMap = check {
177 name = "sameScopeLocalUnsetMap";
178 scope = null;
179 intro = "local -A";
180 values = null;
181 };
182
183 sameScopeLocalEmptyMap = check {
184 name = "sameScopeLocalEmptyMap";
185 scope = null;
186 intro = "local -A";
187 values = "()";
188 };
189
190 sameScopeLocalSingletonMap = check {
191 name = "sameScopeLocalSingletonMap";
192 scope = null;
193 intro = "local -A";
194 values = ''([greeting]="hello!")'';
195 };
196
197 sameScopeDeclareUnsetMap = check {
198 name = "sameScopeDeclareUnsetMap";
199 scope = null;
200 intro = "declare -A";
201 values = null;
202 };
203
204 sameScopeDeclareEmptyMap = check {
205 name = "sameScopeDeclareEmptyMap";
206 scope = null;
207 intro = "declare -A";
208 values = "()";
209 };
210
211 sameScopeDeclareSingletonMap = check {
212 name = "sameScopeDeclareSingletonMap";
213 scope = null;
214 intro = "declare -A";
215 values = ''([greeting]="hello!")'';
216 };
217
218 previousScopeEmptyStringFails = testBuildFailure' {
219 name = "previousScopeEmptyStringFails";
220 drv = check {
221 name = "previousScopeEmptyString";
222 scope = "function";
223 intro = null;
224 values = "";
225 };
226 expectedBuilderLogEntries = [
227 "test failed"
228 ];
229 };
230
231 # Fails because () is ambiguous and defaults to array rather than associative array.
232 previousScopeEmptyMapFails = testBuildFailure' {
233 name = "previousScopeEmptyMapFails";
234 drv = check {
235 name = "previousScopeEmptyMap";
236 scope = "function";
237 intro = null;
238 values = "()";
239 };
240 expectedBuilderLogEntries = [
241 "test failed"
242 ];
243 };
244
245 previousScopeSingletonMapFails = testBuildFailure' {
246 name = "previousScopeSingletonMapFails";
247 drv = check {
248 name = "previousScopeSingletonMap";
249 scope = "function";
250 intro = null;
251 values = ''([greeting]="hello!")'';
252 };
253 expectedBuilderLogEntries = [
254 "greeting: unbound variable"
255 ];
256 };
257
258 previousScopeLocalUnsetMapFails = testBuildFailure' {
259 name = "previousScopeLocalUnsetMapFails";
260 drv = check {
261 name = "previousScopeLocalUnsetMap";
262 scope = "function";
263 intro = "local -A";
264 values = null;
265 };
266 expectedBuilderLogEntries = [
267 "test failed"
268 ];
269 };
270
271 previousScopeLocalEmptyMapFails = testBuildFailure' {
272 name = "previousScopeLocalEmptyMapFails";
273 drv = check {
274 name = "previousScopeLocalEmptyMap";
275 scope = "function";
276 intro = "local -A";
277 values = "()";
278 };
279 expectedBuilderLogEntries = [
280 "test failed"
281 ];
282 };
283
284 previousScopeLocalSingletonMapFails = testBuildFailure' {
285 name = "previousScopeLocalSingletonMapFails";
286 drv = check {
287 name = "previousScopeLocalSingletonMap";
288 scope = "function";
289 intro = "local -A";
290 values = ''([greeting]="hello!")'';
291 };
292 expectedBuilderLogEntries = [
293 "test failed"
294 ];
295 };
296
297 previousScopeLocalGlobalUnsetMap = check {
298 name = "previousScopeLocalGlobalUnsetMap";
299 scope = "function";
300 intro = "local -Ag";
301 values = null;
302 };
303
304 previousScopeLocalGlobalEmptyMap = check {
305 name = "previousScopeLocalGlobalEmptyMap";
306 scope = "function";
307 intro = "local -Ag";
308 values = "()";
309 };
310
311 previousScopeLocalGlobalSingletonMap = check {
312 name = "previousScopeLocalGlobalSingletonMap";
313 scope = "function";
314 intro = "local -Ag";
315 values = ''([greeting]="hello!")'';
316 };
317
318 previousScopeDeclareUnsetMapFails = testBuildFailure' {
319 name = "previousScopeDeclareUnsetMapFails";
320 drv = check {
321 name = "previousScopeDeclareUnsetMap";
322 scope = "function";
323 intro = "declare -A";
324 values = null;
325 };
326 expectedBuilderLogEntries = [
327 "test failed"
328 ];
329 };
330
331 previousScopeDeclareEmptyMapFails = testBuildFailure' {
332 name = "previousScopeDeclareEmptyMapFails";
333 drv = check {
334 name = "previousScopeDeclareEmptyMap";
335 scope = "function";
336 intro = "declare -A";
337 values = "()";
338 };
339 expectedBuilderLogEntries = [
340 "test failed"
341 ];
342 };
343
344 previousScopeDeclareSingletonMapFails = testBuildFailure' {
345 name = "previousScopeDeclareSingletonMapFails";
346 drv = check {
347 name = "previousScopeDeclareSingletonMap";
348 scope = "function";
349 intro = "declare -A";
350 values = ''([greeting]="hello!")'';
351 };
352 expectedBuilderLogEntries = [
353 "test failed"
354 ];
355 };
356
357 previousScopeDeclareGlobalUnsetMap = check {
358 name = "previousScopeDeclareGlobalUnsetMap";
359 scope = "function";
360 intro = "declare -Ag";
361 values = null;
362 };
363
364 previousScopeDeclareGlobalEmptyMap = check {
365 name = "previousScopeDeclareGlobalEmptyMap";
366 scope = "function";
367 intro = "declare -Ag";
368 values = "()";
369 };
370
371 previousScopeDeclareGlobalSingletonMap = check {
372 name = "previousScopeDeclareGlobalSingletonMap";
373 scope = "function";
374 intro = "declare -Ag";
375 values = ''([greeting]="hello!")'';
376 };
377}