1# NOTE: Tests related to isDeclaredArray go here.
2{
3 isDeclaredArray,
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 = [ isDeclaredArray ];
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 isDeclaredArray 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 = "isDeclaredArray";
61 src = ./isDeclaredArray.bash;
62 };
63
64 shfmt = shfmt {
65 name = "isDeclaredArray";
66 src = ./isDeclaredArray.bash;
67 };
68
69 undeclaredFails = testBuildFailure' {
70 name = "undeclaredFails";
71 drv = runCommand "undeclared" commonArgs ''
72 set -eu
73 if isDeclaredArray 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 mapFails = testBuildFailure' {
87 name = "mapFails";
88 drv = runCommand "map" commonArgs ''
89 set -eu
90 local -A map
91 if isDeclaredArray map; 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 isDeclaredArray ""; 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 sameScopeEmptyArray = check {
150 name = "sameScopeEmptyArray";
151 scope = null;
152 intro = null;
153 values = "()";
154 };
155
156 sameScopeSingletonArray = check {
157 name = "sameScopeSingletonArray";
158 scope = null;
159 intro = null;
160 values = ''("hello!")'';
161 };
162
163 sameScopeLocalUnsetArray = check {
164 name = "sameScopeLocalUnsetArray";
165 scope = null;
166 intro = "local -a";
167 values = null;
168 };
169
170 sameScopeLocalEmptyArray = check {
171 name = "sameScopeLocalEmptyArray";
172 scope = null;
173 intro = "local -a";
174 values = "()";
175 };
176
177 sameScopeLocalSingletonArray = check {
178 name = "sameScopeLocalSingletonArray";
179 scope = null;
180 intro = "local -a";
181 values = ''("hello!")'';
182 };
183
184 sameScopeDeclareUnsetArray = check {
185 name = "sameScopeDeclareUnsetArray";
186 scope = null;
187 intro = "declare -a";
188 values = null;
189 };
190
191 sameScopeDeclareEmptyArray = check {
192 name = "sameScopeDeclareEmptyArray";
193 scope = null;
194 intro = "declare -a";
195 values = "()";
196 };
197
198 sameScopeDeclareSingletonArray = check {
199 name = "sameScopeDeclareSingletonArray";
200 scope = null;
201 intro = "declare -a";
202 values = ''("hello!")'';
203 };
204
205 previousScopeEmptyStringFails = testBuildFailure' {
206 name = "previousScopeEmptyStringFails";
207 drv = check {
208 name = "previousScopeEmptyString";
209 scope = "function";
210 intro = null;
211 values = "";
212 };
213 expectedBuilderLogEntries = [
214 "test failed"
215 ];
216 };
217
218 # Works because the variable isn't lexically scoped.
219 previousScopeEmptyArray = check {
220 name = "previousScopeEmptyArray";
221 scope = "function";
222 intro = null;
223 values = "()";
224 };
225
226 # Works because the variable isn't lexically scoped.
227 previousScopeSingletonArray = check {
228 name = "previousScopeSingletonArray";
229 scope = "function";
230 intro = null;
231 values = ''("hello!")'';
232 };
233
234 previousScopeLocalUnsetArrayFails = testBuildFailure' {
235 name = "previousScopeLocalUnsetArrayFails";
236 drv = check {
237 name = "previousScopeLocalUnsetArray";
238 scope = "function";
239 intro = "local -a";
240 values = null;
241 };
242 expectedBuilderLogEntries = [
243 "test failed"
244 ];
245 };
246
247 previousScopeLocalEmptyArrayFails = testBuildFailure' {
248 name = "previousScopeLocalEmptyArrayFails";
249 drv = check {
250 name = "previousScopeLocalEmptyArray";
251 scope = "function";
252 intro = "local -a";
253 values = "()";
254 };
255 expectedBuilderLogEntries = [
256 "test failed"
257 ];
258 };
259
260 previousScopeLocalSingletonArrayFails = testBuildFailure' {
261 name = "previousScopeLocalSingletonArrayFails";
262 drv = check {
263 name = "previousScopeLocalSingletonArray";
264 scope = "function";
265 intro = "local -a";
266 values = ''("hello!")'';
267 };
268 expectedBuilderLogEntries = [
269 "test failed"
270 ];
271 };
272
273 previousScopeLocalGlobalUnsetArray = check {
274 name = "previousScopeLocalGlobalUnsetArray";
275 scope = "function";
276 intro = "local -ag";
277 values = null;
278 };
279
280 previousScopeLocalGlobalEmptyArray = check {
281 name = "previousScopeLocalGlobalEmptyArray";
282 scope = "function";
283 intro = "local -ag";
284 values = "()";
285 };
286
287 previousScopeLocalGlobalSingletonArray = check {
288 name = "previousScopeLocalGlobalSingletonArray";
289 scope = "function";
290 intro = "local -ag";
291 values = ''("hello!")'';
292 };
293
294 previousScopeDeclareUnsetArrayFails = testBuildFailure' {
295 name = "previousScopeDeclareUnsetArrayFails";
296 drv = check {
297 name = "previousScopeDeclareUnsetArray";
298 scope = "function";
299 intro = "declare -a";
300 values = null;
301 };
302 expectedBuilderLogEntries = [
303 "test failed"
304 ];
305 };
306
307 previousScopeDeclareEmptyArrayFails = testBuildFailure' {
308 name = "previousScopeDeclareEmptyArrayFails";
309 drv = check {
310 name = "previousScopeDeclareEmptyArray";
311 scope = "function";
312 intro = "declare -a";
313 values = "()";
314 };
315 expectedBuilderLogEntries = [
316 "test failed"
317 ];
318 };
319
320 previousScopeDeclareSingletonArrayFails = testBuildFailure' {
321 name = "previousScopeDeclareSingletonArrayFails";
322 drv = check {
323 name = "previousScopeDeclareSingletonArray";
324 scope = "function";
325 intro = "declare -a";
326 values = ''("hello!")'';
327 };
328 expectedBuilderLogEntries = [
329 "test failed"
330 ];
331 };
332
333 previousScopeDeclareGlobalUnsetArray = check {
334 name = "previousScopeDeclareGlobalUnsetArray";
335 scope = "function";
336 intro = "declare -ag";
337 values = null;
338 };
339
340 previousScopeDeclareGlobalEmptyArray = check {
341 name = "previousScopeDeclareGlobalEmptyArray";
342 scope = "function";
343 intro = "declare -ag";
344 values = "()";
345 };
346
347 previousScopeDeclareGlobalSingletonArray = check {
348 name = "previousScopeDeclareGlobalSingletonArray";
349 scope = "function";
350 intro = "declare -ag";
351 values = ''("hello!")'';
352 };
353}