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