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