2 Nix evaluation tests for various lib functions.
4 Since 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.
5 If you need to test error messages or more complex evaluations, see ./modules.sh, ./sources.sh or ./filesystem.sh as examples.
9 [nixpkgs]$ nix-instantiate --eval --strict lib/tests/misc.nix
11 If the resulting list is empty, all tests passed.
12 Alternatively, to run all `lib` tests:
14 [nixpkgs]$ nix-build lib/tests/release.nix
16 with import ../default.nix;
19 testingThrow = expr: {
20 expr = (builtins.tryEval (builtins.seq expr "didn't throw"));
21 expected = { success = false; value = false; };
24 expr = (builtins.tryEval expr).success;
27 testingDeepThrow = expr: testingThrow (builtins.deepSeq expr expr);
29 testSanitizeDerivationName = { name, expected }:
32 name = strings.sanitizeDerivationName name;
37 # Evaluate the derivation so an invalid name would be caught
38 expr = builtins.seq drv.drvPath drv.name;
48 testFunctionArgsMakeOverridable = {
49 expr = functionArgs (makeOverridable ({ a, b, c ? null}: {}));
50 expected = { a = false; b = false; c = true; };
53 testFunctionArgsMakeOverridableOverride = {
54 expr = functionArgs (makeOverridable ({ a, b, c ? null }: {}) { a = 1; b = 2; }).override;
55 expected = { a = false; b = false; c = true; };
72 (x: x + 2) # 2 + 2 = 4
73 (x: x * 2) # 4 * 2 = 8
103 expr = and true false;
108 expr = fix (x: {a = if x ? a then "a" else "b";});
109 expected = {a = "a";};
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;
122 testComposeManyExtensions0 = {
123 expr = let obj = makeExtensible (self: { foo = true; });
124 emptyComposition = composeManyExtensions [];
125 composed = obj.extend emptyComposition;
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; });
136 expr = let composition = composeManyExtensions [f g h];
137 composed = obj.extend composition;
139 expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo;
143 expr = (bitAnd 3 10);
153 expr = (bitXor 3 10);
158 expr = toHexString 250;
163 expr = toBaseDigits 2 6;
164 expected = [ 1 1 0 ];
167 testFunctionArgsFunctor = {
168 expr = functionArgs { __functor = self: { a, b }: null; };
169 expected = { a = false; b = false; };
172 testFunctionArgsSetFunctionArgs = {
173 expr = functionArgs (setFunctionArgs (args: args.x) { x = false; });
174 expected = { x = false; };
179 testConcatMapStrings = {
180 expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
184 testConcatStringsSep = {
185 expr = concatStringsSep "," ["a" "b" "c"];
190 expr = concatLines ["a" "b" "c"];
191 expected = "a\nb\nc\n";
194 testReplicateString = {
195 expr = strings.replicate 5 "hello";
196 expected = "hellohellohellohellohello";
199 testSplitStringsSimple = {
200 expr = strings.splitString "." "a.b.c.d";
201 expected = [ "a" "b" "c" "d" ];
204 testSplitStringsEmpty = {
205 expr = strings.splitString "." "a..b";
206 expected = [ "a" "" "b" ];
209 testSplitStringsOne = {
210 expr = strings.splitString ":" "a.b";
211 expected = [ "a.b" ];
214 testSplitStringsNone = {
215 expr = strings.splitString "." "";
219 testSplitStringsFirstEmpty = {
220 expr = strings.splitString "/" "/a/b/c";
221 expected = [ "" "a" "b" "c" ];
224 testSplitStringsLastEmpty = {
225 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
226 expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
229 testSplitStringsRegex = {
230 expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B";
231 expected = [ "A" "B" ];
234 testSplitStringsDerivation = {
235 expr = take 3 (strings.splitString "/" (derivation {
240 expected = ["" "nix" "store"];
243 testSplitVersionSingle = {
244 expr = versions.splitVersion "1";
248 testSplitVersionDouble = {
249 expr = versions.splitVersion "1.2";
250 expected = [ "1" "2" ];
253 testSplitVersionTriple = {
254 expr = versions.splitVersion "1.2.3";
255 expected = [ "1" "2" "3" ];
258 testPadVersionLess = {
259 expr = versions.pad 3 "1.2";
263 testPadVersionLessExtra = {
264 expr = versions.pad 3 "1.3-rc1";
265 expected = "1.3.0-rc1";
268 testPadVersionMore = {
269 expr = versions.pad 3 "1.2.3.4";
276 "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
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";
286 attrset = isStorePath {};
287 list = isStorePath [];
288 int = isStorePath 42;
293 storePathDerivation = true;
294 storePathAppendix = false;
307 expr = escapeXML ''"test" 'test' < & >'';
308 expected = ""test" 'test' < & >";
314 STRing01 = "just a 'string'";
315 _array_ = [ "with" "more strings" ];
316 assoc."with some" = ''
322 foo = "ignored attribute";
326 __toString = _: "hello toString";
327 bar = "ignored attribute";
332 STRing01='just a '\'''string'\''''
333 declare -a _array_=('with' 'more strings')
334 declare -A assoc=(['with some']='strings
339 stringable='hello toString'
343 testHasInfixFalse = {
344 expr = hasInfix "c" "abde";
349 expr = hasInfix "c" "abcde";
353 testHasInfixDerivation = {
354 expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello;
359 expr = hasInfix "tests" ./.;
363 testHasInfixPathStoreDir = {
364 expr = hasInfix builtins.storeDir ./.;
368 testHasInfixToString = {
369 expr = hasInfix "a" { __toString = _: "a"; };
373 testRemovePrefixExample1 = {
374 expr = removePrefix "foo." "foo.bar.baz";
375 expected = "bar.baz";
377 testRemovePrefixExample2 = {
378 expr = removePrefix "xxx" "foo.bar.baz";
379 expected = "foo.bar.baz";
381 testRemovePrefixEmptyPrefix = {
382 expr = removePrefix "" "foo";
385 testRemovePrefixEmptyString = {
386 expr = removePrefix "foo" "";
389 testRemovePrefixEmptyBoth = {
390 expr = removePrefix "" "";
394 testNormalizePath = {
395 expr = strings.normalizePath "//a/b//c////d/";
396 expected = "/a/b/c/d/";
400 expr = strings.charToInt "A";
405 expr = strings.escapeC [ " " ] "Hello World";
406 expected = "Hello\\x20World";
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:/@$'()*,;")
418 testToInt = testAllTrue [
423 (123 == toInt " 123")
424 (123 == toInt "123 ")
425 (123 == toInt " 123 ")
426 (123 == toInt " 123 ")
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; } )
450 testToIntBase10 = testAllTrue [
452 (123 == toIntBase10 "123")
453 (0 == toIntBase10 "0")
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 ")
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 ")
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; } )
496 expr = filter (x: x != "a") ["a" "b" "c" "a"];
497 expected = ["b" "c"];
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;
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;
521 assocRightIsLeft = true;
523 nonAssocLeft = (-5050);
524 nonAssocRightIsNotLeft = true;
530 expr = foldl' (acc: el: abort "operation not called") 0 [ ];
534 testFoldl'IntegerAdding = {
535 expr = foldl' (acc: el: acc + el) 0 [ 1 2 3 ];
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") ]
545 expected = [ 3 2 1 ];
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;
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
557 expr = foldl' (acc: el: acc + el) 0 (range 0 100000);
558 expected = 5000050000;
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 ]))
569 testListHasPrefixExample1 = {
570 expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ];
573 testListHasPrefixExample2 = {
574 expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ];
577 testListHasPrefixLazy = {
578 expr = lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ];
581 testListHasPrefixEmptyPrefix = {
582 expr = lists.hasPrefix [ ] [ 1 2 ];
585 testListHasPrefixEmptyList = {
586 expr = lists.hasPrefix [ 1 2 ] [ ];
590 testListRemovePrefixExample1 = {
591 expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ];
594 testListRemovePrefixExample2 = {
595 expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success;
598 testListRemovePrefixEmptyPrefix = {
599 expr = lists.removePrefix [ ] [ 1 2 ];
602 testListRemovePrefixEmptyList = {
603 expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success;
608 expr = foldAttrs (n: a: [n] ++ a) [] [
612 expected = { a = [ 2 3 ]; b = [7]; c = [8];};
615 testListCommonPrefixExample1 = {
616 expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ];
619 testListCommonPrefixExample2 = {
620 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ];
621 expected = [ 1 2 3 ];
623 testListCommonPrefixExample3 = {
624 expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ];
627 testListCommonPrefixEmpty = {
628 expr = lists.commonPrefix [ ] [ 1 2 3 ];
631 testListCommonPrefixSame = {
632 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ];
633 expected = [ 1 2 3 ];
635 testListCommonPrefixLazy = {
636 expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this")];
639 # This would stack overflow if `commonPrefix` were implemented using recursion
640 testListCommonPrefixLong =
642 longList = genList (n: n) 100000;
644 expr = lists.commonPrefix longList longList;
649 expr = sort builtins.lessThan [ 40 2 30 42 ];
650 expected = [2 30 40 42];
654 expr = replicate 3 "a";
655 expected = ["a" "a" "a"];
658 testToIntShouldConvertStringToInt = {
663 testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
664 expr = builtins.tryEval (toInt "\"foo\"");
665 expected = { success = false; value = false; };
668 testHasAttrByPathTrue = {
669 expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; };
673 testHasAttrByPathFalse = {
674 expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; };
678 testFindFirstIndexExample1 = {
679 expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
683 testFindFirstIndexExample2 = {
684 expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ 1 6 4 ];
685 expected = "a very specific default";
688 testFindFirstIndexEmpty = {
689 expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null [];
693 testFindFirstIndexSingleMatch = {
694 expr = lists.findFirstIndex (x: x == 5) null [ 5 ];
698 testFindFirstIndexSingleDefault = {
699 expr = lists.findFirstIndex (x: false) null [ (abort "if the predicate doesn't access the value, it must not be evaluated") ];
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; };
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);
714 testFindFirstIndexLazy = {
715 expr = lists.findFirstIndex (x: x == 1) null [ 1 (abort "list elements after the match must not be evaluated") ];
719 testFindFirstExample1 = {
720 expr = lists.findFirst (x: x > 3) 7 [ 1 6 4 ];
724 testFindFirstExample2 = {
725 expr = lists.findFirst (x: x > 9) 7 [ 1 6 4 ];
729 testAllUnique_true = {
730 expr = allUnique [ 3 2 4 1 ];
733 testAllUnique_false = {
734 expr = allUnique [ 3 2 3 4 ];
740 testConcatMapAttrs = {
741 expr = concatMapAttrs
744 ${name + value} = value;
762 sum = acc.sum + value;
763 names = acc.names ++ [ name ];
765 { sum = 0; names = [ ]; }
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; };
780 names = [ "bar" "foo" ];
789 testMergeAttrsListExample1 = {
790 expr = attrsets.mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ];
791 expected = { a = 0; b = 1; c = 2; d = 3; };
793 testMergeAttrsListExample2 = {
794 expr = attrsets.mergeAttrsList [ { a = 0; } { a = 1; } ];
795 expected = { a = 1; };
797 testMergeAttrsListExampleMany =
800 listToAttrs (genList (m:
802 # Integer divide n by two to create duplicate attributes
803 str = "halfn${toString (n / 2)}m${toString m}";
805 nameValuePair str str
809 expr = attrsets.mergeAttrsList list;
810 expected = foldl' mergeAttrs { } list;
813 # code from the example
814 testRecursiveUpdateUntil = {
815 expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
816 # first attribute set
821 #second attribute set
827 foo.bar = 1; # 'foo.*' from the second set
829 bar = 3; # 'bar' from the first set
830 baz = 4; # 'baz' from the second set
834 testOverrideExistingEmpty = {
835 expr = overrideExisting {} { a = 1; };
839 testOverrideExistingDisjoint = {
840 expr = overrideExisting { b = 2; } { a = 1; };
841 expected = { b = 2; };
844 testOverrideExistingOverride = {
845 expr = overrideExisting { a = 3; b = 2; } { a = 1; };
846 expected = { a = 1; b = 2; };
849 testListAttrsReverse = let
850 exampleAttrs = {foo=1; bar="asdf"; baz = [1 3 3 7]; fnord=null;};
851 exampleSingletonList = [{name="foo"; value=1;}];
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;}]);
859 isReverseToAttrsToList = true;
860 isReverseToListToAttrs = true;
861 testDuplicatePruningBehaviour = [{name="a"; value=2;}];
865 testAttrsToListsCanDealWithFunctions = testingEval (
866 attrsToList { someFunc= a: a + 1;}
870 # these tests assume attributes are converted to lists
871 # in alphabetical order
873 testMkKeyValueDefault = {
874 expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar";
875 expected = ''f\:oo:bar'';
878 testMkValueString = {
886 # float = 42.23; # floats are strange
889 (const (generators.mkValueStringDefault {}))
897 # float = "42.23" true false [ "bar" ] ]'';
902 expr = generators.toKeyValue {} {
913 expr = generators.toINI {} {};
917 testToINIEmptySection = {
918 expr = generators.toINI {} { foo = {}; bar = {}; };
926 testToINIDuplicateKeys = {
927 expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; };
938 testToINIDefaultEscapes = {
939 expr = generators.toINI {} {
940 "no [ and ] allowed unescaped" = {
941 "and also no = in keys" = 42;
945 [no \[ and \] allowed unescaped]
946 and also no \= in keys=42
950 testToINIDefaultFull = {
951 expr = generators.toINI {} {
954 x = "Me-se JarJar Binx";
955 # booleans are converted verbatim by default
959 "he\\h=he" = "this is okay";
964 he\h\=he=this is okay
973 testToINIWithGlobalSectionEmpty = {
974 expr = generators.toINIWithGlobalSection {} {
984 testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
989 x = "Me-se JarJar Binx";
992 "he\\h=he" = "this is okay";
997 generators.toINIWithGlobalSection {} {
1001 expected = generators.toINI {} sections;
1004 testToINIWithGlobalSectionFull = {
1005 expr = generators.toINIWithGlobalSection {} {
1013 x = "Me-se JarJar Binx";
1016 "he\\h=he" = "this is okay";
1025 he\h\=he=this is okay
1034 expr = generators.toGitINI {
1036 email = "user@example.org";
1038 signingKey = "00112233445566778899AABBCCDDEEFF";
1040 gpg.program = "path-to-gpg";
1042 include.path = "~/path/to/config.inc";
1043 includeIf."gitdif:~/src/dir".path = "~/path/to/conditional.inc";
1048 subsection.value = "test";
1052 ${"\t"}boolean = true
1054 ${"\t"}name = "value"
1056 [extra "subsection"]
1057 ${"\t"}value = "test"
1060 ${"\t"}program = "path-to-gpg"
1063 ${"\t"}path = "~/path/to/config.inc"
1065 [includeIf "gitdif:~/src/dir"]
1066 ${"\t"}path = "~/path/to/conditional.inc"
1069 ${"\t"}gpgSign = true
1072 ${"\t"}email = "user@example.org"
1073 ${"\t"}name = "John Doe"
1074 ${"\t"}signingKey = "00112233445566778899AABBCCDDEEFF"
1078 /* right now only invocation check */
1081 foobar = [ "baz" 1 2 3 ];
1084 expr = generators.toJSON {} val;
1085 # trivial implementation
1086 expected = builtins.toJSON val;
1089 /* right now only invocation check */
1092 list = [ { one = 1; } { two = 2; } ];
1096 expr = generators.toYAML {} val;
1097 # trivial implementation
1098 expected = builtins.toJSON val;
1103 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
1105 expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
1110 string = "fn\${o}\"r\\d";
1111 newlinestring = "\n";
1115 functionArgs = { arg ? 4, foo }: arg;
1116 list = [ 3 4 function [ false ] ];
1118 attrs = { foo = null; "foo b/ar" = "baz"; };
1126 emptystring = ''""'';
1127 string = ''"fn\''${o}\"r\\d"'';
1128 newlinestring = "\"\\n\"";
1131 function = "<function>";
1132 functionArgs = "<function, args: {arg?, foo}>";
1133 list = "[ 3 4 ${function} [ false ] ]";
1135 attrs = "{ foo = null; \"foo b/ar\" = \"baz\"; }";
1137 drv = "<derivation ${deriv.name}>";
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}";
1150 testToPrettyLimitThrow =
1155 expr = (builtins.tryEval
1156 (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
1160 testWithRecursionDealsWithFunctors =
1163 __functor = self: { a, b, }: null;
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}";
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 = ''
1185 multilinestring' = ''
1206 newlinestring = "''\n \n''";
1207 multilinestring = ''
1213 multilinestring' = ''
1222 testToPrettyAllowPrettyValues = {
1223 expr = generators.toPretty { allowPrettyValues = true; }
1224 { __pretty = v: "«" + v + "»"; val = "foo"; };
1225 expected = "«foo»";
1230 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
1232 expr = mapAttrs (const (generators.toPlist { })) {
1234 nested.values = rec {
1239 string = "fn\${o}\"r\\d";
1240 newlinestring = "\n";
1243 list = [ 3 4 "test" ];
1245 attrs = { foo = null; "foo b/ar" = "baz"; };
1250 expected = { value = builtins.readFile ./test-to-plist-expected.plist; };
1253 testToLuaEmptyAttrSet = {
1254 expr = generators.toLua {} {};
1258 testToLuaEmptyList = {
1259 expr = generators.toLua {} [];
1263 testToLuaListOfVariousTypes = {
1264 expr = generators.toLua {} [ null 43 3.14159 true ];
1275 expr = generators.toLua {} ''double-quote (") and single quotes (')'';
1276 expected = ''"double-quote (\") and single quotes (')"'';
1279 testToLuaAttrsetWithLuaInline = {
1280 expr = generators.toLua {} { x = generators.mkLuaInline ''"abc" .. "def"''; };
1283 ["x"] = ("abc" .. "def")
1287 testToLuaAttrsetWithSpaceInKey = {
1288 expr = generators.toLua {} { "some space and double-quote (\")" = 42; };
1291 ["some space and double-quote (\")"] = 42
1295 testToLuaWithoutMultiline = {
1296 expr = generators.toLua { multiline = false; } [ 41 43 ];
1297 expected = ''{ 41, 43 }'';
1300 testToLuaEmptyBindings = {
1301 expr = generators.toLua { asBindings = true; } {};
1305 testToLuaBindings = {
1306 expr = generators.toLua { asBindings = true; } { x1 = 41; _y = { a = 43; }; };
1315 testToLuaPartialTableBindings = {
1316 expr = generators.toLua { asBindings = true; } { "x.y" = 42; };
1322 testToLuaIndentedBindings = {
1323 expr = generators.toLua { asBindings = true; indent = " "; } { x = { y = 42; }; };
1324 expected = " x = {\n [\"y\"] = 42\n }\n";
1327 testToLuaBindingsWithSpace = testingThrow (
1328 generators.toLua { asBindings = true; } { "with space" = 42; }
1331 testToLuaBindingsWithLeadingDigit = testingThrow (
1332 generators.toLua { asBindings = true; } { "11eleven" = 42; }
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)'';
1343 "typescript-language-server",
1348 ["library"] = (vim.api.nvim_get_runtime_file("", true))
1356 testToGNUCommandLine = {
1357 expr = cli.toGNUCommandLine {} {
1358 data = builtins.toJSON { id = 0; };
1362 url = [ "https://example.com/foo" "https://example.com/bar" ];
1369 "--data" "{\"id\":0}"
1371 "--url" "https://example.com/foo"
1372 "--url" "https://example.com/bar"
1377 testToGNUCommandLineShell = {
1378 expr = cli.toGNUCommandLineShell {} {
1379 data = builtins.toJSON { id = 0; };
1383 url = [ "https://example.com/foo" "https://example.com/bar" ];
1388 expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
1391 testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
1396 testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
1401 testSanitizeDerivationNameAscii = testSanitizeDerivationName {
1402 name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
1403 expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
1406 testSanitizeDerivationNameTooLong = testSanitizeDerivationName {
1407 name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
1408 expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
1411 testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName {
1412 name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&";
1413 expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-";
1416 testSanitizeDerivationNameEmpty = testSanitizeDerivationName {
1418 expected = "unknown";
1421 testFreeformOptions = {
1424 submodule = { lib, ... }: {
1425 freeformType = lib.types.attrsOf (lib.types.submodule {
1426 options.bar = lib.mkOption {};
1428 options.bar = lib.mkOption {};
1431 module = { lib, ... }: {
1432 options.foo = lib.mkOption {
1433 type = lib.types.submodule submodule;
1437 options = (evalModules {
1438 modules = [ module ];
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" ] ];
1446 testCartesianProductOfEmptySet = {
1447 expr = cartesianProductOfSets {};
1451 testCartesianProductOfOneSet = {
1452 expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
1453 expected = [ { a = 1; } { a = 2; } { a = 3; } ];
1456 testCartesianProductOfTwoSets = {
1457 expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
1464 testCartesianProductOfTwoSetsWithOneEmpty = {
1465 expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
1469 testCartesianProductOfThreeSets = {
1470 expr = cartesianProductOfSets {
1473 c = [ 100 200 300 ];
1476 { a = 1; b = 10; c = 100; }
1477 { a = 1; b = 10; c = 200; }
1478 { a = 1; b = 10; c = 300; }
1480 { a = 1; b = 20; c = 100; }
1481 { a = 1; b = 20; c = 200; }
1482 { a = 1; b = 20; c = 300; }
1484 { a = 1; b = 30; c = 100; }
1485 { a = 1; b = 30; c = 200; }
1486 { a = 1; b = 30; c = 300; }
1488 { a = 2; b = 10; c = 100; }
1489 { a = 2; b = 10; c = 200; }
1490 { a = 2; b = 10; c = 300; }
1492 { a = 2; b = 20; c = 100; }
1493 { a = 2; b = 20; c = 200; }
1494 { a = 2; b = 20; c = 300; }
1496 { a = 2; b = 30; c = 100; }
1497 { a = 2; b = 30; c = 200; }
1498 { a = 2; b = 30; c = 300; }
1500 { a = 3; b = 10; c = 100; }
1501 { a = 3; b = 10; c = 200; }
1502 { a = 3; b = 10; c = 300; }
1504 { a = 3; b = 20; c = 100; }
1505 { a = 3; b = 20; c = 200; }
1506 { a = 3; b = 20; c = 300; }
1508 { a = 3; b = 30; c = 100; }
1509 { a = 3; b = 30; c = 200; }
1510 { a = 3; b = 30; c = 300; }
1514 # The example from the showAttrPath documentation
1515 testShowAttrPathExample = {
1516 expr = showAttrPath [ "foo" "10" "bar" ];
1517 expected = "foo.\"10\".bar";
1520 testShowAttrPathEmpty = {
1521 expr = showAttrPath [];
1522 expected = "<root attribute path>";
1525 testShowAttrPathVarious = {
1526 expr = showAttrPath [
1533 expected = ''".".foo."2".a2-b._bc'de'';
1537 expr = groupBy (n: toString (mod n 5)) (range 0 16);
1539 "0" = [ 0 5 10 15 ];
1540 "1" = [ 1 6 11 16 ];
1548 expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ];
1549 expected = { false = 3; true = 12; };
1552 # The example from the updateManyAttrsByPath documentation
1553 testUpdateManyAttrsByPathExample = {
1554 expr = updateManyAttrsByPath [
1557 update = old: { d = old.c; };
1560 path = [ "a" "b" "c" ];
1561 update = old: old + 1;
1568 expected = { a = { b = { d = 1; }; }; x = { y = "xy"; }; };
1571 # If there are no updates, the value is passed through
1572 testUpdateManyAttrsByPathNone = {
1573 expr = updateManyAttrsByPath [] "something";
1574 expected = "something";
1577 # A single update to the root path is just like applying the function directly
1578 testUpdateManyAttrsByPathSingleIncrement = {
1579 expr = updateManyAttrsByPath [
1582 update = old: old + 1;
1588 # Multiple updates can be applied are done in order
1589 testUpdateManyAttrsByPathMultipleIncrements = {
1590 expr = updateManyAttrsByPath [
1593 update = old: old + "a";
1597 update = old: old + "b";
1601 update = old: old + "c";
1607 # If an update doesn't use the value, all previous updates are not evaluated
1608 testUpdateManyAttrsByPathLazy = {
1609 expr = updateManyAttrsByPath [
1612 update = old: old + throw "nope";
1616 update = old: "untainted";
1619 expected = "untainted";
1622 # Deeply nested attributes can be updated without affecting others
1623 testUpdateManyAttrsByPathDeep = {
1624 expr = updateManyAttrsByPath [
1626 path = [ "a" "b" "c" ];
1627 update = old: old + 1;
1645 # Nested attributes are updated first
1646 testUpdateManyAttrsByPathNestedBeforehand = {
1647 expr = updateManyAttrsByPath [
1650 update = old: old // { x = old.b; };
1654 update = old: old + 1;
1665 ## Levenshtein distance functions and co.
1666 testCommonPrefixLengthEmpty = {
1667 expr = strings.commonPrefixLength "" "hello";
1671 testCommonPrefixLengthSame = {
1672 expr = strings.commonPrefixLength "hello" "hello";
1676 testCommonPrefixLengthDiffering = {
1677 expr = strings.commonPrefixLength "hello" "hey";
1681 testCommonSuffixLengthEmpty = {
1682 expr = strings.commonSuffixLength "" "hello";
1686 testCommonSuffixLengthSame = {
1687 expr = strings.commonSuffixLength "hello" "hello";
1691 testCommonSuffixLengthDiffering = {
1692 expr = strings.commonSuffixLength "test" "rest";
1696 testLevenshteinEmpty = {
1697 expr = strings.levenshtein "" "";
1701 testLevenshteinOnlyAdd = {
1702 expr = strings.levenshtein "" "hello there";
1706 testLevenshteinOnlyRemove = {
1707 expr = strings.levenshtein "hello there" "";
1711 testLevenshteinOnlyTransform = {
1712 expr = strings.levenshtein "abcdef" "ghijkl";
1716 testLevenshteinMixed = {
1717 expr = strings.levenshtein "kitchen" "sitting";
1721 testLevenshteinAtMostZeroFalse = {
1722 expr = strings.levenshteinAtMost 0 "foo" "boo";
1726 testLevenshteinAtMostZeroTrue = {
1727 expr = strings.levenshteinAtMost 0 "foo" "foo";
1731 testLevenshteinAtMostOneFalse = {
1732 expr = strings.levenshteinAtMost 1 "car" "ct";
1736 testLevenshteinAtMostOneTrue = {
1737 expr = strings.levenshteinAtMost 1 "car" "cr";
1741 # We test levenshteinAtMost 2 particularly well because it uses a complicated
1743 testLevenshteinAtMostTwoIsEmpty = {
1744 expr = strings.levenshteinAtMost 2 "" "";
1748 testLevenshteinAtMostTwoIsZero = {
1749 expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
1753 testLevenshteinAtMostTwoIsOne = {
1754 expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
1758 testLevenshteinAtMostTwoDiff0False = {
1759 expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
1763 testLevenshteinAtMostTwoDiff0Outer = {
1764 expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
1768 testLevenshteinAtMostTwoDiff0DelLeft = {
1769 expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
1773 testLevenshteinAtMostTwoDiff0DelRight = {
1774 expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
1778 testLevenshteinAtMostTwoDiff1False = {
1779 expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
1783 testLevenshteinAtMostTwoDiff1DelLeft = {
1784 expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
1788 testLevenshteinAtMostTwoDiff1DelRight = {
1789 expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
1793 testLevenshteinAtMostTwoDiff2False = {
1794 expr = strings.levenshteinAtMost 2 "hello" "hxo";
1798 testLevenshteinAtMostTwoDiff2True = {
1799 expr = strings.levenshteinAtMost 2 "hello" "heo";
1803 testLevenshteinAtMostTwoDiff3 = {
1804 expr = strings.levenshteinAtMost 2 "hello" "ho";
1808 testLevenshteinAtMostThreeFalse = {
1809 expr = strings.levenshteinAtMost 3 "hello" "Holla!";
1813 testLevenshteinAtMostThreeTrue = {
1814 expr = strings.levenshteinAtMost 3 "hello" "Holla";
1820 testLazyDerivationIsLazyInDerivationForAttrNames = {
1821 expr = attrNames (lazyDerivation {
1822 derivation = throw "not lazy enough";
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" ];
1829 testLazyDerivationIsLazyInDerivationForPassthruAttr = {
1830 expr = (lazyDerivation {
1831 derivation = throw "not lazy enough";
1832 passthru.tests = "whatever is in tests";
1834 expected = "whatever is in tests";
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";
1843 expected = "whatever is in foo";
1846 testLazyDerivationIsLazyInDerivationForMeta = {
1847 expr = (lazyDerivation {
1848 derivation = throw "not lazy enough";
1849 meta = "whatever is in meta";
1851 expected = "whatever is in meta";
1854 testLazyDerivationReturnsDerivationAttrs = let
1856 type = "derivation";
1859 outPath = "test outPath";
1861 drvPath = "test drvPath";
1863 system = "test system";
1867 expr = lazyDerivation { inherit derivation; };
1868 expected = derivation;
1871 testTypeDescriptionInt = {
1872 expr = (with types; int).description;
1873 expected = "signed integer";
1875 testTypeDescriptionListOfInt = {
1876 expr = (with types; listOf int).description;
1877 expected = "list of signed integer";
1879 testTypeDescriptionListOfListOfInt = {
1880 expr = (with types; listOf (listOf int)).description;
1881 expected = "list of list of signed integer";
1883 testTypeDescriptionListOfEitherStrOrBool = {
1884 expr = (with types; listOf (either str bool)).description;
1885 expected = "list of (string or boolean)";
1887 testTypeDescriptionEitherListOfStrOrBool = {
1888 expr = (with types; either (listOf bool) str).description;
1889 expected = "(list of boolean) or string";
1891 testTypeDescriptionEitherStrOrListOfBool = {
1892 expr = (with types; either str (listOf bool)).description;
1893 expected = "string or list of boolean";
1895 testTypeDescriptionOneOfListOfStrOrBool = {
1896 expr = (with types; oneOf [ (listOf bool) str ]).description;
1897 expected = "(list of boolean) or string";
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";
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";
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";
1911 testTypeDescriptionEitherNullOrBoolOrString = {
1912 expr = (with types; either (nullOr bool) str).description;
1913 expected = "null or boolean or string";
1915 testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = {
1916 expr = (with types; either (listOf (either bool str)) int).description;
1917 expected = "(list of (boolean or string)) or signed integer";
1919 testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = {
1920 expr = (with types; either int (listOf (either bool str))).description;
1921 expected = "signed integer or list of (boolean or string)";
1925 testGetExe'Output = {
1927 type = "derivation";
1928 out = "somelonghash";
1929 bin = "somelonghash";
1931 expected = "somelonghash/bin/executable";
1934 testGetExeOutput = {
1936 type = "derivation";
1937 out = "somelonghash";
1938 bin = "somelonghash";
1939 meta.mainProgram = "mainProgram";
1941 expected = "somelonghash/bin/mainProgram";
1944 testGetExe'FailureFirstArg = testingThrow (
1945 getExe' "not a derivation" "executable"
1948 testGetExe'FailureSecondArg = testingThrow (
1949 getExe' { type = "derivation"; } "dir/executable"
1952 testPlatformMatch = {
1953 expr = meta.platformMatch { system = "x86_64-linux"; } "x86_64-linux";
1957 testPlatformMatchAttrs = {
1958 expr = meta.platformMatch (systems.elaborate "x86_64-linux") (systems.elaborate "x86_64-linux").parsed;
1962 testPlatformMatchNoMatch = {
1963 expr = meta.platformMatch { system = "x86_64-darwin"; } "x86_64-linux";
1967 testPlatformMatchMissingSystem = {
1968 expr = meta.platformMatch { } "x86_64-linux";