1/*
2Nix evaluation tests for various lib functions.
3
4Since these tests are implemented with Nix evaluation, error checking is limited to what `builtins.tryEval` can detect, which is `throw`'s and `abort`'s, without error messages.
5If you need to test error messages or more complex evaluations, see ./modules.sh, ./sources.sh or ./filesystem.sh as examples.
6
7To run these tests:
8
9 [nixpkgs]$ nix-instantiate --eval --strict lib/tests/misc.nix
10
11If the resulting list is empty, all tests passed.
12Alternatively, to run all `lib` tests:
13
14 [nixpkgs]$ nix-build lib/tests/release.nix
15*/
16with import ../default.nix;
17
18let
19 testingThrow = expr: {
20 expr = (builtins.tryEval (builtins.seq expr "didn't throw"));
21 expected = { success = false; value = false; };
22 };
23 testingEval = expr: {
24 expr = (builtins.tryEval expr).success;
25 expected = true;
26 };
27 testingDeepThrow = expr: testingThrow (builtins.deepSeq expr expr);
28
29 testSanitizeDerivationName = { name, expected }:
30 let
31 drv = derivation {
32 name = strings.sanitizeDerivationName name;
33 builder = "x";
34 system = "x";
35 };
36 in {
37 # Evaluate the derivation so an invalid name would be caught
38 expr = builtins.seq drv.drvPath drv.name;
39 inherit expected;
40 };
41
42in
43
44runTests {
45
46# CUSTOMIZATION
47
48 testFunctionArgsMakeOverridable = {
49 expr = functionArgs (makeOverridable ({ a, b, c ? null}: {}));
50 expected = { a = false; b = false; c = true; };
51 };
52
53 testFunctionArgsMakeOverridableOverride = {
54 expr = functionArgs (makeOverridable ({ a, b, c ? null }: {}) { a = 1; b = 2; }).override;
55 expected = { a = false; b = false; c = true; };
56 };
57
58# TRIVIAL
59
60 testId = {
61 expr = id 1;
62 expected = 1;
63 };
64
65 testConst = {
66 expr = const 2 3;
67 expected = 2;
68 };
69
70 testPipe = {
71 expr = pipe 2 [
72 (x: x + 2) # 2 + 2 = 4
73 (x: x * 2) # 4 * 2 = 8
74 ];
75 expected = 8;
76 };
77
78 testPipeEmpty = {
79 expr = pipe 2 [];
80 expected = 2;
81 };
82
83 testPipeStrings = {
84 expr = pipe [ 3 4 ] [
85 (map toString)
86 (map (s: s + "\n"))
87 concatStrings
88 ];
89 expected = ''
90 3
91 4
92 '';
93 };
94
95 /*
96 testOr = {
97 expr = or true false;
98 expected = true;
99 };
100 */
101
102 testAnd = {
103 expr = and true false;
104 expected = false;
105 };
106
107 testFix = {
108 expr = fix (x: {a = if x ? a then "a" else "b";});
109 expected = {a = "a";};
110 };
111
112 testComposeExtensions = {
113 expr = let obj = makeExtensible (self: { foo = self.bar; });
114 f = self: super: { bar = false; baz = true; };
115 g = self: super: { bar = super.baz or false; };
116 f_o_g = composeExtensions f g;
117 composed = obj.extend f_o_g;
118 in composed.foo;
119 expected = true;
120 };
121
122 testComposeManyExtensions0 = {
123 expr = let obj = makeExtensible (self: { foo = true; });
124 emptyComposition = composeManyExtensions [];
125 composed = obj.extend emptyComposition;
126 in composed.foo;
127 expected = true;
128 };
129
130 testComposeManyExtensions =
131 let f = self: super: { bar = false; baz = true; };
132 g = self: super: { bar = super.baz or false; };
133 h = self: super: { qux = super.bar or false; };
134 obj = makeExtensible (self: { foo = self.qux; });
135 in {
136 expr = let composition = composeManyExtensions [f g h];
137 composed = obj.extend composition;
138 in composed.foo;
139 expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo;
140 };
141
142 testBitAnd = {
143 expr = (bitAnd 3 10);
144 expected = 2;
145 };
146
147 testBitOr = {
148 expr = (bitOr 3 10);
149 expected = 11;
150 };
151
152 testBitXor = {
153 expr = (bitXor 3 10);
154 expected = 9;
155 };
156
157 testToHexString = {
158 expr = toHexString 250;
159 expected = "FA";
160 };
161
162 testToBaseDigits = {
163 expr = toBaseDigits 2 6;
164 expected = [ 1 1 0 ];
165 };
166
167 testFunctionArgsFunctor = {
168 expr = functionArgs { __functor = self: { a, b }: null; };
169 expected = { a = false; b = false; };
170 };
171
172 testFunctionArgsSetFunctionArgs = {
173 expr = functionArgs (setFunctionArgs (args: args.x) { x = false; });
174 expected = { x = false; };
175 };
176
177# STRINGS
178
179 testConcatMapStrings = {
180 expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
181 expected = "a;b;c;";
182 };
183
184 testConcatStringsSep = {
185 expr = concatStringsSep "," ["a" "b" "c"];
186 expected = "a,b,c";
187 };
188
189 testConcatLines = {
190 expr = concatLines ["a" "b" "c"];
191 expected = "a\nb\nc\n";
192 };
193
194 testReplicateString = {
195 expr = strings.replicate 5 "hello";
196 expected = "hellohellohellohellohello";
197 };
198
199 testSplitStringsSimple = {
200 expr = strings.splitString "." "a.b.c.d";
201 expected = [ "a" "b" "c" "d" ];
202 };
203
204 testSplitStringsEmpty = {
205 expr = strings.splitString "." "a..b";
206 expected = [ "a" "" "b" ];
207 };
208
209 testSplitStringsOne = {
210 expr = strings.splitString ":" "a.b";
211 expected = [ "a.b" ];
212 };
213
214 testSplitStringsNone = {
215 expr = strings.splitString "." "";
216 expected = [ "" ];
217 };
218
219 testSplitStringsFirstEmpty = {
220 expr = strings.splitString "/" "/a/b/c";
221 expected = [ "" "a" "b" "c" ];
222 };
223
224 testSplitStringsLastEmpty = {
225 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
226 expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
227 };
228
229 testSplitStringsRegex = {
230 expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B";
231 expected = [ "A" "B" ];
232 };
233
234 testSplitStringsDerivation = {
235 expr = take 3 (strings.splitString "/" (derivation {
236 name = "name";
237 builder = "builder";
238 system = "system";
239 }));
240 expected = ["" "nix" "store"];
241 };
242
243 testSplitVersionSingle = {
244 expr = versions.splitVersion "1";
245 expected = [ "1" ];
246 };
247
248 testSplitVersionDouble = {
249 expr = versions.splitVersion "1.2";
250 expected = [ "1" "2" ];
251 };
252
253 testSplitVersionTriple = {
254 expr = versions.splitVersion "1.2.3";
255 expected = [ "1" "2" "3" ];
256 };
257
258 testPadVersionLess = {
259 expr = versions.pad 3 "1.2";
260 expected = "1.2.0";
261 };
262
263 testPadVersionLessExtra = {
264 expr = versions.pad 3 "1.3-rc1";
265 expected = "1.3.0-rc1";
266 };
267
268 testPadVersionMore = {
269 expr = versions.pad 3 "1.2.3.4";
270 expected = "1.2.3";
271 };
272
273 testIsStorePath = {
274 expr =
275 let goodPath =
276 "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
277 in {
278 storePath = isStorePath goodPath;
279 storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello;
280 storePathAppendix = isStorePath
281 "${goodPath}/bin/python";
282 nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
283 asPath = isStorePath (/. + goodPath);
284 otherPath = isStorePath "/something/else";
285 otherVals = {
286 attrset = isStorePath {};
287 list = isStorePath [];
288 int = isStorePath 42;
289 };
290 };
291 expected = {
292 storePath = true;
293 storePathDerivation = true;
294 storePathAppendix = false;
295 nonAbsolute = false;
296 asPath = true;
297 otherPath = false;
298 otherVals = {
299 attrset = false;
300 list = false;
301 int = false;
302 };
303 };
304 };
305
306 testEscapeXML = {
307 expr = escapeXML ''"test" 'test' < & >'';
308 expected = ""test" 'test' < & >";
309 };
310
311 testToShellVars = {
312 expr = ''
313 ${toShellVars {
314 STRing01 = "just a 'string'";
315 _array_ = [ "with" "more strings" ];
316 assoc."with some" = ''
317 strings
318 possibly newlines
319 '';
320 drv = {
321 outPath = "/drv";
322 foo = "ignored attribute";
323 };
324 path = /path;
325 stringable = {
326 __toString = _: "hello toString";
327 bar = "ignored attribute";
328 };
329 }}
330 '';
331 expected = ''
332 STRing01='just a '\'''string'\''''
333 declare -a _array_=('with' 'more strings')
334 declare -A assoc=(['with some']='strings
335 possibly newlines
336 ')
337 drv='/drv'
338 path='/path'
339 stringable='hello toString'
340 '';
341 };
342
343 testHasInfixFalse = {
344 expr = hasInfix "c" "abde";
345 expected = false;
346 };
347
348 testHasInfixTrue = {
349 expr = hasInfix "c" "abcde";
350 expected = true;
351 };
352
353 testHasInfixDerivation = {
354 expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello;
355 expected = true;
356 };
357
358 testHasInfixPath = {
359 expr = hasInfix "tests" ./.;
360 expected = true;
361 };
362
363 testHasInfixPathStoreDir = {
364 expr = hasInfix builtins.storeDir ./.;
365 expected = true;
366 };
367
368 testHasInfixToString = {
369 expr = hasInfix "a" { __toString = _: "a"; };
370 expected = true;
371 };
372
373 testRemovePrefixExample1 = {
374 expr = removePrefix "foo." "foo.bar.baz";
375 expected = "bar.baz";
376 };
377 testRemovePrefixExample2 = {
378 expr = removePrefix "xxx" "foo.bar.baz";
379 expected = "foo.bar.baz";
380 };
381 testRemovePrefixEmptyPrefix = {
382 expr = removePrefix "" "foo";
383 expected = "foo";
384 };
385 testRemovePrefixEmptyString = {
386 expr = removePrefix "foo" "";
387 expected = "";
388 };
389 testRemovePrefixEmptyBoth = {
390 expr = removePrefix "" "";
391 expected = "";
392 };
393
394 testNormalizePath = {
395 expr = strings.normalizePath "//a/b//c////d/";
396 expected = "/a/b/c/d/";
397 };
398
399 testCharToInt = {
400 expr = strings.charToInt "A";
401 expected = 65;
402 };
403
404 testEscapeC = {
405 expr = strings.escapeC [ " " ] "Hello World";
406 expected = "Hello\\x20World";
407 };
408
409 testEscapeURL = testAllTrue [
410 ("" == strings.escapeURL "")
411 ("Hello" == strings.escapeURL "Hello")
412 ("Hello%20World" == strings.escapeURL "Hello World")
413 ("Hello%2FWorld" == strings.escapeURL "Hello/World")
414 ("42%25" == strings.escapeURL "42%")
415 ("%20%3F%26%3D%23%2B%25%21%3C%3E%23%22%7B%7D%7C%5C%5E%5B%5D%60%09%3A%2F%40%24%27%28%29%2A%2C%3B" == strings.escapeURL " ?&=#+%!<>#\"{}|\\^[]`\t:/@$'()*,;")
416 ];
417
418 testToInt = testAllTrue [
419 # Naive
420 (123 == toInt "123")
421 (0 == toInt "0")
422 # Whitespace Padding
423 (123 == toInt " 123")
424 (123 == toInt "123 ")
425 (123 == toInt " 123 ")
426 (123 == toInt " 123 ")
427 (0 == toInt " 0")
428 (0 == toInt "0 ")
429 (0 == toInt " 0 ")
430 (-1 == toInt "-1")
431 (-1 == toInt " -1 ")
432 ];
433
434 testToIntFails = testAllTrue [
435 ( builtins.tryEval (toInt "") == { success = false; value = false; } )
436 ( builtins.tryEval (toInt "123 123") == { success = false; value = false; } )
437 ( builtins.tryEval (toInt "0 123") == { success = false; value = false; } )
438 ( builtins.tryEval (toInt " 0d ") == { success = false; value = false; } )
439 ( builtins.tryEval (toInt " 1d ") == { success = false; value = false; } )
440 ( builtins.tryEval (toInt " d0 ") == { success = false; value = false; } )
441 ( builtins.tryEval (toInt "00") == { success = false; value = false; } )
442 ( builtins.tryEval (toInt "01") == { success = false; value = false; } )
443 ( builtins.tryEval (toInt "002") == { success = false; value = false; } )
444 ( builtins.tryEval (toInt " 002 ") == { success = false; value = false; } )
445 ( builtins.tryEval (toInt " foo ") == { success = false; value = false; } )
446 ( builtins.tryEval (toInt " foo 123 ") == { success = false; value = false; } )
447 ( builtins.tryEval (toInt " foo123 ") == { success = false; value = false; } )
448 ];
449
450 testToIntBase10 = testAllTrue [
451 # Naive
452 (123 == toIntBase10 "123")
453 (0 == toIntBase10 "0")
454 # Whitespace Padding
455 (123 == toIntBase10 " 123")
456 (123 == toIntBase10 "123 ")
457 (123 == toIntBase10 " 123 ")
458 (123 == toIntBase10 " 123 ")
459 (0 == toIntBase10 " 0")
460 (0 == toIntBase10 "0 ")
461 (0 == toIntBase10 " 0 ")
462 # Zero Padding
463 (123 == toIntBase10 "0123")
464 (123 == toIntBase10 "0000123")
465 (0 == toIntBase10 "000000")
466 # Whitespace and Zero Padding
467 (123 == toIntBase10 " 0123")
468 (123 == toIntBase10 "0123 ")
469 (123 == toIntBase10 " 0123 ")
470 (123 == toIntBase10 " 0000123")
471 (123 == toIntBase10 "0000123 ")
472 (123 == toIntBase10 " 0000123 ")
473 (0 == toIntBase10 " 000000")
474 (0 == toIntBase10 "000000 ")
475 (0 == toIntBase10 " 000000 ")
476 (-1 == toIntBase10 "-1")
477 (-1 == toIntBase10 " -1 ")
478 ];
479
480 testToIntBase10Fails = testAllTrue [
481 ( builtins.tryEval (toIntBase10 "") == { success = false; value = false; } )
482 ( builtins.tryEval (toIntBase10 "123 123") == { success = false; value = false; } )
483 ( builtins.tryEval (toIntBase10 "0 123") == { success = false; value = false; } )
484 ( builtins.tryEval (toIntBase10 " 0d ") == { success = false; value = false; } )
485 ( builtins.tryEval (toIntBase10 " 1d ") == { success = false; value = false; } )
486 ( builtins.tryEval (toIntBase10 " d0 ") == { success = false; value = false; } )
487 ( builtins.tryEval (toIntBase10 " foo ") == { success = false; value = false; } )
488 ( builtins.tryEval (toIntBase10 " foo 123 ") == { success = false; value = false; } )
489 ( builtins.tryEval (toIntBase10 " foo 00123 ") == { success = false; value = false; } )
490 ( builtins.tryEval (toIntBase10 " foo00123 ") == { success = false; value = false; } )
491 ];
492
493# LISTS
494
495 testFilter = {
496 expr = filter (x: x != "a") ["a" "b" "c" "a"];
497 expected = ["b" "c"];
498 };
499
500 testFold =
501 let
502 f = op: fold: fold op 0 (range 0 100);
503 # fold with associative operator
504 assoc = f builtins.add;
505 # fold with non-associative operator
506 nonAssoc = f builtins.sub;
507 in {
508 expr = {
509 assocRight = assoc foldr;
510 # right fold with assoc operator is same as left fold
511 assocRightIsLeft = assoc foldr == assoc foldl;
512 nonAssocRight = nonAssoc foldr;
513 nonAssocLeft = nonAssoc foldl;
514 # with non-assoc operator the fold results are not the same
515 nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr;
516 # fold is an alias for foldr
517 foldIsRight = nonAssoc fold == nonAssoc foldr;
518 };
519 expected = {
520 assocRight = 5050;
521 assocRightIsLeft = true;
522 nonAssocRight = 50;
523 nonAssocLeft = (-5050);
524 nonAssocRightIsNotLeft = true;
525 foldIsRight = true;
526 };
527 };
528
529 testFoldl'Empty = {
530 expr = foldl' (acc: el: abort "operation not called") 0 [ ];
531 expected = 0;
532 };
533
534 testFoldl'IntegerAdding = {
535 expr = foldl' (acc: el: acc + el) 0 [ 1 2 3 ];
536 expected = 6;
537 };
538
539 # The accumulator isn't forced deeply
540 testFoldl'NonDeep = {
541 expr = take 3 (foldl'
542 (acc: el: [ el ] ++ acc)
543 [ (abort "unevaluated list entry") ]
544 [ 1 2 3 ]);
545 expected = [ 3 2 1 ];
546 };
547
548 # Compared to builtins.foldl', lib.foldl' evaluates the first accumulator strictly too
549 testFoldl'StrictInitial = {
550 expr = (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [])).success;
551 expected = false;
552 };
553
554 # Make sure we don't get a stack overflow for large lists
555 # This number of elements would notably cause a stack overflow if it was implemented without the `foldl'` builtin
556 testFoldl'Large = {
557 expr = foldl' (acc: el: acc + el) 0 (range 0 100000);
558 expected = 5000050000;
559 };
560
561 testTake = testAllTrue [
562 ([] == (take 0 [ 1 2 3 ]))
563 ([1] == (take 1 [ 1 2 3 ]))
564 ([ 1 2 ] == (take 2 [ 1 2 3 ]))
565 ([ 1 2 3 ] == (take 3 [ 1 2 3 ]))
566 ([ 1 2 3 ] == (take 4 [ 1 2 3 ]))
567 ];
568
569 testListHasPrefixExample1 = {
570 expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ];
571 expected = true;
572 };
573 testListHasPrefixExample2 = {
574 expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ];
575 expected = false;
576 };
577 testListHasPrefixLazy = {
578 expr = lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ];
579 expected = true;
580 };
581 testListHasPrefixEmptyPrefix = {
582 expr = lists.hasPrefix [ ] [ 1 2 ];
583 expected = true;
584 };
585 testListHasPrefixEmptyList = {
586 expr = lists.hasPrefix [ 1 2 ] [ ];
587 expected = false;
588 };
589
590 testListRemovePrefixExample1 = {
591 expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ];
592 expected = [ 3 4 ];
593 };
594 testListRemovePrefixExample2 = {
595 expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success;
596 expected = false;
597 };
598 testListRemovePrefixEmptyPrefix = {
599 expr = lists.removePrefix [ ] [ 1 2 ];
600 expected = [ 1 2 ];
601 };
602 testListRemovePrefixEmptyList = {
603 expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success;
604 expected = false;
605 };
606
607 testFoldAttrs = {
608 expr = foldAttrs (n: a: [n] ++ a) [] [
609 { a = 2; b = 7; }
610 { a = 3; c = 8; }
611 ];
612 expected = { a = [ 2 3 ]; b = [7]; c = [8];};
613 };
614
615 testListCommonPrefixExample1 = {
616 expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ];
617 expected = [ 1 2 ];
618 };
619 testListCommonPrefixExample2 = {
620 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ];
621 expected = [ 1 2 3 ];
622 };
623 testListCommonPrefixExample3 = {
624 expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ];
625 expected = [ ];
626 };
627 testListCommonPrefixEmpty = {
628 expr = lists.commonPrefix [ ] [ 1 2 3 ];
629 expected = [ ];
630 };
631 testListCommonPrefixSame = {
632 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ];
633 expected = [ 1 2 3 ];
634 };
635 testListCommonPrefixLazy = {
636 expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this")];
637 expected = [ 1 ];
638 };
639 # This would stack overflow if `commonPrefix` were implemented using recursion
640 testListCommonPrefixLong =
641 let
642 longList = genList (n: n) 100000;
643 in {
644 expr = lists.commonPrefix longList longList;
645 expected = longList;
646 };
647
648 testSort = {
649 expr = sort builtins.lessThan [ 40 2 30 42 ];
650 expected = [2 30 40 42];
651 };
652
653 testReplicate = {
654 expr = replicate 3 "a";
655 expected = ["a" "a" "a"];
656 };
657
658 testToIntShouldConvertStringToInt = {
659 expr = toInt "27";
660 expected = 27;
661 };
662
663 testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
664 expr = builtins.tryEval (toInt "\"foo\"");
665 expected = { success = false; value = false; };
666 };
667
668 testHasAttrByPathTrue = {
669 expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; };
670 expected = true;
671 };
672
673 testHasAttrByPathFalse = {
674 expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; };
675 expected = false;
676 };
677
678 testFindFirstIndexExample1 = {
679 expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
680 expected = 1;
681 };
682
683 testFindFirstIndexExample2 = {
684 expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ 1 6 4 ];
685 expected = "a very specific default";
686 };
687
688 testFindFirstIndexEmpty = {
689 expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null [];
690 expected = null;
691 };
692
693 testFindFirstIndexSingleMatch = {
694 expr = lists.findFirstIndex (x: x == 5) null [ 5 ];
695 expected = 0;
696 };
697
698 testFindFirstIndexSingleDefault = {
699 expr = lists.findFirstIndex (x: false) null [ (abort "if the predicate doesn't access the value, it must not be evaluated") ];
700 expected = null;
701 };
702
703 testFindFirstIndexNone = {
704 expr = builtins.tryEval (lists.findFirstIndex (x: x == 2) null [ 1 (throw "the last element must be evaluated when there's no match") ]);
705 expected = { success = false; value = false; };
706 };
707
708 # Makes sure that the implementation doesn't cause a stack overflow
709 testFindFirstIndexBig = {
710 expr = lists.findFirstIndex (x: x == 1000000) null (range 0 1000000);
711 expected = 1000000;
712 };
713
714 testFindFirstIndexLazy = {
715 expr = lists.findFirstIndex (x: x == 1) null [ 1 (abort "list elements after the match must not be evaluated") ];
716 expected = 0;
717 };
718
719 testFindFirstExample1 = {
720 expr = lists.findFirst (x: x > 3) 7 [ 1 6 4 ];
721 expected = 6;
722 };
723
724 testFindFirstExample2 = {
725 expr = lists.findFirst (x: x > 9) 7 [ 1 6 4 ];
726 expected = 7;
727 };
728
729 testAllUnique_true = {
730 expr = allUnique [ 3 2 4 1 ];
731 expected = true;
732 };
733 testAllUnique_false = {
734 expr = allUnique [ 3 2 3 4 ];
735 expected = false;
736 };
737
738# ATTRSETS
739
740 testConcatMapAttrs = {
741 expr = concatMapAttrs
742 (name: value: {
743 ${name} = value;
744 ${name + value} = value;
745 })
746 {
747 foo = "bar";
748 foobar = "baz";
749 };
750 expected = {
751 foo = "bar";
752 foobar = "baz";
753 foobarbaz = "baz";
754 };
755 };
756
757 # code from example
758 testFoldlAttrs = {
759 expr = {
760 example = foldlAttrs
761 (acc: name: value: {
762 sum = acc.sum + value;
763 names = acc.names ++ [ name ];
764 })
765 { sum = 0; names = [ ]; }
766 {
767 foo = 1;
768 bar = 10;
769 };
770 # should just return the initial value
771 emptySet = foldlAttrs (throw "function not needed") 123 { };
772 # should just evaluate to the last value
773 valuesNotNeeded = foldlAttrs (acc: _name: _v: acc) 3 { z = throw "value z not needed"; a = throw "value a not needed"; };
774 # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string
775 trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { z = 1; a = 2; };
776 };
777 expected = {
778 example = {
779 sum = 11;
780 names = [ "bar" "foo" ];
781 };
782 emptySet = 123;
783 valuesNotNeeded = 3;
784 trivialAcc = 121;
785 };
786 };
787
788
789 testMergeAttrsListExample1 = {
790 expr = attrsets.mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ];
791 expected = { a = 0; b = 1; c = 2; d = 3; };
792 };
793 testMergeAttrsListExample2 = {
794 expr = attrsets.mergeAttrsList [ { a = 0; } { a = 1; } ];
795 expected = { a = 1; };
796 };
797 testMergeAttrsListExampleMany =
798 let
799 list = genList (n:
800 listToAttrs (genList (m:
801 let
802 # Integer divide n by two to create duplicate attributes
803 str = "halfn${toString (n / 2)}m${toString m}";
804 in
805 nameValuePair str str
806 ) 100)
807 ) 100;
808 in {
809 expr = attrsets.mergeAttrsList list;
810 expected = foldl' mergeAttrs { } list;
811 };
812
813 # code from the example
814 testRecursiveUpdateUntil = {
815 expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
816 # first attribute set
817 foo.bar = 1;
818 foo.baz = 2;
819 bar = 3;
820 } {
821 #second attribute set
822 foo.bar = 1;
823 foo.quz = 2;
824 baz = 4;
825 };
826 expected = {
827 foo.bar = 1; # 'foo.*' from the second set
828 foo.quz = 2; #
829 bar = 3; # 'bar' from the first set
830 baz = 4; # 'baz' from the second set
831 };
832 };
833
834 testOverrideExistingEmpty = {
835 expr = overrideExisting {} { a = 1; };
836 expected = {};
837 };
838
839 testOverrideExistingDisjoint = {
840 expr = overrideExisting { b = 2; } { a = 1; };
841 expected = { b = 2; };
842 };
843
844 testOverrideExistingOverride = {
845 expr = overrideExisting { a = 3; b = 2; } { a = 1; };
846 expected = { a = 1; b = 2; };
847 };
848
849 testListAttrsReverse = let
850 exampleAttrs = {foo=1; bar="asdf"; baz = [1 3 3 7]; fnord=null;};
851 exampleSingletonList = [{name="foo"; value=1;}];
852 in {
853 expr = {
854 isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) == exampleAttrs;
855 isReverseToAttrsToList = attrsToList (builtins.listToAttrs exampleSingletonList) == exampleSingletonList;
856 testDuplicatePruningBehaviour = attrsToList (builtins.listToAttrs [{name="a"; value=2;} {name="a"; value=1;}]);
857 };
858 expected = {
859 isReverseToAttrsToList = true;
860 isReverseToListToAttrs = true;
861 testDuplicatePruningBehaviour = [{name="a"; value=2;}];
862 };
863 };
864
865 testAttrsToListsCanDealWithFunctions = testingEval (
866 attrsToList { someFunc= a: a + 1;}
867 );
868
869# GENERATORS
870# these tests assume attributes are converted to lists
871# in alphabetical order
872
873 testMkKeyValueDefault = {
874 expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar";
875 expected = ''f\:oo:bar'';
876 };
877
878 testMkValueString = {
879 expr = let
880 vals = {
881 int = 42;
882 string = ''fo"o'';
883 bool = true;
884 bool2 = false;
885 null = null;
886 # float = 42.23; # floats are strange
887 };
888 in mapAttrs
889 (const (generators.mkValueStringDefault {}))
890 vals;
891 expected = {
892 int = "42";
893 string = ''fo"o'';
894 bool = "true";
895 bool2 = "false";
896 null = "null";
897 # float = "42.23" true false [ "bar" ] ]'';
898 };
899 };
900
901 testToKeyValue = {
902 expr = generators.toKeyValue {} {
903 key = "value";
904 "other=key" = "baz";
905 };
906 expected = ''
907 key=value
908 other\=key=baz
909 '';
910 };
911
912 testToINIEmpty = {
913 expr = generators.toINI {} {};
914 expected = "";
915 };
916
917 testToINIEmptySection = {
918 expr = generators.toINI {} { foo = {}; bar = {}; };
919 expected = ''
920 [bar]
921
922 [foo]
923 '';
924 };
925
926 testToINIDuplicateKeys = {
927 expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; };
928 expected = ''
929 [baz]
930 qux=1
931 qux=false
932
933 [foo]
934 bar=true
935 '';
936 };
937
938 testToINIDefaultEscapes = {
939 expr = generators.toINI {} {
940 "no [ and ] allowed unescaped" = {
941 "and also no = in keys" = 42;
942 };
943 };
944 expected = ''
945 [no \[ and \] allowed unescaped]
946 and also no \= in keys=42
947 '';
948 };
949
950 testToINIDefaultFull = {
951 expr = generators.toINI {} {
952 "section 1" = {
953 attribute1 = 5;
954 x = "Me-se JarJar Binx";
955 # booleans are converted verbatim by default
956 boolean = false;
957 };
958 "foo[]" = {
959 "he\\h=he" = "this is okay";
960 };
961 };
962 expected = ''
963 [foo\[\]]
964 he\h\=he=this is okay
965
966 [section 1]
967 attribute1=5
968 boolean=false
969 x=Me-se JarJar Binx
970 '';
971 };
972
973 testToINIWithGlobalSectionEmpty = {
974 expr = generators.toINIWithGlobalSection {} {
975 globalSection = {
976 };
977 sections = {
978 };
979 };
980 expected = ''
981 '';
982 };
983
984 testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
985 let
986 sections = {
987 "section 1" = {
988 attribute1 = 5;
989 x = "Me-se JarJar Binx";
990 };
991 "foo" = {
992 "he\\h=he" = "this is okay";
993 };
994 };
995 in {
996 expr =
997 generators.toINIWithGlobalSection {} {
998 globalSection = {};
999 sections = sections;
1000 };
1001 expected = generators.toINI {} sections;
1002 };
1003
1004 testToINIWithGlobalSectionFull = {
1005 expr = generators.toINIWithGlobalSection {} {
1006 globalSection = {
1007 foo = "bar";
1008 test = false;
1009 };
1010 sections = {
1011 "section 1" = {
1012 attribute1 = 5;
1013 x = "Me-se JarJar Binx";
1014 };
1015 "foo" = {
1016 "he\\h=he" = "this is okay";
1017 };
1018 };
1019 };
1020 expected = ''
1021 foo=bar
1022 test=false
1023
1024 [foo]
1025 he\h\=he=this is okay
1026
1027 [section 1]
1028 attribute1=5
1029 x=Me-se JarJar Binx
1030 '';
1031 };
1032
1033 testToGitINI = {
1034 expr = generators.toGitINI {
1035 user = {
1036 email = "user@example.org";
1037 name = "John Doe";
1038 signingKey = "00112233445566778899AABBCCDDEEFF";
1039 };
1040 gpg.program = "path-to-gpg";
1041 tag.gpgSign = true;
1042 include.path = "~/path/to/config.inc";
1043 includeIf."gitdif:~/src/dir".path = "~/path/to/conditional.inc";
1044 extra = {
1045 boolean = true;
1046 integer = 38;
1047 name = "value";
1048 subsection.value = "test";
1049 };};
1050 expected = ''
1051 [extra]
1052 ${"\t"}boolean = true
1053 ${"\t"}integer = 38
1054 ${"\t"}name = "value"
1055
1056 [extra "subsection"]
1057 ${"\t"}value = "test"
1058
1059 [gpg]
1060 ${"\t"}program = "path-to-gpg"
1061
1062 [include]
1063 ${"\t"}path = "~/path/to/config.inc"
1064
1065 [includeIf "gitdif:~/src/dir"]
1066 ${"\t"}path = "~/path/to/conditional.inc"
1067
1068 [tag]
1069 ${"\t"}gpgSign = true
1070
1071 [user]
1072 ${"\t"}email = "user@example.org"
1073 ${"\t"}name = "John Doe"
1074 ${"\t"}signingKey = "00112233445566778899AABBCCDDEEFF"
1075 '';
1076 };
1077
1078 /* right now only invocation check */
1079 testToJSONSimple =
1080 let val = {
1081 foobar = [ "baz" 1 2 3 ];
1082 };
1083 in {
1084 expr = generators.toJSON {} val;
1085 # trivial implementation
1086 expected = builtins.toJSON val;
1087 };
1088
1089 /* right now only invocation check */
1090 testToYAMLSimple =
1091 let val = {
1092 list = [ { one = 1; } { two = 2; } ];
1093 all = 42;
1094 };
1095 in {
1096 expr = generators.toYAML {} val;
1097 # trivial implementation
1098 expected = builtins.toJSON val;
1099 };
1100
1101 testToPretty =
1102 let
1103 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
1104 in {
1105 expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
1106 int = 42;
1107 float = 0.1337;
1108 bool = true;
1109 emptystring = "";
1110 string = "fn\${o}\"r\\d";
1111 newlinestring = "\n";
1112 path = /. + "/foo";
1113 null_ = null;
1114 function = x: x;
1115 functionArgs = { arg ? 4, foo }: arg;
1116 list = [ 3 4 function [ false ] ];
1117 emptylist = [];
1118 attrs = { foo = null; "foo b/ar" = "baz"; };
1119 emptyattrs = {};
1120 drv = deriv;
1121 };
1122 expected = rec {
1123 int = "42";
1124 float = "0.1337";
1125 bool = "true";
1126 emptystring = ''""'';
1127 string = ''"fn\''${o}\"r\\d"'';
1128 newlinestring = "\"\\n\"";
1129 path = "/foo";
1130 null_ = "null";
1131 function = "<function>";
1132 functionArgs = "<function, args: {arg?, foo}>";
1133 list = "[ 3 4 ${function} [ false ] ]";
1134 emptylist = "[ ]";
1135 attrs = "{ foo = null; \"foo b/ar\" = \"baz\"; }";
1136 emptyattrs = "{ }";
1137 drv = "<derivation ${deriv.name}>";
1138 };
1139 };
1140
1141 testToPrettyLimit =
1142 let
1143 a.b = 1;
1144 a.c = a;
1145 in {
1146 expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a);
1147 expected = "{\n b = 1;\n c = {\n b = \"<unevaluated>\";\n c = {\n b = \"<unevaluated>\";\n c = \"<unevaluated>\";\n };\n };\n}";
1148 };
1149
1150 testToPrettyLimitThrow =
1151 let
1152 a.b = 1;
1153 a.c = a;
1154 in {
1155 expr = (builtins.tryEval
1156 (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
1157 expected = false;
1158 };
1159
1160 testWithRecursionDealsWithFunctors =
1161 let
1162 functor = {
1163 __functor = self: { a, b, }: null;
1164 };
1165 a = {
1166 value = "1234";
1167 b = functor;
1168 c.d = functor;
1169 };
1170 in {
1171 expr = generators.toPretty { } (generators.withRecursion { depthLimit = 1; throwOnDepthLimit = false; } a);
1172 expected = "{\n b = <function, args: {a, b}>;\n c = {\n d = \"<unevaluated>\";\n };\n value = \"<unevaluated>\";\n}";
1173 };
1174
1175 testToPrettyMultiline = {
1176 expr = mapAttrs (const (generators.toPretty { })) rec {
1177 list = [ 3 4 [ false ] ];
1178 attrs = { foo = null; bar.foo = "baz"; };
1179 newlinestring = "\n";
1180 multilinestring = ''
1181 hello
1182 ''${there}
1183 te'''st
1184 '';
1185 multilinestring' = ''
1186 hello
1187 there
1188 test'';
1189 };
1190 expected = rec {
1191 list = ''
1192 [
1193 3
1194 4
1195 [
1196 false
1197 ]
1198 ]'';
1199 attrs = ''
1200 {
1201 bar = {
1202 foo = "baz";
1203 };
1204 foo = null;
1205 }'';
1206 newlinestring = "''\n \n''";
1207 multilinestring = ''
1208 '''
1209 hello
1210 '''''${there}
1211 te''''st
1212 ''''';
1213 multilinestring' = ''
1214 '''
1215 hello
1216 there
1217 test''''';
1218
1219 };
1220 };
1221
1222 testToPrettyAllowPrettyValues = {
1223 expr = generators.toPretty { allowPrettyValues = true; }
1224 { __pretty = v: "«" + v + "»"; val = "foo"; };
1225 expected = "«foo»";
1226 };
1227
1228 testToPlist =
1229 let
1230 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
1231 in {
1232 expr = mapAttrs (const (generators.toPlist { })) {
1233 value = {
1234 nested.values = rec {
1235 int = 42;
1236 float = 0.1337;
1237 bool = true;
1238 emptystring = "";
1239 string = "fn\${o}\"r\\d";
1240 newlinestring = "\n";
1241 path = /. + "/foo";
1242 null_ = null;
1243 list = [ 3 4 "test" ];
1244 emptylist = [];
1245 attrs = { foo = null; "foo b/ar" = "baz"; };
1246 emptyattrs = {};
1247 };
1248 };
1249 };
1250 expected = { value = builtins.readFile ./test-to-plist-expected.plist; };
1251 };
1252
1253 testToLuaEmptyAttrSet = {
1254 expr = generators.toLua {} {};
1255 expected = ''{}'';
1256 };
1257
1258 testToLuaEmptyList = {
1259 expr = generators.toLua {} [];
1260 expected = ''{}'';
1261 };
1262
1263 testToLuaListOfVariousTypes = {
1264 expr = generators.toLua {} [ null 43 3.14159 true ];
1265 expected = ''
1266 {
1267 nil,
1268 43,
1269 3.14159,
1270 true
1271 }'';
1272 };
1273
1274 testToLuaString = {
1275 expr = generators.toLua {} ''double-quote (") and single quotes (')'';
1276 expected = ''"double-quote (\") and single quotes (')"'';
1277 };
1278
1279 testToLuaAttrsetWithLuaInline = {
1280 expr = generators.toLua {} { x = generators.mkLuaInline ''"abc" .. "def"''; };
1281 expected = ''
1282 {
1283 ["x"] = ("abc" .. "def")
1284 }'';
1285 };
1286
1287 testToLuaAttrsetWithSpaceInKey = {
1288 expr = generators.toLua {} { "some space and double-quote (\")" = 42; };
1289 expected = ''
1290 {
1291 ["some space and double-quote (\")"] = 42
1292 }'';
1293 };
1294
1295 testToLuaWithoutMultiline = {
1296 expr = generators.toLua { multiline = false; } [ 41 43 ];
1297 expected = ''{ 41, 43 }'';
1298 };
1299
1300 testToLuaEmptyBindings = {
1301 expr = generators.toLua { asBindings = true; } {};
1302 expected = "";
1303 };
1304
1305 testToLuaBindings = {
1306 expr = generators.toLua { asBindings = true; } { x1 = 41; _y = { a = 43; }; };
1307 expected = ''
1308 _y = {
1309 ["a"] = 43
1310 }
1311 x1 = 41
1312 '';
1313 };
1314
1315 testToLuaPartialTableBindings = {
1316 expr = generators.toLua { asBindings = true; } { "x.y" = 42; };
1317 expected = ''
1318 x.y = 42
1319 '';
1320 };
1321
1322 testToLuaIndentedBindings = {
1323 expr = generators.toLua { asBindings = true; indent = " "; } { x = { y = 42; }; };
1324 expected = " x = {\n [\"y\"] = 42\n }\n";
1325 };
1326
1327 testToLuaBindingsWithSpace = testingThrow (
1328 generators.toLua { asBindings = true; } { "with space" = 42; }
1329 );
1330
1331 testToLuaBindingsWithLeadingDigit = testingThrow (
1332 generators.toLua { asBindings = true; } { "11eleven" = 42; }
1333 );
1334
1335 testToLuaBasicExample = {
1336 expr = generators.toLua {} {
1337 cmd = [ "typescript-language-server" "--stdio" ];
1338 settings.workspace.library = generators.mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
1339 };
1340 expected = ''
1341 {
1342 ["cmd"] = {
1343 "typescript-language-server",
1344 "--stdio"
1345 },
1346 ["settings"] = {
1347 ["workspace"] = {
1348 ["library"] = (vim.api.nvim_get_runtime_file("", true))
1349 }
1350 }
1351 }'';
1352 };
1353
1354# CLI
1355
1356 testToGNUCommandLine = {
1357 expr = cli.toGNUCommandLine {} {
1358 data = builtins.toJSON { id = 0; };
1359 X = "PUT";
1360 retry = 3;
1361 retry-delay = null;
1362 url = [ "https://example.com/foo" "https://example.com/bar" ];
1363 silent = false;
1364 verbose = true;
1365 };
1366
1367 expected = [
1368 "-X" "PUT"
1369 "--data" "{\"id\":0}"
1370 "--retry" "3"
1371 "--url" "https://example.com/foo"
1372 "--url" "https://example.com/bar"
1373 "--verbose"
1374 ];
1375 };
1376
1377 testToGNUCommandLineShell = {
1378 expr = cli.toGNUCommandLineShell {} {
1379 data = builtins.toJSON { id = 0; };
1380 X = "PUT";
1381 retry = 3;
1382 retry-delay = null;
1383 url = [ "https://example.com/foo" "https://example.com/bar" ];
1384 silent = false;
1385 verbose = true;
1386 };
1387
1388 expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
1389 };
1390
1391 testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
1392 name = "..foo";
1393 expected = "foo";
1394 };
1395
1396 testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
1397 name = "fö";
1398 expected = "f-";
1399 };
1400
1401 testSanitizeDerivationNameAscii = testSanitizeDerivationName {
1402 name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
1403 expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
1404 };
1405
1406 testSanitizeDerivationNameTooLong = testSanitizeDerivationName {
1407 name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
1408 expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
1409 };
1410
1411 testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName {
1412 name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&";
1413 expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-";
1414 };
1415
1416 testSanitizeDerivationNameEmpty = testSanitizeDerivationName {
1417 name = "";
1418 expected = "unknown";
1419 };
1420
1421 testFreeformOptions = {
1422 expr =
1423 let
1424 submodule = { lib, ... }: {
1425 freeformType = lib.types.attrsOf (lib.types.submodule {
1426 options.bar = lib.mkOption {};
1427 });
1428 options.bar = lib.mkOption {};
1429 };
1430
1431 module = { lib, ... }: {
1432 options.foo = lib.mkOption {
1433 type = lib.types.submodule submodule;
1434 };
1435 };
1436
1437 options = (evalModules {
1438 modules = [ module ];
1439 }).options;
1440
1441 locs = filter (o: ! o.internal) (optionAttrSetToDocList options);
1442 in map (o: o.loc) locs;
1443 expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
1444 };
1445
1446 testCartesianProductOfEmptySet = {
1447 expr = cartesianProductOfSets {};
1448 expected = [ {} ];
1449 };
1450
1451 testCartesianProductOfOneSet = {
1452 expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
1453 expected = [ { a = 1; } { a = 2; } { a = 3; } ];
1454 };
1455
1456 testCartesianProductOfTwoSets = {
1457 expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
1458 expected = [
1459 { a = 1; b = 10; }
1460 { a = 1; b = 20; }
1461 ];
1462 };
1463
1464 testCartesianProductOfTwoSetsWithOneEmpty = {
1465 expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
1466 expected = [ ];
1467 };
1468
1469 testCartesianProductOfThreeSets = {
1470 expr = cartesianProductOfSets {
1471 a = [ 1 2 3 ];
1472 b = [ 10 20 30 ];
1473 c = [ 100 200 300 ];
1474 };
1475 expected = [
1476 { a = 1; b = 10; c = 100; }
1477 { a = 1; b = 10; c = 200; }
1478 { a = 1; b = 10; c = 300; }
1479
1480 { a = 1; b = 20; c = 100; }
1481 { a = 1; b = 20; c = 200; }
1482 { a = 1; b = 20; c = 300; }
1483
1484 { a = 1; b = 30; c = 100; }
1485 { a = 1; b = 30; c = 200; }
1486 { a = 1; b = 30; c = 300; }
1487
1488 { a = 2; b = 10; c = 100; }
1489 { a = 2; b = 10; c = 200; }
1490 { a = 2; b = 10; c = 300; }
1491
1492 { a = 2; b = 20; c = 100; }
1493 { a = 2; b = 20; c = 200; }
1494 { a = 2; b = 20; c = 300; }
1495
1496 { a = 2; b = 30; c = 100; }
1497 { a = 2; b = 30; c = 200; }
1498 { a = 2; b = 30; c = 300; }
1499
1500 { a = 3; b = 10; c = 100; }
1501 { a = 3; b = 10; c = 200; }
1502 { a = 3; b = 10; c = 300; }
1503
1504 { a = 3; b = 20; c = 100; }
1505 { a = 3; b = 20; c = 200; }
1506 { a = 3; b = 20; c = 300; }
1507
1508 { a = 3; b = 30; c = 100; }
1509 { a = 3; b = 30; c = 200; }
1510 { a = 3; b = 30; c = 300; }
1511 ];
1512 };
1513
1514 # The example from the showAttrPath documentation
1515 testShowAttrPathExample = {
1516 expr = showAttrPath [ "foo" "10" "bar" ];
1517 expected = "foo.\"10\".bar";
1518 };
1519
1520 testShowAttrPathEmpty = {
1521 expr = showAttrPath [];
1522 expected = "<root attribute path>";
1523 };
1524
1525 testShowAttrPathVarious = {
1526 expr = showAttrPath [
1527 "."
1528 "foo"
1529 "2"
1530 "a2-b"
1531 "_bc'de"
1532 ];
1533 expected = ''".".foo."2".a2-b._bc'de'';
1534 };
1535
1536 testGroupBy = {
1537 expr = groupBy (n: toString (mod n 5)) (range 0 16);
1538 expected = {
1539 "0" = [ 0 5 10 15 ];
1540 "1" = [ 1 6 11 16 ];
1541 "2" = [ 2 7 12 ];
1542 "3" = [ 3 8 13 ];
1543 "4" = [ 4 9 14 ];
1544 };
1545 };
1546
1547 testGroupBy' = {
1548 expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ];
1549 expected = { false = 3; true = 12; };
1550 };
1551
1552 # The example from the updateManyAttrsByPath documentation
1553 testUpdateManyAttrsByPathExample = {
1554 expr = updateManyAttrsByPath [
1555 {
1556 path = [ "a" "b" ];
1557 update = old: { d = old.c; };
1558 }
1559 {
1560 path = [ "a" "b" "c" ];
1561 update = old: old + 1;
1562 }
1563 {
1564 path = [ "x" "y" ];
1565 update = old: "xy";
1566 }
1567 ] { a.b.c = 0; };
1568 expected = { a = { b = { d = 1; }; }; x = { y = "xy"; }; };
1569 };
1570
1571 # If there are no updates, the value is passed through
1572 testUpdateManyAttrsByPathNone = {
1573 expr = updateManyAttrsByPath [] "something";
1574 expected = "something";
1575 };
1576
1577 # A single update to the root path is just like applying the function directly
1578 testUpdateManyAttrsByPathSingleIncrement = {
1579 expr = updateManyAttrsByPath [
1580 {
1581 path = [ ];
1582 update = old: old + 1;
1583 }
1584 ] 0;
1585 expected = 1;
1586 };
1587
1588 # Multiple updates can be applied are done in order
1589 testUpdateManyAttrsByPathMultipleIncrements = {
1590 expr = updateManyAttrsByPath [
1591 {
1592 path = [ ];
1593 update = old: old + "a";
1594 }
1595 {
1596 path = [ ];
1597 update = old: old + "b";
1598 }
1599 {
1600 path = [ ];
1601 update = old: old + "c";
1602 }
1603 ] "";
1604 expected = "abc";
1605 };
1606
1607 # If an update doesn't use the value, all previous updates are not evaluated
1608 testUpdateManyAttrsByPathLazy = {
1609 expr = updateManyAttrsByPath [
1610 {
1611 path = [ ];
1612 update = old: old + throw "nope";
1613 }
1614 {
1615 path = [ ];
1616 update = old: "untainted";
1617 }
1618 ] (throw "start");
1619 expected = "untainted";
1620 };
1621
1622 # Deeply nested attributes can be updated without affecting others
1623 testUpdateManyAttrsByPathDeep = {
1624 expr = updateManyAttrsByPath [
1625 {
1626 path = [ "a" "b" "c" ];
1627 update = old: old + 1;
1628 }
1629 ] {
1630 a.b.c = 0;
1631
1632 a.b.z = 0;
1633 a.y.z = 0;
1634 x.y.z = 0;
1635 };
1636 expected = {
1637 a.b.c = 1;
1638
1639 a.b.z = 0;
1640 a.y.z = 0;
1641 x.y.z = 0;
1642 };
1643 };
1644
1645 # Nested attributes are updated first
1646 testUpdateManyAttrsByPathNestedBeforehand = {
1647 expr = updateManyAttrsByPath [
1648 {
1649 path = [ "a" ];
1650 update = old: old // { x = old.b; };
1651 }
1652 {
1653 path = [ "a" "b" ];
1654 update = old: old + 1;
1655 }
1656 ] {
1657 a.b = 0;
1658 };
1659 expected = {
1660 a.b = 1;
1661 a.x = 1;
1662 };
1663 };
1664
1665 ## Levenshtein distance functions and co.
1666 testCommonPrefixLengthEmpty = {
1667 expr = strings.commonPrefixLength "" "hello";
1668 expected = 0;
1669 };
1670
1671 testCommonPrefixLengthSame = {
1672 expr = strings.commonPrefixLength "hello" "hello";
1673 expected = 5;
1674 };
1675
1676 testCommonPrefixLengthDiffering = {
1677 expr = strings.commonPrefixLength "hello" "hey";
1678 expected = 2;
1679 };
1680
1681 testCommonSuffixLengthEmpty = {
1682 expr = strings.commonSuffixLength "" "hello";
1683 expected = 0;
1684 };
1685
1686 testCommonSuffixLengthSame = {
1687 expr = strings.commonSuffixLength "hello" "hello";
1688 expected = 5;
1689 };
1690
1691 testCommonSuffixLengthDiffering = {
1692 expr = strings.commonSuffixLength "test" "rest";
1693 expected = 3;
1694 };
1695
1696 testLevenshteinEmpty = {
1697 expr = strings.levenshtein "" "";
1698 expected = 0;
1699 };
1700
1701 testLevenshteinOnlyAdd = {
1702 expr = strings.levenshtein "" "hello there";
1703 expected = 11;
1704 };
1705
1706 testLevenshteinOnlyRemove = {
1707 expr = strings.levenshtein "hello there" "";
1708 expected = 11;
1709 };
1710
1711 testLevenshteinOnlyTransform = {
1712 expr = strings.levenshtein "abcdef" "ghijkl";
1713 expected = 6;
1714 };
1715
1716 testLevenshteinMixed = {
1717 expr = strings.levenshtein "kitchen" "sitting";
1718 expected = 5;
1719 };
1720
1721 testLevenshteinAtMostZeroFalse = {
1722 expr = strings.levenshteinAtMost 0 "foo" "boo";
1723 expected = false;
1724 };
1725
1726 testLevenshteinAtMostZeroTrue = {
1727 expr = strings.levenshteinAtMost 0 "foo" "foo";
1728 expected = true;
1729 };
1730
1731 testLevenshteinAtMostOneFalse = {
1732 expr = strings.levenshteinAtMost 1 "car" "ct";
1733 expected = false;
1734 };
1735
1736 testLevenshteinAtMostOneTrue = {
1737 expr = strings.levenshteinAtMost 1 "car" "cr";
1738 expected = true;
1739 };
1740
1741 # We test levenshteinAtMost 2 particularly well because it uses a complicated
1742 # implementation
1743 testLevenshteinAtMostTwoIsEmpty = {
1744 expr = strings.levenshteinAtMost 2 "" "";
1745 expected = true;
1746 };
1747
1748 testLevenshteinAtMostTwoIsZero = {
1749 expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
1750 expected = true;
1751 };
1752
1753 testLevenshteinAtMostTwoIsOne = {
1754 expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
1755 expected = true;
1756 };
1757
1758 testLevenshteinAtMostTwoDiff0False = {
1759 expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
1760 expected = false;
1761 };
1762
1763 testLevenshteinAtMostTwoDiff0Outer = {
1764 expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
1765 expected = true;
1766 };
1767
1768 testLevenshteinAtMostTwoDiff0DelLeft = {
1769 expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
1770 expected = true;
1771 };
1772
1773 testLevenshteinAtMostTwoDiff0DelRight = {
1774 expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
1775 expected = true;
1776 };
1777
1778 testLevenshteinAtMostTwoDiff1False = {
1779 expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
1780 expected = false;
1781 };
1782
1783 testLevenshteinAtMostTwoDiff1DelLeft = {
1784 expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
1785 expected = true;
1786 };
1787
1788 testLevenshteinAtMostTwoDiff1DelRight = {
1789 expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
1790 expected = true;
1791 };
1792
1793 testLevenshteinAtMostTwoDiff2False = {
1794 expr = strings.levenshteinAtMost 2 "hello" "hxo";
1795 expected = false;
1796 };
1797
1798 testLevenshteinAtMostTwoDiff2True = {
1799 expr = strings.levenshteinAtMost 2 "hello" "heo";
1800 expected = true;
1801 };
1802
1803 testLevenshteinAtMostTwoDiff3 = {
1804 expr = strings.levenshteinAtMost 2 "hello" "ho";
1805 expected = false;
1806 };
1807
1808 testLevenshteinAtMostThreeFalse = {
1809 expr = strings.levenshteinAtMost 3 "hello" "Holla!";
1810 expected = false;
1811 };
1812
1813 testLevenshteinAtMostThreeTrue = {
1814 expr = strings.levenshteinAtMost 3 "hello" "Holla";
1815 expected = true;
1816 };
1817
1818 # lazyDerivation
1819
1820 testLazyDerivationIsLazyInDerivationForAttrNames = {
1821 expr = attrNames (lazyDerivation {
1822 derivation = throw "not lazy enough";
1823 });
1824 # It's ok to add attribute names here when lazyDerivation is improved
1825 # in accordance with its inline comments.
1826 expected = [ "drvPath" "meta" "name" "out" "outPath" "outputName" "outputs" "system" "type" ];
1827 };
1828
1829 testLazyDerivationIsLazyInDerivationForPassthruAttr = {
1830 expr = (lazyDerivation {
1831 derivation = throw "not lazy enough";
1832 passthru.tests = "whatever is in tests";
1833 }).tests;
1834 expected = "whatever is in tests";
1835 };
1836
1837 testLazyDerivationIsLazyInDerivationForPassthruAttr2 = {
1838 # passthru.tests is not a special case. It works for any attr.
1839 expr = (lazyDerivation {
1840 derivation = throw "not lazy enough";
1841 passthru.foo = "whatever is in foo";
1842 }).foo;
1843 expected = "whatever is in foo";
1844 };
1845
1846 testLazyDerivationIsLazyInDerivationForMeta = {
1847 expr = (lazyDerivation {
1848 derivation = throw "not lazy enough";
1849 meta = "whatever is in meta";
1850 }).meta;
1851 expected = "whatever is in meta";
1852 };
1853
1854 testLazyDerivationReturnsDerivationAttrs = let
1855 derivation = {
1856 type = "derivation";
1857 outputs = ["out"];
1858 out = "test out";
1859 outPath = "test outPath";
1860 outputName = "out";
1861 drvPath = "test drvPath";
1862 name = "test name";
1863 system = "test system";
1864 meta = "test meta";
1865 };
1866 in {
1867 expr = lazyDerivation { inherit derivation; };
1868 expected = derivation;
1869 };
1870
1871 testTypeDescriptionInt = {
1872 expr = (with types; int).description;
1873 expected = "signed integer";
1874 };
1875 testTypeDescriptionListOfInt = {
1876 expr = (with types; listOf int).description;
1877 expected = "list of signed integer";
1878 };
1879 testTypeDescriptionListOfListOfInt = {
1880 expr = (with types; listOf (listOf int)).description;
1881 expected = "list of list of signed integer";
1882 };
1883 testTypeDescriptionListOfEitherStrOrBool = {
1884 expr = (with types; listOf (either str bool)).description;
1885 expected = "list of (string or boolean)";
1886 };
1887 testTypeDescriptionEitherListOfStrOrBool = {
1888 expr = (with types; either (listOf bool) str).description;
1889 expected = "(list of boolean) or string";
1890 };
1891 testTypeDescriptionEitherStrOrListOfBool = {
1892 expr = (with types; either str (listOf bool)).description;
1893 expected = "string or list of boolean";
1894 };
1895 testTypeDescriptionOneOfListOfStrOrBool = {
1896 expr = (with types; oneOf [ (listOf bool) str ]).description;
1897 expected = "(list of boolean) or string";
1898 };
1899 testTypeDescriptionOneOfListOfStrOrBoolOrNumber = {
1900 expr = (with types; oneOf [ (listOf bool) str number ]).description;
1901 expected = "(list of boolean) or string or signed integer or floating point number";
1902 };
1903 testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = {
1904 expr = (with types; either (listOf bool) (either str number)).description;
1905 expected = "(list of boolean) or string or signed integer or floating point number";
1906 };
1907 testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = {
1908 expr = (with types; either (either (listOf bool) str) number).description;
1909 expected = "(list of boolean) or string or signed integer or floating point number";
1910 };
1911 testTypeDescriptionEitherNullOrBoolOrString = {
1912 expr = (with types; either (nullOr bool) str).description;
1913 expected = "null or boolean or string";
1914 };
1915 testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = {
1916 expr = (with types; either (listOf (either bool str)) int).description;
1917 expected = "(list of (boolean or string)) or signed integer";
1918 };
1919 testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = {
1920 expr = (with types; either int (listOf (either bool str))).description;
1921 expected = "signed integer or list of (boolean or string)";
1922 };
1923
1924# Meta
1925 testGetExe'Output = {
1926 expr = getExe' {
1927 type = "derivation";
1928 out = "somelonghash";
1929 bin = "somelonghash";
1930 } "executable";
1931 expected = "somelonghash/bin/executable";
1932 };
1933
1934 testGetExeOutput = {
1935 expr = getExe {
1936 type = "derivation";
1937 out = "somelonghash";
1938 bin = "somelonghash";
1939 meta.mainProgram = "mainProgram";
1940 };
1941 expected = "somelonghash/bin/mainProgram";
1942 };
1943
1944 testGetExe'FailureFirstArg = testingThrow (
1945 getExe' "not a derivation" "executable"
1946 );
1947
1948 testGetExe'FailureSecondArg = testingThrow (
1949 getExe' { type = "derivation"; } "dir/executable"
1950 );
1951}