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
4with import ../default.nix;
5
6let
7
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 };
20
21in
22
23runTests {
24
25# TRIVIAL
26
27 testId = {
28 expr = id 1;
29 expected = 1;
30 };
31
32 testConst = {
33 expr = const 2 3;
34 expected = 2;
35 };
36
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 };
44
45 testPipeEmpty = {
46 expr = pipe 2 [];
47 expected = 2;
48 };
49
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 };
61
62 /*
63 testOr = {
64 expr = or true false;
65 expected = true;
66 };
67 */
68
69 testAnd = {
70 expr = and true false;
71 expected = false;
72 };
73
74 testFix = {
75 expr = fix (x: {a = if x ? a then "a" else "b";});
76 expected = {a = "a";};
77 };
78
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 };
88
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 };
96
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 };
108
109 testBitAnd = {
110 expr = (bitAnd 3 10);
111 expected = 2;
112 };
113
114 testBitOr = {
115 expr = (bitOr 3 10);
116 expected = 11;
117 };
118
119 testBitXor = {
120 expr = (bitXor 3 10);
121 expected = 9;
122 };
123
124 testToHexString = {
125 expr = toHexString 250;
126 expected = "FA";
127 };
128
129 testToBaseDigits = {
130 expr = toBaseDigits 2 6;
131 expected = [ 1 1 0 ];
132 };
133
134 testFunctionArgsFunctor = {
135 expr = functionArgs { __functor = self: { a, b }: null; };
136 expected = { a = false; b = false; };
137 };
138
139 testFunctionArgsSetFunctionArgs = {
140 expr = functionArgs (setFunctionArgs (args: args.x) { x = false; });
141 expected = { x = false; };
142 };
143
144# STRINGS
145
146 testConcatMapStrings = {
147 expr = concatMapStrings (x: x + ";") ["a" "b" "c"];
148 expected = "a;b;c;";
149 };
150
151 testConcatStringsSep = {
152 expr = concatStringsSep "," ["a" "b" "c"];
153 expected = "a,b,c";
154 };
155
156 testSplitStringsSimple = {
157 expr = strings.splitString "." "a.b.c.d";
158 expected = [ "a" "b" "c" "d" ];
159 };
160
161 testSplitStringsEmpty = {
162 expr = strings.splitString "." "a..b";
163 expected = [ "a" "" "b" ];
164 };
165
166 testSplitStringsOne = {
167 expr = strings.splitString ":" "a.b";
168 expected = [ "a.b" ];
169 };
170
171 testSplitStringsNone = {
172 expr = strings.splitString "." "";
173 expected = [ "" ];
174 };
175
176 testSplitStringsFirstEmpty = {
177 expr = strings.splitString "/" "/a/b/c";
178 expected = [ "" "a" "b" "c" ];
179 };
180
181 testSplitStringsLastEmpty = {
182 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
183 expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ];
184 };
185
186 testSplitStringsRegex = {
187 expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B";
188 expected = [ "A" "B" ];
189 };
190
191 testSplitStringsDerivation = {
192 expr = take 3 (strings.splitString "/" (derivation {
193 name = "name";
194 builder = "builder";
195 system = "system";
196 }));
197 expected = ["" "nix" "store"];
198 };
199
200 testSplitVersionSingle = {
201 expr = versions.splitVersion "1";
202 expected = [ "1" ];
203 };
204
205 testSplitVersionDouble = {
206 expr = versions.splitVersion "1.2";
207 expected = [ "1" "2" ];
208 };
209
210 testSplitVersionTriple = {
211 expr = versions.splitVersion "1.2.3";
212 expected = [ "1" "2" "3" ];
213 };
214
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 };
247
248 testEscapeXML = {
249 expr = escapeXML ''"test" 'test' < & >'';
250 expected = ""test" 'test' < & >";
251 };
252
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 };
284
285 testHasInfixFalse = {
286 expr = hasInfix "c" "abde";
287 expected = false;
288 };
289
290 testHasInfixTrue = {
291 expr = hasInfix "c" "abcde";
292 expected = true;
293 };
294
295 testHasInfixDerivation = {
296 expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello;
297 expected = true;
298 };
299
300 testHasInfixPath = {
301 expr = hasInfix "tests" ./.;
302 expected = true;
303 };
304
305 testHasInfixPathStoreDir = {
306 expr = hasInfix builtins.storeDir ./.;
307 expected = true;
308 };
309
310 testHasInfixToString = {
311 expr = hasInfix "a" { __toString = _: "a"; };
312 expected = true;
313 };
314
315 testNormalizePath = {
316 expr = strings.normalizePath "//a/b//c////d/";
317 expected = "/a/b/c/d/";
318 };
319
320 testCharToInt = {
321 expr = strings.charToInt "A";
322 expected = 65;
323 };
324
325 testEscapeC = {
326 expr = strings.escapeC [ " " ] "Hello World";
327 expected = "Hello\\x20World";
328 };
329
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 ];
343
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 ];
359
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 ];
387
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 ];
400
401# LISTS
402
403 testFilter = {
404 expr = filter (x: x != "a") ["a" "b" "c" "a"];
405 expected = ["b" "c"];
406 };
407
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 };
436
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 ];
444
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 };
452
453 testSort = {
454 expr = sort builtins.lessThan [ 40 2 30 42 ];
455 expected = [2 30 40 42];
456 };
457
458 testToIntShouldConvertStringToInt = {
459 expr = toInt "27";
460 expected = 27;
461 };
462
463 testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
464 expr = builtins.tryEval (toInt "\"foo\"");
465 expected = { success = false; value = false; };
466 };
467
468 testHasAttrByPathTrue = {
469 expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; };
470 expected = true;
471 };
472
473 testHasAttrByPathFalse = {
474 expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; };
475 expected = false;
476 };
477
478
479# ATTRSETS
480
481 testConcatMapAttrs = {
482 expr = concatMapAttrs
483 (name: value: {
484 ${name} = value;
485 ${name + value} = value;
486 })
487 {
488 foo = "bar";
489 foobar = "baz";
490 };
491 expected = {
492 foo = "bar";
493 foobar = "baz";
494 foobarbaz = "baz";
495 };
496 };
497
498 # code from the example
499 testRecursiveUpdateUntil = {
500 expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) {
501 # first attribute set
502 foo.bar = 1;
503 foo.baz = 2;
504 bar = 3;
505 } {
506 #second attribute set
507 foo.bar = 1;
508 foo.quz = 2;
509 baz = 4;
510 };
511 expected = {
512 foo.bar = 1; # 'foo.*' from the second set
513 foo.quz = 2; #
514 bar = 3; # 'bar' from the first set
515 baz = 4; # 'baz' from the second set
516 };
517 };
518
519 testOverrideExistingEmpty = {
520 expr = overrideExisting {} { a = 1; };
521 expected = {};
522 };
523
524 testOverrideExistingDisjoint = {
525 expr = overrideExisting { b = 2; } { a = 1; };
526 expected = { b = 2; };
527 };
528
529 testOverrideExistingOverride = {
530 expr = overrideExisting { a = 3; b = 2; } { a = 1; };
531 expected = { a = 1; b = 2; };
532 };
533
534# GENERATORS
535# these tests assume attributes are converted to lists
536# in alphabetical order
537
538 testMkKeyValueDefault = {
539 expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar";
540 expected = ''f\:oo:bar'';
541 };
542
543 testMkValueString = {
544 expr = let
545 vals = {
546 int = 42;
547 string = ''fo"o'';
548 bool = true;
549 bool2 = false;
550 null = null;
551 # float = 42.23; # floats are strange
552 };
553 in mapAttrs
554 (const (generators.mkValueStringDefault {}))
555 vals;
556 expected = {
557 int = "42";
558 string = ''fo"o'';
559 bool = "true";
560 bool2 = "false";
561 null = "null";
562 # float = "42.23" true false [ "bar" ] ]'';
563 };
564 };
565
566 testToKeyValue = {
567 expr = generators.toKeyValue {} {
568 key = "value";
569 "other=key" = "baz";
570 };
571 expected = ''
572 key=value
573 other\=key=baz
574 '';
575 };
576
577 testToINIEmpty = {
578 expr = generators.toINI {} {};
579 expected = "";
580 };
581
582 testToINIEmptySection = {
583 expr = generators.toINI {} { foo = {}; bar = {}; };
584 expected = ''
585 [bar]
586
587 [foo]
588 '';
589 };
590
591 testToINIDuplicateKeys = {
592 expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; };
593 expected = ''
594 [baz]
595 qux=1
596 qux=false
597
598 [foo]
599 bar=true
600 '';
601 };
602
603 testToINIDefaultEscapes = {
604 expr = generators.toINI {} {
605 "no [ and ] allowed unescaped" = {
606 "and also no = in keys" = 42;
607 };
608 };
609 expected = ''
610 [no \[ and \] allowed unescaped]
611 and also no \= in keys=42
612 '';
613 };
614
615 testToINIDefaultFull = {
616 expr = generators.toINI {} {
617 "section 1" = {
618 attribute1 = 5;
619 x = "Me-se JarJar Binx";
620 # booleans are converted verbatim by default
621 boolean = false;
622 };
623 "foo[]" = {
624 "he\\h=he" = "this is okay";
625 };
626 };
627 expected = ''
628 [foo\[\]]
629 he\h\=he=this is okay
630
631 [section 1]
632 attribute1=5
633 boolean=false
634 x=Me-se JarJar Binx
635 '';
636 };
637
638 testToINIWithGlobalSectionEmpty = {
639 expr = generators.toINIWithGlobalSection {} {
640 globalSection = {
641 };
642 sections = {
643 };
644 };
645 expected = ''
646 '';
647 };
648
649 testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
650 let
651 sections = {
652 "section 1" = {
653 attribute1 = 5;
654 x = "Me-se JarJar Binx";
655 };
656 "foo" = {
657 "he\\h=he" = "this is okay";
658 };
659 };
660 in {
661 expr =
662 generators.toINIWithGlobalSection {} {
663 globalSection = {};
664 sections = sections;
665 };
666 expected = generators.toINI {} sections;
667 };
668
669 testToINIWithGlobalSectionFull = {
670 expr = generators.toINIWithGlobalSection {} {
671 globalSection = {
672 foo = "bar";
673 test = false;
674 };
675 sections = {
676 "section 1" = {
677 attribute1 = 5;
678 x = "Me-se JarJar Binx";
679 };
680 "foo" = {
681 "he\\h=he" = "this is okay";
682 };
683 };
684 };
685 expected = ''
686 foo=bar
687 test=false
688
689 [foo]
690 he\h\=he=this is okay
691
692 [section 1]
693 attribute1=5
694 x=Me-se JarJar Binx
695 '';
696 };
697
698 /* right now only invocation check */
699 testToJSONSimple =
700 let val = {
701 foobar = [ "baz" 1 2 3 ];
702 };
703 in {
704 expr = generators.toJSON {} val;
705 # trivial implementation
706 expected = builtins.toJSON val;
707 };
708
709 /* right now only invocation check */
710 testToYAMLSimple =
711 let val = {
712 list = [ { one = 1; } { two = 2; } ];
713 all = 42;
714 };
715 in {
716 expr = generators.toYAML {} val;
717 # trivial implementation
718 expected = builtins.toJSON val;
719 };
720
721 testToPretty =
722 let
723 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; };
724 in {
725 expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
726 int = 42;
727 float = 0.1337;
728 bool = true;
729 emptystring = "";
730 string = ''fno"rd'';
731 newlinestring = "\n";
732 path = /. + "/foo";
733 null_ = null;
734 function = x: x;
735 functionArgs = { arg ? 4, foo }: arg;
736 list = [ 3 4 function [ false ] ];
737 emptylist = [];
738 attrs = { foo = null; "foo bar" = "baz"; };
739 emptyattrs = {};
740 drv = deriv;
741 };
742 expected = rec {
743 int = "42";
744 float = "~0.133700";
745 bool = "true";
746 emptystring = ''""'';
747 string = ''"fno\"rd"'';
748 newlinestring = "\"\\n\"";
749 path = "/foo";
750 null_ = "null";
751 function = "<function>";
752 functionArgs = "<function, args: {arg?, foo}>";
753 list = "[ 3 4 ${function} [ false ] ]";
754 emptylist = "[ ]";
755 attrs = "{ foo = null; \"foo bar\" = \"baz\"; }";
756 emptyattrs = "{ }";
757 drv = "<derivation ${deriv.drvPath}>";
758 };
759 };
760
761 testToPrettyLimit =
762 let
763 a.b = 1;
764 a.c = a;
765 in {
766 expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a);
767 expected = "{\n b = 1;\n c = {\n b = \"<unevaluated>\";\n c = {\n b = \"<unevaluated>\";\n c = \"<unevaluated>\";\n };\n };\n}";
768 };
769
770 testToPrettyLimitThrow =
771 let
772 a.b = 1;
773 a.c = a;
774 in {
775 expr = (builtins.tryEval
776 (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success;
777 expected = false;
778 };
779
780 testWithRecursionDealsWithFunctors =
781 let
782 functor = {
783 __functor = self: { a, b, }: null;
784 };
785 a = {
786 value = "1234";
787 b = functor;
788 c.d = functor;
789 };
790 in {
791 expr = generators.toPretty { } (generators.withRecursion { depthLimit = 1; throwOnDepthLimit = false; } a);
792 expected = "{\n b = <function, args: {a, b}>;\n c = {\n d = \"<unevaluated>\";\n };\n value = \"<unevaluated>\";\n}";
793 };
794
795 testToPrettyMultiline = {
796 expr = mapAttrs (const (generators.toPretty { })) rec {
797 list = [ 3 4 [ false ] ];
798 attrs = { foo = null; bar.foo = "baz"; };
799 newlinestring = "\n";
800 multilinestring = ''
801 hello
802 there
803 test
804 '';
805 multilinestring' = ''
806 hello
807 there
808 test'';
809 };
810 expected = rec {
811 list = ''
812 [
813 3
814 4
815 [
816 false
817 ]
818 ]'';
819 attrs = ''
820 {
821 bar = {
822 foo = "baz";
823 };
824 foo = null;
825 }'';
826 newlinestring = "''\n \n''";
827 multilinestring = ''
828 '''
829 hello
830 there
831 test
832 ''''';
833 multilinestring' = ''
834 '''
835 hello
836 there
837 test''''';
838
839 };
840 };
841
842 testToPrettyAllowPrettyValues = {
843 expr = generators.toPretty { allowPrettyValues = true; }
844 { __pretty = v: "«" + v + "»"; val = "foo"; };
845 expected = "«foo»";
846 };
847
848
849# CLI
850
851 testToGNUCommandLine = {
852 expr = cli.toGNUCommandLine {} {
853 data = builtins.toJSON { id = 0; };
854 X = "PUT";
855 retry = 3;
856 retry-delay = null;
857 url = [ "https://example.com/foo" "https://example.com/bar" ];
858 silent = false;
859 verbose = true;
860 };
861
862 expected = [
863 "-X" "PUT"
864 "--data" "{\"id\":0}"
865 "--retry" "3"
866 "--url" "https://example.com/foo"
867 "--url" "https://example.com/bar"
868 "--verbose"
869 ];
870 };
871
872 testToGNUCommandLineShell = {
873 expr = cli.toGNUCommandLineShell {} {
874 data = builtins.toJSON { id = 0; };
875 X = "PUT";
876 retry = 3;
877 retry-delay = null;
878 url = [ "https://example.com/foo" "https://example.com/bar" ];
879 silent = false;
880 verbose = true;
881 };
882
883 expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'";
884 };
885
886 testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
887 name = "..foo";
888 expected = "foo";
889 };
890
891 testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
892 name = "fö";
893 expected = "f-";
894 };
895
896 testSanitizeDerivationNameAscii = testSanitizeDerivationName {
897 name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
898 expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
899 };
900
901 testSanitizeDerivationNameTooLong = testSanitizeDerivationName {
902 name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
903 expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
904 };
905
906 testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName {
907 name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&";
908 expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-";
909 };
910
911 testSanitizeDerivationNameEmpty = testSanitizeDerivationName {
912 name = "";
913 expected = "unknown";
914 };
915
916 testFreeformOptions = {
917 expr =
918 let
919 submodule = { lib, ... }: {
920 freeformType = lib.types.attrsOf (lib.types.submodule {
921 options.bar = lib.mkOption {};
922 });
923 options.bar = lib.mkOption {};
924 };
925
926 module = { lib, ... }: {
927 options.foo = lib.mkOption {
928 type = lib.types.submodule submodule;
929 };
930 };
931
932 options = (evalModules {
933 modules = [ module ];
934 }).options;
935
936 locs = filter (o: ! o.internal) (optionAttrSetToDocList options);
937 in map (o: o.loc) locs;
938 expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ];
939 };
940
941 testCartesianProductOfEmptySet = {
942 expr = cartesianProductOfSets {};
943 expected = [ {} ];
944 };
945
946 testCartesianProductOfOneSet = {
947 expr = cartesianProductOfSets { a = [ 1 2 3 ]; };
948 expected = [ { a = 1; } { a = 2; } { a = 3; } ];
949 };
950
951 testCartesianProductOfTwoSets = {
952 expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; };
953 expected = [
954 { a = 1; b = 10; }
955 { a = 1; b = 20; }
956 ];
957 };
958
959 testCartesianProductOfTwoSetsWithOneEmpty = {
960 expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; };
961 expected = [ ];
962 };
963
964 testCartesianProductOfThreeSets = {
965 expr = cartesianProductOfSets {
966 a = [ 1 2 3 ];
967 b = [ 10 20 30 ];
968 c = [ 100 200 300 ];
969 };
970 expected = [
971 { a = 1; b = 10; c = 100; }
972 { a = 1; b = 10; c = 200; }
973 { a = 1; b = 10; c = 300; }
974
975 { a = 1; b = 20; c = 100; }
976 { a = 1; b = 20; c = 200; }
977 { a = 1; b = 20; c = 300; }
978
979 { a = 1; b = 30; c = 100; }
980 { a = 1; b = 30; c = 200; }
981 { a = 1; b = 30; c = 300; }
982
983 { a = 2; b = 10; c = 100; }
984 { a = 2; b = 10; c = 200; }
985 { a = 2; b = 10; c = 300; }
986
987 { a = 2; b = 20; c = 100; }
988 { a = 2; b = 20; c = 200; }
989 { a = 2; b = 20; c = 300; }
990
991 { a = 2; b = 30; c = 100; }
992 { a = 2; b = 30; c = 200; }
993 { a = 2; b = 30; c = 300; }
994
995 { a = 3; b = 10; c = 100; }
996 { a = 3; b = 10; c = 200; }
997 { a = 3; b = 10; c = 300; }
998
999 { a = 3; b = 20; c = 100; }
1000 { a = 3; b = 20; c = 200; }
1001 { a = 3; b = 20; c = 300; }
1002
1003 { a = 3; b = 30; c = 100; }
1004 { a = 3; b = 30; c = 200; }
1005 { a = 3; b = 30; c = 300; }
1006 ];
1007 };
1008
1009 # The example from the showAttrPath documentation
1010 testShowAttrPathExample = {
1011 expr = showAttrPath [ "foo" "10" "bar" ];
1012 expected = "foo.\"10\".bar";
1013 };
1014
1015 testShowAttrPathEmpty = {
1016 expr = showAttrPath [];
1017 expected = "<root attribute path>";
1018 };
1019
1020 testShowAttrPathVarious = {
1021 expr = showAttrPath [
1022 "."
1023 "foo"
1024 "2"
1025 "a2-b"
1026 "_bc'de"
1027 ];
1028 expected = ''".".foo."2".a2-b._bc'de'';
1029 };
1030
1031 testGroupBy = {
1032 expr = groupBy (n: toString (mod n 5)) (range 0 16);
1033 expected = {
1034 "0" = [ 0 5 10 15 ];
1035 "1" = [ 1 6 11 16 ];
1036 "2" = [ 2 7 12 ];
1037 "3" = [ 3 8 13 ];
1038 "4" = [ 4 9 14 ];
1039 };
1040 };
1041
1042 testGroupBy' = {
1043 expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ];
1044 expected = { false = 3; true = 12; };
1045 };
1046
1047 # The example from the updateManyAttrsByPath documentation
1048 testUpdateManyAttrsByPathExample = {
1049 expr = updateManyAttrsByPath [
1050 {
1051 path = [ "a" "b" ];
1052 update = old: { d = old.c; };
1053 }
1054 {
1055 path = [ "a" "b" "c" ];
1056 update = old: old + 1;
1057 }
1058 {
1059 path = [ "x" "y" ];
1060 update = old: "xy";
1061 }
1062 ] { a.b.c = 0; };
1063 expected = { a = { b = { d = 1; }; }; x = { y = "xy"; }; };
1064 };
1065
1066 # If there are no updates, the value is passed through
1067 testUpdateManyAttrsByPathNone = {
1068 expr = updateManyAttrsByPath [] "something";
1069 expected = "something";
1070 };
1071
1072 # A single update to the root path is just like applying the function directly
1073 testUpdateManyAttrsByPathSingleIncrement = {
1074 expr = updateManyAttrsByPath [
1075 {
1076 path = [ ];
1077 update = old: old + 1;
1078 }
1079 ] 0;
1080 expected = 1;
1081 };
1082
1083 # Multiple updates can be applied are done in order
1084 testUpdateManyAttrsByPathMultipleIncrements = {
1085 expr = updateManyAttrsByPath [
1086 {
1087 path = [ ];
1088 update = old: old + "a";
1089 }
1090 {
1091 path = [ ];
1092 update = old: old + "b";
1093 }
1094 {
1095 path = [ ];
1096 update = old: old + "c";
1097 }
1098 ] "";
1099 expected = "abc";
1100 };
1101
1102 # If an update doesn't use the value, all previous updates are not evaluated
1103 testUpdateManyAttrsByPathLazy = {
1104 expr = updateManyAttrsByPath [
1105 {
1106 path = [ ];
1107 update = old: old + throw "nope";
1108 }
1109 {
1110 path = [ ];
1111 update = old: "untainted";
1112 }
1113 ] (throw "start");
1114 expected = "untainted";
1115 };
1116
1117 # Deeply nested attributes can be updated without affecting others
1118 testUpdateManyAttrsByPathDeep = {
1119 expr = updateManyAttrsByPath [
1120 {
1121 path = [ "a" "b" "c" ];
1122 update = old: old + 1;
1123 }
1124 ] {
1125 a.b.c = 0;
1126
1127 a.b.z = 0;
1128 a.y.z = 0;
1129 x.y.z = 0;
1130 };
1131 expected = {
1132 a.b.c = 1;
1133
1134 a.b.z = 0;
1135 a.y.z = 0;
1136 x.y.z = 0;
1137 };
1138 };
1139
1140 # Nested attributes are updated first
1141 testUpdateManyAttrsByPathNestedBeforehand = {
1142 expr = updateManyAttrsByPath [
1143 {
1144 path = [ "a" ];
1145 update = old: old // { x = old.b; };
1146 }
1147 {
1148 path = [ "a" "b" ];
1149 update = old: old + 1;
1150 }
1151 ] {
1152 a.b = 0;
1153 };
1154 expected = {
1155 a.b = 1;
1156 a.x = 1;
1157 };
1158 };
1159
1160 ## Levenshtein distance functions and co.
1161 testCommonPrefixLengthEmpty = {
1162 expr = strings.commonPrefixLength "" "hello";
1163 expected = 0;
1164 };
1165
1166 testCommonPrefixLengthSame = {
1167 expr = strings.commonPrefixLength "hello" "hello";
1168 expected = 5;
1169 };
1170
1171 testCommonPrefixLengthDiffering = {
1172 expr = strings.commonPrefixLength "hello" "hey";
1173 expected = 2;
1174 };
1175
1176 testCommonSuffixLengthEmpty = {
1177 expr = strings.commonSuffixLength "" "hello";
1178 expected = 0;
1179 };
1180
1181 testCommonSuffixLengthSame = {
1182 expr = strings.commonSuffixLength "hello" "hello";
1183 expected = 5;
1184 };
1185
1186 testCommonSuffixLengthDiffering = {
1187 expr = strings.commonSuffixLength "test" "rest";
1188 expected = 3;
1189 };
1190
1191 testLevenshteinEmpty = {
1192 expr = strings.levenshtein "" "";
1193 expected = 0;
1194 };
1195
1196 testLevenshteinOnlyAdd = {
1197 expr = strings.levenshtein "" "hello there";
1198 expected = 11;
1199 };
1200
1201 testLevenshteinOnlyRemove = {
1202 expr = strings.levenshtein "hello there" "";
1203 expected = 11;
1204 };
1205
1206 testLevenshteinOnlyTransform = {
1207 expr = strings.levenshtein "abcdef" "ghijkl";
1208 expected = 6;
1209 };
1210
1211 testLevenshteinMixed = {
1212 expr = strings.levenshtein "kitchen" "sitting";
1213 expected = 5;
1214 };
1215
1216 testLevenshteinAtMostZeroFalse = {
1217 expr = strings.levenshteinAtMost 0 "foo" "boo";
1218 expected = false;
1219 };
1220
1221 testLevenshteinAtMostZeroTrue = {
1222 expr = strings.levenshteinAtMost 0 "foo" "foo";
1223 expected = true;
1224 };
1225
1226 testLevenshteinAtMostOneFalse = {
1227 expr = strings.levenshteinAtMost 1 "car" "ct";
1228 expected = false;
1229 };
1230
1231 testLevenshteinAtMostOneTrue = {
1232 expr = strings.levenshteinAtMost 1 "car" "cr";
1233 expected = true;
1234 };
1235
1236 # We test levenshteinAtMost 2 particularly well because it uses a complicated
1237 # implementation
1238 testLevenshteinAtMostTwoIsEmpty = {
1239 expr = strings.levenshteinAtMost 2 "" "";
1240 expected = true;
1241 };
1242
1243 testLevenshteinAtMostTwoIsZero = {
1244 expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
1245 expected = true;
1246 };
1247
1248 testLevenshteinAtMostTwoIsOne = {
1249 expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
1250 expected = true;
1251 };
1252
1253 testLevenshteinAtMostTwoDiff0False = {
1254 expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
1255 expected = false;
1256 };
1257
1258 testLevenshteinAtMostTwoDiff0Outer = {
1259 expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
1260 expected = true;
1261 };
1262
1263 testLevenshteinAtMostTwoDiff0DelLeft = {
1264 expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
1265 expected = true;
1266 };
1267
1268 testLevenshteinAtMostTwoDiff0DelRight = {
1269 expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
1270 expected = true;
1271 };
1272
1273 testLevenshteinAtMostTwoDiff1False = {
1274 expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
1275 expected = false;
1276 };
1277
1278 testLevenshteinAtMostTwoDiff1DelLeft = {
1279 expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
1280 expected = true;
1281 };
1282
1283 testLevenshteinAtMostTwoDiff1DelRight = {
1284 expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
1285 expected = true;
1286 };
1287
1288 testLevenshteinAtMostTwoDiff2False = {
1289 expr = strings.levenshteinAtMost 2 "hello" "hxo";
1290 expected = false;
1291 };
1292
1293 testLevenshteinAtMostTwoDiff2True = {
1294 expr = strings.levenshteinAtMost 2 "hello" "heo";
1295 expected = true;
1296 };
1297
1298 testLevenshteinAtMostTwoDiff3 = {
1299 expr = strings.levenshteinAtMost 2 "hello" "ho";
1300 expected = false;
1301 };
1302
1303 testLevenshteinAtMostThreeFalse = {
1304 expr = strings.levenshteinAtMost 3 "hello" "Holla!";
1305 expected = false;
1306 };
1307
1308 testLevenshteinAtMostThreeTrue = {
1309 expr = strings.levenshteinAtMost 3 "hello" "Holla";
1310 expected = true;
1311 };
1312
1313 # lazyDerivation
1314
1315 testLazyDerivationIsLazyInDerivationForAttrNames = {
1316 expr = attrNames (lazyDerivation {
1317 derivation = throw "not lazy enough";
1318 });
1319 # It's ok to add attribute names here when lazyDerivation is improved
1320 # in accordance with its inline comments.
1321 expected = [ "drvPath" "meta" "name" "out" "outPath" "outputName" "outputs" "system" "type" ];
1322 };
1323
1324 testLazyDerivationIsLazyInDerivationForPassthruAttr = {
1325 expr = (lazyDerivation {
1326 derivation = throw "not lazy enough";
1327 passthru.tests = "whatever is in tests";
1328 }).tests;
1329 expected = "whatever is in tests";
1330 };
1331
1332 testLazyDerivationIsLazyInDerivationForPassthruAttr2 = {
1333 # passthru.tests is not a special case. It works for any attr.
1334 expr = (lazyDerivation {
1335 derivation = throw "not lazy enough";
1336 passthru.foo = "whatever is in foo";
1337 }).foo;
1338 expected = "whatever is in foo";
1339 };
1340
1341 testLazyDerivationIsLazyInDerivationForMeta = {
1342 expr = (lazyDerivation {
1343 derivation = throw "not lazy enough";
1344 meta = "whatever is in meta";
1345 }).meta;
1346 expected = "whatever is in meta";
1347 };
1348
1349 testLazyDerivationReturnsDerivationAttrs = let
1350 derivation = {
1351 type = "derivation";
1352 outputs = ["out"];
1353 out = "test out";
1354 outPath = "test outPath";
1355 outputName = "out";
1356 drvPath = "test drvPath";
1357 name = "test name";
1358 system = "test system";
1359 meta = "test meta";
1360 };
1361 in {
1362 expr = lazyDerivation { inherit derivation; };
1363 expected = derivation;
1364 };
1365
1366 testTypeDescriptionInt = {
1367 expr = (with types; int).description;
1368 expected = "signed integer";
1369 };
1370 testTypeDescriptionListOfInt = {
1371 expr = (with types; listOf int).description;
1372 expected = "list of signed integer";
1373 };
1374 testTypeDescriptionListOfListOfInt = {
1375 expr = (with types; listOf (listOf int)).description;
1376 expected = "list of list of signed integer";
1377 };
1378 testTypeDescriptionListOfEitherStrOrBool = {
1379 expr = (with types; listOf (either str bool)).description;
1380 expected = "list of (string or boolean)";
1381 };
1382 testTypeDescriptionEitherListOfStrOrBool = {
1383 expr = (with types; either (listOf bool) str).description;
1384 expected = "(list of boolean) or string";
1385 };
1386 testTypeDescriptionEitherStrOrListOfBool = {
1387 expr = (with types; either str (listOf bool)).description;
1388 expected = "string or list of boolean";
1389 };
1390 testTypeDescriptionOneOfListOfStrOrBool = {
1391 expr = (with types; oneOf [ (listOf bool) str ]).description;
1392 expected = "(list of boolean) or string";
1393 };
1394 testTypeDescriptionOneOfListOfStrOrBoolOrNumber = {
1395 expr = (with types; oneOf [ (listOf bool) str number ]).description;
1396 expected = "(list of boolean) or string or signed integer or floating point number";
1397 };
1398 testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = {
1399 expr = (with types; either (listOf bool) (either str number)).description;
1400 expected = "(list of boolean) or string or signed integer or floating point number";
1401 };
1402 testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = {
1403 expr = (with types; either (either (listOf bool) str) number).description;
1404 expected = "(list of boolean) or string or signed integer or floating point number";
1405 };
1406 testTypeDescriptionEitherNullOrBoolOrString = {
1407 expr = (with types; either (nullOr bool) str).description;
1408 expected = "null or boolean or string";
1409 };
1410 testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = {
1411 expr = (with types; either (listOf (either bool str)) int).description;
1412 expected = "(list of (boolean or string)) or signed integer";
1413 };
1414 testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = {
1415 expr = (with types; either int (listOf (either bool str))).description;
1416 expected = "signed integer or list of (boolean or string)";
1417 };
1418}