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