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