1# to run these tests:
2# nix-instantiate --eval --strict nixpkgs/lib/tests/misc.nix
3# if the resulting list is empty, all tests passed
4with import ../default.nix;
5
6runTests {
7
8
9# TRIVIAL
10
11 testId = {
12 expr = id 1;
13 expected = 1;
14 };
15
16 testConst = {
17 expr = const 2 3;
18 expected = 2;
19 };
20
21 /*
22 testOr = {
23 expr = or true false;
24 expected = true;
25 };
26 */
27
28 testAnd = {
29 expr = and true false;
30 expected = false;
31 };
32
33 testFix = {
34 expr = fix (x: {a = if x ? a then "a" else "b";});
35 expected = {a = "a";};
36 };
37
38 testComposeExtensions = {
39 expr = let obj = makeExtensible (self: { foo = self.bar; });
40 f = self: super: { bar = false; baz = true; };
41 g = self: super: { bar = super.baz or false; };
42 f_o_g = composeExtensions f g;
43 composed = obj.extend f_o_g;
44 in composed.foo;
45 expected = true;
46 };
47
48 testBitAnd = {
49 expr = (bitAnd 3 10);
50 expected = 2;
51 };
52
53 testBitOr = {
54 expr = (bitOr 3 10);
55 expected = 11;
56 };
57
58 testBitXor = {
59 expr = (bitXor 3 10);
60 expected = 9;
61 };
62
63# STRINGS
64
65 testConcatMapStrings = {
66 expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
67 expected = "a;b;c;";
68 };
69
70 testConcatStringsSep = {
71 expr = concatStringsSep "," ["a" "b" "c"];
72 expected = "a,b,c";
73 };
74
75 testSplitStringsSimple = {
76 expr = strings.splitString "." "a.b.c.d";
77 expected = [ "a" "b" "c" "d" ];
78 };
79
80 testSplitStringsEmpty = {
81 expr = strings.splitString "." "a..b";
82 expected = [ "a" "" "b" ];
83 };
84
85 testSplitStringsOne = {
86 expr = strings.splitString ":" "a.b";
87 expected = [ "a.b" ];
88 };
89
90 testSplitStringsNone = {
91 expr = strings.splitString "." "";
92 expected = [ "" ];
93 };
94
95 testSplitStringsFirstEmpty = {
96 expr = strings.splitString "/" "/a/b/c";
97 expected = [ "" "a" "b" "c" ];
98 };
99
100 testSplitStringsLastEmpty = {
101 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
102 expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
103 };
104
105 testIsStorePath = {
106 expr =
107 let goodPath =
108 "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
109 in {
110 storePath = isStorePath goodPath;
111 storePathDerivation = isStorePath (import ../.. {}).hello;
112 storePathAppendix = isStorePath
113 "${goodPath}/bin/python";
114 nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
115 asPath = isStorePath (builtins.toPath goodPath);
116 otherPath = isStorePath "/something/else";
117 otherVals = {
118 attrset = isStorePath {};
119 list = isStorePath [];
120 int = isStorePath 42;
121 };
122 };
123 expected = {
124 storePath = true;
125 storePathDerivation = true;
126 storePathAppendix = false;
127 nonAbsolute = false;
128 asPath = true;
129 otherPath = false;
130 otherVals = {
131 attrset = false;
132 list = false;
133 int = false;
134 };
135 };
136 };
137
138# LISTS
139
140 testFilter = {
141 expr = filter (x: x != "a") ["a" "b" "c" "a"];
142 expected = ["b" "c"];
143 };
144
145 testFold =
146 let
147 f = op: fold: fold op 0 (range 0 100);
148 # fold with associative operator
149 assoc = f builtins.add;
150 # fold with non-associative operator
151 nonAssoc = f builtins.sub;
152 in {
153 expr = {
154 assocRight = assoc foldr;
155 # right fold with assoc operator is same as left fold
156 assocRightIsLeft = assoc foldr == assoc foldl;
157 nonAssocRight = nonAssoc foldr;
158 nonAssocLeft = nonAssoc foldl;
159 # with non-assoc operator the fold results are not the same
160 nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr;
161 # fold is an alias for foldr
162 foldIsRight = nonAssoc fold == nonAssoc foldr;
163 };
164 expected = {
165 assocRight = 5050;
166 assocRightIsLeft = true;
167 nonAssocRight = 50;
168 nonAssocLeft = (-5050);
169 nonAssocRightIsNotLeft = true;
170 foldIsRight = true;
171 };
172 };
173
174 testTake = testAllTrue [
175 ([] == (take 0 [ 1 2 3 ]))
176 ([1] == (take 1 [ 1 2 3 ]))
177 ([ 1 2 ] == (take 2 [ 1 2 3 ]))
178 ([ 1 2 3 ] == (take 3 [ 1 2 3 ]))
179 ([ 1 2 3 ] == (take 4 [ 1 2 3 ]))
180 ];
181
182 testFoldAttrs = {
183 expr = foldAttrs (n: a: [n] ++ a) [] [
184 { a = 2; b = 7; }
185 { a = 3; c = 8; }
186 ];
187 expected = { a = [ 2 3 ]; b = [7]; c = [8];};
188 };
189
190 testSort = {
191 expr = sort builtins.lessThan [ 40 2 30 42 ];
192 expected = [2 30 40 42];
193 };
194
195 testToIntShouldConvertStringToInt = {
196 expr = toInt "27";
197 expected = 27;
198 };
199
200 testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
201 expr = builtins.tryEval (toInt "\"foo\"");
202 expected = { success = false; value = false; };
203 };
204
205 testHasAttrByPathTrue = {
206 expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; };
207 expected = true;
208 };
209
210 testHasAttrByPathFalse = {
211 expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; };
212 expected = false;
213 };
214
215
216# ATTRSETS
217
218 # code from the example
219 testRecursiveUpdateUntil = {
220 expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
221 # first attribute set
222 foo.bar = 1;
223 foo.baz = 2;
224 bar = 3;
225 } {
226 #second attribute set
227 foo.bar = 1;
228 foo.quz = 2;
229 baz = 4;
230 };
231 expected = {
232 foo.bar = 1; # 'foo.*' from the second set
233 foo.quz = 2; #
234 bar = 3; # 'bar' from the first set
235 baz = 4; # 'baz' from the second set
236 };
237 };
238
239
240# GENERATORS
241# these tests assume attributes are converted to lists
242# in alphabetical order
243
244 testMkKeyValueDefault = {
245 expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar";
246 expected = ''f\:oo:bar'';
247 };
248
249 testMkValueString = {
250 expr = let
251 vals = {
252 int = 42;
253 string = ''fo"o'';
254 bool = true;
255 bool2 = false;
256 null = null;
257 # float = 42.23; # floats are strange
258 };
259 in mapAttrs
260 (const (generators.mkValueStringDefault {}))
261 vals;
262 expected = {
263 int = "42";
264 string = ''fo"o'';
265 bool = "true";
266 bool2 = "false";
267 null = "null";
268 # float = "42.23" true false [ "bar" ] ]'';
269 };
270 };
271
272 testToKeyValue = {
273 expr = generators.toKeyValue {} {
274 key = "value";
275 "other=key" = "baz";
276 };
277 expected = ''
278 key=value
279 other\=key=baz
280 '';
281 };
282
283 testToINIEmpty = {
284 expr = generators.toINI {} {};
285 expected = "";
286 };
287
288 testToINIEmptySection = {
289 expr = generators.toINI {} { foo = {}; bar = {}; };
290 expected = ''
291 [bar]
292
293 [foo]
294 '';
295 };
296
297 testToINIDefaultEscapes = {
298 expr = generators.toINI {} {
299 "no [ and ] allowed unescaped" = {
300 "and also no = in keys" = 42;
301 };
302 };
303 expected = ''
304 [no \[ and \] allowed unescaped]
305 and also no \= in keys=42
306 '';
307 };
308
309 testToINIDefaultFull = {
310 expr = generators.toINI {} {
311 "section 1" = {
312 attribute1 = 5;
313 x = "Me-se JarJar Binx";
314 # booleans are converted verbatim by default
315 boolean = false;
316 };
317 "foo[]" = {
318 "he\\h=he" = "this is okay";
319 };
320 };
321 expected = ''
322 [foo\[\]]
323 he\h\=he=this is okay
324
325 [section 1]
326 attribute1=5
327 boolean=false
328 x=Me-se JarJar Binx
329 '';
330 };
331
332 /* right now only invocation check */
333 testToJSONSimple =
334 let val = {
335 foobar = [ "baz" 1 2 3 ];
336 };
337 in {
338 expr = generators.toJSON {} val;
339 # trivial implementation
340 expected = builtins.toJSON val;
341 };
342
343 /* right now only invocation check */
344 testToYAMLSimple =
345 let val = {
346 list = [ { one = 1; } { two = 2; } ];
347 all = 42;
348 };
349 in {
350 expr = generators.toYAML {} val;
351 # trivial implementation
352 expected = builtins.toJSON val;
353 };
354
355 testToPretty = {
356 expr = mapAttrs (const (generators.toPretty {})) rec {
357 int = 42;
358 bool = true;
359 string = ''fno"rd'';
360 path = /. + "/foo"; # toPath returns a string
361 null_ = null;
362 function = x: x;
363 functionArgs = { arg ? 4, foo }: arg;
364 list = [ 3 4 function [ false ] ];
365 attrs = { foo = null; "foo bar" = "baz"; };
366 drv = derivation { name = "test"; system = builtins.currentSystem; };
367 };
368 expected = rec {
369 int = "42";
370 bool = "true";
371 string = ''"fno\"rd"'';
372 path = "/foo";
373 null_ = "null";
374 function = "<λ>";
375 functionArgs = "<λ:{(arg),foo}>";
376 list = "[ 3 4 ${function} [ false ] ]";
377 attrs = "{ \"foo\" = null; \"foo bar\" = \"baz\"; }";
378 drv = "<δ:test>";
379 };
380 };
381
382 testToPrettyAllowPrettyValues = {
383 expr = generators.toPretty { allowPrettyValues = true; }
384 { __pretty = v: "«" + v + "»"; val = "foo"; };
385 expected = "«foo»";
386 };
387
388
389# MISC
390
391 testOverridableDelayableArgsTest = {
392 expr =
393 let res1 = defaultOverridableDelayableArgs id {};
394 res2 = defaultOverridableDelayableArgs id { a = 7; };
395 res3 = let x = defaultOverridableDelayableArgs id { a = 7; };
396 in (x.merge) { b = 10; };
397 res4 = let x = defaultOverridableDelayableArgs id { a = 7; };
398 in (x.merge) ( x: { b = 10; });
399 res5 = let x = defaultOverridableDelayableArgs id { a = 7; };
400 in (x.merge) ( x: { a = builtins.add x.a 3; });
401 res6 = let x = defaultOverridableDelayableArgs id { a = 7; mergeAttrBy = { a = builtins.add; }; };
402 y = x.merge {};
403 in (y.merge) { a = 10; };
404
405 resRem7 = res6.replace (a: removeAttrs a ["a"]);
406
407 # fixed tests (delayed args): (when using them add some comments, please)
408 resFixed1 =
409 let x = defaultOverridableDelayableArgs id ( x: { a = 7; c = x.fixed.b; });
410 y = x.merge (x: { name = "name-${builtins.toString x.fixed.c}"; });
411 in (y.merge) { b = 10; };
412 strip = attrs: removeAttrs attrs ["merge" "replace"];
413 in all id
414 [ ((strip res1) == { })
415 ((strip res2) == { a = 7; })
416 ((strip res3) == { a = 7; b = 10; })
417 ((strip res4) == { a = 7; b = 10; })
418 ((strip res5) == { a = 10; })
419 ((strip res6) == { a = 17; })
420 ((strip resRem7) == {})
421 ((strip resFixed1) == { a = 7; b = 10; c =10; name = "name-10"; })
422 ];
423 expected = true;
424 };
425
426}