openvswitch: generalize builder
[NixPkgs.git] / lib / tests / misc.nix
blob31c938a8ffda165b9bf9f375cd7e0ee05e6fb45e
1 # to run these tests:
2 # nix-instantiate --eval --strict nixpkgs/lib/tests/misc.nix
3 # if the resulting list is empty, all tests passed
4 with import ../default.nix;
6 let
8   testSanitizeDerivationName = { name, expected }:
9   let
10     drv = derivation {
11       name = strings.sanitizeDerivationName name;
12       builder = "x";
13       system = "x";
14     };
15   in {
16     # Evaluate the derivation so an invalid name would be caught
17     expr = builtins.seq drv.drvPath drv.name;
18     inherit expected;
19   };
23 runTests {
25 # TRIVIAL
27   testId = {
28     expr = id 1;
29     expected = 1;
30   };
32   testConst = {
33     expr = const 2 3;
34     expected = 2;
35   };
37   testPipe = {
38     expr = pipe 2 [
39       (x: x + 2) # 2 + 2 = 4
40       (x: x * 2) # 4 * 2 = 8
41     ];
42     expected = 8;
43   };
45   testPipeEmpty = {
46     expr = pipe 2 [];
47     expected = 2;
48   };
50   testPipeStrings = {
51     expr = pipe [ 3 4 ] [
52       (map toString)
53       (map (s: s + "\n"))
54       concatStrings
55     ];
56     expected = ''
57       3
58       4
59     '';
60   };
62   /*
63   testOr = {
64     expr = or true false;
65     expected = true;
66   };
67   */
69   testAnd = {
70     expr = and true false;
71     expected = false;
72   };
74   testFix = {
75     expr = fix (x: {a = if x ? a then "a" else "b";});
76     expected = {a = "a";};
77   };
79   testComposeExtensions = {
80     expr = let obj = makeExtensible (self: { foo = self.bar; });
81                f = self: super: { bar = false; baz = true; };
82                g = self: super: { bar = super.baz or false; };
83                f_o_g = composeExtensions f g;
84                composed = obj.extend f_o_g;
85            in composed.foo;
86     expected = true;
87   };
89   testComposeManyExtensions0 = {
90     expr = let obj = makeExtensible (self: { foo = true; });
91                emptyComposition = composeManyExtensions [];
92                composed = obj.extend emptyComposition;
93            in composed.foo;
94     expected = true;
95   };
97   testComposeManyExtensions =
98     let f = self: super: { bar = false; baz = true; };
99         g = self: super: { bar = super.baz or false; };
100         h = self: super: { qux = super.bar or false; };
101         obj = makeExtensible (self: { foo = self.qux; });
102     in {
103     expr = let composition = composeManyExtensions [f g h];
104                composed = obj.extend composition;
105            in composed.foo;
106     expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo;
107   };
109   testBitAnd = {
110     expr = (bitAnd 3 10);
111     expected = 2;
112   };
114   testBitOr = {
115     expr = (bitOr 3 10);
116     expected = 11;
117   };
119   testBitXor = {
120     expr = (bitXor 3 10);
121     expected = 9;
122   };
124   testToHexString = {
125     expr = toHexString 250;
126     expected = "FA";
127   };
129   testToBaseDigits = {
130     expr = toBaseDigits 2 6;
131     expected = [ 1 1 0 ];
132   };
134   testFunctionArgsFunctor = {
135     expr = functionArgs { __functor = self: { a, b }: null; };
136     expected = { a = false; b = false; };
137   };
139   testFunctionArgsSetFunctionArgs = {
140     expr = functionArgs (setFunctionArgs (args: args.x) { x = false; });
141     expected = { x = false; };
142   };
144 # STRINGS
146   testConcatMapStrings = {
147     expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
148     expected = "a;b;c;";
149   };
151   testConcatStringsSep = {
152     expr = concatStringsSep "," ["a" "b" "c"];
153     expected = "a,b,c";
154   };
156   testSplitStringsSimple = {
157     expr = strings.splitString "." "a.b.c.d";
158     expected = [ "a" "b" "c" "d" ];
159   };
161   testSplitStringsEmpty = {
162     expr = strings.splitString "." "a..b";
163     expected = [ "a" "" "b" ];
164   };
166   testSplitStringsOne = {
167     expr = strings.splitString ":" "a.b";
168     expected = [ "a.b" ];
169   };
171   testSplitStringsNone = {
172     expr = strings.splitString "." "";
173     expected = [ "" ];
174   };
176   testSplitStringsFirstEmpty = {
177     expr = strings.splitString "/" "/a/b/c";
178     expected = [ "" "a" "b" "c" ];
179   };
181   testSplitStringsLastEmpty = {
182     expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
183     expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
184   };
186   testSplitStringsRegex = {
187     expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B";
188     expected = [ "A" "B" ];
189   };
191   testSplitStringsDerivation = {
192     expr = take 3  (strings.splitString "/" (derivation {
193       name = "name";
194       builder = "builder";
195       system = "system";
196     }));
197     expected = ["" "nix" "store"];
198   };
200   testSplitVersionSingle = {
201     expr = versions.splitVersion "1";
202     expected = [ "1" ];
203   };
205   testSplitVersionDouble = {
206     expr = versions.splitVersion "1.2";
207     expected = [ "1" "2" ];
208   };
210   testSplitVersionTriple = {
211     expr = versions.splitVersion "1.2.3";
212     expected = [ "1" "2" "3" ];
213   };
215   testIsStorePath =  {
216     expr =
217       let goodPath =
218             "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
219       in {
220         storePath = isStorePath goodPath;
221         storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello;
222         storePathAppendix = isStorePath
223           "${goodPath}/bin/python";
224         nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
225         asPath = isStorePath (/. + goodPath);
226         otherPath = isStorePath "/something/else";
227         otherVals = {
228           attrset = isStorePath {};
229           list = isStorePath [];
230           int = isStorePath 42;
231         };
232       };
233     expected = {
234       storePath = true;
235       storePathDerivation = true;
236       storePathAppendix = false;
237       nonAbsolute = false;
238       asPath = true;
239       otherPath = false;
240       otherVals = {
241         attrset = false;
242         list = false;
243         int = false;
244       };
245     };
246   };
248   testEscapeXML = {
249     expr = escapeXML ''"test" 'test' < & >'';
250     expected = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;";
251   };
253   testToShellVars = {
254     expr = ''
255       ${toShellVars {
256         STRing01 = "just a 'string'";
257         _array_ = [ "with" "more strings" ];
258         assoc."with some" = ''
259           strings
260           possibly newlines
261         '';
262         drv = {
263           outPath = "/drv";
264           foo = "ignored attribute";
265         };
266         path = /path;
267         stringable = {
268           __toString = _: "hello toString";
269           bar = "ignored attribute";
270         };
271       }}
272     '';
273     expected = ''
274       STRing01='just a '\'''string'\''''
275       declare -a _array_=('with' 'more strings')
276       declare -A assoc=(['with some']='strings
277       possibly newlines
278       ')
279       drv='/drv'
280       path='/path'
281       stringable='hello toString'
282     '';
283   };
285   testHasInfixFalse = {
286     expr = hasInfix "c" "abde";
287     expected = false;
288   };
290   testHasInfixTrue = {
291     expr = hasInfix "c" "abcde";
292     expected = true;
293   };
295   testHasInfixDerivation = {
296     expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello;
297     expected = true;
298   };
300   testHasInfixPath = {
301     expr = hasInfix "tests" ./.;
302     expected = true;
303   };
305   testHasInfixPathStoreDir = {
306     expr = hasInfix builtins.storeDir ./.;
307     expected = true;
308   };
310   testHasInfixToString = {
311     expr = hasInfix "a" { __toString = _: "a"; };
312     expected = true;
313   };
315   testNormalizePath = {
316     expr = strings.normalizePath "//a/b//c////d/";
317     expected = "/a/b/c/d/";
318   };
320   testCharToInt = {
321     expr = strings.charToInt "A";
322     expected = 65;
323   };
325   testEscapeC = {
326     expr = strings.escapeC [ " " ] "Hello World";
327     expected = "Hello\\x20World";
328   };
330   testToInt = testAllTrue [
331     # Naive
332     (123 == toInt "123")
333     (0 == toInt "0")
334     # Whitespace Padding
335     (123 == toInt " 123")
336     (123 == toInt "123 ")
337     (123 == toInt " 123 ")
338     (123 == toInt "   123   ")
339     (0 == toInt " 0")
340     (0 == toInt "0 ")
341     (0 == toInt " 0 ")
342   ];
344   testToIntFails = testAllTrue [
345     ( builtins.tryEval (toInt "") == { success = false; value = false; } )
346     ( builtins.tryEval (toInt "123 123") == { success = false; value = false; } )
347     ( builtins.tryEval (toInt "0 123") == { success = false; value = false; } )
348     ( builtins.tryEval (toInt " 0d ") == { success = false; value = false; } )
349     ( builtins.tryEval (toInt " 1d ") == { success = false; value = false; } )
350     ( builtins.tryEval (toInt " d0 ") == { success = false; value = false; } )
351     ( builtins.tryEval (toInt "00") == { success = false; value = false; } )
352     ( builtins.tryEval (toInt "01") == { success = false; value = false; } )
353     ( builtins.tryEval (toInt "002") == { success = false; value = false; } )
354     ( builtins.tryEval (toInt " 002 ") == { success = false; value = false; } )
355     ( builtins.tryEval (toInt " foo ") == { success = false; value = false; } )
356     ( builtins.tryEval (toInt " foo 123 ") == { success = false; value = false; } )
357     ( builtins.tryEval (toInt " foo123 ") == { success = false; value = false; } )
358   ];
360   testToIntBase10 = testAllTrue [
361     # Naive
362     (123 == toIntBase10 "123")
363     (0 == toIntBase10 "0")
364     # Whitespace Padding
365     (123 == toIntBase10 " 123")
366     (123 == toIntBase10 "123 ")
367     (123 == toIntBase10 " 123 ")
368     (123 == toIntBase10 "   123   ")
369     (0 == toIntBase10 " 0")
370     (0 == toIntBase10 "0 ")
371     (0 == toIntBase10 " 0 ")
372     # Zero Padding
373     (123 == toIntBase10 "0123")
374     (123 == toIntBase10 "0000123")
375     (0 == toIntBase10 "000000")
376     # Whitespace and Zero Padding
377     (123 == toIntBase10 " 0123")
378     (123 == toIntBase10 "0123 ")
379     (123 == toIntBase10 " 0123 ")
380     (123 == toIntBase10 " 0000123")
381     (123 == toIntBase10 "0000123 ")
382     (123 == toIntBase10 " 0000123 ")
383     (0 == toIntBase10 " 000000")
384     (0 == toIntBase10 "000000 ")
385     (0 == toIntBase10 " 000000 ")
386   ];
388   testToIntBase10Fails = testAllTrue [
389     ( builtins.tryEval (toIntBase10 "") == { success = false; value = false; } )
390     ( builtins.tryEval (toIntBase10 "123 123") == { success = false; value = false; } )
391     ( builtins.tryEval (toIntBase10 "0 123") == { success = false; value = false; } )
392     ( builtins.tryEval (toIntBase10 " 0d ") == { success = false; value = false; } )
393     ( builtins.tryEval (toIntBase10 " 1d ") == { success = false; value = false; } )
394     ( builtins.tryEval (toIntBase10 " d0 ") == { success = false; value = false; } )
395     ( builtins.tryEval (toIntBase10 " foo ") == { success = false; value = false; } )
396     ( builtins.tryEval (toIntBase10 " foo 123 ") == { success = false; value = false; } )
397     ( builtins.tryEval (toIntBase10 " foo 00123 ") == { success = false; value = false; } )
398     ( builtins.tryEval (toIntBase10 " foo00123 ") == { success = false; value = false; } )
399   ];
401 # LISTS
403   testFilter = {
404     expr = filter (x: x != "a") ["a" "b" "c" "a"];
405     expected = ["b" "c"];
406   };
408   testFold =
409     let
410       f = op: fold: fold op 0 (range 0 100);
411       # fold with associative operator
412       assoc = f builtins.add;
413       # fold with non-associative operator
414       nonAssoc = f builtins.sub;
415     in {
416       expr = {
417         assocRight = assoc foldr;
418         # right fold with assoc operator is same as left fold
419         assocRightIsLeft = assoc foldr == assoc foldl;
420         nonAssocRight = nonAssoc foldr;
421         nonAssocLeft = nonAssoc foldl;
422         # with non-assoc operator the fold results are not the same
423         nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr;
424         # fold is an alias for foldr
425         foldIsRight = nonAssoc fold == nonAssoc foldr;
426       };
427       expected = {
428         assocRight = 5050;
429         assocRightIsLeft = true;
430         nonAssocRight = 50;
431         nonAssocLeft = (-5050);
432         nonAssocRightIsNotLeft = true;
433         foldIsRight = true;
434       };
435     };
437   testTake = testAllTrue [
438     ([] == (take 0 [  1 2 3 ]))
439     ([1] == (take 1 [  1 2 3 ]))
440     ([ 1 2 ] == (take 2 [  1 2 3 ]))
441     ([ 1 2 3 ] == (take 3 [  1 2 3 ]))
442     ([ 1 2 3 ] == (take 4 [  1 2 3 ]))
443   ];
445   testFoldAttrs = {
446     expr = foldAttrs (n: a: [n] ++ a) [] [
447     { a = 2; b = 7; }
448     { a = 3;        c = 8; }
449     ];
450     expected = { a = [ 2 3 ]; b = [7]; c = [8];};
451   };
453   testSort = {
454     expr = sort builtins.lessThan [ 40 2 30 42 ];
455     expected = [2 30 40 42];
456   };
458   testToIntShouldConvertStringToInt = {
459     expr = toInt "27";
460     expected = 27;
461   };
463   testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
464     expr = builtins.tryEval (toInt "\"foo\"");
465     expected = { success = false; value = false; };
466   };
468   testHasAttrByPathTrue = {
469     expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; };
470     expected = true;
471   };
473   testHasAttrByPathFalse = {
474     expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; };
475     expected = false;
476   };
479 # ATTRSETS
481   # code from the example
482   testRecursiveUpdateUntil = {
483     expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
484       # first attribute set
485       foo.bar = 1;
486       foo.baz = 2;
487       bar = 3;
488     } {
489       #second attribute set
490       foo.bar = 1;
491       foo.quz = 2;
492       baz = 4;
493     };
494     expected = {
495       foo.bar = 1; # 'foo.*' from the second set
496       foo.quz = 2; #
497       bar = 3;     # 'bar' from the first set
498       baz = 4;     # 'baz' from the second set
499     };
500   };
502   testOverrideExistingEmpty = {
503     expr = overrideExisting {} { a = 1; };
504     expected = {};
505   };
507   testOverrideExistingDisjoint = {
508     expr = overrideExisting { b = 2; } { a = 1; };
509     expected = { b = 2; };
510   };
512   testOverrideExistingOverride = {
513     expr = overrideExisting { a = 3; b = 2; } { a = 1; };
514     expected = { a = 1; b = 2; };
515   };
517 # GENERATORS
518 # these tests assume attributes are converted to lists
519 # in alphabetical order
521   testMkKeyValueDefault = {
522     expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar";
523     expected = ''f\:oo:bar'';
524   };
526   testMkValueString = {
527     expr = let
528       vals = {
529         int = 42;
530         string = ''fo"o'';
531         bool = true;
532         bool2 = false;
533         null = null;
534         # float = 42.23; # floats are strange
535       };
536       in mapAttrs
537         (const (generators.mkValueStringDefault {}))
538         vals;
539     expected = {
540       int = "42";
541       string = ''fo"o'';
542       bool = "true";
543       bool2 = "false";
544       null = "null";
545       # float = "42.23" true false [ "bar" ] ]'';
546     };
547   };
549   testToKeyValue = {
550     expr = generators.toKeyValue {} {
551       key = "value";
552       "other=key" = "baz";
553     };
554     expected = ''
555       key=value
556       other\=key=baz
557     '';
558   };
560   testToINIEmpty = {
561     expr = generators.toINI {} {};
562     expected = "";
563   };
565   testToINIEmptySection = {
566     expr = generators.toINI {} { foo = {}; bar = {}; };
567     expected = ''
568       [bar]
570       [foo]
571     '';
572   };
574   testToINIDuplicateKeys = {
575     expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; };
576     expected = ''
577       [baz]
578       qux=1
579       qux=false
581       [foo]
582       bar=true
583     '';
584   };
586   testToINIDefaultEscapes = {
587     expr = generators.toINI {} {
588       "no [ and ] allowed unescaped" = {
589         "and also no = in keys" = 42;
590       };
591     };
592     expected = ''
593       [no \[ and \] allowed unescaped]
594       and also no \= in keys=42
595     '';
596   };
598   testToINIDefaultFull = {
599     expr = generators.toINI {} {
600       "section 1" = {
601         attribute1 = 5;
602         x = "Me-se JarJar Binx";
603         # booleans are converted verbatim by default
604         boolean = false;
605       };
606       "foo[]" = {
607         "he\\h=he" = "this is okay";
608       };
609     };
610     expected = ''
611       [foo\[\]]
612       he\h\=he=this is okay
614       [section 1]
615       attribute1=5
616       boolean=false
617       x=Me-se JarJar Binx
618     '';
619   };
621   testToINIWithGlobalSectionEmpty = {
622     expr = generators.toINIWithGlobalSection {} {
623       globalSection = {
624       };
625       sections = {
626       };
627     };
628     expected = ''
629     '';
630   };
632   testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
633     let
634       sections = {
635         "section 1" = {
636           attribute1 = 5;
637           x = "Me-se JarJar Binx";
638         };
639         "foo" = {
640           "he\\h=he" = "this is okay";
641         };
642       };
643     in {
644       expr =
645         generators.toINIWithGlobalSection {} {
646             globalSection = {};
647             sections = sections;
648         };
649       expected = generators.toINI {} sections;
650   };
652   testToINIWithGlobalSectionFull = {
653     expr = generators.toINIWithGlobalSection {} {
654       globalSection = {
655         foo = "bar";
656         test = false;
657       };
658       sections = {
659         "section 1" = {
660           attribute1 = 5;
661           x = "Me-se JarJar Binx";
662         };
663         "foo" = {
664           "he\\h=he" = "this is okay";
665         };
666       };
667     };
668     expected = ''
669       foo=bar
670       test=false
672       [foo]
673       he\h\=he=this is okay
675       [section 1]
676       attribute1=5
677       x=Me-se JarJar Binx
678     '';
679   };
681   /* right now only invocation check */
682   testToJSONSimple =
683     let val = {
684       foobar = [ "baz" 1 2 3 ];
685     };
686     in {
687       expr = generators.toJSON {} val;
688       # trivial implementation
689       expected = builtins.toJSON val;
690   };
692   /* right now only invocation check */
693   testToYAMLSimple =
694     let val = {
695       list = [ { one = 1; } { two = 2; } ];
696       all = 42;
697     };
698     in {
699       expr = generators.toYAML {} val;
700       # trivial implementation
701       expected = builtins.toJSON val;
702   };
704   testToPretty =
705     let
706       deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
707     in {
708     expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
709       int = 42;
710       float = 0.1337;
711       bool = true;
712       emptystring = "";
713       string = ''fno"rd'';
714       newlinestring = "\n";
715       path = /. + "/foo";
716       null_ = null;
717       function = x: x;
718       functionArgs = { arg ? 4, foo }: arg;
719       list = [ 3 4 function [ false ] ];
720       emptylist = [];
721       attrs = { foo = null; "foo bar" = "baz"; };
722       emptyattrs = {};
723       drv = deriv;
724     };
725     expected = rec {
726       int = "42";
727       float = "~0.133700";
728       bool = "true";
729       emptystring = ''""'';
730       string = ''"fno\"rd"'';
731       newlinestring = "\"\\n\"";
732       path = "/foo";
733       null_ = "null";
734       function = "<function>";
735       functionArgs = "<function, args: {arg?, foo}>";
736       list = "[ 3 4 ${function} [ false ] ]";
737       emptylist = "[ ]";
738       attrs = "{ foo = null; \"foo bar\" = \"baz\"; }";
739       emptyattrs = "{ }";
740       drv = "<derivation ${deriv.drvPath}>";
741     };
742   };
744   testToPrettyLimit =
745     let
746       a.b = 1;
747       a.c = a;
748     in {
749       expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a);
750       expected = "{\n  b = 1;\n  c = {\n    b = \"<unevaluated>\";\n    c = {\n      b = \"<unevaluated>\";\n      c = \"<unevaluated>\";\n    };\n  };\n}";
751     };
753   testToPrettyLimitThrow =
754     let
755       a.b = 1;
756       a.c = a;
757     in {
758       expr = (builtins.tryEval
759         (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
760       expected = false;
761     };
763   testWithRecursionDealsWithFunctors =
764     let
765       functor = {
766         __functor = self: { a, b, }: null;
767       };
768       a = {
769         value = "1234";
770         b = functor;
771         c.d = functor;
772       };
773     in {
774       expr = generators.toPretty { } (generators.withRecursion { depthLimit = 1; throwOnDepthLimit = false; } a);
775       expected = "{\n  b = <function, args: {a, b}>;\n  c = {\n    d = \"<unevaluated>\";\n  };\n  value = \"<unevaluated>\";\n}";
776     };
778   testToPrettyMultiline = {
779     expr = mapAttrs (const (generators.toPretty { })) rec {
780       list = [ 3 4 [ false ] ];
781       attrs = { foo = null; bar.foo = "baz"; };
782       newlinestring = "\n";
783       multilinestring = ''
784         hello
785         there
786         test
787       '';
788       multilinestring' = ''
789         hello
790         there
791         test'';
792     };
793     expected = rec {
794       list = ''
795         [
796           3
797           4
798           [
799             false
800           ]
801         ]'';
802       attrs = ''
803         {
804           bar = {
805             foo = "baz";
806           };
807           foo = null;
808         }'';
809       newlinestring = "''\n  \n''";
810       multilinestring = ''
811         '''
812           hello
813           there
814           test
815         ''''';
816       multilinestring' = ''
817         '''
818           hello
819           there
820           test''''';
822     };
823   };
825   testToPrettyAllowPrettyValues = {
826     expr = generators.toPretty { allowPrettyValues = true; }
827              { __pretty = v: "«" + v + "»"; val = "foo"; };
828     expected  = "«foo»";
829   };
832 # CLI
834   testToGNUCommandLine = {
835     expr = cli.toGNUCommandLine {} {
836       data = builtins.toJSON { id = 0; };
837       X = "PUT";
838       retry = 3;
839       retry-delay = null;
840       url = [ "https://example.com/foo" "https://example.com/bar" ];
841       silent = false;
842       verbose = true;
843     };
845     expected = [
846       "-X" "PUT"
847       "--data" "{\"id\":0}"
848       "--retry" "3"
849       "--url" "https://example.com/foo"
850       "--url" "https://example.com/bar"
851       "--verbose"
852     ];
853   };
855   testToGNUCommandLineShell = {
856     expr = cli.toGNUCommandLineShell {} {
857       data = builtins.toJSON { id = 0; };
858       X = "PUT";
859       retry = 3;
860       retry-delay = null;
861       url = [ "https://example.com/foo" "https://example.com/bar" ];
862       silent = false;
863       verbose = true;
864     };
866     expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
867   };
869   testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
870     name = "..foo";
871     expected = "foo";
872   };
874   testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
875     name = "fö";
876     expected = "f-";
877   };
879   testSanitizeDerivationNameAscii = testSanitizeDerivationName {
880     name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
881     expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
882   };
884   testSanitizeDerivationNameTooLong = testSanitizeDerivationName {
885     name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
886     expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
887   };
889   testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName {
890     name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&";
891     expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-";
892   };
894   testSanitizeDerivationNameEmpty = testSanitizeDerivationName {
895     name = "";
896     expected = "unknown";
897   };
899   testFreeformOptions = {
900     expr =
901       let
902         submodule = { lib, ... }: {
903           freeformType = lib.types.attrsOf (lib.types.submodule {
904             options.bar = lib.mkOption {};
905           });
906           options.bar = lib.mkOption {};
907         };
909         module = { lib, ... }: {
910           options.foo = lib.mkOption {
911             type = lib.types.submodule submodule;
912           };
913         };
915         options = (evalModules {
916           modules = [ module ];
917         }).options;
919         locs = filter (o: ! o.internal) (optionAttrSetToDocList options);
920       in map (o: o.loc) locs;
921     expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
922   };
924   testCartesianProductOfEmptySet = {
925     expr = cartesianProductOfSets {};
926     expected = [ {} ];
927   };
929   testCartesianProductOfOneSet = {
930     expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
931     expected = [ { a = 1; } { a = 2; } { a = 3; } ];
932   };
934   testCartesianProductOfTwoSets = {
935     expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
936     expected = [
937       { a = 1; b = 10; }
938       { a = 1; b = 20; }
939     ];
940   };
942   testCartesianProductOfTwoSetsWithOneEmpty = {
943     expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
944     expected = [ ];
945   };
947   testCartesianProductOfThreeSets = {
948     expr = cartesianProductOfSets {
949       a = [   1   2   3 ];
950       b = [  10  20  30 ];
951       c = [ 100 200 300 ];
952     };
953     expected = [
954       { a = 1; b = 10; c = 100; }
955       { a = 1; b = 10; c = 200; }
956       { a = 1; b = 10; c = 300; }
958       { a = 1; b = 20; c = 100; }
959       { a = 1; b = 20; c = 200; }
960       { a = 1; b = 20; c = 300; }
962       { a = 1; b = 30; c = 100; }
963       { a = 1; b = 30; c = 200; }
964       { a = 1; b = 30; c = 300; }
966       { a = 2; b = 10; c = 100; }
967       { a = 2; b = 10; c = 200; }
968       { a = 2; b = 10; c = 300; }
970       { a = 2; b = 20; c = 100; }
971       { a = 2; b = 20; c = 200; }
972       { a = 2; b = 20; c = 300; }
974       { a = 2; b = 30; c = 100; }
975       { a = 2; b = 30; c = 200; }
976       { a = 2; b = 30; c = 300; }
978       { a = 3; b = 10; c = 100; }
979       { a = 3; b = 10; c = 200; }
980       { a = 3; b = 10; c = 300; }
982       { a = 3; b = 20; c = 100; }
983       { a = 3; b = 20; c = 200; }
984       { a = 3; b = 20; c = 300; }
986       { a = 3; b = 30; c = 100; }
987       { a = 3; b = 30; c = 200; }
988       { a = 3; b = 30; c = 300; }
989     ];
990   };
992   # The example from the showAttrPath documentation
993   testShowAttrPathExample = {
994     expr = showAttrPath [ "foo" "10" "bar" ];
995     expected = "foo.\"10\".bar";
996   };
998   testShowAttrPathEmpty = {
999     expr = showAttrPath [];
1000     expected = "<root attribute path>";
1001   };
1003   testShowAttrPathVarious = {
1004     expr = showAttrPath [
1005       "."
1006       "foo"
1007       "2"
1008       "a2-b"
1009       "_bc'de"
1010     ];
1011     expected = ''".".foo."2".a2-b._bc'de'';
1012   };
1014   testGroupBy = {
1015     expr = groupBy (n: toString (mod n 5)) (range 0 16);
1016     expected = {
1017       "0" = [ 0 5 10 15 ];
1018       "1" = [ 1 6 11 16 ];
1019       "2" = [ 2 7 12 ];
1020       "3" = [ 3 8 13 ];
1021       "4" = [ 4 9 14 ];
1022     };
1023   };
1025   testGroupBy' = {
1026     expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ];
1027     expected = { false = 3; true = 12; };
1028   };
1030   # The example from the updateManyAttrsByPath documentation
1031   testUpdateManyAttrsByPathExample = {
1032     expr = updateManyAttrsByPath [
1033       {
1034         path = [ "a" "b" ];
1035         update = old: { d = old.c; };
1036       }
1037       {
1038         path = [ "a" "b" "c" ];
1039         update = old: old + 1;
1040       }
1041       {
1042         path = [ "x" "y" ];
1043         update = old: "xy";
1044       }
1045     ] { a.b.c = 0; };
1046     expected = { a = { b = { d = 1; }; }; x = { y = "xy"; }; };
1047   };
1049   # If there are no updates, the value is passed through
1050   testUpdateManyAttrsByPathNone = {
1051     expr = updateManyAttrsByPath [] "something";
1052     expected = "something";
1053   };
1055   # A single update to the root path is just like applying the function directly
1056   testUpdateManyAttrsByPathSingleIncrement = {
1057     expr = updateManyAttrsByPath [
1058       {
1059         path = [ ];
1060         update = old: old + 1;
1061       }
1062     ] 0;
1063     expected = 1;
1064   };
1066   # Multiple updates can be applied are done in order
1067   testUpdateManyAttrsByPathMultipleIncrements = {
1068     expr = updateManyAttrsByPath [
1069       {
1070         path = [ ];
1071         update = old: old + "a";
1072       }
1073       {
1074         path = [ ];
1075         update = old: old + "b";
1076       }
1077       {
1078         path = [ ];
1079         update = old: old + "c";
1080       }
1081     ] "";
1082     expected = "abc";
1083   };
1085   # If an update doesn't use the value, all previous updates are not evaluated
1086   testUpdateManyAttrsByPathLazy = {
1087     expr = updateManyAttrsByPath [
1088       {
1089         path = [ ];
1090         update = old: old + throw "nope";
1091       }
1092       {
1093         path = [ ];
1094         update = old: "untainted";
1095       }
1096     ] (throw "start");
1097     expected = "untainted";
1098   };
1100   # Deeply nested attributes can be updated without affecting others
1101   testUpdateManyAttrsByPathDeep = {
1102     expr = updateManyAttrsByPath [
1103       {
1104         path = [ "a" "b" "c" ];
1105         update = old: old + 1;
1106       }
1107     ] {
1108       a.b.c = 0;
1110       a.b.z = 0;
1111       a.y.z = 0;
1112       x.y.z = 0;
1113     };
1114     expected = {
1115       a.b.c = 1;
1117       a.b.z = 0;
1118       a.y.z = 0;
1119       x.y.z = 0;
1120     };
1121   };
1123   # Nested attributes are updated first
1124   testUpdateManyAttrsByPathNestedBeforehand = {
1125     expr = updateManyAttrsByPath [
1126       {
1127         path = [ "a" ];
1128         update = old: old // { x = old.b; };
1129       }
1130       {
1131         path = [ "a" "b" ];
1132         update = old: old + 1;
1133       }
1134     ] {
1135       a.b = 0;
1136     };
1137     expected = {
1138       a.b = 1;
1139       a.x = 1;
1140     };
1141   };
1143   ## Levenshtein distance functions and co.
1144   testCommonPrefixLengthEmpty = {
1145     expr = strings.commonPrefixLength "" "hello";
1146     expected = 0;
1147   };
1149   testCommonPrefixLengthSame = {
1150     expr = strings.commonPrefixLength "hello" "hello";
1151     expected = 5;
1152   };
1154   testCommonPrefixLengthDiffering = {
1155     expr = strings.commonPrefixLength "hello" "hey";
1156     expected = 2;
1157   };
1159   testCommonSuffixLengthEmpty = {
1160     expr = strings.commonSuffixLength "" "hello";
1161     expected = 0;
1162   };
1164   testCommonSuffixLengthSame = {
1165     expr = strings.commonSuffixLength "hello" "hello";
1166     expected = 5;
1167   };
1169   testCommonSuffixLengthDiffering = {
1170     expr = strings.commonSuffixLength "test" "rest";
1171     expected = 3;
1172   };
1174   testLevenshteinEmpty = {
1175     expr = strings.levenshtein "" "";
1176     expected = 0;
1177   };
1179   testLevenshteinOnlyAdd = {
1180     expr = strings.levenshtein "" "hello there";
1181     expected = 11;
1182   };
1184   testLevenshteinOnlyRemove = {
1185     expr = strings.levenshtein "hello there" "";
1186     expected = 11;
1187   };
1189   testLevenshteinOnlyTransform = {
1190     expr = strings.levenshtein "abcdef" "ghijkl";
1191     expected = 6;
1192   };
1194   testLevenshteinMixed = {
1195     expr = strings.levenshtein "kitchen" "sitting";
1196     expected = 5;
1197   };
1199   testLevenshteinAtMostZeroFalse = {
1200     expr = strings.levenshteinAtMost 0 "foo" "boo";
1201     expected = false;
1202   };
1204   testLevenshteinAtMostZeroTrue = {
1205     expr = strings.levenshteinAtMost 0 "foo" "foo";
1206     expected = true;
1207   };
1209   testLevenshteinAtMostOneFalse = {
1210     expr = strings.levenshteinAtMost 1 "car" "ct";
1211     expected = false;
1212   };
1214   testLevenshteinAtMostOneTrue = {
1215     expr = strings.levenshteinAtMost 1 "car" "cr";
1216     expected = true;
1217   };
1219   # We test levenshteinAtMost 2 particularly well because it uses a complicated
1220   # implementation
1221   testLevenshteinAtMostTwoIsEmpty = {
1222     expr = strings.levenshteinAtMost 2 "" "";
1223     expected = true;
1224   };
1226   testLevenshteinAtMostTwoIsZero = {
1227     expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
1228     expected = true;
1229   };
1231   testLevenshteinAtMostTwoIsOne = {
1232     expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
1233     expected = true;
1234   };
1236   testLevenshteinAtMostTwoDiff0False = {
1237     expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
1238     expected = false;
1239   };
1241   testLevenshteinAtMostTwoDiff0Outer = {
1242     expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
1243     expected = true;
1244   };
1246   testLevenshteinAtMostTwoDiff0DelLeft = {
1247     expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
1248     expected = true;
1249   };
1251   testLevenshteinAtMostTwoDiff0DelRight = {
1252     expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
1253     expected = true;
1254   };
1256   testLevenshteinAtMostTwoDiff1False = {
1257     expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
1258     expected = false;
1259   };
1261   testLevenshteinAtMostTwoDiff1DelLeft = {
1262     expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
1263     expected = true;
1264   };
1266   testLevenshteinAtMostTwoDiff1DelRight = {
1267     expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
1268     expected = true;
1269   };
1271   testLevenshteinAtMostTwoDiff2False = {
1272     expr = strings.levenshteinAtMost 2 "hello" "hxo";
1273     expected = false;
1274   };
1276   testLevenshteinAtMostTwoDiff2True = {
1277     expr = strings.levenshteinAtMost 2 "hello" "heo";
1278     expected = true;
1279   };
1281   testLevenshteinAtMostTwoDiff3 = {
1282     expr = strings.levenshteinAtMost 2 "hello" "ho";
1283     expected = false;
1284   };
1286   testLevenshteinAtMostThreeFalse = {
1287     expr = strings.levenshteinAtMost 3 "hello" "Holla!";
1288     expected = false;
1289   };
1291   testLevenshteinAtMostThreeTrue = {
1292     expr = strings.levenshteinAtMost 3 "hello" "Holla";
1293     expected = true;
1294   };
1296   # lazyDerivation
1298   testLazyDerivationIsLazyInDerivationForAttrNames = {
1299     expr = attrNames (lazyDerivation {
1300       derivation = throw "not lazy enough";
1301     });
1302     # It's ok to add attribute names here when lazyDerivation is improved
1303     # in accordance with its inline comments.
1304     expected = [ "drvPath" "meta" "name" "out" "outPath" "outputName" "outputs" "system" "type" ];
1305   };
1307   testLazyDerivationIsLazyInDerivationForPassthruAttr = {
1308     expr = (lazyDerivation {
1309       derivation = throw "not lazy enough";
1310       passthru.tests = "whatever is in tests";
1311     }).tests;
1312     expected = "whatever is in tests";
1313   };
1315   testLazyDerivationIsLazyInDerivationForPassthruAttr2 = {
1316     # passthru.tests is not a special case. It works for any attr.
1317     expr = (lazyDerivation {
1318       derivation = throw "not lazy enough";
1319       passthru.foo = "whatever is in foo";
1320     }).foo;
1321     expected = "whatever is in foo";
1322   };
1324   testLazyDerivationIsLazyInDerivationForMeta = {
1325     expr = (lazyDerivation {
1326       derivation = throw "not lazy enough";
1327       meta = "whatever is in meta";
1328     }).meta;
1329     expected = "whatever is in meta";
1330   };
1332   testLazyDerivationReturnsDerivationAttrs = let
1333     derivation = {
1334       type = "derivation";
1335       outputs = ["out"];
1336       out = "test out";
1337       outPath = "test outPath";
1338       outputName = "out";
1339       drvPath = "test drvPath";
1340       name = "test name";
1341       system = "test system";
1342       meta = "test meta";
1343     };
1344   in {
1345     expr = lazyDerivation { inherit derivation; };
1346     expected = derivation;
1347   };
1349   testTypeDescriptionInt = {
1350     expr = (with types; int).description;
1351     expected = "signed integer";
1352   };
1353   testTypeDescriptionListOfInt = {
1354     expr = (with types; listOf int).description;
1355     expected = "list of signed integer";
1356   };
1357   testTypeDescriptionListOfListOfInt = {
1358     expr = (with types; listOf (listOf int)).description;
1359     expected = "list of list of signed integer";
1360   };
1361   testTypeDescriptionListOfEitherStrOrBool = {
1362     expr = (with types; listOf (either str bool)).description;
1363     expected = "list of (string or boolean)";
1364   };
1365   testTypeDescriptionEitherListOfStrOrBool = {
1366     expr = (with types; either (listOf bool) str).description;
1367     expected = "(list of boolean) or string";
1368   };
1369   testTypeDescriptionEitherStrOrListOfBool = {
1370     expr = (with types; either str (listOf bool)).description;
1371     expected = "string or list of boolean";
1372   };
1373   testTypeDescriptionOneOfListOfStrOrBool = {
1374     expr = (with types; oneOf [ (listOf bool) str ]).description;
1375     expected = "(list of boolean) or string";
1376   };
1377   testTypeDescriptionOneOfListOfStrOrBoolOrNumber = {
1378     expr = (with types; oneOf [ (listOf bool) str number ]).description;
1379     expected = "(list of boolean) or string or signed integer or floating point number";
1380   };
1381   testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = {
1382     expr = (with types; either (listOf bool) (either str number)).description;
1383     expected = "(list of boolean) or string or signed integer or floating point number";
1384   };
1385   testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = {
1386     expr = (with types; either (either (listOf bool) str) number).description;
1387     expected = "(list of boolean) or string or signed integer or floating point number";
1388   };
1389   testTypeDescriptionEitherNullOrBoolOrString = {
1390     expr = (with types; either (nullOr bool) str).description;
1391     expected = "null or boolean or string";
1392   };
1393   testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = {
1394     expr = (with types; either (listOf (either bool str)) int).description;
1395     expected = "(list of (boolean or string)) or signed integer";
1396   };
1397   testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = {
1398     expr = (with types; either int (listOf (either bool str))).description;
1399     expected = "signed integer or list of (boolean or string)";
1400   };