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