nixpkgs mirror (for testing) github.com/NixOS/nixpkgs
nix
at r-updates 4925 lines 101 kB view raw
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 = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;"; 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}