opencomposite: add meta.platforms (#357198)
[NixPkgs.git] / lib / tests / misc.nix
blob72b3d630b8da8850fdea83ff6b44343a237b96ae
1 /**
2   Nix evaluation tests for various lib functions.
4   Since these tests are implemented with Nix evaluation,
5   error checking is limited to what `builtins.tryEval` can detect,
6   which is `throw`'s and `abort`'s, without error messages.
8   If you need to test error messages or more complex evaluations, see
9   `lib/tests/modules.sh`, `lib/tests/sources.sh` or `lib/tests/filesystem.sh` as examples.
11   To run these tests:
13     [nixpkgs]$ nix-instantiate --eval --strict lib/tests/misc.nix
15   If the resulting list is empty, all tests passed.
16   Alternatively, to run all `lib` tests:
18     [nixpkgs]$ nix-build lib/tests/release.nix
21 let
22   lib = import ../default.nix;
24   inherit (lib)
25     allUnique
26     and
27     attrNames
28     attrsets
29     attrsToList
30     bitAnd
31     bitOr
32     bitXor
33     boolToString
34     callPackagesWith
35     callPackageWith
36     cartesianProduct
37     cli
38     composeExtensions
39     composeManyExtensions
40     concatLines
41     concatMapAttrs
42     concatMapStrings
43     concatStrings
44     concatStringsSep
45     const
46     escapeXML
47     evalModules
48     extends
49     filter
50     filterAttrs
51     fix
52     fold
53     foldAttrs
54     foldl
55     foldl'
56     foldlAttrs
57     foldr
58     functionArgs
59     generators
60     genList
61     getExe
62     getExe'
63     getLicenseFromSpdxIdOr
64     groupBy
65     groupBy'
66     hasAttrByPath
67     hasInfix
68     id
69     ifilter0
70     isStorePath
71     lazyDerivation
72     length
73     lists
74     listToAttrs
75     makeExtensible
76     makeIncludePath
77     makeOverridable
78     mapAttrs
79     mapCartesianProduct
80     matchAttrs
81     mergeAttrs
82     meta
83     mod
84     nameValuePair
85     optionalDrvAttr
86     optionAttrSetToDocList
87     overrideExisting
88     packagesFromDirectoryRecursive
89     pipe
90     range
91     recursiveUpdateUntil
92     removePrefix
93     replicate
94     runTests
95     setFunctionArgs
96     showAttrPath
97     sort
98     sortOn
99     stringLength
100     strings
101     stringToCharacters
102     systems
103     tail
104     take
105     testAllTrue
106     toBaseDigits
107     toExtension
108     toHexString
109     fromHexString
110     toInt
111     toIntBase10
112     toShellVars
113     types
114     updateManyAttrsByPath
115     versions
116     xor
117     ;
119   testingThrow = expr: {
120     expr = (builtins.tryEval (builtins.seq expr "didn't throw"));
121     expected = { success = false; value = false; };
122   };
123   testingEval = expr: {
124     expr = (builtins.tryEval expr).success;
125     expected = true;
126   };
128   testSanitizeDerivationName = { name, expected }:
129   let
130     drv = derivation {
131       name = strings.sanitizeDerivationName name;
132       builder = "x";
133       system = "x";
134     };
135   in {
136     # Evaluate the derivation so an invalid name would be caught
137     expr = builtins.seq drv.drvPath drv.name;
138     inherit expected;
139   };
143 runTests {
145 # CUSTOMIZATION
147   testFunctionArgsMakeOverridable = {
148     expr = functionArgs (makeOverridable ({ a, b, c ? null}: {}));
149     expected = { a = false; b = false; c = true; };
150   };
152   testFunctionArgsMakeOverridableOverride = {
153     expr = functionArgs (makeOverridable ({ a, b, c ? null }: {}) { a = 1; b = 2; }).override;
154     expected = { a = false; b = false; c = true; };
155   };
157   testCallPackageWithOverridePreservesArguments =
158     let
159       f = { a ? 0, b }: {};
160       f' = callPackageWith { a = 1; b = 2; } f {};
161     in {
162       expr = functionArgs f'.override;
163       expected = functionArgs f;
164     };
166   testCallPackagesWithOverridePreservesArguments =
167     let
168       f = { a ? 0, b }: { nested = {}; };
169       f' = callPackagesWith { a = 1; b = 2; } f {};
170     in {
171       expr = functionArgs f'.nested.override;
172       expected = functionArgs f;
173     };
175 # TRIVIAL
177   testId = {
178     expr = id 1;
179     expected = 1;
180   };
182   testConst = {
183     expr = const 2 3;
184     expected = 2;
185   };
187   testPipe = {
188     expr = pipe 2 [
189       (x: x + 2) # 2 + 2 = 4
190       (x: x * 2) # 4 * 2 = 8
191     ];
192     expected = 8;
193   };
195   testPipeEmpty = {
196     expr = pipe 2 [];
197     expected = 2;
198   };
200   testPipeStrings = {
201     expr = pipe [ 3 4 ] [
202       (map toString)
203       (map (s: s + "\n"))
204       concatStrings
205     ];
206     expected = ''
207       3
208       4
209     '';
210   };
212   /*
213     testOr = {
214       expr = or true false;
215       expected = true;
216     };
217   */
219   testAnd = {
220     expr = and true false;
221     expected = false;
222   };
224   testXor = {
225     expr = [
226       (xor true false)
227       (xor true true)
228       (xor false false)
229       (xor false true)
230     ];
231     expected = [
232       true
233       false
234       false
235       true
236     ];
237   };
239   testComposeExtensions = {
240     expr = let obj = makeExtensible (self: { foo = self.bar; });
241                f = self: super: { bar = false; baz = true; };
242                g = self: super: { bar = super.baz or false; };
243                f_o_g = composeExtensions f g;
244                composed = obj.extend f_o_g;
245            in composed.foo;
246     expected = true;
247   };
249   testComposeManyExtensions0 = {
250     expr = let obj = makeExtensible (self: { foo = true; });
251                emptyComposition = composeManyExtensions [];
252                composed = obj.extend emptyComposition;
253            in composed.foo;
254     expected = true;
255   };
257   testComposeManyExtensions =
258     let f = self: super: { bar = false; baz = true; };
259         g = self: super: { bar = super.baz or false; };
260         h = self: super: { qux = super.bar or false; };
261         obj = makeExtensible (self: { foo = self.qux; });
262     in {
263     expr = let composition = composeManyExtensions [f g h];
264                composed = obj.extend composition;
265            in composed.foo;
266     expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo;
267   };
269   testBitAnd = {
270     expr = (bitAnd 3 10);
271     expected = 2;
272   };
274   testBitOr = {
275     expr = (bitOr 3 10);
276     expected = 11;
277   };
279   testBitXor = {
280     expr = (bitXor 3 10);
281     expected = 9;
282   };
284   testToHexString = {
285     expr = toHexString 250;
286     expected = "FA";
287   };
289   testFromHexStringFirstExample = {
290     expr = fromHexString "FF";
291     expected = 255;
292   };
294   testFromHexStringSecondExample = {
295     expr = fromHexString (builtins.hashString "sha256" "test");
296     expected = 9223372036854775807;
297   };
299   testFromHexStringWithPrefix = {
300     expr = fromHexString "0Xf";
301     expected = 15;
302   };
304   testToBaseDigits = {
305     expr = toBaseDigits 2 6;
306     expected = [ 1 1 0 ];
307   };
309   testFunctionArgsFunctor = {
310     expr = functionArgs { __functor = self: { a, b }: null; };
311     expected = { a = false; b = false; };
312   };
314   testFunctionArgsSetFunctionArgs = {
315     expr = functionArgs (setFunctionArgs (args: args.x) { x = false; });
316     expected = { x = false; };
317   };
319 # STRINGS
321   testConcatMapStrings = {
322     expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
323     expected = "a;b;c;";
324   };
326   testConcatStringsSep = {
327     expr = concatStringsSep "," ["a" "b" "c"];
328     expected = "a,b,c";
329   };
331   testConcatLines = {
332     expr = concatLines ["a" "b" "c"];
333     expected = "a\nb\nc\n";
334   };
336   testMakeIncludePathWithPkgs = {
337     expr = (makeIncludePath [
338       # makeIncludePath preferably selects the "dev" output
339       { dev.outPath = "/dev"; out.outPath = "/out"; outPath = "/default"; }
340       # "out" is used if "dev" is not found
341       { out.outPath = "/out"; outPath = "/default"; }
342       # And it returns the derivation directly if there's no "out" either
343       { outPath = "/default"; }
344       # Same if the output is specified explicitly, even if there's a "dev"
345       { dev.outPath = "/dev"; outPath = "/default"; outputSpecified = true; }
346     ]);
347     expected = "/dev/include:/out/include:/default/include:/default/include";
348   };
350   testMakeIncludePathWithEmptyList = {
351     expr = (makeIncludePath [ ]);
352     expected = "";
353   };
355   testMakeIncludePathWithOneString = {
356     expr = (makeIncludePath [ "/usr" ]);
357     expected = "/usr/include";
358   };
360   testMakeIncludePathWithManyString = {
361     expr = (makeIncludePath [ "/usr" "/usr/local" ]);
362     expected = "/usr/include:/usr/local/include";
363   };
365   testReplicateString = {
366     expr = strings.replicate 5 "hello";
367     expected = "hellohellohellohellohello";
368   };
370   # Test various strings are trimmed correctly
371   testTrimString = {
372     expr =
373     let
374       testValues = f: mapAttrs (_: f) {
375         empty = "";
376         cr = "\r";
377         lf = "\n";
378         tab = "\t";
379         spaces = "   ";
380         leading = "  Hello, world";
381         trailing = "Hello, world   ";
382         mixed = " Hello, world ";
383         mixed-tabs = " \t\tHello, world \t \t ";
384         multiline = "  Hello,\n  world!  ";
385         multiline-crlf = "  Hello,\r\n  world!  ";
386       };
387     in
388       {
389         leading = testValues (strings.trimWith { start = true; });
390         trailing = testValues (strings.trimWith { end = true; });
391         both = testValues strings.trim;
392       };
393     expected = {
394       leading = {
395         empty = "";
396         cr = "";
397         lf = "";
398         tab = "";
399         spaces = "";
400         leading = "Hello, world";
401         trailing = "Hello, world   ";
402         mixed = "Hello, world ";
403         mixed-tabs = "Hello, world \t \t ";
404         multiline = "Hello,\n  world!  ";
405         multiline-crlf = "Hello,\r\n  world!  ";
406       };
407       trailing = {
408         empty = "";
409         cr = "";
410         lf = "";
411         tab = "";
412         spaces = "";
413         leading = "  Hello, world";
414         trailing = "Hello, world";
415         mixed = " Hello, world";
416         mixed-tabs = " \t\tHello, world";
417         multiline = "  Hello,\n  world!";
418         multiline-crlf = "  Hello,\r\n  world!";
419       };
420       both = {
421         empty = "";
422         cr = "";
423         lf = "";
424         tab = "";
425         spaces = "";
426         leading = "Hello, world";
427         trailing = "Hello, world";
428         mixed = "Hello, world";
429         mixed-tabs = "Hello, world";
430         multiline = "Hello,\n  world!";
431         multiline-crlf = "Hello,\r\n  world!";
432       };
433     };
434   };
436   testSplitStringsSimple = {
437     expr = strings.splitString "." "a.b.c.d";
438     expected = [ "a" "b" "c" "d" ];
439   };
441   testSplitStringsEmpty = {
442     expr = strings.splitString "." "a..b";
443     expected = [ "a" "" "b" ];
444   };
446   testSplitStringsOne = {
447     expr = strings.splitString ":" "a.b";
448     expected = [ "a.b" ];
449   };
451   testSplitStringsNone = {
452     expr = strings.splitString "." "";
453     expected = [ "" ];
454   };
456   testSplitStringsFirstEmpty = {
457     expr = strings.splitString "/" "/a/b/c";
458     expected = [ "" "a" "b" "c" ];
459   };
461   testSplitStringsLastEmpty = {
462     expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
463     expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
464   };
466   testSplitStringsRegex = {
467     expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B";
468     expected = [ "A" "B" ];
469   };
471   testEscapeShellArg = {
472     expr = strings.escapeShellArg "esc'ape\nme";
473     expected = "'esc'\\''ape\nme'";
474   };
476   testEscapeShellArgEmpty = {
477     expr = strings.escapeShellArg "";
478     expected = "''";
479   };
481   testEscapeShellArgs = {
482     expr = strings.escapeShellArgs ["one" "two three" "four'five"];
483     expected = "one 'two three' 'four'\\''five'";
484   };
486   testEscapeShellArgsUnicode = {
487     expr = strings.escapeShellArg "á";
488     expected = "'á'";
489   };
491   testSplitStringsDerivation = {
492     expr = take 3  (strings.splitString "/" (derivation {
493       name = "name";
494       builder = "builder";
495       system = "system";
496     }));
497     expected = ["" "nix" "store"];
498   };
500   testSplitVersionSingle = {
501     expr = versions.splitVersion "1";
502     expected = [ "1" ];
503   };
505   testSplitVersionDouble = {
506     expr = versions.splitVersion "1.2";
507     expected = [ "1" "2" ];
508   };
510   testSplitVersionTriple = {
511     expr = versions.splitVersion "1.2.3";
512     expected = [ "1" "2" "3" ];
513   };
515   testPadVersionLess = {
516     expr = versions.pad 3 "1.2";
517     expected = "1.2.0";
518   };
520   testPadVersionLessExtra = {
521     expr = versions.pad 3 "1.3-rc1";
522     expected = "1.3.0-rc1";
523   };
525   testPadVersionMore = {
526     expr = versions.pad 3 "1.2.3.4";
527     expected = "1.2.3";
528   };
530   testIsStorePath =  {
531     expr =
532       let goodPath =
533             "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
534       in {
535         storePath = isStorePath goodPath;
536         storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello;
537         storePathAppendix = isStorePath
538           "${goodPath}/bin/python";
539         nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
540         asPath = isStorePath (/. + goodPath);
541         otherPath = isStorePath "/something/else";
542         otherVals = {
543           attrset = isStorePath {};
544           list = isStorePath [];
545           int = isStorePath 42;
546         };
547       };
548     expected = {
549       storePath = true;
550       storePathDerivation = true;
551       storePathAppendix = false;
552       nonAbsolute = false;
553       asPath = true;
554       otherPath = false;
555       otherVals = {
556         attrset = false;
557         list = false;
558         int = false;
559       };
560     };
561   };
563   testEscapeXML = {
564     expr = escapeXML ''"test" 'test' < & >'';
565     expected = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;";
566   };
568   testToShellVars = {
569     expr = ''
570       ${toShellVars {
571         STRing01 = "just a 'string'";
572         _array_ = [ "with" "more strings" ];
573         assoc."with some" = ''
574           strings
575           possibly newlines
576         '';
577         drv = {
578           outPath = "/drv";
579           foo = "ignored attribute";
580         };
581         path = /path;
582         stringable = {
583           __toString = _: "hello toString";
584           bar = "ignored attribute";
585         };
586       }}
587     '';
588     expected = ''
589       STRing01='just a '\'''string'\''''
590       declare -a _array_=(with 'more strings')
591       declare -A assoc=(['with some']='strings
592       possibly newlines
593       ')
594       drv=/drv
595       path=/path
596       stringable='hello toString'
597     '';
598   };
600   testHasInfixFalse = {
601     expr = hasInfix "c" "abde";
602     expected = false;
603   };
605   testHasInfixTrue = {
606     expr = hasInfix "c" "abcde";
607     expected = true;
608   };
610   testHasInfixDerivation = {
611     expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello;
612     expected = true;
613   };
615   testHasInfixPath = {
616     expr = hasInfix "tests" ./.;
617     expected = true;
618   };
620   testHasInfixPathStoreDir = {
621     expr = hasInfix builtins.storeDir ./.;
622     expected = true;
623   };
625   testHasInfixToString = {
626     expr = hasInfix "a" { __toString = _: "a"; };
627     expected = true;
628   };
630   testRemovePrefixExample1 = {
631     expr = removePrefix "foo." "foo.bar.baz";
632     expected = "bar.baz";
633   };
634   testRemovePrefixExample2 = {
635     expr = removePrefix "xxx" "foo.bar.baz";
636     expected = "foo.bar.baz";
637   };
638   testRemovePrefixEmptyPrefix = {
639     expr = removePrefix "" "foo";
640     expected = "foo";
641   };
642   testRemovePrefixEmptyString = {
643     expr = removePrefix "foo" "";
644     expected = "";
645   };
646   testRemovePrefixEmptyBoth = {
647     expr = removePrefix "" "";
648     expected = "";
649   };
651   testNormalizePath = {
652     expr = strings.normalizePath "//a/b//c////d/";
653     expected = "/a/b/c/d/";
654   };
656   testCharToInt = {
657     expr = strings.charToInt "A";
658     expected = 65;
659   };
661   testEscapeC = {
662     expr = strings.escapeC [ " " ] "Hello World";
663     expected = "Hello\\x20World";
664   };
666   testEscapeURL = testAllTrue [
667     ("" == strings.escapeURL "")
668     ("Hello" == strings.escapeURL "Hello")
669     ("Hello%20World" == strings.escapeURL "Hello World")
670     ("Hello%2FWorld" == strings.escapeURL "Hello/World")
671     ("42%25" == strings.escapeURL "42%")
672     ("%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:/@$'()*,;")
673   ];
675   testToInt = testAllTrue [
676     # Naive
677     (123 == toInt "123")
678     (0 == toInt "0")
679     # Whitespace Padding
680     (123 == toInt " 123")
681     (123 == toInt "123 ")
682     (123 == toInt " 123 ")
683     (123 == toInt "   123   ")
684     (0 == toInt " 0")
685     (0 == toInt "0 ")
686     (0 == toInt " 0 ")
687     (-1 == toInt "-1")
688     (-1 == toInt " -1 ")
689   ];
691   testToIntFails = testAllTrue [
692     ( builtins.tryEval (toInt "") == { success = false; value = false; } )
693     ( builtins.tryEval (toInt "123 123") == { success = false; value = false; } )
694     ( builtins.tryEval (toInt "0 123") == { success = false; value = false; } )
695     ( builtins.tryEval (toInt " 0d ") == { success = false; value = false; } )
696     ( builtins.tryEval (toInt " 1d ") == { success = false; value = false; } )
697     ( builtins.tryEval (toInt " d0 ") == { success = false; value = false; } )
698     ( builtins.tryEval (toInt "00") == { success = false; value = false; } )
699     ( builtins.tryEval (toInt "01") == { success = false; value = false; } )
700     ( builtins.tryEval (toInt "002") == { success = false; value = false; } )
701     ( builtins.tryEval (toInt " 002 ") == { success = false; value = false; } )
702     ( builtins.tryEval (toInt " foo ") == { success = false; value = false; } )
703     ( builtins.tryEval (toInt " foo 123 ") == { success = false; value = false; } )
704     ( builtins.tryEval (toInt " foo123 ") == { success = false; value = false; } )
705   ];
707   testToIntBase10 = testAllTrue [
708     # Naive
709     (123 == toIntBase10 "123")
710     (0 == toIntBase10 "0")
711     # Whitespace Padding
712     (123 == toIntBase10 " 123")
713     (123 == toIntBase10 "123 ")
714     (123 == toIntBase10 " 123 ")
715     (123 == toIntBase10 "   123   ")
716     (0 == toIntBase10 " 0")
717     (0 == toIntBase10 "0 ")
718     (0 == toIntBase10 " 0 ")
719     # Zero Padding
720     (123 == toIntBase10 "0123")
721     (123 == toIntBase10 "0000123")
722     (0 == toIntBase10 "000000")
723     # Whitespace and Zero Padding
724     (123 == toIntBase10 " 0123")
725     (123 == toIntBase10 "0123 ")
726     (123 == toIntBase10 " 0123 ")
727     (123 == toIntBase10 " 0000123")
728     (123 == toIntBase10 "0000123 ")
729     (123 == toIntBase10 " 0000123 ")
730     (0 == toIntBase10 " 000000")
731     (0 == toIntBase10 "000000 ")
732     (0 == toIntBase10 " 000000 ")
733     (-1 == toIntBase10 "-1")
734     (-1 == toIntBase10 " -1 ")
735   ];
737   testToIntBase10Fails = testAllTrue [
738     ( builtins.tryEval (toIntBase10 "") == { success = false; value = false; } )
739     ( builtins.tryEval (toIntBase10 "123 123") == { success = false; value = false; } )
740     ( builtins.tryEval (toIntBase10 "0 123") == { success = false; value = false; } )
741     ( builtins.tryEval (toIntBase10 " 0d ") == { success = false; value = false; } )
742     ( builtins.tryEval (toIntBase10 " 1d ") == { success = false; value = false; } )
743     ( builtins.tryEval (toIntBase10 " d0 ") == { success = false; value = false; } )
744     ( builtins.tryEval (toIntBase10 " foo ") == { success = false; value = false; } )
745     ( builtins.tryEval (toIntBase10 " foo 123 ") == { success = false; value = false; } )
746     ( builtins.tryEval (toIntBase10 " foo 00123 ") == { success = false; value = false; } )
747     ( builtins.tryEval (toIntBase10 " foo00123 ") == { success = false; value = false; } )
748   ];
750 # LISTS
752   testFilter = {
753     expr = filter (x: x != "a") ["a" "b" "c" "a"];
754     expected = ["b" "c"];
755   };
757   testIfilter0Example = {
758     expr = ifilter0 (i: v: i == 0 || v > 2) [ 1 2 3 ];
759     expected = [ 1 3 ];
760   };
761   testIfilter0Empty = {
762     expr = ifilter0 (i: v: abort "shouldn't be evaluated!") [ ];
763     expected = [ ];
764   };
765   testIfilter0IndexOnly = {
766     expr = length (ifilter0 (i: v: mod i 2 == 0) [ (throw "0") (throw "1") (throw "2") (throw "3")]);
767     expected = 2;
768   };
769   testIfilter0All = {
770     expr = ifilter0 (i: v: true) [ 10 11 12 13 14 15 ];
771     expected = [ 10 11 12 13 14 15 ];
772   };
773   testIfilter0First = {
774     expr = ifilter0 (i: v: i == 0) [ 10 11 12 13 14 15 ];
775     expected = [ 10 ];
776   };
777   testIfilter0Last = {
778     expr = ifilter0 (i: v: i == 5) [ 10 11 12 13 14 15 ];
779     expected = [ 15 ];
780   };
782   testFold =
783     let
784       f = op: fold: fold op 0 (range 0 100);
785       # fold with associative operator
786       assoc = f builtins.add;
787       # fold with non-associative operator
788       nonAssoc = f builtins.sub;
789     in {
790       expr = {
791         assocRight = assoc foldr;
792         # right fold with assoc operator is same as left fold
793         assocRightIsLeft = assoc foldr == assoc foldl;
794         nonAssocRight = nonAssoc foldr;
795         nonAssocLeft = nonAssoc foldl;
796         # with non-assoc operator the fold results are not the same
797         nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr;
798         # fold is an alias for foldr
799         foldIsRight = nonAssoc fold == nonAssoc foldr;
800       };
801       expected = {
802         assocRight = 5050;
803         assocRightIsLeft = true;
804         nonAssocRight = 50;
805         nonAssocLeft = (-5050);
806         nonAssocRightIsNotLeft = true;
807         foldIsRight = true;
808       };
809     };
811   testFoldl'Empty = {
812     expr = foldl' (acc: el: abort "operation not called") 0 [ ];
813     expected = 0;
814   };
816   testFoldl'IntegerAdding = {
817     expr = foldl' (acc: el: acc + el) 0 [ 1 2 3 ];
818     expected = 6;
819   };
821   # The accumulator isn't forced deeply
822   testFoldl'NonDeep = {
823     expr = take 3 (foldl'
824       (acc: el: [ el ] ++ acc)
825       [ (abort "unevaluated list entry") ]
826       [ 1 2 3 ]);
827     expected = [ 3 2 1 ];
828   };
830   # Compared to builtins.foldl', lib.foldl' evaluates the first accumulator strictly too
831   testFoldl'StrictInitial = {
832     expr = (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [])).success;
833     expected = false;
834   };
836   # Make sure we don't get a stack overflow for large lists
837   # This number of elements would notably cause a stack overflow if it was implemented without the `foldl'` builtin
838   testFoldl'Large = {
839     expr = foldl' (acc: el: acc + el) 0 (range 0 100000);
840     expected = 5000050000;
841   };
843   testTake = testAllTrue [
844     ([] == (take 0 [  1 2 3 ]))
845     ([1] == (take 1 [  1 2 3 ]))
846     ([ 1 2 ] == (take 2 [  1 2 3 ]))
847     ([ 1 2 3 ] == (take 3 [  1 2 3 ]))
848     ([ 1 2 3 ] == (take 4 [  1 2 3 ]))
849   ];
851   testListHasPrefixExample1 = {
852     expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ];
853     expected = true;
854   };
855   testListHasPrefixExample2 = {
856     expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ];
857     expected = false;
858   };
859   testListHasPrefixLazy = {
860     expr = lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ];
861     expected = true;
862   };
863   testListHasPrefixEmptyPrefix = {
864     expr = lists.hasPrefix [ ] [ 1 2 ];
865     expected = true;
866   };
867   testListHasPrefixEmptyList = {
868     expr = lists.hasPrefix [ 1 2 ] [ ];
869     expected = false;
870   };
872   testListRemovePrefixExample1 = {
873     expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ];
874     expected = [ 3 4 ];
875   };
876   testListRemovePrefixExample2 = {
877     expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success;
878     expected = false;
879   };
880   testListRemovePrefixEmptyPrefix = {
881     expr = lists.removePrefix [ ] [ 1 2 ];
882     expected = [ 1 2 ];
883   };
884   testListRemovePrefixEmptyList = {
885     expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success;
886     expected = false;
887   };
889   testFoldAttrs = {
890     expr = foldAttrs (n: a: [n] ++ a) [] [
891     { a = 2; b = 7; }
892     { a = 3;        c = 8; }
893     ];
894     expected = { a = [ 2 3 ]; b = [7]; c = [8];};
895   };
897   testListCommonPrefixExample1 = {
898     expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ];
899     expected = [ 1 2 ];
900   };
901   testListCommonPrefixExample2 = {
902     expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ];
903     expected = [ 1 2 3 ];
904   };
905   testListCommonPrefixExample3 = {
906     expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ];
907     expected = [ ];
908   };
909   testListCommonPrefixEmpty = {
910     expr = lists.commonPrefix [ ] [ 1 2 3 ];
911     expected = [ ];
912   };
913   testListCommonPrefixSame = {
914     expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ];
915     expected = [ 1 2 3 ];
916   };
917   testListCommonPrefixLazy = {
918     expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this")];
919     expected = [ 1 ];
920   };
921   # This would stack overflow if `commonPrefix` were implemented using recursion
922   testListCommonPrefixLong =
923     let
924       longList = genList (n: n) 100000;
925     in {
926       expr = lists.commonPrefix longList longList;
927       expected = longList;
928     };
930   testSort = {
931     expr = sort builtins.lessThan [ 40 2 30 42 ];
932     expected = [2 30 40 42];
933   };
935   testSortOn = {
936     expr = sortOn stringLength [ "aa" "b" "cccc" ];
937     expected = [ "b" "aa" "cccc" ];
938   };
940   testSortOnEmpty = {
941     expr = sortOn (throw "nope") [ ];
942     expected = [ ];
943   };
945   testSortOnIncomparable = {
946     expr =
947       map
948         (x: x.f x.ok)
949         (sortOn (x: x.ok) [
950           { ok = 1; f = x: x; }
951           { ok = 3; f = x: x + 3; }
952           { ok = 2; f = x: x; }
953         ]);
954     expected = [ 1 2 6 ];
955   };
957   testReplicate = {
958     expr = replicate 3 "a";
959     expected = ["a" "a" "a"];
960   };
962   testToIntShouldConvertStringToInt = {
963     expr = toInt "27";
964     expected = 27;
965   };
967   testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
968     expr = builtins.tryEval (toInt "\"foo\"");
969     expected = { success = false; value = false; };
970   };
972   testHasAttrByPathTrue = {
973     expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; };
974     expected = true;
975   };
977   testHasAttrByPathFalse = {
978     expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; };
979     expected = false;
980   };
982   testHasAttrByPathNonStrict = {
983     expr = hasAttrByPath [] (throw "do not use");
984     expected = true;
985   };
987   testLongestValidPathPrefix_empty_empty = {
988     expr = attrsets.longestValidPathPrefix [ ] { };
989     expected = [ ];
990   };
992   testLongestValidPathPrefix_empty_nonStrict = {
993     expr = attrsets.longestValidPathPrefix [ ] (throw "do not use");
994     expected = [ ];
995   };
997   testLongestValidPathPrefix_zero = {
998     expr = attrsets.longestValidPathPrefix [ "a" (throw "do not use") ] { d = null; };
999     expected = [ ];
1000   };
1002   testLongestValidPathPrefix_zero_b = {
1003     expr = attrsets.longestValidPathPrefix [ "z" "z" ] "remarkably harmonious";
1004     expected = [ ];
1005   };
1007   testLongestValidPathPrefix_one = {
1008     expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a = null; };
1009     expected = [ "a" ];
1010   };
1012   testLongestValidPathPrefix_two = {
1013     expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b = null; };
1014     expected = [ "a" "b" ];
1015   };
1017   testLongestValidPathPrefix_three = {
1018     expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c = null; };
1019     expected = [ "a" "b" "c" ];
1020   };
1022   testLongestValidPathPrefix_three_extra = {
1023     expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c.d = throw "nope"; };
1024     expected = [ "a" "b" "c" ];
1025   };
1027   testFindFirstIndexExample1 = {
1028     expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ];
1029     expected = 1;
1030   };
1032   testFindFirstIndexExample2 = {
1033     expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ 1 6 4 ];
1034     expected = "a very specific default";
1035   };
1037   testFindFirstIndexEmpty = {
1038     expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null [];
1039     expected = null;
1040   };
1042   testFindFirstIndexSingleMatch = {
1043     expr = lists.findFirstIndex (x: x == 5) null [ 5 ];
1044     expected = 0;
1045   };
1047   testFindFirstIndexSingleDefault = {
1048     expr = lists.findFirstIndex (x: false) null [ (abort "if the predicate doesn't access the value, it must not be evaluated") ];
1049     expected = null;
1050   };
1052   testFindFirstIndexNone = {
1053     expr = builtins.tryEval (lists.findFirstIndex (x: x == 2) null [ 1 (throw "the last element must be evaluated when there's no match") ]);
1054     expected = { success = false; value = false; };
1055   };
1057   # Makes sure that the implementation doesn't cause a stack overflow
1058   testFindFirstIndexBig = {
1059     expr = lists.findFirstIndex (x: x == 1000000) null (range 0 1000000);
1060     expected = 1000000;
1061   };
1063   testFindFirstIndexLazy = {
1064     expr = lists.findFirstIndex (x: x == 1) null [ 1 (abort "list elements after the match must not be evaluated") ];
1065     expected = 0;
1066   };
1068   testFindFirstExample1 = {
1069     expr = lists.findFirst (x: x > 3) 7 [ 1 6 4 ];
1070     expected = 6;
1071   };
1073   testFindFirstExample2 = {
1074     expr = lists.findFirst (x: x > 9) 7 [ 1 6 4 ];
1075     expected = 7;
1076   };
1078   testAllUnique_true = {
1079     expr = allUnique [ 3 2 4 1 ];
1080     expected = true;
1081   };
1082   testAllUnique_false = {
1083     expr = allUnique [ 3 2 3 4 ];
1084     expected = false;
1085   };
1087 # ATTRSETS
1089   testConcatMapAttrs = {
1090     expr = concatMapAttrs
1091       (name: value: {
1092         ${name} = value;
1093         ${name + value} = value;
1094       })
1095       {
1096         foo = "bar";
1097         foobar = "baz";
1098       };
1099     expected = {
1100       foo = "bar";
1101       foobar = "baz";
1102       foobarbaz = "baz";
1103     };
1104   };
1106   testFilterAttrs = {
1107     expr = filterAttrs (n: v: n != "a" && (v.hello or false) == true) {
1108       a.hello = true;
1109       b.hello = true;
1110       c = {
1111         hello = true;
1112         world = false;
1113       };
1114       d.hello = false;
1115     };
1116     expected = {
1117       b.hello = true;
1118       c = {
1119         hello = true;
1120         world = false;
1121       };
1122     };
1123   };
1125   # code from example
1126   testFoldlAttrs = {
1127     expr = {
1128       example = foldlAttrs
1129         (acc: name: value: {
1130           sum = acc.sum + value;
1131           names = acc.names ++ [ name ];
1132         })
1133         { sum = 0; names = [ ]; }
1134         {
1135           foo = 1;
1136           bar = 10;
1137         };
1138       # should just return the initial value
1139       emptySet = foldlAttrs (throw "function not needed") 123 { };
1140       # should just evaluate to the last value
1141       valuesNotNeeded = foldlAttrs (acc: _name: _v: acc) 3 { z = throw "value z not needed"; a = throw "value a not needed"; };
1142       # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string
1143       trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { z = 1; a = 2; };
1144     };
1145     expected = {
1146       example = {
1147         sum = 11;
1148         names = [ "bar" "foo" ];
1149       };
1150       emptySet = 123;
1151       valuesNotNeeded = 3;
1152       trivialAcc = 121;
1153     };
1154   };
1157   testMergeAttrsListExample1 = {
1158     expr = attrsets.mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ];
1159     expected = { a = 0; b = 1; c = 2; d = 3; };
1160   };
1161   testMergeAttrsListExample2 = {
1162     expr = attrsets.mergeAttrsList [ { a = 0; } { a = 1; } ];
1163     expected = { a = 1; };
1164   };
1165   testMergeAttrsListExampleMany =
1166     let
1167       list = genList (n:
1168         listToAttrs (genList (m:
1169           let
1170             # Integer divide n by two to create duplicate attributes
1171             str = "halfn${toString (n / 2)}m${toString m}";
1172           in
1173           nameValuePair str str
1174         ) 100)
1175       ) 100;
1176     in {
1177       expr = attrsets.mergeAttrsList list;
1178       expected = foldl' mergeAttrs { } list;
1179     };
1181   # code from the example
1182   testRecursiveUpdateUntil = {
1183     expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
1184       # first attribute set
1185       foo.bar = 1;
1186       foo.baz = 2;
1187       bar = 3;
1188     } {
1189       #second attribute set
1190       foo.bar = 1;
1191       foo.quz = 2;
1192       baz = 4;
1193     };
1194     expected = {
1195       foo.bar = 1; # 'foo.*' from the second set
1196       foo.quz = 2; #
1197       bar = 3;     # 'bar' from the first set
1198       baz = 4;     # 'baz' from the second set
1199     };
1200   };
1202   testMatchAttrsMatchingExact = {
1203     expr = matchAttrs { cpu = { bits = 64; }; } { cpu = { bits = 64; }; };
1204     expected = true;
1205   };
1207   testMatchAttrsMismatch = {
1208     expr = matchAttrs { cpu = { bits = 128; }; } { cpu = { bits = 64; }; };
1209     expected = false;
1210   };
1212   testMatchAttrsMatchingImplicit = {
1213     expr = matchAttrs { cpu = { }; } { cpu = { bits = 64; }; };
1214     expected = true;
1215   };
1217   testMatchAttrsMissingAttrs = {
1218     expr = matchAttrs { cpu = {}; } { };
1219     expected = false;
1220   };
1222   testOverrideExistingEmpty = {
1223     expr = overrideExisting {} { a = 1; };
1224     expected = {};
1225   };
1227   testOverrideExistingDisjoint = {
1228     expr = overrideExisting { b = 2; } { a = 1; };
1229     expected = { b = 2; };
1230   };
1232   testOverrideExistingOverride = {
1233     expr = overrideExisting { a = 3; b = 2; } { a = 1; };
1234     expected = { a = 1; b = 2; };
1235   };
1237   testListAttrsReverse = let
1238     exampleAttrs = {foo=1; bar="asdf"; baz = [1 3 3 7]; fnord=null;};
1239     exampleSingletonList = [{name="foo"; value=1;}];
1240   in {
1241     expr = {
1242       isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) == exampleAttrs;
1243       isReverseToAttrsToList = attrsToList (builtins.listToAttrs exampleSingletonList) == exampleSingletonList;
1244       testDuplicatePruningBehaviour = attrsToList (builtins.listToAttrs [{name="a"; value=2;} {name="a"; value=1;}]);
1245     };
1246     expected = {
1247       isReverseToAttrsToList = true;
1248       isReverseToListToAttrs = true;
1249       testDuplicatePruningBehaviour = [{name="a"; value=2;}];
1250     };
1251   };
1253   testAttrsToListsCanDealWithFunctions = testingEval (
1254     attrsToList { someFunc= a: a + 1;}
1255   );
1257 # FIXED-POINTS
1259   testFix = {
1260     expr = fix (x: {a = if x ? a then "a" else "b";});
1261     expected = {a = "a";};
1262   };
1264   testToExtension = {
1265     expr = [
1266       (fix (final: { a = 0; c = final.a; }))
1267       (fix (extends (toExtension { a = 1; b = 2; }) (final: { a = 0; c = final.a; })))
1268       (fix (extends (toExtension (prev: { a = 1; b = prev.a; })) (final: { a = 0; c = final.a; })))
1269       (fix (extends (toExtension (final: prev: { a = 1; b = prev.a; c = final.a + 1; })) (final: { a = 0; c = final.a; })))
1270     ];
1271     expected = [
1272       { a = 0; c = 0; }
1273       { a = 1; b = 2; c = 1; }
1274       { a = 1; b = 0; c = 1; }
1275       { a = 1; b = 0; c = 2; }
1276     ];
1277   };
1279 # GENERATORS
1280 # these tests assume attributes are converted to lists
1281 # in alphabetical order
1283   testMkKeyValueDefault = {
1284     expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar";
1285     expected = ''f\:oo:bar'';
1286   };
1288   testMkValueString = {
1289     expr = let
1290       vals = {
1291         int = 42;
1292         string = ''fo"o'';
1293         bool = true;
1294         bool2 = false;
1295         null = null;
1296         # float = 42.23; # floats are strange
1297       };
1298       in mapAttrs
1299         (const (generators.mkValueStringDefault {}))
1300         vals;
1301     expected = {
1302       int = "42";
1303       string = ''fo"o'';
1304       bool = "true";
1305       bool2 = "false";
1306       null = "null";
1307       # float = "42.23" true false [ "bar" ] ]'';
1308     };
1309   };
1311   testToKeyValue = {
1312     expr = generators.toKeyValue {} {
1313       key = "value";
1314       "other=key" = "baz";
1315     };
1316     expected = ''
1317       key=value
1318       other\=key=baz
1319     '';
1320   };
1322   testToINIEmpty = {
1323     expr = generators.toINI {} {};
1324     expected = "";
1325   };
1327   testToINIEmptySection = {
1328     expr = generators.toINI {} { foo = {}; bar = {}; };
1329     expected = ''
1330       [bar]
1332       [foo]
1333     '';
1334   };
1336   testToINIDuplicateKeys = {
1337     expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; };
1338     expected = ''
1339       [baz]
1340       qux=1
1341       qux=false
1343       [foo]
1344       bar=true
1345     '';
1346   };
1348   testToINIDefaultEscapes = {
1349     expr = generators.toINI {} {
1350       "no [ and ] allowed unescaped" = {
1351         "and also no = in keys" = 42;
1352       };
1353     };
1354     expected = ''
1355       [no \[ and \] allowed unescaped]
1356       and also no \= in keys=42
1357     '';
1358   };
1360   testToINIDefaultFull = {
1361     expr = generators.toINI {} {
1362       "section 1" = {
1363         attribute1 = 5;
1364         x = "Me-se JarJar Binx";
1365         # booleans are converted verbatim by default
1366         boolean = false;
1367       };
1368       "foo[]" = {
1369         "he\\h=he" = "this is okay";
1370       };
1371     };
1372     expected = ''
1373       [foo\[\]]
1374       he\h\=he=this is okay
1376       [section 1]
1377       attribute1=5
1378       boolean=false
1379       x=Me-se JarJar Binx
1380     '';
1381   };
1383   testToINIWithGlobalSectionEmpty = {
1384     expr = generators.toINIWithGlobalSection {} {
1385       globalSection = {
1386       };
1387       sections = {
1388       };
1389     };
1390     expected = ''
1391     '';
1392   };
1394   testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
1395     let
1396       sections = {
1397         "section 1" = {
1398           attribute1 = 5;
1399           x = "Me-se JarJar Binx";
1400         };
1401         "foo" = {
1402           "he\\h=he" = "this is okay";
1403         };
1404       };
1405     in {
1406       expr =
1407         generators.toINIWithGlobalSection {} {
1408             globalSection = {};
1409             sections = sections;
1410         };
1411       expected = generators.toINI {} sections;
1412   };
1414   testToINIWithGlobalSectionFull = {
1415     expr = generators.toINIWithGlobalSection {} {
1416       globalSection = {
1417         foo = "bar";
1418         test = false;
1419       };
1420       sections = {
1421         "section 1" = {
1422           attribute1 = 5;
1423           x = "Me-se JarJar Binx";
1424         };
1425         "foo" = {
1426           "he\\h=he" = "this is okay";
1427         };
1428       };
1429     };
1430     expected = ''
1431       foo=bar
1432       test=false
1434       [foo]
1435       he\h\=he=this is okay
1437       [section 1]
1438       attribute1=5
1439       x=Me-se JarJar Binx
1440     '';
1441   };
1443   testToGitINI = {
1444     expr = generators.toGitINI {
1445       user = {
1446         email = "user@example.org";
1447         name = "John Doe";
1448         signingKey = "00112233445566778899AABBCCDDEEFF";
1449       };
1450       gpg.program = "path-to-gpg";
1451       tag.gpgSign = true;
1452       include.path = "~/path/to/config.inc";
1453       includeIf."gitdif:~/src/dir".path = "~/path/to/conditional.inc";
1454       extra = {
1455         boolean = true;
1456         integer = 38;
1457         name = "value";
1458         subsection.value = "test";
1459       };};
1460     expected = ''
1461       [extra]
1462       ${"\t"}boolean = true
1463       ${"\t"}integer = 38
1464       ${"\t"}name = "value"
1466       [extra "subsection"]
1467       ${"\t"}value = "test"
1469       [gpg]
1470       ${"\t"}program = "path-to-gpg"
1472       [include]
1473       ${"\t"}path = "~/path/to/config.inc"
1475       [includeIf "gitdif:~/src/dir"]
1476       ${"\t"}path = "~/path/to/conditional.inc"
1478       [tag]
1479       ${"\t"}gpgSign = true
1481       [user]
1482       ${"\t"}email = "user@example.org"
1483       ${"\t"}name = "John Doe"
1484       ${"\t"}signingKey = "00112233445566778899AABBCCDDEEFF"
1485     '';
1486   };
1488   # right now only invocation check
1489   testToJSONSimple =
1490     let val = {
1491       foobar = [ "baz" 1 2 3 ];
1492     };
1493     in {
1494       expr = generators.toJSON {} val;
1495       # trivial implementation
1496       expected = builtins.toJSON val;
1497   };
1499   # right now only invocation check
1500   testToYAMLSimple =
1501     let val = {
1502       list = [ { one = 1; } { two = 2; } ];
1503       all = 42;
1504     };
1505     in {
1506       expr = generators.toYAML {} val;
1507       # trivial implementation
1508       expected = builtins.toJSON val;
1509   };
1511   testToPretty =
1512     let
1513       deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
1514     in {
1515     expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
1516       int = 42;
1517       float = 0.1337;
1518       bool = true;
1519       emptystring = "";
1520       string = "fn\${o}\"r\\d";
1521       newlinestring = "\n";
1522       path = /. + "/foo";
1523       null_ = null;
1524       function = x: x;
1525       functionArgs = { arg ? 4, foo }: arg;
1526       list = [ 3 4 function [ false ] ];
1527       emptylist = [];
1528       attrs = { foo = null; "foo b/ar" = "baz"; };
1529       emptyattrs = {};
1530       drv = deriv;
1531     };
1532     expected = rec {
1533       int = "42";
1534       float = "0.1337";
1535       bool = "true";
1536       emptystring = ''""'';
1537       string = ''"fn\''${o}\"r\\d"'';
1538       newlinestring = "\"\\n\"";
1539       path = "/foo";
1540       null_ = "null";
1541       function = "<function>";
1542       functionArgs = "<function, args: {arg?, foo}>";
1543       list = "[ 3 4 ${function} [ false ] ]";
1544       emptylist = "[ ]";
1545       attrs = "{ foo = null; \"foo b/ar\" = \"baz\"; }";
1546       emptyattrs = "{ }";
1547       drv = "<derivation ${deriv.name}>";
1548     };
1549   };
1551   testToPrettyLimit =
1552     let
1553       a.b = 1;
1554       a.c = a;
1555     in {
1556       expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a);
1557       expected = "{\n  b = 1;\n  c = {\n    b = \"<unevaluated>\";\n    c = {\n      b = \"<unevaluated>\";\n      c = \"<unevaluated>\";\n    };\n  };\n}";
1558     };
1560   testToPrettyLimitThrow =
1561     let
1562       a.b = 1;
1563       a.c = a;
1564     in {
1565       expr = (builtins.tryEval
1566         (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
1567       expected = false;
1568     };
1570   testWithRecursionDealsWithFunctors =
1571     let
1572       functor = {
1573         __functor = self: { a, b, }: null;
1574       };
1575       a = {
1576         value = "1234";
1577         b = functor;
1578         c.d = functor;
1579       };
1580     in {
1581       expr = generators.toPretty { } (generators.withRecursion { depthLimit = 1; throwOnDepthLimit = false; } a);
1582       expected = "{\n  b = <function, args: {a, b}>;\n  c = {\n    d = \"<unevaluated>\";\n  };\n  value = \"<unevaluated>\";\n}";
1583     };
1585   testToPrettyMultiline = {
1586     expr = mapAttrs (const (generators.toPretty { })) {
1587       list = [ 3 4 [ false ] ];
1588       attrs = { foo = null; bar.foo = "baz"; };
1589       newlinestring = "\n";
1590       multilinestring = ''
1591         hello
1592         ''${there}
1593         te'''st
1594       '';
1595       multilinestring' = ''
1596         hello
1597         there
1598         test'';
1599     };
1600     expected = {
1601       list = ''
1602         [
1603           3
1604           4
1605           [
1606             false
1607           ]
1608         ]'';
1609       attrs = ''
1610         {
1611           bar = {
1612             foo = "baz";
1613           };
1614           foo = null;
1615         }'';
1616       newlinestring = "''\n  \n''";
1617       multilinestring = ''
1618         '''
1619           hello
1620           '''''${there}
1621           te''''st
1622         ''''';
1623       multilinestring' = ''
1624         '''
1625           hello
1626           there
1627           test''''';
1629     };
1630   };
1632   testToPrettyAllowPrettyValues = {
1633     expr = generators.toPretty { allowPrettyValues = true; }
1634              { __pretty = v: "«" + v + "»"; val = "foo"; };
1635     expected  = "«foo»";
1636   };
1638   testToPlist = {
1639     expr = mapAttrs (const (generators.toPlist { })) {
1640       value = {
1641         nested.values = {
1642           int = 42;
1643           float = 0.1337;
1644           bool = true;
1645           emptystring = "";
1646           string = "fn\${o}\"r\\d";
1647           newlinestring = "\n";
1648           path = /. + "/foo";
1649           null_ = null;
1650           list = [ 3 4 "test" ];
1651           emptylist = [];
1652           attrs = { foo = null; "foo b/ar" = "baz"; };
1653           emptyattrs = {};
1654         };
1655       };
1656     };
1657     expected = { value = builtins.readFile ./test-to-plist-expected.plist; };
1658   };
1660   testToLuaEmptyAttrSet = {
1661     expr = generators.toLua {} {};
1662     expected = ''{}'';
1663   };
1665   testToLuaEmptyList = {
1666     expr = generators.toLua {} [];
1667     expected = ''{}'';
1668   };
1670   testToLuaListOfVariousTypes = {
1671     expr = generators.toLua {} [ null 43 3.14159 true ];
1672     expected = ''
1673       {
1674         nil,
1675         43,
1676         3.14159,
1677         true
1678       }'';
1679   };
1681   testToLuaString = {
1682     expr = generators.toLua {} ''double-quote (") and single quotes (')'';
1683     expected = ''"double-quote (\") and single quotes (')"'';
1684   };
1686   testToLuaAttrsetWithLuaInline = {
1687     expr = generators.toLua {} { x = generators.mkLuaInline ''"abc" .. "def"''; };
1688     expected = ''
1689       {
1690         ["x"] = ("abc" .. "def")
1691       }'';
1692   };
1694   testToLuaAttrsetWithSpaceInKey = {
1695     expr = generators.toLua {} { "some space and double-quote (\")" = 42; };
1696     expected = ''
1697       {
1698         ["some space and double-quote (\")"] = 42
1699       }'';
1700   };
1702   testToLuaWithoutMultiline = {
1703     expr = generators.toLua { multiline = false; } [ 41 43 ];
1704     expected = ''{ 41, 43 }'';
1705   };
1707   testToLuaEmptyBindings = {
1708     expr = generators.toLua { asBindings = true; } {};
1709     expected = "";
1710   };
1712   testToLuaBindings = {
1713     expr = generators.toLua { asBindings = true; } { x1 = 41; _y = { a = 43; }; };
1714     expected = ''
1715       _y = {
1716         ["a"] = 43
1717       }
1718       x1 = 41
1719     '';
1720   };
1722   testToLuaPartialTableBindings = {
1723     expr = generators.toLua { asBindings = true; } { "x.y" = 42; };
1724     expected = ''
1725       x.y = 42
1726     '';
1727   };
1729   testToLuaIndentedBindings = {
1730     expr = generators.toLua { asBindings = true; indent = "  "; } { x = { y = 42; }; };
1731     expected = "  x = {\n    [\"y\"] = 42\n  }\n";
1732   };
1734   testToLuaBindingsWithSpace = testingThrow (
1735     generators.toLua { asBindings = true; } { "with space" = 42; }
1736   );
1738   testToLuaBindingsWithLeadingDigit = testingThrow (
1739     generators.toLua { asBindings = true; } { "11eleven" = 42; }
1740   );
1742   testToLuaBasicExample = {
1743     expr = generators.toLua {} {
1744       cmd = [ "typescript-language-server" "--stdio" ];
1745       settings.workspace.library = generators.mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
1746     };
1747     expected = ''
1748       {
1749         ["cmd"] = {
1750           "typescript-language-server",
1751           "--stdio"
1752         },
1753         ["settings"] = {
1754           ["workspace"] = {
1755             ["library"] = (vim.api.nvim_get_runtime_file("", true))
1756           }
1757         }
1758       }'';
1759   };
1761 # CLI
1763   testToGNUCommandLine = {
1764     expr = cli.toGNUCommandLine {} {
1765       data = builtins.toJSON { id = 0; };
1766       X = "PUT";
1767       retry = 3;
1768       retry-delay = null;
1769       url = [ "https://example.com/foo" "https://example.com/bar" ];
1770       silent = false;
1771       verbose = true;
1772     };
1774     expected = [
1775       "-X" "PUT"
1776       "--data" "{\"id\":0}"
1777       "--retry" "3"
1778       "--url" "https://example.com/foo"
1779       "--url" "https://example.com/bar"
1780       "--verbose"
1781     ];
1782   };
1784   testToGNUCommandLineSeparator = {
1785     expr = cli.toGNUCommandLine { optionValueSeparator = "="; } {
1786       data = builtins.toJSON { id = 0; };
1787       X = "PUT";
1788       retry = 3;
1789       retry-delay = null;
1790       url = [ "https://example.com/foo" "https://example.com/bar" ];
1791       silent = false;
1792       verbose = true;
1793     };
1795     expected = [
1796       "-X=PUT"
1797       "--data={\"id\":0}"
1798       "--retry=3"
1799       "--url=https://example.com/foo"
1800       "--url=https://example.com/bar"
1801       "--verbose"
1802     ];
1803   };
1805   testToGNUCommandLineShell = {
1806     expr = cli.toGNUCommandLineShell {} {
1807       data = builtins.toJSON { id = 0; };
1808       X = "PUT";
1809       retry = 3;
1810       retry-delay = null;
1811       url = [ "https://example.com/foo" "https://example.com/bar" ];
1812       silent = false;
1813       verbose = true;
1814     };
1816     expected = "-X PUT --data '{\"id\":0}' --retry 3 --url https://example.com/foo --url https://example.com/bar --verbose";
1817   };
1819   testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
1820     name = "..foo";
1821     expected = "foo";
1822   };
1824   testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
1825     name = "fö";
1826     expected = "f-";
1827   };
1829   testSanitizeDerivationNameAscii = testSanitizeDerivationName {
1830     name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
1831     expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
1832   };
1834   testSanitizeDerivationNameTooLong = testSanitizeDerivationName {
1835     name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
1836     expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
1837   };
1839   testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName {
1840     name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&";
1841     expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-";
1842   };
1844   testSanitizeDerivationNameEmpty = testSanitizeDerivationName {
1845     name = "";
1846     expected = "unknown";
1847   };
1849   testFreeformOptions = {
1850     expr =
1851       let
1852         submodule = { lib, ... }: {
1853           freeformType = lib.types.attrsOf (lib.types.submodule {
1854             options.bar = lib.mkOption {};
1855           });
1856           options.bar = lib.mkOption {};
1857         };
1859         module = { lib, ... }: {
1860           options.foo = lib.mkOption {
1861             type = lib.types.submodule submodule;
1862           };
1863         };
1865         options = (evalModules {
1866           modules = [ module ];
1867         }).options;
1869         locs = filter (o: ! o.internal) (optionAttrSetToDocList options);
1870       in map (o: o.loc) locs;
1871     expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
1872   };
1874   testCartesianProductOfEmptySet = {
1875     expr = cartesianProduct {};
1876     expected = [ {} ];
1877   };
1879   testCartesianProductOfOneSet = {
1880     expr = cartesianProduct { a = [ 1 2 3 ]; };
1881     expected = [ { a = 1; } { a = 2; } { a = 3; } ];
1882   };
1884   testCartesianProductOfTwoSets = {
1885     expr = cartesianProduct { a = [ 1 ]; b = [ 10 20 ]; };
1886     expected = [
1887       { a = 1; b = 10; }
1888       { a = 1; b = 20; }
1889     ];
1890   };
1892   testCartesianProductOfTwoSetsWithOneEmpty = {
1893     expr = cartesianProduct { a = [ ]; b = [ 10 20 ]; };
1894     expected = [ ];
1895   };
1897   testCartesianProductOfThreeSets = {
1898     expr = cartesianProduct {
1899       a = [   1   2   3 ];
1900       b = [  10  20  30 ];
1901       c = [ 100 200 300 ];
1902     };
1903     expected = [
1904       { a = 1; b = 10; c = 100; }
1905       { a = 1; b = 10; c = 200; }
1906       { a = 1; b = 10; c = 300; }
1908       { a = 1; b = 20; c = 100; }
1909       { a = 1; b = 20; c = 200; }
1910       { a = 1; b = 20; c = 300; }
1912       { a = 1; b = 30; c = 100; }
1913       { a = 1; b = 30; c = 200; }
1914       { a = 1; b = 30; c = 300; }
1916       { a = 2; b = 10; c = 100; }
1917       { a = 2; b = 10; c = 200; }
1918       { a = 2; b = 10; c = 300; }
1920       { a = 2; b = 20; c = 100; }
1921       { a = 2; b = 20; c = 200; }
1922       { a = 2; b = 20; c = 300; }
1924       { a = 2; b = 30; c = 100; }
1925       { a = 2; b = 30; c = 200; }
1926       { a = 2; b = 30; c = 300; }
1928       { a = 3; b = 10; c = 100; }
1929       { a = 3; b = 10; c = 200; }
1930       { a = 3; b = 10; c = 300; }
1932       { a = 3; b = 20; c = 100; }
1933       { a = 3; b = 20; c = 200; }
1934       { a = 3; b = 20; c = 300; }
1936       { a = 3; b = 30; c = 100; }
1937       { a = 3; b = 30; c = 200; }
1938       { a = 3; b = 30; c = 300; }
1939     ];
1940   };
1942   testMapCartesianProductOfOneSet = {
1943     expr = mapCartesianProduct ({a}: a * 2) { a = [ 1 2 3 ]; };
1944     expected = [ 2 4 6 ];
1945   };
1947   testMapCartesianProductOfTwoSets = {
1948     expr = mapCartesianProduct ({a,b}: a + b) { a = [ 1 ]; b = [ 10 20 ]; };
1949     expected = [ 11 21 ];
1950   };
1952   testMapCartesianProcutOfTwoSetsWithOneEmpty = {
1953     expr = mapCartesianProduct (x: x.a + x.b) { a = [ ]; b = [ 10 20 ]; };
1954     expected = [ ];
1955   };
1957   testMapCartesianProductOfThreeSets = {
1958     expr = mapCartesianProduct ({a,b,c}: a + b + c) {
1959       a = [ 1 2 3 ];
1960       b = [ 10 20 30 ];
1961       c = [ 100 200 300 ];
1962     };
1963     expected = [ 111 211 311 121 221 321 131 231 331 112 212 312 122 222 322 132 232 332 113 213 313 123 223 323 133 233 333 ];
1964   };
1966   # The example from the showAttrPath documentation
1967   testShowAttrPathExample = {
1968     expr = showAttrPath [ "foo" "10" "bar" ];
1969     expected = "foo.\"10\".bar";
1970   };
1972   testShowAttrPathEmpty = {
1973     expr = showAttrPath [];
1974     expected = "<root attribute path>";
1975   };
1977   testShowAttrPathVarious = {
1978     expr = showAttrPath [
1979       "."
1980       "foo"
1981       "2"
1982       "a2-b"
1983       "_bc'de"
1984     ];
1985     expected = ''".".foo."2".a2-b._bc'de'';
1986   };
1988   testGroupBy = {
1989     expr = groupBy (n: toString (mod n 5)) (range 0 16);
1990     expected = {
1991       "0" = [ 0 5 10 15 ];
1992       "1" = [ 1 6 11 16 ];
1993       "2" = [ 2 7 12 ];
1994       "3" = [ 3 8 13 ];
1995       "4" = [ 4 9 14 ];
1996     };
1997   };
1999   testGroupBy' = {
2000     expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ];
2001     expected = { false = 3; true = 12; };
2002   };
2004   # The example from the updateManyAttrsByPath documentation
2005   testUpdateManyAttrsByPathExample = {
2006     expr = updateManyAttrsByPath [
2007       {
2008         path = [ "a" "b" ];
2009         update = old: { d = old.c; };
2010       }
2011       {
2012         path = [ "a" "b" "c" ];
2013         update = old: old + 1;
2014       }
2015       {
2016         path = [ "x" "y" ];
2017         update = old: "xy";
2018       }
2019     ] { a.b.c = 0; };
2020     expected = { a = { b = { d = 1; }; }; x = { y = "xy"; }; };
2021   };
2023   # If there are no updates, the value is passed through
2024   testUpdateManyAttrsByPathNone = {
2025     expr = updateManyAttrsByPath [] "something";
2026     expected = "something";
2027   };
2029   # A single update to the root path is just like applying the function directly
2030   testUpdateManyAttrsByPathSingleIncrement = {
2031     expr = updateManyAttrsByPath [
2032       {
2033         path = [ ];
2034         update = old: old + 1;
2035       }
2036     ] 0;
2037     expected = 1;
2038   };
2040   # Multiple updates can be applied are done in order
2041   testUpdateManyAttrsByPathMultipleIncrements = {
2042     expr = updateManyAttrsByPath [
2043       {
2044         path = [ ];
2045         update = old: old + "a";
2046       }
2047       {
2048         path = [ ];
2049         update = old: old + "b";
2050       }
2051       {
2052         path = [ ];
2053         update = old: old + "c";
2054       }
2055     ] "";
2056     expected = "abc";
2057   };
2059   # If an update doesn't use the value, all previous updates are not evaluated
2060   testUpdateManyAttrsByPathLazy = {
2061     expr = updateManyAttrsByPath [
2062       {
2063         path = [ ];
2064         update = old: old + throw "nope";
2065       }
2066       {
2067         path = [ ];
2068         update = old: "untainted";
2069       }
2070     ] (throw "start");
2071     expected = "untainted";
2072   };
2074   # Deeply nested attributes can be updated without affecting others
2075   testUpdateManyAttrsByPathDeep = {
2076     expr = updateManyAttrsByPath [
2077       {
2078         path = [ "a" "b" "c" ];
2079         update = old: old + 1;
2080       }
2081     ] {
2082       a.b.c = 0;
2084       a.b.z = 0;
2085       a.y.z = 0;
2086       x.y.z = 0;
2087     };
2088     expected = {
2089       a.b.c = 1;
2091       a.b.z = 0;
2092       a.y.z = 0;
2093       x.y.z = 0;
2094     };
2095   };
2097   # Nested attributes are updated first
2098   testUpdateManyAttrsByPathNestedBeforehand = {
2099     expr = updateManyAttrsByPath [
2100       {
2101         path = [ "a" ];
2102         update = old: old // { x = old.b; };
2103       }
2104       {
2105         path = [ "a" "b" ];
2106         update = old: old + 1;
2107       }
2108     ] {
2109       a.b = 0;
2110     };
2111     expected = {
2112       a.b = 1;
2113       a.x = 1;
2114     };
2115   };
2117   ## Levenshtein distance functions and co.
2118   testCommonPrefixLengthEmpty = {
2119     expr = strings.commonPrefixLength "" "hello";
2120     expected = 0;
2121   };
2123   testCommonPrefixLengthSame = {
2124     expr = strings.commonPrefixLength "hello" "hello";
2125     expected = 5;
2126   };
2128   testCommonPrefixLengthDiffering = {
2129     expr = strings.commonPrefixLength "hello" "hey";
2130     expected = 2;
2131   };
2133   testCommonSuffixLengthEmpty = {
2134     expr = strings.commonSuffixLength "" "hello";
2135     expected = 0;
2136   };
2138   testCommonSuffixLengthSame = {
2139     expr = strings.commonSuffixLength "hello" "hello";
2140     expected = 5;
2141   };
2143   testCommonSuffixLengthDiffering = {
2144     expr = strings.commonSuffixLength "test" "rest";
2145     expected = 3;
2146   };
2148   testLevenshteinEmpty = {
2149     expr = strings.levenshtein "" "";
2150     expected = 0;
2151   };
2153   testLevenshteinOnlyAdd = {
2154     expr = strings.levenshtein "" "hello there";
2155     expected = 11;
2156   };
2158   testLevenshteinOnlyRemove = {
2159     expr = strings.levenshtein "hello there" "";
2160     expected = 11;
2161   };
2163   testLevenshteinOnlyTransform = {
2164     expr = strings.levenshtein "abcdef" "ghijkl";
2165     expected = 6;
2166   };
2168   testLevenshteinMixed = {
2169     expr = strings.levenshtein "kitchen" "sitting";
2170     expected = 5;
2171   };
2173   testLevenshteinAtMostZeroFalse = {
2174     expr = strings.levenshteinAtMost 0 "foo" "boo";
2175     expected = false;
2176   };
2178   testLevenshteinAtMostZeroTrue = {
2179     expr = strings.levenshteinAtMost 0 "foo" "foo";
2180     expected = true;
2181   };
2183   testLevenshteinAtMostOneFalse = {
2184     expr = strings.levenshteinAtMost 1 "car" "ct";
2185     expected = false;
2186   };
2188   testLevenshteinAtMostOneTrue = {
2189     expr = strings.levenshteinAtMost 1 "car" "cr";
2190     expected = true;
2191   };
2193   # We test levenshteinAtMost 2 particularly well because it uses a complicated
2194   # implementation
2195   testLevenshteinAtMostTwoIsEmpty = {
2196     expr = strings.levenshteinAtMost 2 "" "";
2197     expected = true;
2198   };
2200   testLevenshteinAtMostTwoIsZero = {
2201     expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
2202     expected = true;
2203   };
2205   testLevenshteinAtMostTwoIsOne = {
2206     expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
2207     expected = true;
2208   };
2210   testLevenshteinAtMostTwoDiff0False = {
2211     expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
2212     expected = false;
2213   };
2215   testLevenshteinAtMostTwoDiff0Outer = {
2216     expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
2217     expected = true;
2218   };
2220   testLevenshteinAtMostTwoDiff0DelLeft = {
2221     expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
2222     expected = true;
2223   };
2225   testLevenshteinAtMostTwoDiff0DelRight = {
2226     expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
2227     expected = true;
2228   };
2230   testLevenshteinAtMostTwoDiff1False = {
2231     expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
2232     expected = false;
2233   };
2235   testLevenshteinAtMostTwoDiff1DelLeft = {
2236     expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
2237     expected = true;
2238   };
2240   testLevenshteinAtMostTwoDiff1DelRight = {
2241     expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
2242     expected = true;
2243   };
2245   testLevenshteinAtMostTwoDiff2False = {
2246     expr = strings.levenshteinAtMost 2 "hello" "hxo";
2247     expected = false;
2248   };
2250   testLevenshteinAtMostTwoDiff2True = {
2251     expr = strings.levenshteinAtMost 2 "hello" "heo";
2252     expected = true;
2253   };
2255   testLevenshteinAtMostTwoDiff3 = {
2256     expr = strings.levenshteinAtMost 2 "hello" "ho";
2257     expected = false;
2258   };
2260   testLevenshteinAtMostThreeFalse = {
2261     expr = strings.levenshteinAtMost 3 "hello" "Holla!";
2262     expected = false;
2263   };
2265   testLevenshteinAtMostThreeTrue = {
2266     expr = strings.levenshteinAtMost 3 "hello" "Holla";
2267     expected = true;
2268   };
2270   # DERIVATIONS
2272   testLazyDerivationIsLazyInDerivationForAttrNames = {
2273     expr = attrNames (lazyDerivation {
2274       derivation = throw "not lazy enough";
2275     });
2276     # It's ok to add attribute names here when lazyDerivation is improved
2277     # in accordance with its inline comments.
2278     expected = [ "drvPath" "meta" "name" "out" "outPath" "outputName" "outputs" "system" "type" ];
2279   };
2281   testLazyDerivationIsLazyInDerivationForPassthruAttr = {
2282     expr = (lazyDerivation {
2283       derivation = throw "not lazy enough";
2284       passthru.tests = "whatever is in tests";
2285     }).tests;
2286     expected = "whatever is in tests";
2287   };
2289   testLazyDerivationIsLazyInDerivationForPassthruAttr2 = {
2290     # passthru.tests is not a special case. It works for any attr.
2291     expr = (lazyDerivation {
2292       derivation = throw "not lazy enough";
2293       passthru.foo = "whatever is in foo";
2294     }).foo;
2295     expected = "whatever is in foo";
2296   };
2298   testLazyDerivationIsLazyInDerivationForMeta = {
2299     expr = (lazyDerivation {
2300       derivation = throw "not lazy enough";
2301       meta = "whatever is in meta";
2302     }).meta;
2303     expected = "whatever is in meta";
2304   };
2306   testLazyDerivationReturnsDerivationAttrs = let
2307     derivation = {
2308       type = "derivation";
2309       outputs = ["out"];
2310       out = "test out";
2311       outPath = "test outPath";
2312       outputName = "out";
2313       drvPath = "test drvPath";
2314       name = "test name";
2315       system = "test system";
2316       meta = "test meta";
2317     };
2318   in {
2319     expr = lazyDerivation { inherit derivation; };
2320     expected = derivation;
2321   };
2323   testOptionalDrvAttr = let
2324     mkDerivation = args: derivation (args // {
2325       builder = "builder";
2326       system = "system";
2327       __ignoreNulls = true;
2328     });
2329   in {
2330     expr = (mkDerivation {
2331       name = "foo";
2332       x = optionalDrvAttr true 1;
2333       y = optionalDrvAttr false 1;
2334     }).drvPath;
2335     expected = (mkDerivation {
2336       name = "foo";
2337       x = 1;
2338     }).drvPath;
2339   };
2341   testLazyDerivationMultiOutputReturnsDerivationAttrs = let
2342     derivation = {
2343       type = "derivation";
2344       outputs = ["out" "dev"];
2345       dev = "test dev";
2346       out = "test out";
2347       outPath = "test outPath";
2348       outputName = "out";
2349       drvPath = "test drvPath";
2350       name = "test name";
2351       system = "test system";
2352       meta.position = "/hi:23";
2353     };
2354   in {
2355     expr = lazyDerivation { inherit derivation; outputs = ["out" "dev"]; passthru.meta.position = "/hi:23"; };
2356     expected = derivation;
2357   };
2359   testTypeDescriptionInt = {
2360     expr = (with types; int).description;
2361     expected = "signed integer";
2362   };
2363   testTypeDescriptionIntsPositive = {
2364     expr = (with types; ints.positive).description;
2365     expected = "positive integer, meaning >0";
2366   };
2367   testTypeDescriptionIntsPositiveOrEnumAuto = {
2368     expr = (with types; either ints.positive (enum ["auto"])).description;
2369     expected = ''positive integer, meaning >0, or value "auto" (singular enum)'';
2370   };
2371   testTypeDescriptionListOfPositive = {
2372     expr = (with types; listOf ints.positive).description;
2373     expected = "list of (positive integer, meaning >0)";
2374   };
2375   testTypeDescriptionListOfInt = {
2376     expr = (with types; listOf int).description;
2377     expected = "list of signed integer";
2378   };
2379   testTypeDescriptionListOfListOfInt = {
2380     expr = (with types; listOf (listOf int)).description;
2381     expected = "list of list of signed integer";
2382   };
2383   testTypeDescriptionListOfEitherStrOrBool = {
2384     expr = (with types; listOf (either str bool)).description;
2385     expected = "list of (string or boolean)";
2386   };
2387   testTypeDescriptionEitherListOfStrOrBool = {
2388     expr = (with types; either (listOf bool) str).description;
2389     expected = "(list of boolean) or string";
2390   };
2391   testTypeDescriptionEitherStrOrListOfBool = {
2392     expr = (with types; either str (listOf bool)).description;
2393     expected = "string or list of boolean";
2394   };
2395   testTypeDescriptionOneOfListOfStrOrBool = {
2396     expr = (with types; oneOf [ (listOf bool) str ]).description;
2397     expected = "(list of boolean) or string";
2398   };
2399   testTypeDescriptionOneOfListOfStrOrBoolOrNumber = {
2400     expr = (with types; oneOf [ (listOf bool) str number ]).description;
2401     expected = "(list of boolean) or string or signed integer or floating point number";
2402   };
2403   testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = {
2404     expr = (with types; either (listOf bool) (either str number)).description;
2405     expected = "(list of boolean) or string or signed integer or floating point number";
2406   };
2407   testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = {
2408     expr = (with types; either (either (listOf bool) str) number).description;
2409     expected = "(list of boolean) or string or signed integer or floating point number";
2410   };
2411   testTypeDescriptionEitherNullOrBoolOrString = {
2412     expr = (with types; either (nullOr bool) str).description;
2413     expected = "null or boolean or string";
2414   };
2415   testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = {
2416     expr = (with types; either (listOf (either bool str)) int).description;
2417     expected = "(list of (boolean or string)) or signed integer";
2418   };
2419   testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = {
2420     expr = (with types; either int (listOf (either bool str))).description;
2421     expected = "signed integer or list of (boolean or string)";
2422   };
2424 # Meta
2425   testGetExe'Output = {
2426     expr = getExe' {
2427       type = "derivation";
2428       out = "somelonghash";
2429       bin = "somelonghash";
2430     } "executable";
2431     expected = "somelonghash/bin/executable";
2432   };
2434   testGetExeOutput = {
2435     expr = getExe {
2436       type = "derivation";
2437       out = "somelonghash";
2438       bin = "somelonghash";
2439       meta.mainProgram = "mainProgram";
2440     };
2441     expected = "somelonghash/bin/mainProgram";
2442   };
2444   testGetExe'FailureFirstArg = testingThrow (
2445     getExe' "not a derivation" "executable"
2446   );
2448   testGetExe'FailureSecondArg = testingThrow (
2449     getExe' { type = "derivation"; } "dir/executable"
2450   );
2452   testGetLicenseFromSpdxIdOrExamples = {
2453     expr = [
2454       (getLicenseFromSpdxIdOr "MIT" null)
2455       (getLicenseFromSpdxIdOr "mIt" null)
2456       (getLicenseFromSpdxIdOr "MY LICENSE" lib.licenses.free)
2457       (getLicenseFromSpdxIdOr "MY LICENSE" null)
2458     ];
2459     expected = [
2460       lib.licenses.mit
2461       lib.licenses.mit
2462       lib.licenses.free
2463       null
2464     ];
2465   };
2467   testGetLicenseFromSpdxIdOrThrow = testingThrow (
2468     getLicenseFromSpdxIdOr "MY LICENSE" (throw "No SPDX ID matches MY LICENSE")
2469   );
2471   testPlatformMatch = {
2472     expr = meta.platformMatch { system = "x86_64-linux"; } "x86_64-linux";
2473     expected = true;
2474   };
2476   testPlatformMatchAttrs = {
2477     expr = meta.platformMatch (systems.elaborate "x86_64-linux") (systems.elaborate "x86_64-linux").parsed;
2478     expected = true;
2479   };
2481   testPlatformMatchNoMatch = {
2482     expr = meta.platformMatch { system = "x86_64-darwin"; } "x86_64-linux";
2483     expected = false;
2484   };
2486   testPlatformMatchMissingSystem = {
2487     expr = meta.platformMatch { } "x86_64-linux";
2488     expected = false;
2489   };
2491   testPackagesFromDirectoryRecursive = {
2492     expr = packagesFromDirectoryRecursive {
2493       callPackage = path: overrides: import path overrides;
2494       directory = ./packages-from-directory;
2495     };
2496     expected = {
2497       a = "a";
2498       b = "b";
2499       # Note: Other files/directories in `./test-data/c/` are ignored and can be
2500       # used by `package.nix`.
2501       c = "c";
2502       my-namespace = {
2503         d = "d";
2504         e = "e";
2505         f = "f";
2506         my-sub-namespace = {
2507           g = "g";
2508           h = "h";
2509         };
2510       };
2511     };
2512   };
2514   # Check that `packagesFromDirectoryRecursive` can process a directory with a
2515   # top-level `package.nix` file into a single package.
2516   testPackagesFromDirectoryRecursiveTopLevelPackageNix = {
2517     expr = packagesFromDirectoryRecursive {
2518       callPackage = path: overrides: import path overrides;
2519       directory = ./packages-from-directory/c;
2520     };
2521     expected = "c";
2522   };