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