1/**
2 Nix evaluation tests for various lib functions.
3
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.
7
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.
10
11 To run these tests:
12
13 [nixpkgs]$ nix-instantiate --eval --strict lib/tests/misc.nix
14
15 If the resulting list is empty, all tests passed.
16 Alternatively, to run all `lib` tests:
17
18 [nixpkgs]$ nix-build lib/tests/release.nix
19*/
20
21let
22 lib = import ../default.nix;
23
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 concatMapAttrsStringSep
43 concatMapStrings
44 concatStrings
45 concatStringsSep
46 const
47 escapeXML
48 evalModules
49 extends
50 filter
51 filterAttrs
52 fix
53 fold
54 foldAttrs
55 foldl
56 foldl'
57 foldlAttrs
58 foldr
59 functionArgs
60 generators
61 genList
62 getExe
63 getExe'
64 getLicenseFromSpdxIdOr
65 groupBy
66 groupBy'
67 hasAttrByPath
68 hasInfix
69 id
70 ifilter0
71 isStorePath
72 join
73 lazyDerivation
74 length
75 lists
76 listToAttrs
77 makeExtensible
78 makeIncludePath
79 makeOverridable
80 mapAttrs
81 mapAttrsToListRecursive
82 mapAttrsToListRecursiveCond
83 mapCartesianProduct
84 matchAttrs
85 mergeAttrs
86 meta
87 mod
88 nameValuePair
89 optionalDrvAttr
90 optionAttrSetToDocList
91 overrideExisting
92 packagesFromDirectoryRecursive
93 pipe
94 range
95 recursiveUpdateUntil
96 removePrefix
97 replaceString
98 replicate
99 runTests
100 setFunctionArgs
101 showAttrPath
102 sort
103 sortOn
104 stringLength
105 strings
106 stringToCharacters
107 systems
108 tail
109 take
110 testAllTrue
111 toBaseDigits
112 toExtension
113 toHexString
114 fromHexString
115 toInt
116 toIntBase10
117 toShellVars
118 types
119 uniqueStrings
120 updateManyAttrsByPath
121 versions
122 xor
123 ;
124
125 testingThrow = expr: {
126 expr = (builtins.tryEval (builtins.seq expr "didn't throw"));
127 expected = {
128 success = false;
129 value = false;
130 };
131 };
132 testingEval = expr: {
133 expr = (builtins.tryEval expr).success;
134 expected = true;
135 };
136
137 testSanitizeDerivationName =
138 { name, expected }:
139 let
140 drv = derivation {
141 name = strings.sanitizeDerivationName name;
142 builder = "x";
143 system = "x";
144 };
145 in
146 {
147 # Evaluate the derivation so an invalid name would be caught
148 expr = builtins.seq drv.drvPath drv.name;
149 inherit expected;
150 };
151
152 dummyDerivation = derivation {
153 name = "name";
154 builder = "builder";
155 system = "system";
156 };
157in
158
159runTests {
160
161 # CUSTOMIZATION
162
163 testFunctionArgsMakeOverridable = {
164 expr = functionArgs (
165 makeOverridable (
166 {
167 a,
168 b,
169 c ? null,
170 }:
171 { }
172 )
173 );
174 expected = {
175 a = false;
176 b = false;
177 c = true;
178 };
179 };
180
181 testFunctionArgsMakeOverridableOverride = {
182 expr =
183 functionArgs
184 (makeOverridable
185 (
186 {
187 a,
188 b,
189 c ? null,
190 }:
191 { }
192 )
193 {
194 a = 1;
195 b = 2;
196 }
197 ).override;
198 expected = {
199 a = false;
200 b = false;
201 c = true;
202 };
203 };
204
205 testCallPackageWithOverridePreservesArguments =
206 let
207 f =
208 {
209 a ? 0,
210 b,
211 }:
212 { };
213 f' = callPackageWith {
214 a = 1;
215 b = 2;
216 } f { };
217 in
218 {
219 expr = functionArgs f'.override;
220 expected = functionArgs f;
221 };
222
223 testCallPackagesWithOverridePreservesArguments =
224 let
225 f =
226 {
227 a ? 0,
228 b,
229 }:
230 {
231 nested = { };
232 };
233 f' = callPackagesWith {
234 a = 1;
235 b = 2;
236 } f { };
237 in
238 {
239 expr = functionArgs f'.nested.override;
240 expected = functionArgs f;
241 };
242
243 # TRIVIAL
244
245 testId = {
246 expr = id 1;
247 expected = 1;
248 };
249
250 testConst = {
251 expr = const 2 3;
252 expected = 2;
253 };
254
255 testPipe = {
256 expr = pipe 2 [
257 (x: x + 2) # 2 + 2 = 4
258 (x: x * 2) # 4 * 2 = 8
259 ];
260 expected = 8;
261 };
262
263 testPipeEmpty = {
264 expr = pipe 2 [ ];
265 expected = 2;
266 };
267
268 testPipeStrings = {
269 expr =
270 pipe
271 [ 3 4 ]
272 [
273 (map toString)
274 (map (s: s + "\n"))
275 concatStrings
276 ];
277 expected = ''
278 3
279 4
280 '';
281 };
282
283 /*
284 testOr = {
285 expr = or true false;
286 expected = true;
287 };
288 */
289
290 testAnd = {
291 expr = and true false;
292 expected = false;
293 };
294
295 testXor = {
296 expr = [
297 (xor true false)
298 (xor true true)
299 (xor false false)
300 (xor false true)
301 ];
302 expected = [
303 true
304 false
305 false
306 true
307 ];
308 };
309
310 testComposeExtensions = {
311 expr =
312 let
313 obj = makeExtensible (self: {
314 foo = self.bar;
315 });
316 f = self: super: {
317 bar = false;
318 baz = true;
319 };
320 g = self: super: { bar = super.baz or false; };
321 f_o_g = composeExtensions f g;
322 composed = obj.extend f_o_g;
323 in
324 composed.foo;
325 expected = true;
326 };
327
328 testComposeManyExtensions0 = {
329 expr =
330 let
331 obj = makeExtensible (self: {
332 foo = true;
333 });
334 emptyComposition = composeManyExtensions [ ];
335 composed = obj.extend emptyComposition;
336 in
337 composed.foo;
338 expected = true;
339 };
340
341 testComposeManyExtensions =
342 let
343 f = self: super: {
344 bar = false;
345 baz = true;
346 };
347 g = self: super: { bar = super.baz or false; };
348 h = self: super: { qux = super.bar or false; };
349 obj = makeExtensible (self: {
350 foo = self.qux;
351 });
352 in
353 {
354 expr =
355 let
356 composition = composeManyExtensions [
357 f
358 g
359 h
360 ];
361 composed = obj.extend composition;
362 in
363 composed.foo;
364 expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo;
365 };
366
367 testBitAnd = {
368 expr = (bitAnd 3 10);
369 expected = 2;
370 };
371
372 testBitOr = {
373 expr = (bitOr 3 10);
374 expected = 11;
375 };
376
377 testBitXor = {
378 expr = (bitXor 3 10);
379 expected = 9;
380 };
381
382 testToHexString = {
383 expr = toHexString 250;
384 expected = "FA";
385 };
386
387 testFromHexStringFirstExample = {
388 expr = fromHexString "FF";
389 expected = 255;
390 };
391
392 # Highest supported integer value in Nix.
393 testFromHexStringMaximum = {
394 expr = fromHexString "7fffffffffffffff";
395 expected = 9223372036854775807;
396 };
397
398 testFromHexStringLeadingZeroes = {
399 expr = fromHexString "00ffffffffffffff";
400 expected = 72057594037927935;
401 };
402
403 testFromHexStringWithPrefix = {
404 expr = fromHexString "0xf";
405 expected = 15;
406 };
407
408 testFromHexStringMixedCase = {
409 expr = fromHexString "eEeEe";
410 expected = 978670;
411 };
412
413 testToBaseDigits = {
414 expr = toBaseDigits 2 6;
415 expected = [
416 1
417 1
418 0
419 ];
420 };
421
422 testFunctionArgsFunctor = {
423 expr = functionArgs { __functor = self: { a, b }: null; };
424 expected = {
425 a = false;
426 b = false;
427 };
428 };
429
430 testFunctionArgsSetFunctionArgs = {
431 expr = functionArgs (setFunctionArgs (args: args.x) { x = false; });
432 expected = {
433 x = false;
434 };
435 };
436
437 # STRINGS
438
439 testJoin = {
440 expr = join "," [
441 "a"
442 "b"
443 "c"
444 ];
445 expected = "a,b,c";
446 };
447
448 testConcatMapStrings = {
449 expr = concatMapStrings (x: x + ";") [
450 "a"
451 "b"
452 "c"
453 ];
454 expected = "a;b;c;";
455 };
456
457 testConcatStringsSep = {
458 expr = concatStringsSep "," [
459 "a"
460 "b"
461 "c"
462 ];
463 expected = "a,b,c";
464 };
465
466 testConcatMapAttrsStringSepExamples = {
467 expr = concatMapAttrsStringSep "\n" (name: value: "${name}: foo-${value}") {
468 a = "0.1.0";
469 b = "0.2.0";
470 };
471 expected = "a: foo-0.1.0\nb: foo-0.2.0";
472 };
473
474 testConcatLines = {
475 expr = concatLines [
476 "a"
477 "b"
478 "c"
479 ];
480 expected = "a\nb\nc\n";
481 };
482
483 testMakeIncludePathWithPkgs = {
484 expr = (
485 makeIncludePath [
486 # makeIncludePath preferably selects the "dev" output
487 {
488 dev.outPath = "/dev";
489 out.outPath = "/out";
490 outPath = "/default";
491 }
492 # "out" is used if "dev" is not found
493 {
494 out.outPath = "/out";
495 outPath = "/default";
496 }
497 # And it returns the derivation directly if there's no "out" either
498 { outPath = "/default"; }
499 # Same if the output is specified explicitly, even if there's a "dev"
500 {
501 dev.outPath = "/dev";
502 outPath = "/default";
503 outputSpecified = true;
504 }
505 ]
506 );
507 expected = "/dev/include:/out/include:/default/include:/default/include";
508 };
509
510 testMakeIncludePathWithEmptyList = {
511 expr = (makeIncludePath [ ]);
512 expected = "";
513 };
514
515 testMakeIncludePathWithOneString = {
516 expr = (makeIncludePath [ "/usr" ]);
517 expected = "/usr/include";
518 };
519
520 testMakeIncludePathWithManyString = {
521 expr = (
522 makeIncludePath [
523 "/usr"
524 "/usr/local"
525 ]
526 );
527 expected = "/usr/include:/usr/local/include";
528 };
529
530 testReplaceStringString = {
531 expr = strings.replaceString "." "_" "v1.2.3";
532 expected = "v1_2_3";
533 };
534
535 testReplicateString = {
536 expr = strings.replicate 5 "hello";
537 expected = "hellohellohellohellohello";
538 };
539
540 # Test various strings are trimmed correctly
541 testTrimString = {
542 expr =
543 let
544 testValues =
545 f:
546 mapAttrs (_: f) {
547 empty = "";
548 cr = "\r";
549 lf = "\n";
550 tab = "\t";
551 spaces = " ";
552 leading = " Hello, world";
553 trailing = "Hello, world ";
554 mixed = " Hello, world ";
555 mixed-tabs = " \t\tHello, world \t \t ";
556 multiline = " Hello,\n world! ";
557 multiline-crlf = " Hello,\r\n world! ";
558 };
559 in
560 {
561 leading = testValues (strings.trimWith { start = true; });
562 trailing = testValues (strings.trimWith { end = true; });
563 both = testValues strings.trim;
564 };
565 expected = {
566 leading = {
567 empty = "";
568 cr = "";
569 lf = "";
570 tab = "";
571 spaces = "";
572 leading = "Hello, world";
573 trailing = "Hello, world ";
574 mixed = "Hello, world ";
575 mixed-tabs = "Hello, world \t \t ";
576 multiline = "Hello,\n world! ";
577 multiline-crlf = "Hello,\r\n world! ";
578 };
579 trailing = {
580 empty = "";
581 cr = "";
582 lf = "";
583 tab = "";
584 spaces = "";
585 leading = " Hello, world";
586 trailing = "Hello, world";
587 mixed = " Hello, world";
588 mixed-tabs = " \t\tHello, world";
589 multiline = " Hello,\n world!";
590 multiline-crlf = " Hello,\r\n world!";
591 };
592 both = {
593 empty = "";
594 cr = "";
595 lf = "";
596 tab = "";
597 spaces = "";
598 leading = "Hello, world";
599 trailing = "Hello, world";
600 mixed = "Hello, world";
601 mixed-tabs = "Hello, world";
602 multiline = "Hello,\n world!";
603 multiline-crlf = "Hello,\r\n world!";
604 };
605 };
606 };
607
608 testSplitStringsSimple = {
609 expr = strings.splitString "." "a.b.c.d";
610 expected = [
611 "a"
612 "b"
613 "c"
614 "d"
615 ];
616 };
617
618 testSplitStringsEmpty = {
619 expr = strings.splitString "." "a..b";
620 expected = [
621 "a"
622 ""
623 "b"
624 ];
625 };
626
627 testSplitStringsOne = {
628 expr = strings.splitString ":" "a.b";
629 expected = [ "a.b" ];
630 };
631
632 testSplitStringsNone = {
633 expr = strings.splitString "." "";
634 expected = [ "" ];
635 };
636
637 testSplitStringsFirstEmpty = {
638 expr = strings.splitString "/" "/a/b/c";
639 expected = [
640 ""
641 "a"
642 "b"
643 "c"
644 ];
645 };
646
647 testSplitStringsLastEmpty = {
648 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:";
649 expected = [
650 "2001"
651 "db8"
652 "0"
653 "0042"
654 ""
655 "8a2e"
656 "370"
657 ""
658 ];
659 };
660
661 testSplitStringsRegex = {
662 expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B";
663 expected = [
664 "A"
665 "B"
666 ];
667 };
668
669 testSplitStringBySimpleDelimiter = {
670 expr = strings.splitStringBy (
671 prev: curr:
672 builtins.elem curr [
673 "."
674 "-"
675 ]
676 ) false "foo.bar-baz";
677 expected = [
678 "foo"
679 "bar"
680 "baz"
681 ];
682 };
683
684 testSplitStringByLeadingDelimiter = {
685 expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false ".foo.bar.baz";
686 expected = [
687 ""
688 "foo"
689 "bar"
690 "baz"
691 ];
692 };
693
694 testSplitStringByTrailingDelimiter = {
695 expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "foo.bar.baz.";
696 expected = [
697 "foo"
698 "bar"
699 "baz"
700 ""
701 ];
702 };
703
704 testSplitStringByMultipleConsecutiveDelimiters = {
705 expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "foo...bar";
706 expected = [
707 "foo"
708 ""
709 ""
710 "bar"
711 ];
712 };
713
714 testSplitStringByKeepingSplitChar = {
715 expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) true "foo.bar.baz";
716 expected = [
717 "foo"
718 ".bar"
719 ".baz"
720 ];
721 };
722
723 testSplitStringByCaseTransition = {
724 expr = strings.splitStringBy (
725 prev: curr: builtins.match "[a-z]" prev != null && builtins.match "[A-Z]" curr != null
726 ) true "fooBarBaz";
727 expected = [
728 "foo"
729 "Bar"
730 "Baz"
731 ];
732 };
733
734 testSplitStringByEmptyString = {
735 expr = strings.splitStringBy (prev: curr: builtins.elem curr [ "." ]) false "";
736 expected = [ "" ];
737 };
738
739 testSplitStringByComplexPredicate = {
740 expr = strings.splitStringBy (
741 prev: curr:
742 prev != ""
743 && curr != ""
744 && builtins.match "[0-9]" prev != null
745 && builtins.match "[a-z]" curr != null
746 ) true "123abc456def";
747 expected = [
748 "123"
749 "abc456"
750 "def"
751 ];
752 };
753
754 testSplitStringByUpperCaseStart = {
755 expr = strings.splitStringBy (prev: curr: builtins.match "[A-Z]" curr != null) true "FooBarBaz";
756 expected = [
757 ""
758 "Foo"
759 "Bar"
760 "Baz"
761 ];
762 };
763
764 testEscapeShellArg = {
765 expr = strings.escapeShellArg "esc'ape\nme";
766 expected = "'esc'\\''ape\nme'";
767 };
768
769 testEscapeShellArgEmpty = {
770 expr = strings.escapeShellArg "";
771 expected = "''";
772 };
773
774 testEscapeShellArgs = {
775 expr = strings.escapeShellArgs [
776 "one"
777 "two three"
778 "four'five"
779 ];
780 expected = "one 'two three' 'four'\\''five'";
781 };
782
783 testEscapeShellArgsUnicode = {
784 expr = strings.escapeShellArg "á";
785 expected = "'á'";
786 };
787
788 testSplitStringsDerivation = {
789 expr = lib.dropEnd 1 (strings.splitString "/" dummyDerivation);
790 expected = strings.splitString "/" builtins.storeDir;
791 };
792
793 testSplitVersionSingle = {
794 expr = versions.splitVersion "1";
795 expected = [ "1" ];
796 };
797
798 testSplitVersionDouble = {
799 expr = versions.splitVersion "1.2";
800 expected = [
801 "1"
802 "2"
803 ];
804 };
805
806 testSplitVersionTriple = {
807 expr = versions.splitVersion "1.2.3";
808 expected = [
809 "1"
810 "2"
811 "3"
812 ];
813 };
814
815 testPadVersionLess = {
816 expr = versions.pad 3 "1.2";
817 expected = "1.2.0";
818 };
819
820 testPadVersionLessExtra = {
821 expr = versions.pad 3 "1.3-rc1";
822 expected = "1.3.0-rc1";
823 };
824
825 testPadVersionMore = {
826 expr = versions.pad 3 "1.2.3.4";
827 expected = "1.2.3";
828 };
829
830 testIsStorePath = {
831 expr =
832 let
833 goodPath = "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11";
834 goodCAPath = "/1121rp0gvr1qya7hvy925g5kjwg66acz6sn1ra1hca09f1z5dsab";
835 in
836 {
837 storePath = isStorePath goodPath;
838 storePathDerivation = isStorePath dummyDerivation;
839 storePathAppendix = isStorePath "${goodPath}/bin/python";
840 nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath)));
841 asPath = isStorePath (/. + goodPath);
842 otherPath = isStorePath "/something/else";
843
844 caPath = isStorePath goodCAPath;
845 caPathAppendix = isStorePath "${goodCAPath}/bin/python";
846 caAsPath = isStorePath (/. + goodCAPath);
847
848 otherVals = {
849 attrset = isStorePath { };
850 list = isStorePath [ ];
851 int = isStorePath 42;
852 };
853 };
854 expected = {
855 storePath = true;
856 storePathDerivation = true;
857 storePathAppendix = false;
858 nonAbsolute = false;
859 asPath = true;
860 caPath = true;
861 caPathAppendix = false;
862 caAsPath = true;
863 otherPath = false;
864 otherVals = {
865 attrset = false;
866 list = false;
867 int = false;
868 };
869 };
870 };
871
872 testEscapeXML = {
873 expr = escapeXML ''"test" 'test' < & >'';
874 expected = ""test" 'test' < & >";
875 };
876
877 testToShellVars = {
878 expr = ''
879 ${toShellVars {
880 STRing01 = "just a 'string'";
881 _array_ = [
882 "with"
883 "more strings"
884 ];
885 assoc."with some" = ''
886 strings
887 possibly newlines
888 '';
889 drv = {
890 outPath = "/drv";
891 foo = "ignored attribute";
892 };
893 path = /path;
894 stringable = {
895 __toString = _: "hello toString";
896 bar = "ignored attribute";
897 };
898 }}
899 '';
900 expected = ''
901 STRing01='just a '\'''string'\''''
902 declare -a _array_=(with 'more strings')
903 declare -A assoc=(['with some']='strings
904 possibly newlines
905 ')
906 drv=/drv
907 path=/path
908 stringable='hello toString'
909 '';
910 };
911
912 testHasInfixFalse = {
913 expr = hasInfix "c" "abde";
914 expected = false;
915 };
916
917 testHasInfixTrue = {
918 expr = hasInfix "c" "abcde";
919 expected = true;
920 };
921
922 testHasInfixDerivation = {
923 expr = hasInfix "name" dummyDerivation;
924 expected = true;
925 };
926
927 testHasInfixPath = {
928 expr = hasInfix "tests" ./.;
929 expected = true;
930 };
931
932 testHasInfixPathStoreDir = {
933 expr = hasInfix builtins.storeDir ./.;
934 expected = true;
935 };
936
937 testHasInfixToString = {
938 expr = hasInfix "a" { __toString = _: "a"; };
939 expected = true;
940 };
941
942 testRemovePrefixExample1 = {
943 expr = removePrefix "foo." "foo.bar.baz";
944 expected = "bar.baz";
945 };
946 testRemovePrefixExample2 = {
947 expr = removePrefix "xxx" "foo.bar.baz";
948 expected = "foo.bar.baz";
949 };
950 testRemovePrefixEmptyPrefix = {
951 expr = removePrefix "" "foo";
952 expected = "foo";
953 };
954 testRemovePrefixEmptyString = {
955 expr = removePrefix "foo" "";
956 expected = "";
957 };
958 testRemovePrefixEmptyBoth = {
959 expr = removePrefix "" "";
960 expected = "";
961 };
962
963 testNormalizePath = {
964 expr = strings.normalizePath "//a/b//c////d/";
965 expected = "/a/b/c/d/";
966 };
967
968 testCharToInt = {
969 expr = strings.charToInt "A";
970 expected = 65;
971 };
972
973 testEscapeC = {
974 expr = strings.escapeC [ "\n" " " ] "Hello World\n";
975 expected = "Hello\\x20World\\x0a";
976 };
977
978 testEscapeURL = testAllTrue [
979 ("" == strings.escapeURL "")
980 ("Hello" == strings.escapeURL "Hello")
981 ("Hello%20World" == strings.escapeURL "Hello World")
982 ("Hello%2FWorld" == strings.escapeURL "Hello/World")
983 ("42%25" == strings.escapeURL "42%")
984 (
985 "%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"
986 == strings.escapeURL " ?&=#+%!<>#\"{}|\\^[]`\t:/@$'()*,;"
987 )
988 ];
989
990 testToSentenceCase = {
991 expr = strings.toSentenceCase "hello world";
992 expected = "Hello world";
993 };
994
995 testToSentenceCasePath = testingThrow (strings.toSentenceCase ./.);
996
997 testToCamelCase = {
998 expr = strings.toCamelCase "hello world";
999 expected = "helloWorld";
1000 };
1001
1002 testToCamelCaseFromKebab = {
1003 expr = strings.toCamelCase "hello-world";
1004 expected = "helloWorld";
1005 };
1006
1007 testToCamelCaseFromSnake = {
1008 expr = strings.toCamelCase "hello_world";
1009 expected = "helloWorld";
1010 };
1011
1012 testToCamelCaseFromPascal = {
1013 expr = strings.toCamelCase "HelloWorld";
1014 expected = "helloWorld";
1015 };
1016
1017 testToCamelCasePath = testingThrow (strings.toCamelCase ./.);
1018
1019 testToInt = testAllTrue [
1020 # Naive
1021 (123 == toInt "123")
1022 (0 == toInt "0")
1023 # Whitespace Padding
1024 (123 == toInt " 123")
1025 (123 == toInt "123 ")
1026 (123 == toInt " 123 ")
1027 (123 == toInt " 123 ")
1028 (0 == toInt " 0")
1029 (0 == toInt "0 ")
1030 (0 == toInt " 0 ")
1031 (-1 == toInt "-1")
1032 (-1 == toInt " -1 ")
1033 ];
1034
1035 testToIntFails = testAllTrue [
1036 (
1037 builtins.tryEval (toInt "") == {
1038 success = false;
1039 value = false;
1040 }
1041 )
1042 (
1043 builtins.tryEval (toInt "123 123") == {
1044 success = false;
1045 value = false;
1046 }
1047 )
1048 (
1049 builtins.tryEval (toInt "0 123") == {
1050 success = false;
1051 value = false;
1052 }
1053 )
1054 (
1055 builtins.tryEval (toInt " 0d ") == {
1056 success = false;
1057 value = false;
1058 }
1059 )
1060 (
1061 builtins.tryEval (toInt " 1d ") == {
1062 success = false;
1063 value = false;
1064 }
1065 )
1066 (
1067 builtins.tryEval (toInt " d0 ") == {
1068 success = false;
1069 value = false;
1070 }
1071 )
1072 (
1073 builtins.tryEval (toInt "00") == {
1074 success = false;
1075 value = false;
1076 }
1077 )
1078 (
1079 builtins.tryEval (toInt "01") == {
1080 success = false;
1081 value = false;
1082 }
1083 )
1084 (
1085 builtins.tryEval (toInt "002") == {
1086 success = false;
1087 value = false;
1088 }
1089 )
1090 (
1091 builtins.tryEval (toInt " 002 ") == {
1092 success = false;
1093 value = false;
1094 }
1095 )
1096 (
1097 builtins.tryEval (toInt " foo ") == {
1098 success = false;
1099 value = false;
1100 }
1101 )
1102 (
1103 builtins.tryEval (toInt " foo 123 ") == {
1104 success = false;
1105 value = false;
1106 }
1107 )
1108 (
1109 builtins.tryEval (toInt " foo123 ") == {
1110 success = false;
1111 value = false;
1112 }
1113 )
1114 ];
1115
1116 testToIntBase10 = testAllTrue [
1117 # Naive
1118 (123 == toIntBase10 "123")
1119 (0 == toIntBase10 "0")
1120 # Whitespace Padding
1121 (123 == toIntBase10 " 123")
1122 (123 == toIntBase10 "123 ")
1123 (123 == toIntBase10 " 123 ")
1124 (123 == toIntBase10 " 123 ")
1125 (0 == toIntBase10 " 0")
1126 (0 == toIntBase10 "0 ")
1127 (0 == toIntBase10 " 0 ")
1128 # Zero Padding
1129 (123 == toIntBase10 "0123")
1130 (123 == toIntBase10 "0000123")
1131 (0 == toIntBase10 "000000")
1132 # Whitespace and Zero Padding
1133 (123 == toIntBase10 " 0123")
1134 (123 == toIntBase10 "0123 ")
1135 (123 == toIntBase10 " 0123 ")
1136 (123 == toIntBase10 " 0000123")
1137 (123 == toIntBase10 "0000123 ")
1138 (123 == toIntBase10 " 0000123 ")
1139 (0 == toIntBase10 " 000000")
1140 (0 == toIntBase10 "000000 ")
1141 (0 == toIntBase10 " 000000 ")
1142 (-1 == toIntBase10 "-1")
1143 (-1 == toIntBase10 " -1 ")
1144 ];
1145
1146 testToIntBase10Fails = testAllTrue [
1147 (
1148 builtins.tryEval (toIntBase10 "") == {
1149 success = false;
1150 value = false;
1151 }
1152 )
1153 (
1154 builtins.tryEval (toIntBase10 "123 123") == {
1155 success = false;
1156 value = false;
1157 }
1158 )
1159 (
1160 builtins.tryEval (toIntBase10 "0 123") == {
1161 success = false;
1162 value = false;
1163 }
1164 )
1165 (
1166 builtins.tryEval (toIntBase10 " 0d ") == {
1167 success = false;
1168 value = false;
1169 }
1170 )
1171 (
1172 builtins.tryEval (toIntBase10 " 1d ") == {
1173 success = false;
1174 value = false;
1175 }
1176 )
1177 (
1178 builtins.tryEval (toIntBase10 " d0 ") == {
1179 success = false;
1180 value = false;
1181 }
1182 )
1183 (
1184 builtins.tryEval (toIntBase10 " foo ") == {
1185 success = false;
1186 value = false;
1187 }
1188 )
1189 (
1190 builtins.tryEval (toIntBase10 " foo 123 ") == {
1191 success = false;
1192 value = false;
1193 }
1194 )
1195 (
1196 builtins.tryEval (toIntBase10 " foo 00123 ") == {
1197 success = false;
1198 value = false;
1199 }
1200 )
1201 (
1202 builtins.tryEval (toIntBase10 " foo00123 ") == {
1203 success = false;
1204 value = false;
1205 }
1206 )
1207 ];
1208
1209 # LISTS
1210
1211 testFilter = {
1212 expr = filter (x: x != "a") [
1213 "a"
1214 "b"
1215 "c"
1216 "a"
1217 ];
1218 expected = [
1219 "b"
1220 "c"
1221 ];
1222 };
1223
1224 testIfilter0Example = {
1225 expr = ifilter0 (i: v: i == 0 || v > 2) [
1226 1
1227 2
1228 3
1229 ];
1230 expected = [
1231 1
1232 3
1233 ];
1234 };
1235 testIfilter0Empty = {
1236 expr = ifilter0 (i: v: abort "shouldn't be evaluated!") [ ];
1237 expected = [ ];
1238 };
1239 testIfilter0IndexOnly = {
1240 expr = length (
1241 ifilter0 (i: v: mod i 2 == 0) [
1242 (throw "0")
1243 (throw "1")
1244 (throw "2")
1245 (throw "3")
1246 ]
1247 );
1248 expected = 2;
1249 };
1250 testIfilter0All = {
1251 expr = ifilter0 (i: v: true) [
1252 10
1253 11
1254 12
1255 13
1256 14
1257 15
1258 ];
1259 expected = [
1260 10
1261 11
1262 12
1263 13
1264 14
1265 15
1266 ];
1267 };
1268 testIfilter0First = {
1269 expr = ifilter0 (i: v: i == 0) [
1270 10
1271 11
1272 12
1273 13
1274 14
1275 15
1276 ];
1277 expected = [ 10 ];
1278 };
1279 testIfilter0Last = {
1280 expr = ifilter0 (i: v: i == 5) [
1281 10
1282 11
1283 12
1284 13
1285 14
1286 15
1287 ];
1288 expected = [ 15 ];
1289 };
1290
1291 testFold =
1292 let
1293 f = op: fold: fold op 0 (range 0 100);
1294 # fold with associative operator
1295 assoc = f builtins.add;
1296 # fold with non-associative operator
1297 nonAssoc = f builtins.sub;
1298 in
1299 {
1300 expr = {
1301 assocRight = assoc foldr;
1302 # right fold with assoc operator is same as left fold
1303 assocRightIsLeft = assoc foldr == assoc foldl;
1304 nonAssocRight = nonAssoc foldr;
1305 nonAssocLeft = nonAssoc foldl;
1306 # with non-assoc operator the fold results are not the same
1307 nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr;
1308 # fold is an alias for foldr
1309 foldIsRight = nonAssoc fold == nonAssoc foldr;
1310 };
1311 expected = {
1312 assocRight = 5050;
1313 assocRightIsLeft = true;
1314 nonAssocRight = 50;
1315 nonAssocLeft = (-5050);
1316 nonAssocRightIsNotLeft = true;
1317 foldIsRight = true;
1318 };
1319 };
1320
1321 testFoldl'Empty = {
1322 expr = foldl' (acc: el: abort "operation not called") 0 [ ];
1323 expected = 0;
1324 };
1325
1326 testFoldl'IntegerAdding = {
1327 expr = foldl' (acc: el: acc + el) 0 [
1328 1
1329 2
1330 3
1331 ];
1332 expected = 6;
1333 };
1334
1335 # The accumulator isn't forced deeply
1336 testFoldl'NonDeep = {
1337 expr = take 3 (foldl' (acc: el: [ el ] ++ acc) [ (abort "unevaluated list entry") ] [ 1 2 3 ]);
1338 expected = [
1339 3
1340 2
1341 1
1342 ];
1343 };
1344
1345 # Compared to builtins.foldl', lib.foldl' evaluates the first accumulator strictly too
1346 testFoldl'StrictInitial = {
1347 expr = (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [ ])).success;
1348 expected = false;
1349 };
1350
1351 # Make sure we don't get a stack overflow for large lists
1352 # This number of elements would notably cause a stack overflow if it was implemented without the `foldl'` builtin
1353 testFoldl'Large = {
1354 expr = foldl' (acc: el: acc + el) 0 (range 0 100000);
1355 expected = 5000050000;
1356 };
1357
1358 testTake = testAllTrue [
1359 (
1360 [ ] == (take 0 [
1361 1
1362 2
1363 3
1364 ])
1365 )
1366 (
1367 [ 1 ] == (take 1 [
1368 1
1369 2
1370 3
1371 ])
1372 )
1373 (
1374 [
1375 1
1376 2
1377 ] == (take 2 [
1378 1
1379 2
1380 3
1381 ])
1382 )
1383 (
1384 [
1385 1
1386 2
1387 3
1388 ] == (take 3 [
1389 1
1390 2
1391 3
1392 ])
1393 )
1394 (
1395 [
1396 1
1397 2
1398 3
1399 ] == (take 4 [
1400 1
1401 2
1402 3
1403 ])
1404 )
1405 ];
1406
1407 testTakeEnd =
1408 let
1409 inherit (lib) takeEnd;
1410 in
1411 testAllTrue [
1412 (
1413 takeEnd 0 [
1414 1
1415 2
1416 3
1417 ] == [ ]
1418 )
1419 (
1420 takeEnd 1 [
1421 1
1422 2
1423 3
1424 ] == [ 3 ]
1425 )
1426 (
1427 takeEnd 2 [
1428 1
1429 2
1430 3
1431 ] == [
1432 2
1433 3
1434 ]
1435 )
1436 (
1437 takeEnd 3 [
1438 1
1439 2
1440 3
1441 ] == [
1442 1
1443 2
1444 3
1445 ]
1446 )
1447 (
1448 takeEnd 4 [
1449 1
1450 2
1451 3
1452 ] == [
1453 1
1454 2
1455 3
1456 ]
1457 )
1458 (takeEnd 0 [ ] == [ ])
1459 (takeEnd 1 [ ] == [ ])
1460 (
1461 takeEnd (-1) [
1462 1
1463 2
1464 3
1465 ] == [ ]
1466 )
1467 (takeEnd (-1) [ ] == [ ])
1468 ];
1469
1470 testDrop =
1471 let
1472 inherit (lib) drop;
1473 in
1474 testAllTrue [
1475 # list index -1 is out of bounds
1476 # ([ 1 2 3 ] == (drop (-1) [ 1 2 3 ]))
1477 (
1478 drop 0 [
1479 1
1480 2
1481 3
1482 ] == [
1483 1
1484 2
1485 3
1486 ]
1487 )
1488 (
1489 drop 1 [
1490 1
1491 2
1492 3
1493 ] == [
1494 2
1495 3
1496 ]
1497 )
1498 (
1499 drop 2 [
1500 1
1501 2
1502 3
1503 ] == [ 3 ]
1504 )
1505 (
1506 drop 3 [
1507 1
1508 2
1509 3
1510 ] == [ ]
1511 )
1512 (
1513 drop 4 [
1514 1
1515 2
1516 3
1517 ] == [ ]
1518 )
1519 (drop 0 [ ] == [ ])
1520 (drop 1 [ ] == [ ])
1521 ];
1522
1523 testDropEnd =
1524 let
1525 inherit (lib) dropEnd;
1526 in
1527 testAllTrue [
1528 (
1529 dropEnd 0 [
1530 1
1531 2
1532 3
1533 ] == [
1534 1
1535 2
1536 3
1537 ]
1538 )
1539 (
1540 dropEnd 1 [
1541 1
1542 2
1543 3
1544 ] == [
1545 1
1546 2
1547 ]
1548 )
1549 (
1550 dropEnd 2 [
1551 1
1552 2
1553 3
1554 ] == [ 1 ]
1555 )
1556 (
1557 dropEnd 3 [
1558 1
1559 2
1560 3
1561 ] == [ ]
1562 )
1563 (
1564 dropEnd 4 [
1565 1
1566 2
1567 3
1568 ] == [ ]
1569 )
1570 (dropEnd 0 [ ] == [ ])
1571 (dropEnd 1 [ ] == [ ])
1572 (
1573 dropEnd (-1) [
1574 1
1575 2
1576 3
1577 ] == [
1578 1
1579 2
1580 3
1581 ]
1582 )
1583 (dropEnd (-1) [ ] == [ ])
1584 ];
1585
1586 testListHasPrefixExample1 = {
1587 expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ];
1588 expected = true;
1589 };
1590 testListHasPrefixExample2 = {
1591 expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ];
1592 expected = false;
1593 };
1594 testListHasPrefixLazy = {
1595 expr = lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ];
1596 expected = true;
1597 };
1598 testListHasPrefixEmptyPrefix = {
1599 expr = lists.hasPrefix [ ] [ 1 2 ];
1600 expected = true;
1601 };
1602 testListHasPrefixEmptyList = {
1603 expr = lists.hasPrefix [ 1 2 ] [ ];
1604 expected = false;
1605 };
1606
1607 testListRemovePrefixExample1 = {
1608 expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ];
1609 expected = [
1610 3
1611 4
1612 ];
1613 };
1614 testListRemovePrefixExample2 = {
1615 expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success;
1616 expected = false;
1617 };
1618 testListRemovePrefixEmptyPrefix = {
1619 expr = lists.removePrefix [ ] [ 1 2 ];
1620 expected = [
1621 1
1622 2
1623 ];
1624 };
1625 testListRemovePrefixEmptyList = {
1626 expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success;
1627 expected = false;
1628 };
1629
1630 testFoldAttrs = {
1631 expr =
1632 foldAttrs (n: a: [ n ] ++ a)
1633 [ ]
1634 [
1635 {
1636 a = 2;
1637 b = 7;
1638 }
1639 {
1640 a = 3;
1641 c = 8;
1642 }
1643 ];
1644 expected = {
1645 a = [
1646 2
1647 3
1648 ];
1649 b = [ 7 ];
1650 c = [ 8 ];
1651 };
1652 };
1653
1654 testListCommonPrefixExample1 = {
1655 expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ];
1656 expected = [
1657 1
1658 2
1659 ];
1660 };
1661 testListCommonPrefixExample2 = {
1662 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ];
1663 expected = [
1664 1
1665 2
1666 3
1667 ];
1668 };
1669 testListCommonPrefixExample3 = {
1670 expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ];
1671 expected = [ ];
1672 };
1673 testListCommonPrefixEmpty = {
1674 expr = lists.commonPrefix [ ] [ 1 2 3 ];
1675 expected = [ ];
1676 };
1677 testListCommonPrefixSame = {
1678 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ];
1679 expected = [
1680 1
1681 2
1682 3
1683 ];
1684 };
1685 testListCommonPrefixLazy = {
1686 expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this") ];
1687 expected = [ 1 ];
1688 };
1689 # This would stack overflow if `commonPrefix` were implemented using recursion
1690 testListCommonPrefixLong =
1691 let
1692 longList = genList (n: n) 100000;
1693 in
1694 {
1695 expr = lists.commonPrefix longList longList;
1696 expected = longList;
1697 };
1698
1699 testSort = {
1700 expr = sort builtins.lessThan [
1701 40
1702 2
1703 30
1704 42
1705 ];
1706 expected = [
1707 2
1708 30
1709 40
1710 42
1711 ];
1712 };
1713
1714 testSortOn = {
1715 expr = sortOn stringLength [
1716 "aa"
1717 "b"
1718 "cccc"
1719 ];
1720 expected = [
1721 "b"
1722 "aa"
1723 "cccc"
1724 ];
1725 };
1726
1727 testSortOnEmpty = {
1728 expr = sortOn (throw "nope") [ ];
1729 expected = [ ];
1730 };
1731
1732 testSortOnIncomparable = {
1733 expr = map (x: x.f x.ok) (
1734 sortOn (x: x.ok) [
1735 {
1736 ok = 1;
1737 f = x: x;
1738 }
1739 {
1740 ok = 3;
1741 f = x: x + 3;
1742 }
1743 {
1744 ok = 2;
1745 f = x: x;
1746 }
1747 ]
1748 );
1749 expected = [
1750 1
1751 2
1752 6
1753 ];
1754 };
1755
1756 testReplaceString = {
1757 expr = replaceString "world" "Nix" "Hello, world!";
1758 expected = "Hello, Nix!";
1759 };
1760
1761 testReplicate = {
1762 expr = replicate 3 "a";
1763 expected = [
1764 "a"
1765 "a"
1766 "a"
1767 ];
1768 };
1769
1770 testToIntShouldConvertStringToInt = {
1771 expr = toInt "27";
1772 expected = 27;
1773 };
1774
1775 testToIntShouldThrowErrorIfItCouldNotConvertToInt = {
1776 expr = builtins.tryEval (toInt "\"foo\"");
1777 expected = {
1778 success = false;
1779 value = false;
1780 };
1781 };
1782
1783 testHasAttrByPathTrue = {
1784 expr = hasAttrByPath [ "a" "b" ] {
1785 a = {
1786 b = "yey";
1787 };
1788 };
1789 expected = true;
1790 };
1791
1792 testHasAttrByPathFalse = {
1793 expr = hasAttrByPath [ "a" "b" ] {
1794 a = {
1795 c = "yey";
1796 };
1797 };
1798 expected = false;
1799 };
1800
1801 testHasAttrByPathNonStrict = {
1802 expr = hasAttrByPath [ ] (throw "do not use");
1803 expected = true;
1804 };
1805
1806 testLongestValidPathPrefix_empty_empty = {
1807 expr = attrsets.longestValidPathPrefix [ ] { };
1808 expected = [ ];
1809 };
1810
1811 testLongestValidPathPrefix_empty_nonStrict = {
1812 expr = attrsets.longestValidPathPrefix [ ] (throw "do not use");
1813 expected = [ ];
1814 };
1815
1816 testLongestValidPathPrefix_zero = {
1817 expr = attrsets.longestValidPathPrefix [ "a" (throw "do not use") ] { d = null; };
1818 expected = [ ];
1819 };
1820
1821 testLongestValidPathPrefix_zero_b = {
1822 expr = attrsets.longestValidPathPrefix [ "z" "z" ] "remarkably harmonious";
1823 expected = [ ];
1824 };
1825
1826 testLongestValidPathPrefix_one = {
1827 expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a = null; };
1828 expected = [ "a" ];
1829 };
1830
1831 testLongestValidPathPrefix_two = {
1832 expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b = null; };
1833 expected = [
1834 "a"
1835 "b"
1836 ];
1837 };
1838
1839 testLongestValidPathPrefix_three = {
1840 expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c = null; };
1841 expected = [
1842 "a"
1843 "b"
1844 "c"
1845 ];
1846 };
1847
1848 testLongestValidPathPrefix_three_extra = {
1849 expr = attrsets.longestValidPathPrefix [ "a" "b" "c" ] { a.b.c.d = throw "nope"; };
1850 expected = [
1851 "a"
1852 "b"
1853 "c"
1854 ];
1855 };
1856
1857 testFindFirstIndexExample1 = {
1858 expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [
1859 1
1860 6
1861 4
1862 ];
1863 expected = 1;
1864 };
1865
1866 testFindFirstIndexExample2 = {
1867 expr = lists.findFirstIndex (x: x > 9) "a very specific default" [
1868 1
1869 6
1870 4
1871 ];
1872 expected = "a very specific default";
1873 };
1874
1875 testFindFirstIndexEmpty = {
1876 expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null [ ];
1877 expected = null;
1878 };
1879
1880 testFindFirstIndexSingleMatch = {
1881 expr = lists.findFirstIndex (x: x == 5) null [ 5 ];
1882 expected = 0;
1883 };
1884
1885 testFindFirstIndexSingleDefault = {
1886 expr = lists.findFirstIndex (x: false) null [
1887 (abort "if the predicate doesn't access the value, it must not be evaluated")
1888 ];
1889 expected = null;
1890 };
1891
1892 testFindFirstIndexNone = {
1893 expr = builtins.tryEval (
1894 lists.findFirstIndex (x: x == 2) null [
1895 1
1896 (throw "the last element must be evaluated when there's no match")
1897 ]
1898 );
1899 expected = {
1900 success = false;
1901 value = false;
1902 };
1903 };
1904
1905 # Makes sure that the implementation doesn't cause a stack overflow
1906 testFindFirstIndexBig = {
1907 expr = lists.findFirstIndex (x: x == 1000000) null (range 0 1000000);
1908 expected = 1000000;
1909 };
1910
1911 testFindFirstIndexLazy = {
1912 expr = lists.findFirstIndex (x: x == 1) null [
1913 1
1914 (abort "list elements after the match must not be evaluated")
1915 ];
1916 expected = 0;
1917 };
1918
1919 testFindFirstExample1 = {
1920 expr = lists.findFirst (x: x > 3) 7 [
1921 1
1922 6
1923 4
1924 ];
1925 expected = 6;
1926 };
1927
1928 testFindFirstExample2 = {
1929 expr = lists.findFirst (x: x > 9) 7 [
1930 1
1931 6
1932 4
1933 ];
1934 expected = 7;
1935 };
1936
1937 testAllUnique_true = {
1938 expr = allUnique [
1939 3
1940 2
1941 4
1942 1
1943 ];
1944 expected = true;
1945 };
1946 testAllUnique_false = {
1947 expr = allUnique [
1948 3
1949 2
1950 3
1951 4
1952 ];
1953 expected = false;
1954 };
1955
1956 testUniqueStrings_empty = {
1957 expr = uniqueStrings [ ];
1958 expected = [ ];
1959 };
1960 testUniqueStrings_singles = {
1961 expr = uniqueStrings [
1962 "all"
1963 "unique"
1964 "already"
1965 ];
1966 expected = [
1967 "all"
1968 "already"
1969 "unique"
1970 ];
1971 };
1972 testUniqueStrings_allDuplicates = {
1973 expr = uniqueStrings [
1974 "dup"
1975 "dup"
1976 "dup"
1977 ];
1978 expected = [ "dup" ];
1979 };
1980 testUniqueStrings_some_duplicates = {
1981 expr = uniqueStrings [
1982 "foo"
1983 "foo"
1984 "bar"
1985 "bar"
1986 "baz"
1987 ];
1988 expected = [
1989 "bar"
1990 "baz"
1991 "foo"
1992 ];
1993 };
1994 testUniqueStrings_unicode = {
1995 expr = uniqueStrings [
1996 "café"
1997 "@"
1998 "#"
1999 "@"
2000 "#"
2001 "$"
2002 "😎"
2003 "😎"
2004 "🙃"
2005 ""
2006 ""
2007 ];
2008 expected = [
2009 ""
2010 "#"
2011 "$"
2012 "@"
2013 "café"
2014 "😎"
2015 "🙃"
2016 ];
2017 };
2018
2019 # ATTRSETS
2020
2021 testGenAttrs = {
2022 expr = attrsets.genAttrs [ "foo" "bar" ] (name: "x_" + name);
2023 expected = {
2024 foo = "x_foo";
2025 bar = "x_bar";
2026 };
2027 };
2028 testGenAttrs' = {
2029 expr = attrsets.genAttrs' [ "foo" "bar" ] (s: nameValuePair ("x_" + s) ("y_" + s));
2030 expected = {
2031 x_foo = "y_foo";
2032 x_bar = "y_bar";
2033 };
2034 };
2035 testGenAttrs'Example2 = {
2036 expr = attrsets.genAttrs' [
2037 {
2038 x = "foo";
2039 y = "baz";
2040 }
2041 ] (s: lib.nameValuePair ("x_" + s.x) ("y_" + s.y));
2042 expected = {
2043 x_foo = "y_baz";
2044 };
2045 };
2046 testGenAttrs'ConflictingName = {
2047 # c.f. warning of genAttrs'
2048 expr = attrsets.genAttrs' [ "foo" "bar" "baz" ] (s: nameValuePair "foo" s);
2049 expected = {
2050 foo = "foo";
2051 };
2052 };
2053
2054 testConcatMapAttrs = {
2055 expr =
2056 concatMapAttrs
2057 (name: value: {
2058 ${name} = value;
2059 ${name + value} = value;
2060 })
2061 {
2062 foo = "bar";
2063 foobar = "baz";
2064 };
2065 expected = {
2066 foo = "bar";
2067 foobar = "baz";
2068 foobarbaz = "baz";
2069 };
2070 };
2071
2072 testFilterAttrs = {
2073 expr = filterAttrs (n: v: n != "a" && (v.hello or false) == true) {
2074 a.hello = true;
2075 b.hello = true;
2076 c = {
2077 hello = true;
2078 world = false;
2079 };
2080 d.hello = false;
2081 };
2082 expected = {
2083 b.hello = true;
2084 c = {
2085 hello = true;
2086 world = false;
2087 };
2088 };
2089 };
2090
2091 # code from example
2092 testFoldlAttrs = {
2093 expr = {
2094 example =
2095 foldlAttrs
2096 (acc: name: value: {
2097 sum = acc.sum + value;
2098 names = acc.names ++ [ name ];
2099 })
2100 {
2101 sum = 0;
2102 names = [ ];
2103 }
2104 {
2105 foo = 1;
2106 bar = 10;
2107 };
2108 # should just return the initial value
2109 emptySet = foldlAttrs (throw "function not needed") 123 { };
2110 # should just evaluate to the last value
2111 valuesNotNeeded =
2112 foldlAttrs
2113 (
2114 acc: _name: _v:
2115 acc
2116 )
2117 3
2118 {
2119 z = throw "value z not needed";
2120 a = throw "value a not needed";
2121 };
2122 # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string
2123 trivialAcc =
2124 foldlAttrs
2125 (
2126 acc: _name: v:
2127 acc * 10 + v
2128 )
2129 1
2130 {
2131 z = 1;
2132 a = 2;
2133 };
2134 };
2135 expected = {
2136 example = {
2137 sum = 11;
2138 names = [
2139 "bar"
2140 "foo"
2141 ];
2142 };
2143 emptySet = 123;
2144 valuesNotNeeded = 3;
2145 trivialAcc = 121;
2146 };
2147 };
2148
2149 testMergeAttrsListExample1 = {
2150 expr = attrsets.mergeAttrsList [
2151 {
2152 a = 0;
2153 b = 1;
2154 }
2155 {
2156 c = 2;
2157 d = 3;
2158 }
2159 ];
2160 expected = {
2161 a = 0;
2162 b = 1;
2163 c = 2;
2164 d = 3;
2165 };
2166 };
2167 testMergeAttrsListExample2 = {
2168 expr = attrsets.mergeAttrsList [
2169 { a = 0; }
2170 { a = 1; }
2171 ];
2172 expected = {
2173 a = 1;
2174 };
2175 };
2176 testMergeAttrsListExampleMany =
2177 let
2178 list = genList (
2179 n:
2180 listToAttrs (
2181 genList (
2182 m:
2183 let
2184 # Integer divide n by two to create duplicate attributes
2185 str = "halfn${toString (n / 2)}m${toString m}";
2186 in
2187 nameValuePair str str
2188 ) 100
2189 )
2190 ) 100;
2191 in
2192 {
2193 expr = attrsets.mergeAttrsList list;
2194 expected = foldl' mergeAttrs { } list;
2195 };
2196
2197 # code from the example
2198 testRecursiveUpdateUntil = {
2199 expr =
2200 recursiveUpdateUntil
2201 (
2202 path: l: r:
2203 path == [ "foo" ]
2204 )
2205 {
2206 # first attribute set
2207 foo.bar = 1;
2208 foo.baz = 2;
2209 bar = 3;
2210 }
2211 {
2212 #second attribute set
2213 foo.bar = 1;
2214 foo.quz = 2;
2215 baz = 4;
2216 };
2217 expected = {
2218 foo.bar = 1; # 'foo.*' from the second set
2219 foo.quz = 2;
2220 bar = 3; # 'bar' from the first set
2221 baz = 4; # 'baz' from the second set
2222 };
2223 };
2224
2225 testMatchAttrsMatchingExact = {
2226 expr =
2227 matchAttrs
2228 {
2229 cpu = {
2230 bits = 64;
2231 };
2232 }
2233 {
2234 cpu = {
2235 bits = 64;
2236 };
2237 };
2238 expected = true;
2239 };
2240
2241 testMatchAttrsMismatch = {
2242 expr =
2243 matchAttrs
2244 {
2245 cpu = {
2246 bits = 128;
2247 };
2248 }
2249 {
2250 cpu = {
2251 bits = 64;
2252 };
2253 };
2254 expected = false;
2255 };
2256
2257 testMatchAttrsMatchingImplicit = {
2258 expr = matchAttrs { cpu = { }; } {
2259 cpu = {
2260 bits = 64;
2261 };
2262 };
2263 expected = true;
2264 };
2265
2266 testMatchAttrsMissingAttrs = {
2267 expr = matchAttrs { cpu = { }; } { };
2268 expected = false;
2269 };
2270
2271 testOverrideExistingEmpty = {
2272 expr = overrideExisting { } { a = 1; };
2273 expected = { };
2274 };
2275
2276 testOverrideExistingDisjoint = {
2277 expr = overrideExisting { b = 2; } { a = 1; };
2278 expected = {
2279 b = 2;
2280 };
2281 };
2282
2283 testOverrideExistingOverride = {
2284 expr = overrideExisting {
2285 a = 3;
2286 b = 2;
2287 } { a = 1; };
2288 expected = {
2289 a = 1;
2290 b = 2;
2291 };
2292 };
2293
2294 testListAttrsReverse =
2295 let
2296 exampleAttrs = {
2297 foo = 1;
2298 bar = "asdf";
2299 baz = [
2300 1
2301 3
2302 3
2303 7
2304 ];
2305 fnord = null;
2306 };
2307 exampleSingletonList = [
2308 {
2309 name = "foo";
2310 value = 1;
2311 }
2312 ];
2313 in
2314 {
2315 expr = {
2316 isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) == exampleAttrs;
2317 isReverseToAttrsToList =
2318 attrsToList (builtins.listToAttrs exampleSingletonList) == exampleSingletonList;
2319 testDuplicatePruningBehaviour = attrsToList (
2320 builtins.listToAttrs [
2321 {
2322 name = "a";
2323 value = 2;
2324 }
2325 {
2326 name = "a";
2327 value = 1;
2328 }
2329 ]
2330 );
2331 };
2332 expected = {
2333 isReverseToAttrsToList = true;
2334 isReverseToListToAttrs = true;
2335 testDuplicatePruningBehaviour = [
2336 {
2337 name = "a";
2338 value = 2;
2339 }
2340 ];
2341 };
2342 };
2343
2344 testAttrsToListsCanDealWithFunctions = testingEval (attrsToList {
2345 someFunc = a: a + 1;
2346 });
2347
2348 # FIXED-POINTS
2349
2350 testFix = {
2351 expr = fix (x: {
2352 a = if x ? a then "a" else "b";
2353 });
2354 expected = {
2355 a = "a";
2356 };
2357 };
2358
2359 testToExtension = {
2360 expr = [
2361 (fix (final: {
2362 a = 0;
2363 c = final.a;
2364 }))
2365 (fix (
2366 extends
2367 (toExtension {
2368 a = 1;
2369 b = 2;
2370 })
2371 (final: {
2372 a = 0;
2373 c = final.a;
2374 })
2375 ))
2376 (fix (
2377 extends
2378 (toExtension (prev: {
2379 a = 1;
2380 b = prev.a;
2381 }))
2382 (final: {
2383 a = 0;
2384 c = final.a;
2385 })
2386 ))
2387 (fix (
2388 extends
2389 (toExtension (
2390 final: prev: {
2391 a = 1;
2392 b = prev.a;
2393 c = final.a + 1;
2394 }
2395 ))
2396 (final: {
2397 a = 0;
2398 c = final.a;
2399 })
2400 ))
2401 ];
2402 expected = [
2403 {
2404 a = 0;
2405 c = 0;
2406 }
2407 {
2408 a = 1;
2409 b = 2;
2410 c = 1;
2411 }
2412 {
2413 a = 1;
2414 b = 0;
2415 c = 1;
2416 }
2417 {
2418 a = 1;
2419 b = 0;
2420 c = 2;
2421 }
2422 ];
2423 };
2424
2425 # GENERATORS
2426 # these tests assume attributes are converted to lists
2427 # in alphabetical order
2428
2429 testMkKeyValueDefault = {
2430 expr = generators.mkKeyValueDefault { } ":" "f:oo" "bar";
2431 expected = ''f\:oo:bar'';
2432 };
2433
2434 testMkValueString = {
2435 expr =
2436 let
2437 vals = {
2438 int = 42;
2439 string = ''fo"o'';
2440 bool = true;
2441 bool2 = false;
2442 null = null;
2443 # float = 42.23; # floats are strange
2444 };
2445 in
2446 mapAttrs (const (generators.mkValueStringDefault { })) vals;
2447 expected = {
2448 int = "42";
2449 string = ''fo"o'';
2450 bool = "true";
2451 bool2 = "false";
2452 null = "null";
2453 # float = "42.23" true false [ "bar" ] ]'';
2454 };
2455 };
2456
2457 testToKeyValue = {
2458 expr = generators.toKeyValue { } {
2459 key = "value";
2460 "other=key" = "baz";
2461 };
2462 expected = ''
2463 key=value
2464 other\=key=baz
2465 '';
2466 };
2467
2468 testToINIEmpty = {
2469 expr = generators.toINI { } { };
2470 expected = "";
2471 };
2472
2473 testToINIEmptySection = {
2474 expr = generators.toINI { } {
2475 foo = { };
2476 bar = { };
2477 };
2478 expected = ''
2479 [bar]
2480
2481 [foo]
2482 '';
2483 };
2484
2485 testToINIDuplicateKeys = {
2486 expr = generators.toINI { listsAsDuplicateKeys = true; } {
2487 foo.bar = true;
2488 baz.qux = [
2489 1
2490 false
2491 ];
2492 };
2493 expected = ''
2494 [baz]
2495 qux=1
2496 qux=false
2497
2498 [foo]
2499 bar=true
2500 '';
2501 };
2502
2503 testToINIDefaultEscapes = {
2504 expr = generators.toINI { } {
2505 "no [ and ] allowed unescaped" = {
2506 "and also no = in keys" = 42;
2507 };
2508 };
2509 expected = ''
2510 [no \[ and \] allowed unescaped]
2511 and also no \= in keys=42
2512 '';
2513 };
2514
2515 testToINIDefaultFull = {
2516 expr = generators.toINI { } {
2517 "section 1" = {
2518 attribute1 = 5;
2519 x = "Me-se JarJar Binx";
2520 # booleans are converted verbatim by default
2521 boolean = false;
2522 };
2523 "foo[]" = {
2524 "he\\h=he" = "this is okay";
2525 };
2526 };
2527 expected = ''
2528 [foo\[\]]
2529 he\h\=he=this is okay
2530
2531 [section 1]
2532 attribute1=5
2533 boolean=false
2534 x=Me-se JarJar Binx
2535 '';
2536 };
2537
2538 testToINIWithGlobalSectionEmpty = {
2539 expr = generators.toINIWithGlobalSection { } {
2540 globalSection = {
2541 };
2542 sections = {
2543 };
2544 };
2545 expected = '''';
2546 };
2547
2548 testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI =
2549 let
2550 sections = {
2551 "section 1" = {
2552 attribute1 = 5;
2553 x = "Me-se JarJar Binx";
2554 };
2555 "foo" = {
2556 "he\\h=he" = "this is okay";
2557 };
2558 };
2559 in
2560 {
2561 expr = generators.toINIWithGlobalSection { } {
2562 globalSection = { };
2563 sections = sections;
2564 };
2565 expected = generators.toINI { } sections;
2566 };
2567
2568 testToINIWithGlobalSectionFull = {
2569 expr = generators.toINIWithGlobalSection { } {
2570 globalSection = {
2571 foo = "bar";
2572 test = false;
2573 };
2574 sections = {
2575 "section 1" = {
2576 attribute1 = 5;
2577 x = "Me-se JarJar Binx";
2578 };
2579 "foo" = {
2580 "he\\h=he" = "this is okay";
2581 };
2582 };
2583 };
2584 expected = ''
2585 foo=bar
2586 test=false
2587
2588 [foo]
2589 he\h\=he=this is okay
2590
2591 [section 1]
2592 attribute1=5
2593 x=Me-se JarJar Binx
2594 '';
2595 };
2596
2597 testToGitINI = {
2598 expr = generators.toGitINI {
2599 user = {
2600 email = "user@example.org";
2601 name = "John Doe";
2602 signingKey = "00112233445566778899AABBCCDDEEFF";
2603 };
2604 gpg.program = "path-to-gpg";
2605 tag.gpgSign = true;
2606 include.path = "~/path/to/config.inc";
2607 includeIf."gitdif:~/src/dir".path = "~/path/to/conditional.inc";
2608 extra = {
2609 boolean = true;
2610 integer = 38;
2611 name = "value";
2612 subsection.value = "test";
2613 };
2614 };
2615 expected = ''
2616 [extra]
2617 ${"\t"}boolean = true
2618 ${"\t"}integer = 38
2619 ${"\t"}name = "value"
2620
2621 [extra "subsection"]
2622 ${"\t"}value = "test"
2623
2624 [gpg]
2625 ${"\t"}program = "path-to-gpg"
2626
2627 [include]
2628 ${"\t"}path = "~/path/to/config.inc"
2629
2630 [includeIf "gitdif:~/src/dir"]
2631 ${"\t"}path = "~/path/to/conditional.inc"
2632
2633 [tag]
2634 ${"\t"}gpgSign = true
2635
2636 [user]
2637 ${"\t"}email = "user@example.org"
2638 ${"\t"}name = "John Doe"
2639 ${"\t"}signingKey = "00112233445566778899AABBCCDDEEFF"
2640 '';
2641 };
2642
2643 # right now only invocation check
2644 testToJSONSimple =
2645 let
2646 val = {
2647 foobar = [
2648 "baz"
2649 1
2650 2
2651 3
2652 ];
2653 };
2654 in
2655 {
2656 expr = generators.toJSON { } val;
2657 # trivial implementation
2658 expected = builtins.toJSON val;
2659 };
2660
2661 # right now only invocation check
2662 testToYAMLSimple =
2663 let
2664 val = {
2665 list = [
2666 { one = 1; }
2667 { two = 2; }
2668 ];
2669 all = 42;
2670 };
2671 in
2672 {
2673 expr = generators.toYAML { } val;
2674 # trivial implementation
2675 expected = builtins.toJSON val;
2676 };
2677
2678 testToPretty =
2679 let
2680 deriv = derivation {
2681 name = "test";
2682 builder = "/bin/sh";
2683 system = "aarch64-linux";
2684 };
2685 in
2686 {
2687 expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec {
2688 int = 42;
2689 float = 0.1337;
2690 bool = true;
2691 emptystring = "";
2692 string = "fn\${o}\"r\\d";
2693 newlinestring = "\n";
2694 path = /. + "/foo";
2695 null_ = null;
2696 function = x: x;
2697 functionArgs =
2698 {
2699 arg ? 4,
2700 foo,
2701 }:
2702 arg;
2703 list = [
2704 3
2705 4
2706 function
2707 [ false ]
2708 ];
2709 emptylist = [ ];
2710 attrs = {
2711 foo = null;
2712 "foo b/ar" = "baz";
2713 };
2714 emptyattrs = { };
2715 drv = deriv;
2716 };
2717 expected = rec {
2718 int = "42";
2719 float = "0.1337";
2720 bool = "true";
2721 emptystring = ''""'';
2722 string = ''"fn\''${o}\"r\\d"'';
2723 newlinestring = "\"\\n\"";
2724 path = "/foo";
2725 null_ = "null";
2726 function = "<function>";
2727 functionArgs = "<function, args: {arg?, foo}>";
2728 list = "[ 3 4 ${function} [ false ] ]";
2729 emptylist = "[ ]";
2730 attrs = "{ foo = null; \"foo b/ar\" = \"baz\"; }";
2731 emptyattrs = "{ }";
2732 drv = "<derivation ${deriv.name}>";
2733 };
2734 };
2735
2736 testToPrettyLimit =
2737 let
2738 a.b = 1;
2739 a.c = a;
2740 in
2741 {
2742 expr = generators.toPretty { } (
2743 generators.withRecursion {
2744 throwOnDepthLimit = false;
2745 depthLimit = 2;
2746 } a
2747 );
2748 expected = "{\n b = 1;\n c = {\n b = \"<unevaluated>\";\n c = {\n b = \"<unevaluated>\";\n c = \"<unevaluated>\";\n };\n };\n}";
2749 };
2750
2751 testToPrettyLimitThrow =
2752 let
2753 a.b = 1;
2754 a.c = a;
2755 in
2756 {
2757 expr =
2758 (builtins.tryEval (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a)))
2759 .success;
2760 expected = false;
2761 };
2762
2763 testWithRecursionDealsWithFunctors =
2764 let
2765 functor = {
2766 __functor = self: { a, b }: null;
2767 };
2768 a = {
2769 value = "1234";
2770 b = functor;
2771 c.d = functor;
2772 };
2773 in
2774 {
2775 expr = generators.toPretty { } (
2776 generators.withRecursion {
2777 depthLimit = 1;
2778 throwOnDepthLimit = false;
2779 } a
2780 );
2781 expected = "{\n b = <function, args: {a, b}>;\n c = {\n d = \"<unevaluated>\";\n };\n value = \"<unevaluated>\";\n}";
2782 };
2783
2784 testToPrettyMultiline = {
2785 expr = mapAttrs (const (generators.toPretty { })) {
2786 list = [
2787 3
2788 4
2789 [ false ]
2790 ];
2791 attrs = {
2792 foo = null;
2793 bar.foo = "baz";
2794 };
2795 newlinestring = "\n";
2796 multilinestring = ''
2797 hello
2798 ''${there}
2799 te'''st
2800 '';
2801 multilinestring' = ''
2802 hello
2803 there
2804 test'';
2805 };
2806 expected = {
2807 list = ''
2808 [
2809 3
2810 4
2811 [
2812 false
2813 ]
2814 ]'';
2815 attrs = ''
2816 {
2817 bar = {
2818 foo = "baz";
2819 };
2820 foo = null;
2821 }'';
2822 newlinestring = "''\n \n''";
2823 multilinestring = ''
2824 '''
2825 hello
2826 '''''${there}
2827 te''''st
2828 ''''';
2829 multilinestring' = ''
2830 '''
2831 hello
2832 there
2833 test''''';
2834
2835 };
2836 };
2837
2838 testToPrettyAllowPrettyValues = {
2839 expr = generators.toPretty { allowPrettyValues = true; } {
2840 __pretty = v: "«" + v + "»";
2841 val = "foo";
2842 };
2843 expected = "«foo»";
2844 };
2845
2846 testToPlistUnescaped = {
2847 expr = mapAttrs (const (generators.toPlist { })) {
2848 value = {
2849 nested.values = {
2850 int = 42;
2851 float = 0.1337;
2852 bool = true;
2853 emptystring = "";
2854 string = "fn\${o}\"r\\d";
2855 newlinestring = "\n";
2856 path = /. + "/foo";
2857 null_ = null;
2858 list = [
2859 3
2860 4
2861 "test"
2862 ];
2863 emptylist = [ ];
2864 attrs = {
2865 foo = null;
2866 "foo b/ar" = "baz";
2867 };
2868 emptyattrs = { };
2869 "keys are not <escaped>" = "and < neither are string values";
2870 };
2871 };
2872 };
2873 expected = {
2874 value = builtins.readFile ./test-to-plist-unescaped-expected.plist;
2875 };
2876 };
2877
2878 testToPlistEscaped = {
2879 expr = mapAttrs (const (generators.toPlist { escape = true; })) {
2880 value = {
2881 nested.values = {
2882 int = 42;
2883 float = 0.1337;
2884 bool = true;
2885 emptystring = "";
2886 string = "fn\${o}\"r\\d";
2887 newlinestring = "\n";
2888 path = /. + "/foo";
2889 null_ = null;
2890 list = [
2891 3
2892 4
2893 "test"
2894 ];
2895 emptylist = [ ];
2896 attrs = {
2897 foo = null;
2898 "foo b/ar" = "baz";
2899 };
2900 emptyattrs = { };
2901 "keys are <escaped>" = "and < so are string values";
2902 };
2903 };
2904 };
2905 expected = {
2906 value = builtins.readFile ./test-to-plist-escaped-expected.plist;
2907 };
2908 };
2909
2910 testToLuaEmptyAttrSet = {
2911 expr = generators.toLua { } { };
2912 expected = ''{}'';
2913 };
2914
2915 testToLuaEmptyList = {
2916 expr = generators.toLua { } [ ];
2917 expected = ''{}'';
2918 };
2919
2920 testToLuaListOfVariousTypes = {
2921 expr = generators.toLua { } [
2922 null
2923 43
2924 3.14159
2925 true
2926 ];
2927 expected = ''
2928 {
2929 nil,
2930 43,
2931 3.14159,
2932 true
2933 }'';
2934 };
2935
2936 testToLuaString = {
2937 expr = generators.toLua { } ''double-quote (") and single quotes (')'';
2938 expected = ''"double-quote (\") and single quotes (')"'';
2939 };
2940
2941 testToLuaAttrsetWithLuaInline = {
2942 expr = generators.toLua { } { x = generators.mkLuaInline ''"abc" .. "def"''; };
2943 expected = ''
2944 {
2945 ["x"] = ("abc" .. "def")
2946 }'';
2947 };
2948
2949 testToLuaAttrsetWithSpaceInKey = {
2950 expr = generators.toLua { } { "some space and double-quote (\")" = 42; };
2951 expected = ''
2952 {
2953 ["some space and double-quote (\")"] = 42
2954 }'';
2955 };
2956
2957 testToLuaWithoutMultiline = {
2958 expr = generators.toLua { multiline = false; } [
2959 41
2960 43
2961 ];
2962 expected = ''{ 41, 43 }'';
2963 };
2964
2965 testToLuaEmptyBindings = {
2966 expr = generators.toLua { asBindings = true; } { };
2967 expected = "";
2968 };
2969
2970 testToLuaBindings = {
2971 expr = generators.toLua { asBindings = true; } {
2972 x1 = 41;
2973 _y = {
2974 a = 43;
2975 };
2976 };
2977 expected = ''
2978 _y = {
2979 ["a"] = 43
2980 }
2981 x1 = 41
2982 '';
2983 };
2984
2985 testToLuaPartialTableBindings = {
2986 expr = generators.toLua { asBindings = true; } { "x.y" = 42; };
2987 expected = ''
2988 x.y = 42
2989 '';
2990 };
2991
2992 testToLuaIndentedBindings = {
2993 expr =
2994 generators.toLua
2995 {
2996 asBindings = true;
2997 indent = " ";
2998 }
2999 {
3000 x = {
3001 y = 42;
3002 };
3003 };
3004 expected = " x = {\n [\"y\"] = 42\n }\n";
3005 };
3006
3007 testToLuaBindingsWithSpace = testingThrow (
3008 generators.toLua { asBindings = true; } { "with space" = 42; }
3009 );
3010
3011 testToLuaBindingsWithLeadingDigit = testingThrow (
3012 generators.toLua { asBindings = true; } { "11eleven" = 42; }
3013 );
3014
3015 testToLuaBasicExample = {
3016 expr = generators.toLua { } {
3017 cmd = [
3018 "typescript-language-server"
3019 "--stdio"
3020 ];
3021 settings.workspace.library = generators.mkLuaInline ''vim.api.nvim_get_runtime_file("", true)'';
3022 };
3023 expected = ''
3024 {
3025 ["cmd"] = {
3026 "typescript-language-server",
3027 "--stdio"
3028 },
3029 ["settings"] = {
3030 ["workspace"] = {
3031 ["library"] = (vim.api.nvim_get_runtime_file("", true))
3032 }
3033 }
3034 }'';
3035 };
3036
3037 # CLI
3038
3039 testToGNUCommandLine = {
3040 expr = cli.toGNUCommandLine { } {
3041 data = builtins.toJSON { id = 0; };
3042 X = "PUT";
3043 retry = 3;
3044 retry-delay = null;
3045 url = [
3046 "https://example.com/foo"
3047 "https://example.com/bar"
3048 ];
3049 silent = false;
3050 verbose = true;
3051 };
3052
3053 expected = [
3054 "-X"
3055 "PUT"
3056 "--data"
3057 "{\"id\":0}"
3058 "--retry"
3059 "3"
3060 "--url"
3061 "https://example.com/foo"
3062 "--url"
3063 "https://example.com/bar"
3064 "--verbose"
3065 ];
3066 };
3067
3068 testToGNUCommandLineSeparator = {
3069 expr = cli.toGNUCommandLine { optionValueSeparator = "="; } {
3070 data = builtins.toJSON { id = 0; };
3071 X = "PUT";
3072 retry = 3;
3073 retry-delay = null;
3074 url = [
3075 "https://example.com/foo"
3076 "https://example.com/bar"
3077 ];
3078 silent = false;
3079 verbose = true;
3080 };
3081
3082 expected = [
3083 "-X=PUT"
3084 "--data={\"id\":0}"
3085 "--retry=3"
3086 "--url=https://example.com/foo"
3087 "--url=https://example.com/bar"
3088 "--verbose"
3089 ];
3090 };
3091
3092 testToGNUCommandLineShell = {
3093 expr = cli.toGNUCommandLineShell { } {
3094 data = builtins.toJSON { id = 0; };
3095 X = "PUT";
3096 retry = 3;
3097 retry-delay = null;
3098 url = [
3099 "https://example.com/foo"
3100 "https://example.com/bar"
3101 ];
3102 silent = false;
3103 verbose = true;
3104 };
3105
3106 expected = "-X PUT --data '{\"id\":0}' --retry 3 --url https://example.com/foo --url https://example.com/bar --verbose";
3107 };
3108
3109 testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName {
3110 name = "..foo";
3111 expected = "foo";
3112 };
3113
3114 testSanitizeDerivationNameUnicode = testSanitizeDerivationName {
3115 name = "fö";
3116 expected = "f-";
3117 };
3118
3119 testSanitizeDerivationNameAscii = testSanitizeDerivationName {
3120 name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
3121 expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-";
3122 };
3123
3124 testSanitizeDerivationNameTooLong = testSanitizeDerivationName {
3125 name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
3126 expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong";
3127 };
3128
3129 testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName {
3130 name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&";
3131 expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-";
3132 };
3133
3134 testSanitizeDerivationNameEmpty = testSanitizeDerivationName {
3135 name = "";
3136 expected = "unknown";
3137 };
3138
3139 # https://github.com/NixOS/nixpkgs/issues/396849
3140 "test: submodule definitions aren't unchecked when evaluating submodule documentation" = {
3141 expr =
3142 let
3143 module =
3144 { lib, ... }:
3145 {
3146 options.foo = lib.mkOption { type = lib.types.submodule submodule; };
3147 };
3148
3149 submodule = {
3150 options.bar = lib.mkOption { type = lib.types.int; };
3151 config.submoduleWrong = throw "yikes";
3152 };
3153
3154 options = (evalModules { modules = [ module ]; }).options;
3155
3156 renderableOpts = filter (o: !o.internal) (optionAttrSetToDocList options);
3157 # Evaluate the whole docs
3158 in
3159 builtins.deepSeq renderableOpts
3160 # Return the locations
3161 (map (o: o.loc) renderableOpts);
3162 expected = [
3163 [
3164 "_module"
3165 "args"
3166 ]
3167 [ "foo" ]
3168 [
3169 "foo"
3170 "bar"
3171 ]
3172 ];
3173 };
3174
3175 testFreeformOptions = {
3176 expr =
3177 let
3178 submodule =
3179 { lib, ... }:
3180 {
3181 freeformType = lib.types.attrsOf (
3182 lib.types.submodule {
3183 options.bar = lib.mkOption { };
3184 }
3185 );
3186 options.bar = lib.mkOption { };
3187 };
3188
3189 module =
3190 { lib, ... }:
3191 {
3192 options.foo = lib.mkOption {
3193 type = lib.types.submodule submodule;
3194 };
3195 };
3196
3197 options =
3198 (evalModules {
3199 modules = [ module ];
3200 }).options;
3201
3202 locs = filter (o: !o.internal) (optionAttrSetToDocList options);
3203 in
3204 map (o: o.loc) locs;
3205 expected = [
3206 [
3207 "_module"
3208 "args"
3209 ]
3210 [ "foo" ]
3211 [
3212 "foo"
3213 "<name>"
3214 "bar"
3215 ]
3216 [
3217 "foo"
3218 "bar"
3219 ]
3220 ];
3221 };
3222
3223 testDocOptionVisiblity = {
3224 expr =
3225 let
3226 submodule =
3227 { lib, ... }:
3228 {
3229 freeformType = lib.types.attrsOf (
3230 lib.types.submodule {
3231 options.bar = lib.mkOption { };
3232 }
3233 );
3234 options.foo = lib.mkOption { };
3235 };
3236
3237 module =
3238 { lib, ... }:
3239 {
3240 options = {
3241 shallow = lib.mkOption {
3242 type = lib.types.submodule submodule;
3243 visible = "shallow";
3244 };
3245 transparent = lib.mkOption {
3246 type = lib.types.submodule submodule;
3247 visible = "transparent";
3248 };
3249 "true" = lib.mkOption {
3250 type = lib.types.submodule submodule;
3251 visible = true;
3252 };
3253 "false" = lib.mkOption {
3254 type = lib.types.submodule submodule;
3255 visible = false;
3256 };
3257 "internal" = lib.mkOption {
3258 type = lib.types.submodule submodule;
3259 internal = true;
3260 };
3261 };
3262 };
3263
3264 options =
3265 (evalModules {
3266 modules = [ module ];
3267 }).options;
3268 in
3269 pipe options [
3270 optionAttrSetToDocList
3271 (filter (opt: !(builtins.elem "_module" opt.loc)))
3272 (map (
3273 opt:
3274 nameValuePair opt.name {
3275 inherit (opt) visible internal;
3276 }
3277 ))
3278 listToAttrs
3279 ];
3280 expected = {
3281 shallow = {
3282 visible = true;
3283 internal = false;
3284 };
3285 transparent = {
3286 visible = false;
3287 internal = false;
3288 };
3289 "transparent.foo" = {
3290 visible = true;
3291 internal = false;
3292 };
3293 "transparent.<name>.bar" = {
3294 visible = true;
3295 internal = false;
3296 };
3297 "true" = {
3298 visible = true;
3299 internal = false;
3300 };
3301 "true.foo" = {
3302 visible = true;
3303 internal = false;
3304 };
3305 "true.<name>.bar" = {
3306 visible = true;
3307 internal = false;
3308 };
3309 "false" = {
3310 visible = false;
3311 internal = false;
3312 };
3313 "internal" = {
3314 visible = true;
3315 internal = true;
3316 };
3317 "internal.foo" = {
3318 visible = true;
3319 internal = false;
3320 };
3321 "internal.<name>.bar" = {
3322 visible = true;
3323 internal = false;
3324 };
3325 };
3326 };
3327
3328 testAttrsWithName = {
3329 expr =
3330 let
3331 eval = evalModules {
3332 modules = [
3333 {
3334 options = {
3335 foo = lib.mkOption {
3336 type = lib.types.attrsWith {
3337 placeholder = "MyCustomPlaceholder";
3338 elemType = lib.types.submodule {
3339 options.bar = lib.mkOption {
3340 type = lib.types.int;
3341 default = 42;
3342 };
3343 };
3344 };
3345 };
3346 };
3347 }
3348 ];
3349 };
3350 opt = eval.options.foo;
3351 in
3352 (opt.type.getSubOptions opt.loc).bar.loc;
3353 expected = [
3354 "foo"
3355 "<MyCustomPlaceholder>"
3356 "bar"
3357 ];
3358 };
3359
3360 testShowOptionWithPlaceholder = {
3361 # <name>, *, should not be escaped. It is used as a placeholder by convention.
3362 # Other symbols should be escaped. `{}`
3363 expr = lib.showOption [
3364 "<name>"
3365 "<myName>"
3366 "*"
3367 "{foo}"
3368 ];
3369 expected = "<name>.<myName>.*.\"{foo}\"";
3370 };
3371
3372 testCartesianProductOfEmptySet = {
3373 expr = cartesianProduct { };
3374 expected = [ { } ];
3375 };
3376
3377 testCartesianProductOfOneSet = {
3378 expr = cartesianProduct {
3379 a = [
3380 1
3381 2
3382 3
3383 ];
3384 };
3385 expected = [
3386 { a = 1; }
3387 { a = 2; }
3388 { a = 3; }
3389 ];
3390 };
3391
3392 testCartesianProductOfTwoSets = {
3393 expr = cartesianProduct {
3394 a = [ 1 ];
3395 b = [
3396 10
3397 20
3398 ];
3399 };
3400 expected = [
3401 {
3402 a = 1;
3403 b = 10;
3404 }
3405 {
3406 a = 1;
3407 b = 20;
3408 }
3409 ];
3410 };
3411
3412 testCartesianProductOfTwoSetsWithOneEmpty = {
3413 expr = cartesianProduct {
3414 a = [ ];
3415 b = [
3416 10
3417 20
3418 ];
3419 };
3420 expected = [ ];
3421 };
3422
3423 testCartesianProductOfThreeSets = {
3424 expr = cartesianProduct {
3425 a = [
3426 1
3427 2
3428 3
3429 ];
3430 b = [
3431 10
3432 20
3433 30
3434 ];
3435 c = [
3436 100
3437 200
3438 300
3439 ];
3440 };
3441 expected = [
3442 {
3443 a = 1;
3444 b = 10;
3445 c = 100;
3446 }
3447 {
3448 a = 1;
3449 b = 10;
3450 c = 200;
3451 }
3452 {
3453 a = 1;
3454 b = 10;
3455 c = 300;
3456 }
3457
3458 {
3459 a = 1;
3460 b = 20;
3461 c = 100;
3462 }
3463 {
3464 a = 1;
3465 b = 20;
3466 c = 200;
3467 }
3468 {
3469 a = 1;
3470 b = 20;
3471 c = 300;
3472 }
3473
3474 {
3475 a = 1;
3476 b = 30;
3477 c = 100;
3478 }
3479 {
3480 a = 1;
3481 b = 30;
3482 c = 200;
3483 }
3484 {
3485 a = 1;
3486 b = 30;
3487 c = 300;
3488 }
3489
3490 {
3491 a = 2;
3492 b = 10;
3493 c = 100;
3494 }
3495 {
3496 a = 2;
3497 b = 10;
3498 c = 200;
3499 }
3500 {
3501 a = 2;
3502 b = 10;
3503 c = 300;
3504 }
3505
3506 {
3507 a = 2;
3508 b = 20;
3509 c = 100;
3510 }
3511 {
3512 a = 2;
3513 b = 20;
3514 c = 200;
3515 }
3516 {
3517 a = 2;
3518 b = 20;
3519 c = 300;
3520 }
3521
3522 {
3523 a = 2;
3524 b = 30;
3525 c = 100;
3526 }
3527 {
3528 a = 2;
3529 b = 30;
3530 c = 200;
3531 }
3532 {
3533 a = 2;
3534 b = 30;
3535 c = 300;
3536 }
3537
3538 {
3539 a = 3;
3540 b = 10;
3541 c = 100;
3542 }
3543 {
3544 a = 3;
3545 b = 10;
3546 c = 200;
3547 }
3548 {
3549 a = 3;
3550 b = 10;
3551 c = 300;
3552 }
3553
3554 {
3555 a = 3;
3556 b = 20;
3557 c = 100;
3558 }
3559 {
3560 a = 3;
3561 b = 20;
3562 c = 200;
3563 }
3564 {
3565 a = 3;
3566 b = 20;
3567 c = 300;
3568 }
3569
3570 {
3571 a = 3;
3572 b = 30;
3573 c = 100;
3574 }
3575 {
3576 a = 3;
3577 b = 30;
3578 c = 200;
3579 }
3580 {
3581 a = 3;
3582 b = 30;
3583 c = 300;
3584 }
3585 ];
3586 };
3587
3588 testMapCartesianProductOfOneSet = {
3589 expr = mapCartesianProduct ({ a }: a * 2) {
3590 a = [
3591 1
3592 2
3593 3
3594 ];
3595 };
3596 expected = [
3597 2
3598 4
3599 6
3600 ];
3601 };
3602
3603 testMapCartesianProductOfTwoSets = {
3604 expr = mapCartesianProduct ({ a, b }: a + b) {
3605 a = [ 1 ];
3606 b = [
3607 10
3608 20
3609 ];
3610 };
3611 expected = [
3612 11
3613 21
3614 ];
3615 };
3616
3617 testMapCartesianProcutOfTwoSetsWithOneEmpty = {
3618 expr = mapCartesianProduct (x: x.a + x.b) {
3619 a = [ ];
3620 b = [
3621 10
3622 20
3623 ];
3624 };
3625 expected = [ ];
3626 };
3627
3628 testMapCartesianProductOfThreeSets = {
3629 expr =
3630 mapCartesianProduct
3631 (
3632 {
3633 a,
3634 b,
3635 c,
3636 }:
3637 a + b + c
3638 )
3639 {
3640 a = [
3641 1
3642 2
3643 3
3644 ];
3645 b = [
3646 10
3647 20
3648 30
3649 ];
3650 c = [
3651 100
3652 200
3653 300
3654 ];
3655 };
3656 expected = [
3657 111
3658 211
3659 311
3660 121
3661 221
3662 321
3663 131
3664 231
3665 331
3666 112
3667 212
3668 312
3669 122
3670 222
3671 322
3672 132
3673 232
3674 332
3675 113
3676 213
3677 313
3678 123
3679 223
3680 323
3681 133
3682 233
3683 333
3684 ];
3685 };
3686
3687 testMapAttrsToListRecursive = {
3688 expr = mapAttrsToListRecursive (p: v: "${concatStringsSep "." p}=${v}") {
3689 a = {
3690 b = "A";
3691 };
3692 c = {
3693 d = "B";
3694 e = {
3695 f = "C";
3696 g = "D";
3697 };
3698 };
3699 h = {
3700 i = {
3701 j = {
3702 k = "E";
3703 };
3704 };
3705 };
3706 };
3707 expected = [
3708 "a.b=A"
3709 "c.d=B"
3710 "c.e.f=C"
3711 "c.e.g=D"
3712 "h.i.j.k=E"
3713 ];
3714 };
3715
3716 testMapAttrsToListRecursiveWithLists = {
3717 expr = mapAttrsToListRecursive (p: v: v) {
3718 a = [ ];
3719 b = {
3720 c = [ [ ] ];
3721 };
3722 d = {
3723 e = {
3724 f = [ [ [ ] ] ];
3725 };
3726 };
3727 };
3728 expected = [
3729 [ ]
3730 [ [ ] ]
3731 [ [ [ ] ] ]
3732 ];
3733 };
3734
3735 testMapAttrsToListRecursiveCond = {
3736 expr = mapAttrsToListRecursiveCond (p: as: !(as ? stop)) (p: v: v) {
3737 a = {
3738 b = "A";
3739 };
3740 c = {
3741 d = "B";
3742 e = {
3743 stop = null;
3744 f = "C";
3745 g = {
3746 h = "D";
3747 };
3748 };
3749 };
3750 };
3751 expected = [
3752 "A"
3753 "B"
3754 {
3755 stop = null;
3756 f = "C";
3757 g = {
3758 h = "D";
3759 };
3760 }
3761 ];
3762 };
3763
3764 testMapAttrsToListRecursiveCondPath = {
3765 expr = mapAttrsToListRecursiveCond (p: as: length p < 2) (p: v: v) {
3766 a = {
3767 b = "A";
3768 };
3769 c = {
3770 d = "B";
3771 e = {
3772 f = "C";
3773 g = "D";
3774 };
3775 };
3776 h = {
3777 i = {
3778 j = {
3779 k = "E";
3780 };
3781 };
3782 };
3783 };
3784 expected = [
3785 "A"
3786 "B"
3787 {
3788 f = "C";
3789 g = "D";
3790 }
3791 {
3792 j = {
3793 k = "E";
3794 };
3795 }
3796 ];
3797 };
3798
3799 # The example from the showAttrPath documentation
3800 testShowAttrPathExample = {
3801 expr = showAttrPath [
3802 "foo"
3803 "10"
3804 "bar"
3805 ];
3806 expected = "foo.\"10\".bar";
3807 };
3808
3809 testShowAttrPathEmpty = {
3810 expr = showAttrPath [ ];
3811 expected = "<root attribute path>";
3812 };
3813
3814 testShowAttrPathVarious = {
3815 expr = showAttrPath [
3816 "."
3817 "foo"
3818 "2"
3819 "a2-b"
3820 "_bc'de"
3821 ];
3822 expected = ''".".foo."2".a2-b._bc'de'';
3823 };
3824
3825 testGroupBy = {
3826 expr = groupBy (n: toString (mod n 5)) (range 0 16);
3827 expected = {
3828 "0" = [
3829 0
3830 5
3831 10
3832 15
3833 ];
3834 "1" = [
3835 1
3836 6
3837 11
3838 16
3839 ];
3840 "2" = [
3841 2
3842 7
3843 12
3844 ];
3845 "3" = [
3846 3
3847 8
3848 13
3849 ];
3850 "4" = [
3851 4
3852 9
3853 14
3854 ];
3855 };
3856 };
3857
3858 testGroupBy' = {
3859 expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [
3860 5
3861 1
3862 2
3863 3
3864 4
3865 ];
3866 expected = {
3867 false = 3;
3868 true = 12;
3869 };
3870 };
3871
3872 # The example from the updateManyAttrsByPath documentation
3873 testUpdateManyAttrsByPathExample = {
3874 expr = updateManyAttrsByPath [
3875 {
3876 path = [
3877 "a"
3878 "b"
3879 ];
3880 update = old: { d = old.c; };
3881 }
3882 {
3883 path = [
3884 "a"
3885 "b"
3886 "c"
3887 ];
3888 update = old: old + 1;
3889 }
3890 {
3891 path = [
3892 "x"
3893 "y"
3894 ];
3895 update = old: "xy";
3896 }
3897 ] { a.b.c = 0; };
3898 expected = {
3899 a = {
3900 b = {
3901 d = 1;
3902 };
3903 };
3904 x = {
3905 y = "xy";
3906 };
3907 };
3908 };
3909
3910 # If there are no updates, the value is passed through
3911 testUpdateManyAttrsByPathNone = {
3912 expr = updateManyAttrsByPath [ ] "something";
3913 expected = "something";
3914 };
3915
3916 # A single update to the root path is just like applying the function directly
3917 testUpdateManyAttrsByPathSingleIncrement = {
3918 expr = updateManyAttrsByPath [
3919 {
3920 path = [ ];
3921 update = old: old + 1;
3922 }
3923 ] 0;
3924 expected = 1;
3925 };
3926
3927 # Multiple updates can be applied are done in order
3928 testUpdateManyAttrsByPathMultipleIncrements = {
3929 expr = updateManyAttrsByPath [
3930 {
3931 path = [ ];
3932 update = old: old + "a";
3933 }
3934 {
3935 path = [ ];
3936 update = old: old + "b";
3937 }
3938 {
3939 path = [ ];
3940 update = old: old + "c";
3941 }
3942 ] "";
3943 expected = "abc";
3944 };
3945
3946 # If an update doesn't use the value, all previous updates are not evaluated
3947 testUpdateManyAttrsByPathLazy = {
3948 expr = updateManyAttrsByPath [
3949 {
3950 path = [ ];
3951 update = old: old + throw "nope";
3952 }
3953 {
3954 path = [ ];
3955 update = old: "untainted";
3956 }
3957 ] (throw "start");
3958 expected = "untainted";
3959 };
3960
3961 # Deeply nested attributes can be updated without affecting others
3962 testUpdateManyAttrsByPathDeep = {
3963 expr =
3964 updateManyAttrsByPath
3965 [
3966 {
3967 path = [
3968 "a"
3969 "b"
3970 "c"
3971 ];
3972 update = old: old + 1;
3973 }
3974 ]
3975 {
3976 a.b.c = 0;
3977
3978 a.b.z = 0;
3979 a.y.z = 0;
3980 x.y.z = 0;
3981 };
3982 expected = {
3983 a.b.c = 1;
3984
3985 a.b.z = 0;
3986 a.y.z = 0;
3987 x.y.z = 0;
3988 };
3989 };
3990
3991 # Nested attributes are updated first
3992 testUpdateManyAttrsByPathNestedBeforehand = {
3993 expr =
3994 updateManyAttrsByPath
3995 [
3996 {
3997 path = [ "a" ];
3998 update = old: old // { x = old.b; };
3999 }
4000 {
4001 path = [
4002 "a"
4003 "b"
4004 ];
4005 update = old: old + 1;
4006 }
4007 ]
4008 {
4009 a.b = 0;
4010 };
4011 expected = {
4012 a.b = 1;
4013 a.x = 1;
4014 };
4015 };
4016
4017 ## Levenshtein distance functions and co.
4018 testCommonPrefixLengthEmpty = {
4019 expr = strings.commonPrefixLength "" "hello";
4020 expected = 0;
4021 };
4022
4023 testCommonPrefixLengthSame = {
4024 expr = strings.commonPrefixLength "hello" "hello";
4025 expected = 5;
4026 };
4027
4028 testCommonPrefixLengthDiffering = {
4029 expr = strings.commonPrefixLength "hello" "hey";
4030 expected = 2;
4031 };
4032
4033 testCommonSuffixLengthEmpty = {
4034 expr = strings.commonSuffixLength "" "hello";
4035 expected = 0;
4036 };
4037
4038 testCommonSuffixLengthSame = {
4039 expr = strings.commonSuffixLength "hello" "hello";
4040 expected = 5;
4041 };
4042
4043 testCommonSuffixLengthDiffering = {
4044 expr = strings.commonSuffixLength "test" "rest";
4045 expected = 3;
4046 };
4047
4048 testLevenshteinEmpty = {
4049 expr = strings.levenshtein "" "";
4050 expected = 0;
4051 };
4052
4053 testLevenshteinOnlyAdd = {
4054 expr = strings.levenshtein "" "hello there";
4055 expected = 11;
4056 };
4057
4058 testLevenshteinOnlyRemove = {
4059 expr = strings.levenshtein "hello there" "";
4060 expected = 11;
4061 };
4062
4063 testLevenshteinOnlyTransform = {
4064 expr = strings.levenshtein "abcdef" "ghijkl";
4065 expected = 6;
4066 };
4067
4068 testLevenshteinMixed = {
4069 expr = strings.levenshtein "kitchen" "sitting";
4070 expected = 5;
4071 };
4072
4073 testLevenshteinAtMostZeroFalse = {
4074 expr = strings.levenshteinAtMost 0 "foo" "boo";
4075 expected = false;
4076 };
4077
4078 testLevenshteinAtMostZeroTrue = {
4079 expr = strings.levenshteinAtMost 0 "foo" "foo";
4080 expected = true;
4081 };
4082
4083 testLevenshteinAtMostOneFalse = {
4084 expr = strings.levenshteinAtMost 1 "car" "ct";
4085 expected = false;
4086 };
4087
4088 testLevenshteinAtMostOneTrue = {
4089 expr = strings.levenshteinAtMost 1 "car" "cr";
4090 expected = true;
4091 };
4092
4093 # We test levenshteinAtMost 2 particularly well because it uses a complicated
4094 # implementation
4095 testLevenshteinAtMostTwoIsEmpty = {
4096 expr = strings.levenshteinAtMost 2 "" "";
4097 expected = true;
4098 };
4099
4100 testLevenshteinAtMostTwoIsZero = {
4101 expr = strings.levenshteinAtMost 2 "abcdef" "abcdef";
4102 expected = true;
4103 };
4104
4105 testLevenshteinAtMostTwoIsOne = {
4106 expr = strings.levenshteinAtMost 2 "abcdef" "abddef";
4107 expected = true;
4108 };
4109
4110 testLevenshteinAtMostTwoDiff0False = {
4111 expr = strings.levenshteinAtMost 2 "abcdef" "aczyef";
4112 expected = false;
4113 };
4114
4115 testLevenshteinAtMostTwoDiff0Outer = {
4116 expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez";
4117 expected = true;
4118 };
4119
4120 testLevenshteinAtMostTwoDiff0DelLeft = {
4121 expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz";
4122 expected = true;
4123 };
4124
4125 testLevenshteinAtMostTwoDiff0DelRight = {
4126 expr = strings.levenshteinAtMost 2 "abcdef" "zabcde";
4127 expected = true;
4128 };
4129
4130 testLevenshteinAtMostTwoDiff1False = {
4131 expr = strings.levenshteinAtMost 2 "abcdef" "bddez";
4132 expected = false;
4133 };
4134
4135 testLevenshteinAtMostTwoDiff1DelLeft = {
4136 expr = strings.levenshteinAtMost 2 "abcdef" "bcdez";
4137 expected = true;
4138 };
4139
4140 testLevenshteinAtMostTwoDiff1DelRight = {
4141 expr = strings.levenshteinAtMost 2 "abcdef" "zbcde";
4142 expected = true;
4143 };
4144
4145 testLevenshteinAtMostTwoDiff2False = {
4146 expr = strings.levenshteinAtMost 2 "hello" "hxo";
4147 expected = false;
4148 };
4149
4150 testLevenshteinAtMostTwoDiff2True = {
4151 expr = strings.levenshteinAtMost 2 "hello" "heo";
4152 expected = true;
4153 };
4154
4155 testLevenshteinAtMostTwoDiff3 = {
4156 expr = strings.levenshteinAtMost 2 "hello" "ho";
4157 expected = false;
4158 };
4159
4160 testLevenshteinAtMostThreeFalse = {
4161 expr = strings.levenshteinAtMost 3 "hello" "Holla!";
4162 expected = false;
4163 };
4164
4165 testLevenshteinAtMostThreeTrue = {
4166 expr = strings.levenshteinAtMost 3 "hello" "Holla";
4167 expected = true;
4168 };
4169
4170 # DERIVATIONS
4171
4172 testLazyDerivationIsLazyInDerivationForAttrNames = {
4173 expr = attrNames (lazyDerivation {
4174 derivation = throw "not lazy enough";
4175 });
4176 # It's ok to add attribute names here when lazyDerivation is improved
4177 # in accordance with its inline comments.
4178 expected = [
4179 "drvPath"
4180 "meta"
4181 "name"
4182 "out"
4183 "outPath"
4184 "outputName"
4185 "outputs"
4186 "system"
4187 "type"
4188 ];
4189 };
4190
4191 testLazyDerivationIsLazyInDerivationForPassthruAttr = {
4192 expr =
4193 (lazyDerivation {
4194 derivation = throw "not lazy enough";
4195 passthru.tests = "whatever is in tests";
4196 }).tests;
4197 expected = "whatever is in tests";
4198 };
4199
4200 testLazyDerivationIsLazyInDerivationForPassthruAttr2 = {
4201 # passthru.tests is not a special case. It works for any attr.
4202 expr =
4203 (lazyDerivation {
4204 derivation = throw "not lazy enough";
4205 passthru.foo = "whatever is in foo";
4206 }).foo;
4207 expected = "whatever is in foo";
4208 };
4209
4210 testLazyDerivationIsLazyInDerivationForMeta = {
4211 expr =
4212 (lazyDerivation {
4213 derivation = throw "not lazy enough";
4214 meta = "whatever is in meta";
4215 }).meta;
4216 expected = "whatever is in meta";
4217 };
4218
4219 testLazyDerivationReturnsDerivationAttrs =
4220 let
4221 derivation = {
4222 type = "derivation";
4223 outputs = [ "out" ];
4224 out = "test out";
4225 outPath = "test outPath";
4226 outputName = "out";
4227 drvPath = "test drvPath";
4228 name = "test name";
4229 system = "test system";
4230 meta = "test meta";
4231 };
4232 in
4233 {
4234 expr = lazyDerivation { inherit derivation; };
4235 expected = derivation;
4236 };
4237
4238 testOptionalDrvAttr =
4239 let
4240 mkDerivation =
4241 args:
4242 derivation (
4243 args
4244 // {
4245 builder = "builder";
4246 system = "system";
4247 __ignoreNulls = true;
4248 }
4249 );
4250 in
4251 {
4252 expr =
4253 (mkDerivation {
4254 name = "foo";
4255 x = optionalDrvAttr true 1;
4256 y = optionalDrvAttr false 1;
4257 }).drvPath;
4258 expected =
4259 (mkDerivation {
4260 name = "foo";
4261 x = 1;
4262 }).drvPath;
4263 };
4264
4265 testLazyDerivationMultiOutputReturnsDerivationAttrs =
4266 let
4267 derivation = {
4268 type = "derivation";
4269 outputs = [
4270 "out"
4271 "dev"
4272 ];
4273 dev = "test dev";
4274 out = "test out";
4275 outPath = "test outPath";
4276 outputName = "out";
4277 drvPath = "test drvPath";
4278 name = "test name";
4279 system = "test system";
4280 meta.position = "/hi:23";
4281 };
4282 in
4283 {
4284 expr = lazyDerivation {
4285 inherit derivation;
4286 outputs = [
4287 "out"
4288 "dev"
4289 ];
4290 passthru.meta.position = "/hi:23";
4291 };
4292 expected = derivation;
4293 };
4294
4295 testTypeDescriptionInt = {
4296 expr = (with types; int).description;
4297 expected = "signed integer";
4298 };
4299 testTypeDescriptionIntsPositive = {
4300 expr = (with types; ints.positive).description;
4301 expected = "positive integer, meaning >0";
4302 };
4303 testTypeDescriptionIntsPositiveOrEnumAuto = {
4304 expr = (with types; either ints.positive (enum [ "auto" ])).description;
4305 expected = ''positive integer, meaning >0, or value "auto" (singular enum)'';
4306 };
4307 testTypeDescriptionListOfPositive = {
4308 expr = (with types; listOf ints.positive).description;
4309 expected = "list of (positive integer, meaning >0)";
4310 };
4311 testTypeDescriptionListOfInt = {
4312 expr = (with types; listOf int).description;
4313 expected = "list of signed integer";
4314 };
4315 testTypeDescriptionListOfListOfInt = {
4316 expr = (with types; listOf (listOf int)).description;
4317 expected = "list of list of signed integer";
4318 };
4319 testTypeDescriptionListOfEitherStrOrBool = {
4320 expr = (with types; listOf (either str bool)).description;
4321 expected = "list of (string or boolean)";
4322 };
4323 testTypeDescriptionEitherListOfStrOrBool = {
4324 expr = (with types; either (listOf bool) str).description;
4325 expected = "(list of boolean) or string";
4326 };
4327 testTypeDescriptionEitherStrOrListOfBool = {
4328 expr = (with types; either str (listOf bool)).description;
4329 expected = "string or list of boolean";
4330 };
4331 testTypeDescriptionOneOfListOfStrOrBool = {
4332 expr =
4333 (
4334 with types;
4335 oneOf [
4336 (listOf bool)
4337 str
4338 ]
4339 ).description;
4340 expected = "(list of boolean) or string";
4341 };
4342 testTypeDescriptionOneOfListOfStrOrBoolOrNumber = {
4343 expr =
4344 (
4345 with types;
4346 oneOf [
4347 (listOf bool)
4348 str
4349 number
4350 ]
4351 ).description;
4352 expected = "(list of boolean) or string or signed integer or floating point number";
4353 };
4354 testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = {
4355 expr = (with types; either (listOf bool) (either str number)).description;
4356 expected = "(list of boolean) or string or signed integer or floating point number";
4357 };
4358 testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = {
4359 expr = (with types; either (either (listOf bool) str) number).description;
4360 expected = "(list of boolean) or string or signed integer or floating point number";
4361 };
4362 testTypeDescriptionEitherNullOrBoolOrString = {
4363 expr = (with types; either (nullOr bool) str).description;
4364 expected = "null or boolean or string";
4365 };
4366 testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = {
4367 expr = (with types; either (listOf (either bool str)) int).description;
4368 expected = "(list of (boolean or string)) or signed integer";
4369 };
4370 testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = {
4371 expr = (with types; either int (listOf (either bool str))).description;
4372 expected = "signed integer or list of (boolean or string)";
4373 };
4374 testTypeFunctionToPropagateFunctionArgs = {
4375 expr = lib.functionArgs (
4376 (types.functionTo types.null).merge
4377 [ ]
4378 [
4379 {
4380 value =
4381 {
4382 a,
4383 b ? false,
4384 ...
4385 }:
4386 null;
4387 }
4388 {
4389 value =
4390 {
4391 b,
4392 c ? false,
4393 ...
4394 }:
4395 null;
4396 }
4397 ]
4398 );
4399 expected = {
4400 a = false;
4401 b = false;
4402 c = true;
4403 };
4404 };
4405
4406 # Meta
4407 testGetExe'Output = {
4408 expr = getExe' {
4409 type = "derivation";
4410 out = "somelonghash";
4411 bin = "somelonghash";
4412 } "executable";
4413 expected = "somelonghash/bin/executable";
4414 };
4415
4416 testGetExeOutput = {
4417 expr = getExe {
4418 type = "derivation";
4419 out = "somelonghash";
4420 bin = "somelonghash";
4421 meta.mainProgram = "mainProgram";
4422 };
4423 expected = "somelonghash/bin/mainProgram";
4424 };
4425
4426 testGetExe'FailureFirstArg = testingThrow (getExe' "not a derivation" "executable");
4427
4428 testGetExe'FailureSecondArg = testingThrow (getExe' { type = "derivation"; } "dir/executable");
4429
4430 testGetLicenseFromSpdxIdOrExamples = {
4431 expr = [
4432 (getLicenseFromSpdxIdOr "MIT" null)
4433 (getLicenseFromSpdxIdOr "mIt" null)
4434 (getLicenseFromSpdxIdOr "MY LICENSE" lib.licenses.free)
4435 (getLicenseFromSpdxIdOr "MY LICENSE" null)
4436 ];
4437 expected = [
4438 lib.licenses.mit
4439 lib.licenses.mit
4440 lib.licenses.free
4441 null
4442 ];
4443 };
4444
4445 testGetLicenseFromSpdxIdOrThrow = testingThrow (
4446 getLicenseFromSpdxIdOr "MY LICENSE" (throw "No SPDX ID matches MY LICENSE")
4447 );
4448
4449 testPlatformMatch = {
4450 expr = meta.platformMatch { system = "x86_64-linux"; } "x86_64-linux";
4451 expected = true;
4452 };
4453
4454 testPlatformMatchAttrs = {
4455 expr = meta.platformMatch (systems.elaborate "x86_64-linux") (systems.elaborate "x86_64-linux")
4456 .parsed;
4457 expected = true;
4458 };
4459
4460 testPlatformMatchNoMatch = {
4461 expr = meta.platformMatch { system = "x86_64-darwin"; } "x86_64-linux";
4462 expected = false;
4463 };
4464
4465 testPlatformMatchMissingSystem = {
4466 expr = meta.platformMatch { } "x86_64-linux";
4467 expected = false;
4468 };
4469
4470 testPackagesFromDirectoryRecursive = {
4471 expr = packagesFromDirectoryRecursive {
4472 callPackage = path: overrides: import path overrides;
4473 directory = ./packages-from-directory/plain;
4474 };
4475 expected = {
4476 a = "a";
4477 b = "b";
4478 # Note: Other files/directories in `./test-data/c/` are ignored and can be
4479 # used by `package.nix`.
4480 c = "c";
4481 my-namespace = {
4482 d = "d";
4483 e = "e";
4484 f = "f";
4485 my-sub-namespace = {
4486 g = "g";
4487 h = "h";
4488 };
4489 };
4490 };
4491 };
4492
4493 # Make sure that passing a string for the `directory` works.
4494 #
4495 # See: https://github.com/NixOS/nixpkgs/pull/361424#discussion_r1934813568
4496 # See: https://github.com/NixOS/nix/issues/9428
4497 testPackagesFromDirectoryRecursiveStringDirectory = {
4498 expr = packagesFromDirectoryRecursive {
4499 callPackage = path: overrides: import path overrides;
4500 # Do NOT remove the `builtins.toString` call here!!!
4501 directory = toString ./packages-from-directory/plain;
4502 };
4503 expected = {
4504 a = "a";
4505 b = "b";
4506 # Note: Other files/directories in `./test-data/c/` are ignored and can be
4507 # used by `package.nix`.
4508 c = "c";
4509 my-namespace = {
4510 d = "d";
4511 e = "e";
4512 f = "f";
4513 my-sub-namespace = {
4514 g = "g";
4515 h = "h";
4516 };
4517 };
4518 };
4519 };
4520
4521 # Check that `packagesFromDirectoryRecursive` can process a directory with a
4522 # top-level `package.nix` file into a single package.
4523 testPackagesFromDirectoryRecursiveTopLevelPackageNix = {
4524 expr = packagesFromDirectoryRecursive {
4525 callPackage = path: overrides: import path overrides;
4526 directory = ./packages-from-directory/plain/c;
4527 };
4528 expected = "c";
4529 };
4530
4531 testMergeTypesSimple =
4532 let
4533 mergedType = types.mergeTypes types.str types.str;
4534 in
4535 {
4536 expr = mergedType.name;
4537 expected = "str";
4538 };
4539
4540 testMergeTypesFail =
4541 let
4542 mergedType = types.mergeTypes types.str types.int;
4543 in
4544 {
4545 expr = types.isType "merge-error" mergedType;
4546 expected = true;
4547 };
4548
4549 testMergeTypesEnum =
4550 let
4551 enumAB = lib.types.enum [
4552 "A"
4553 "B"
4554 ];
4555 enumXY = lib.types.enum [
4556 "X"
4557 "Y"
4558 ];
4559 merged = lib.types.mergeTypes enumAB enumXY; # -> enum [ "A" "B" "X" "Y" ]
4560 in
4561 {
4562 expr = {
4563 checkA = merged.check "A";
4564 checkB = merged.check "B";
4565 checkX = merged.check "X";
4566 checkY = merged.check "Y";
4567 checkC = merged.check "C";
4568 };
4569 expected = {
4570 checkA = true;
4571 checkB = true;
4572 checkX = true;
4573 checkY = true;
4574 checkC = false;
4575 };
4576 };
4577
4578 # Check that `packagesFromDirectoryRecursive` can be used to create scopes
4579 # for sub-directories
4580 testPackagesFromDirectoryNestedScopes =
4581 let
4582 inherit (lib) makeScope recurseIntoAttrs;
4583 emptyScope = makeScope lib.callPackageWith (_: { });
4584 in
4585 {
4586 expr =
4587 lib.filterAttrsRecursive
4588 (
4589 name: value:
4590 !lib.elem name [
4591 "callPackage"
4592 "newScope"
4593 "overrideScope"
4594 "packages"
4595 ]
4596 )
4597 (packagesFromDirectoryRecursive {
4598 inherit (emptyScope) callPackage newScope;
4599 directory = ./packages-from-directory/scope;
4600 });
4601 expected = lib.recurseIntoAttrs {
4602 a = "a";
4603 b = "b";
4604 # Note: Other files/directories in `./test-data/c/` are ignored and can be
4605 # used by `package.nix`.
4606 c = "c";
4607 my-namespace = lib.recurseIntoAttrs {
4608 d = "d";
4609 e = "e";
4610 f = "f";
4611 my-sub-namespace = lib.recurseIntoAttrs {
4612 g = "g";
4613 h = "h";
4614 };
4615 };
4616 };
4617 };
4618
4619 testFilesystemResolveDefaultNixFile1 = {
4620 expr = lib.filesystem.resolveDefaultNix ./foo.nix;
4621 expected = ./foo.nix;
4622 };
4623
4624 testFilesystemResolveDefaultNixFile2 = {
4625 expr = lib.filesystem.resolveDefaultNix ./default.nix;
4626 expected = ./default.nix;
4627 };
4628
4629 testFilesystemResolveDefaultNixDir1 = {
4630 expr = lib.filesystem.resolveDefaultNix ./.;
4631 expected = ./default.nix;
4632 };
4633
4634 testFilesystemResolveDefaultNixFile1_toString = {
4635 expr = lib.filesystem.resolveDefaultNix (toString ./foo.nix);
4636 expected = toString ./foo.nix;
4637 };
4638
4639 testFilesystemResolveDefaultNixFile2_toString = {
4640 expr = lib.filesystem.resolveDefaultNix (toString ./default.nix);
4641 expected = toString ./default.nix;
4642 };
4643
4644 testFilesystemResolveDefaultNixDir1_toString = {
4645 expr = lib.filesystem.resolveDefaultNix (toString ./.);
4646 expected = toString ./default.nix;
4647 };
4648
4649 testFilesystemResolveDefaultNixDir1_toString2 = {
4650 expr = lib.filesystem.resolveDefaultNix (toString ./.);
4651 expected = toString ./. + "/default.nix";
4652 };
4653
4654 testFilesystemResolveDefaultNixNonExistent = {
4655 expr = lib.filesystem.resolveDefaultNix "/non-existent/this/does/not/exist/for/real/please-dont-mess-with-your-local-fs";
4656 expected = "/non-existent/this/does/not/exist/for/real/please-dont-mess-with-your-local-fs";
4657 };
4658
4659 testFilesystemResolveDefaultNixNonExistentDir = {
4660 expr = lib.filesystem.resolveDefaultNix "/non-existent/this/does/not/exist/for/real/please-dont-mess-with-your-local-fs/";
4661 expected = "/non-existent/this/does/not/exist/for/real/please-dont-mess-with-your-local-fs/default.nix";
4662 };
4663
4664}