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