at 23.11-beta 51 kB view raw
1/* 2Nix evaluation tests for various lib functions. 3 4Since these tests are implemented with Nix evaluation, error checking is limited to what `builtins.tryEval` can detect, which is `throw`'s and `abort`'s, without error messages. 5If you need to test error messages or more complex evaluations, see ./modules.sh, ./sources.sh or ./filesystem.sh as examples. 6 7To run these tests: 8 9 [nixpkgs]$ nix-instantiate --eval --strict lib/tests/misc.nix 10 11If the resulting list is empty, all tests passed. 12Alternatively, to run all `lib` tests: 13 14 [nixpkgs]$ nix-build lib/tests/release.nix 15*/ 16with import ../default.nix; 17 18let 19 testingThrow = expr: { 20 expr = (builtins.tryEval (builtins.seq expr "didn't throw")); 21 expected = { success = false; value = false; }; 22 }; 23 testingEval = expr: { 24 expr = (builtins.tryEval expr).success; 25 expected = true; 26 }; 27 testingDeepThrow = expr: testingThrow (builtins.deepSeq expr expr); 28 29 testSanitizeDerivationName = { name, expected }: 30 let 31 drv = derivation { 32 name = strings.sanitizeDerivationName name; 33 builder = "x"; 34 system = "x"; 35 }; 36 in { 37 # Evaluate the derivation so an invalid name would be caught 38 expr = builtins.seq drv.drvPath drv.name; 39 inherit expected; 40 }; 41 42in 43 44runTests { 45 46# CUSTOMIZATION 47 48 testFunctionArgsMakeOverridable = { 49 expr = functionArgs (makeOverridable ({ a, b, c ? null}: {})); 50 expected = { a = false; b = false; c = true; }; 51 }; 52 53 testFunctionArgsMakeOverridableOverride = { 54 expr = functionArgs (makeOverridable ({ a, b, c ? null }: {}) { a = 1; b = 2; }).override; 55 expected = { a = false; b = false; c = true; }; 56 }; 57 58# TRIVIAL 59 60 testId = { 61 expr = id 1; 62 expected = 1; 63 }; 64 65 testConst = { 66 expr = const 2 3; 67 expected = 2; 68 }; 69 70 testPipe = { 71 expr = pipe 2 [ 72 (x: x + 2) # 2 + 2 = 4 73 (x: x * 2) # 4 * 2 = 8 74 ]; 75 expected = 8; 76 }; 77 78 testPipeEmpty = { 79 expr = pipe 2 []; 80 expected = 2; 81 }; 82 83 testPipeStrings = { 84 expr = pipe [ 3 4 ] [ 85 (map toString) 86 (map (s: s + "\n")) 87 concatStrings 88 ]; 89 expected = '' 90 3 91 4 92 ''; 93 }; 94 95 /* 96 testOr = { 97 expr = or true false; 98 expected = true; 99 }; 100 */ 101 102 testAnd = { 103 expr = and true false; 104 expected = false; 105 }; 106 107 testFix = { 108 expr = fix (x: {a = if x ? a then "a" else "b";}); 109 expected = {a = "a";}; 110 }; 111 112 testComposeExtensions = { 113 expr = let obj = makeExtensible (self: { foo = self.bar; }); 114 f = self: super: { bar = false; baz = true; }; 115 g = self: super: { bar = super.baz or false; }; 116 f_o_g = composeExtensions f g; 117 composed = obj.extend f_o_g; 118 in composed.foo; 119 expected = true; 120 }; 121 122 testComposeManyExtensions0 = { 123 expr = let obj = makeExtensible (self: { foo = true; }); 124 emptyComposition = composeManyExtensions []; 125 composed = obj.extend emptyComposition; 126 in composed.foo; 127 expected = true; 128 }; 129 130 testComposeManyExtensions = 131 let f = self: super: { bar = false; baz = true; }; 132 g = self: super: { bar = super.baz or false; }; 133 h = self: super: { qux = super.bar or false; }; 134 obj = makeExtensible (self: { foo = self.qux; }); 135 in { 136 expr = let composition = composeManyExtensions [f g h]; 137 composed = obj.extend composition; 138 in composed.foo; 139 expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo; 140 }; 141 142 testBitAnd = { 143 expr = (bitAnd 3 10); 144 expected = 2; 145 }; 146 147 testBitOr = { 148 expr = (bitOr 3 10); 149 expected = 11; 150 }; 151 152 testBitXor = { 153 expr = (bitXor 3 10); 154 expected = 9; 155 }; 156 157 testToHexString = { 158 expr = toHexString 250; 159 expected = "FA"; 160 }; 161 162 testToBaseDigits = { 163 expr = toBaseDigits 2 6; 164 expected = [ 1 1 0 ]; 165 }; 166 167 testFunctionArgsFunctor = { 168 expr = functionArgs { __functor = self: { a, b }: null; }; 169 expected = { a = false; b = false; }; 170 }; 171 172 testFunctionArgsSetFunctionArgs = { 173 expr = functionArgs (setFunctionArgs (args: args.x) { x = false; }); 174 expected = { x = false; }; 175 }; 176 177# STRINGS 178 179 testConcatMapStrings = { 180 expr = concatMapStrings (x: x + ";") ["a" "b" "c"]; 181 expected = "a;b;c;"; 182 }; 183 184 testConcatStringsSep = { 185 expr = concatStringsSep "," ["a" "b" "c"]; 186 expected = "a,b,c"; 187 }; 188 189 testConcatLines = { 190 expr = concatLines ["a" "b" "c"]; 191 expected = "a\nb\nc\n"; 192 }; 193 194 testReplicateString = { 195 expr = strings.replicate 5 "hello"; 196 expected = "hellohellohellohellohello"; 197 }; 198 199 testSplitStringsSimple = { 200 expr = strings.splitString "." "a.b.c.d"; 201 expected = [ "a" "b" "c" "d" ]; 202 }; 203 204 testSplitStringsEmpty = { 205 expr = strings.splitString "." "a..b"; 206 expected = [ "a" "" "b" ]; 207 }; 208 209 testSplitStringsOne = { 210 expr = strings.splitString ":" "a.b"; 211 expected = [ "a.b" ]; 212 }; 213 214 testSplitStringsNone = { 215 expr = strings.splitString "." ""; 216 expected = [ "" ]; 217 }; 218 219 testSplitStringsFirstEmpty = { 220 expr = strings.splitString "/" "/a/b/c"; 221 expected = [ "" "a" "b" "c" ]; 222 }; 223 224 testSplitStringsLastEmpty = { 225 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:"; 226 expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ]; 227 }; 228 229 testSplitStringsRegex = { 230 expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B"; 231 expected = [ "A" "B" ]; 232 }; 233 234 testSplitStringsDerivation = { 235 expr = take 3 (strings.splitString "/" (derivation { 236 name = "name"; 237 builder = "builder"; 238 system = "system"; 239 })); 240 expected = ["" "nix" "store"]; 241 }; 242 243 testSplitVersionSingle = { 244 expr = versions.splitVersion "1"; 245 expected = [ "1" ]; 246 }; 247 248 testSplitVersionDouble = { 249 expr = versions.splitVersion "1.2"; 250 expected = [ "1" "2" ]; 251 }; 252 253 testSplitVersionTriple = { 254 expr = versions.splitVersion "1.2.3"; 255 expected = [ "1" "2" "3" ]; 256 }; 257 258 testPadVersionLess = { 259 expr = versions.pad 3 "1.2"; 260 expected = "1.2.0"; 261 }; 262 263 testPadVersionLessExtra = { 264 expr = versions.pad 3 "1.3-rc1"; 265 expected = "1.3.0-rc1"; 266 }; 267 268 testPadVersionMore = { 269 expr = versions.pad 3 "1.2.3.4"; 270 expected = "1.2.3"; 271 }; 272 273 testIsStorePath = { 274 expr = 275 let goodPath = 276 "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"; 277 in { 278 storePath = isStorePath goodPath; 279 storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello; 280 storePathAppendix = isStorePath 281 "${goodPath}/bin/python"; 282 nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath))); 283 asPath = isStorePath (/. + goodPath); 284 otherPath = isStorePath "/something/else"; 285 otherVals = { 286 attrset = isStorePath {}; 287 list = isStorePath []; 288 int = isStorePath 42; 289 }; 290 }; 291 expected = { 292 storePath = true; 293 storePathDerivation = true; 294 storePathAppendix = false; 295 nonAbsolute = false; 296 asPath = true; 297 otherPath = false; 298 otherVals = { 299 attrset = false; 300 list = false; 301 int = false; 302 }; 303 }; 304 }; 305 306 testEscapeXML = { 307 expr = escapeXML ''"test" 'test' < & >''; 308 expected = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;"; 309 }; 310 311 testToShellVars = { 312 expr = '' 313 ${toShellVars { 314 STRing01 = "just a 'string'"; 315 _array_ = [ "with" "more strings" ]; 316 assoc."with some" = '' 317 strings 318 possibly newlines 319 ''; 320 drv = { 321 outPath = "/drv"; 322 foo = "ignored attribute"; 323 }; 324 path = /path; 325 stringable = { 326 __toString = _: "hello toString"; 327 bar = "ignored attribute"; 328 }; 329 }} 330 ''; 331 expected = '' 332 STRing01='just a '\'''string'\'''' 333 declare -a _array_=('with' 'more strings') 334 declare -A assoc=(['with some']='strings 335 possibly newlines 336 ') 337 drv='/drv' 338 path='/path' 339 stringable='hello toString' 340 ''; 341 }; 342 343 testHasInfixFalse = { 344 expr = hasInfix "c" "abde"; 345 expected = false; 346 }; 347 348 testHasInfixTrue = { 349 expr = hasInfix "c" "abcde"; 350 expected = true; 351 }; 352 353 testHasInfixDerivation = { 354 expr = hasInfix "hello" (import ../.. { system = "x86_64-linux"; }).hello; 355 expected = true; 356 }; 357 358 testHasInfixPath = { 359 expr = hasInfix "tests" ./.; 360 expected = true; 361 }; 362 363 testHasInfixPathStoreDir = { 364 expr = hasInfix builtins.storeDir ./.; 365 expected = true; 366 }; 367 368 testHasInfixToString = { 369 expr = hasInfix "a" { __toString = _: "a"; }; 370 expected = true; 371 }; 372 373 testRemovePrefixExample1 = { 374 expr = removePrefix "foo." "foo.bar.baz"; 375 expected = "bar.baz"; 376 }; 377 testRemovePrefixExample2 = { 378 expr = removePrefix "xxx" "foo.bar.baz"; 379 expected = "foo.bar.baz"; 380 }; 381 testRemovePrefixEmptyPrefix = { 382 expr = removePrefix "" "foo"; 383 expected = "foo"; 384 }; 385 testRemovePrefixEmptyString = { 386 expr = removePrefix "foo" ""; 387 expected = ""; 388 }; 389 testRemovePrefixEmptyBoth = { 390 expr = removePrefix "" ""; 391 expected = ""; 392 }; 393 394 testNormalizePath = { 395 expr = strings.normalizePath "//a/b//c////d/"; 396 expected = "/a/b/c/d/"; 397 }; 398 399 testCharToInt = { 400 expr = strings.charToInt "A"; 401 expected = 65; 402 }; 403 404 testEscapeC = { 405 expr = strings.escapeC [ " " ] "Hello World"; 406 expected = "Hello\\x20World"; 407 }; 408 409 testEscapeURL = testAllTrue [ 410 ("" == strings.escapeURL "") 411 ("Hello" == strings.escapeURL "Hello") 412 ("Hello%20World" == strings.escapeURL "Hello World") 413 ("Hello%2FWorld" == strings.escapeURL "Hello/World") 414 ("42%25" == strings.escapeURL "42%") 415 ("%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" == strings.escapeURL " ?&=#+%!<>#\"{}|\\^[]`\t:/@$'()*,;") 416 ]; 417 418 testToInt = testAllTrue [ 419 # Naive 420 (123 == toInt "123") 421 (0 == toInt "0") 422 # Whitespace Padding 423 (123 == toInt " 123") 424 (123 == toInt "123 ") 425 (123 == toInt " 123 ") 426 (123 == toInt " 123 ") 427 (0 == toInt " 0") 428 (0 == toInt "0 ") 429 (0 == toInt " 0 ") 430 (-1 == toInt "-1") 431 (-1 == toInt " -1 ") 432 ]; 433 434 testToIntFails = testAllTrue [ 435 ( builtins.tryEval (toInt "") == { success = false; value = false; } ) 436 ( builtins.tryEval (toInt "123 123") == { success = false; value = false; } ) 437 ( builtins.tryEval (toInt "0 123") == { success = false; value = false; } ) 438 ( builtins.tryEval (toInt " 0d ") == { success = false; value = false; } ) 439 ( builtins.tryEval (toInt " 1d ") == { success = false; value = false; } ) 440 ( builtins.tryEval (toInt " d0 ") == { success = false; value = false; } ) 441 ( builtins.tryEval (toInt "00") == { success = false; value = false; } ) 442 ( builtins.tryEval (toInt "01") == { success = false; value = false; } ) 443 ( builtins.tryEval (toInt "002") == { success = false; value = false; } ) 444 ( builtins.tryEval (toInt " 002 ") == { success = false; value = false; } ) 445 ( builtins.tryEval (toInt " foo ") == { success = false; value = false; } ) 446 ( builtins.tryEval (toInt " foo 123 ") == { success = false; value = false; } ) 447 ( builtins.tryEval (toInt " foo123 ") == { success = false; value = false; } ) 448 ]; 449 450 testToIntBase10 = testAllTrue [ 451 # Naive 452 (123 == toIntBase10 "123") 453 (0 == toIntBase10 "0") 454 # Whitespace Padding 455 (123 == toIntBase10 " 123") 456 (123 == toIntBase10 "123 ") 457 (123 == toIntBase10 " 123 ") 458 (123 == toIntBase10 " 123 ") 459 (0 == toIntBase10 " 0") 460 (0 == toIntBase10 "0 ") 461 (0 == toIntBase10 " 0 ") 462 # Zero Padding 463 (123 == toIntBase10 "0123") 464 (123 == toIntBase10 "0000123") 465 (0 == toIntBase10 "000000") 466 # Whitespace and Zero Padding 467 (123 == toIntBase10 " 0123") 468 (123 == toIntBase10 "0123 ") 469 (123 == toIntBase10 " 0123 ") 470 (123 == toIntBase10 " 0000123") 471 (123 == toIntBase10 "0000123 ") 472 (123 == toIntBase10 " 0000123 ") 473 (0 == toIntBase10 " 000000") 474 (0 == toIntBase10 "000000 ") 475 (0 == toIntBase10 " 000000 ") 476 (-1 == toIntBase10 "-1") 477 (-1 == toIntBase10 " -1 ") 478 ]; 479 480 testToIntBase10Fails = testAllTrue [ 481 ( builtins.tryEval (toIntBase10 "") == { success = false; value = false; } ) 482 ( builtins.tryEval (toIntBase10 "123 123") == { success = false; value = false; } ) 483 ( builtins.tryEval (toIntBase10 "0 123") == { success = false; value = false; } ) 484 ( builtins.tryEval (toIntBase10 " 0d ") == { success = false; value = false; } ) 485 ( builtins.tryEval (toIntBase10 " 1d ") == { success = false; value = false; } ) 486 ( builtins.tryEval (toIntBase10 " d0 ") == { success = false; value = false; } ) 487 ( builtins.tryEval (toIntBase10 " foo ") == { success = false; value = false; } ) 488 ( builtins.tryEval (toIntBase10 " foo 123 ") == { success = false; value = false; } ) 489 ( builtins.tryEval (toIntBase10 " foo 00123 ") == { success = false; value = false; } ) 490 ( builtins.tryEval (toIntBase10 " foo00123 ") == { success = false; value = false; } ) 491 ]; 492 493# LISTS 494 495 testFilter = { 496 expr = filter (x: x != "a") ["a" "b" "c" "a"]; 497 expected = ["b" "c"]; 498 }; 499 500 testFold = 501 let 502 f = op: fold: fold op 0 (range 0 100); 503 # fold with associative operator 504 assoc = f builtins.add; 505 # fold with non-associative operator 506 nonAssoc = f builtins.sub; 507 in { 508 expr = { 509 assocRight = assoc foldr; 510 # right fold with assoc operator is same as left fold 511 assocRightIsLeft = assoc foldr == assoc foldl; 512 nonAssocRight = nonAssoc foldr; 513 nonAssocLeft = nonAssoc foldl; 514 # with non-assoc operator the fold results are not the same 515 nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr; 516 # fold is an alias for foldr 517 foldIsRight = nonAssoc fold == nonAssoc foldr; 518 }; 519 expected = { 520 assocRight = 5050; 521 assocRightIsLeft = true; 522 nonAssocRight = 50; 523 nonAssocLeft = (-5050); 524 nonAssocRightIsNotLeft = true; 525 foldIsRight = true; 526 }; 527 }; 528 529 testFoldl'Empty = { 530 expr = foldl' (acc: el: abort "operation not called") 0 [ ]; 531 expected = 0; 532 }; 533 534 testFoldl'IntegerAdding = { 535 expr = foldl' (acc: el: acc + el) 0 [ 1 2 3 ]; 536 expected = 6; 537 }; 538 539 # The accumulator isn't forced deeply 540 testFoldl'NonDeep = { 541 expr = take 3 (foldl' 542 (acc: el: [ el ] ++ acc) 543 [ (abort "unevaluated list entry") ] 544 [ 1 2 3 ]); 545 expected = [ 3 2 1 ]; 546 }; 547 548 # Compared to builtins.foldl', lib.foldl' evaluates the first accumulator strictly too 549 testFoldl'StrictInitial = { 550 expr = (builtins.tryEval (foldl' (acc: el: el) (throw "hello") [])).success; 551 expected = false; 552 }; 553 554 # Make sure we don't get a stack overflow for large lists 555 # This number of elements would notably cause a stack overflow if it was implemented without the `foldl'` builtin 556 testFoldl'Large = { 557 expr = foldl' (acc: el: acc + el) 0 (range 0 100000); 558 expected = 5000050000; 559 }; 560 561 testTake = testAllTrue [ 562 ([] == (take 0 [ 1 2 3 ])) 563 ([1] == (take 1 [ 1 2 3 ])) 564 ([ 1 2 ] == (take 2 [ 1 2 3 ])) 565 ([ 1 2 3 ] == (take 3 [ 1 2 3 ])) 566 ([ 1 2 3 ] == (take 4 [ 1 2 3 ])) 567 ]; 568 569 testListHasPrefixExample1 = { 570 expr = lists.hasPrefix [ 1 2 ] [ 1 2 3 4 ]; 571 expected = true; 572 }; 573 testListHasPrefixExample2 = { 574 expr = lists.hasPrefix [ 0 1 ] [ 1 2 3 4 ]; 575 expected = false; 576 }; 577 testListHasPrefixLazy = { 578 expr = lists.hasPrefix [ 1 ] [ 1 (abort "lib.lists.hasPrefix is not lazy") ]; 579 expected = true; 580 }; 581 testListHasPrefixEmptyPrefix = { 582 expr = lists.hasPrefix [ ] [ 1 2 ]; 583 expected = true; 584 }; 585 testListHasPrefixEmptyList = { 586 expr = lists.hasPrefix [ 1 2 ] [ ]; 587 expected = false; 588 }; 589 590 testListRemovePrefixExample1 = { 591 expr = lists.removePrefix [ 1 2 ] [ 1 2 3 4 ]; 592 expected = [ 3 4 ]; 593 }; 594 testListRemovePrefixExample2 = { 595 expr = (builtins.tryEval (lists.removePrefix [ 0 1 ] [ 1 2 3 4 ])).success; 596 expected = false; 597 }; 598 testListRemovePrefixEmptyPrefix = { 599 expr = lists.removePrefix [ ] [ 1 2 ]; 600 expected = [ 1 2 ]; 601 }; 602 testListRemovePrefixEmptyList = { 603 expr = (builtins.tryEval (lists.removePrefix [ 1 2 ] [ ])).success; 604 expected = false; 605 }; 606 607 testFoldAttrs = { 608 expr = foldAttrs (n: a: [n] ++ a) [] [ 609 { a = 2; b = 7; } 610 { a = 3; c = 8; } 611 ]; 612 expected = { a = [ 2 3 ]; b = [7]; c = [8];}; 613 }; 614 615 testListCommonPrefixExample1 = { 616 expr = lists.commonPrefix [ 1 2 3 4 5 6 ] [ 1 2 4 8 ]; 617 expected = [ 1 2 ]; 618 }; 619 testListCommonPrefixExample2 = { 620 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 4 5 ]; 621 expected = [ 1 2 3 ]; 622 }; 623 testListCommonPrefixExample3 = { 624 expr = lists.commonPrefix [ 1 2 3 ] [ 4 5 6 ]; 625 expected = [ ]; 626 }; 627 testListCommonPrefixEmpty = { 628 expr = lists.commonPrefix [ ] [ 1 2 3 ]; 629 expected = [ ]; 630 }; 631 testListCommonPrefixSame = { 632 expr = lists.commonPrefix [ 1 2 3 ] [ 1 2 3 ]; 633 expected = [ 1 2 3 ]; 634 }; 635 testListCommonPrefixLazy = { 636 expr = lists.commonPrefix [ 1 ] [ 1 (abort "lib.lists.commonPrefix shouldn't evaluate this")]; 637 expected = [ 1 ]; 638 }; 639 # This would stack overflow if `commonPrefix` were implemented using recursion 640 testListCommonPrefixLong = 641 let 642 longList = genList (n: n) 100000; 643 in { 644 expr = lists.commonPrefix longList longList; 645 expected = longList; 646 }; 647 648 testSort = { 649 expr = sort builtins.lessThan [ 40 2 30 42 ]; 650 expected = [2 30 40 42]; 651 }; 652 653 testReplicate = { 654 expr = replicate 3 "a"; 655 expected = ["a" "a" "a"]; 656 }; 657 658 testToIntShouldConvertStringToInt = { 659 expr = toInt "27"; 660 expected = 27; 661 }; 662 663 testToIntShouldThrowErrorIfItCouldNotConvertToInt = { 664 expr = builtins.tryEval (toInt "\"foo\""); 665 expected = { success = false; value = false; }; 666 }; 667 668 testHasAttrByPathTrue = { 669 expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; }; 670 expected = true; 671 }; 672 673 testHasAttrByPathFalse = { 674 expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; }; 675 expected = false; 676 }; 677 678 testFindFirstIndexExample1 = { 679 expr = lists.findFirstIndex (x: x > 3) (abort "index found, so a default must not be evaluated") [ 1 6 4 ]; 680 expected = 1; 681 }; 682 683 testFindFirstIndexExample2 = { 684 expr = lists.findFirstIndex (x: x > 9) "a very specific default" [ 1 6 4 ]; 685 expected = "a very specific default"; 686 }; 687 688 testFindFirstIndexEmpty = { 689 expr = lists.findFirstIndex (abort "when the list is empty, the predicate is not needed") null []; 690 expected = null; 691 }; 692 693 testFindFirstIndexSingleMatch = { 694 expr = lists.findFirstIndex (x: x == 5) null [ 5 ]; 695 expected = 0; 696 }; 697 698 testFindFirstIndexSingleDefault = { 699 expr = lists.findFirstIndex (x: false) null [ (abort "if the predicate doesn't access the value, it must not be evaluated") ]; 700 expected = null; 701 }; 702 703 testFindFirstIndexNone = { 704 expr = builtins.tryEval (lists.findFirstIndex (x: x == 2) null [ 1 (throw "the last element must be evaluated when there's no match") ]); 705 expected = { success = false; value = false; }; 706 }; 707 708 # Makes sure that the implementation doesn't cause a stack overflow 709 testFindFirstIndexBig = { 710 expr = lists.findFirstIndex (x: x == 1000000) null (range 0 1000000); 711 expected = 1000000; 712 }; 713 714 testFindFirstIndexLazy = { 715 expr = lists.findFirstIndex (x: x == 1) null [ 1 (abort "list elements after the match must not be evaluated") ]; 716 expected = 0; 717 }; 718 719 testFindFirstExample1 = { 720 expr = lists.findFirst (x: x > 3) 7 [ 1 6 4 ]; 721 expected = 6; 722 }; 723 724 testFindFirstExample2 = { 725 expr = lists.findFirst (x: x > 9) 7 [ 1 6 4 ]; 726 expected = 7; 727 }; 728 729 testAllUnique_true = { 730 expr = allUnique [ 3 2 4 1 ]; 731 expected = true; 732 }; 733 testAllUnique_false = { 734 expr = allUnique [ 3 2 3 4 ]; 735 expected = false; 736 }; 737 738# ATTRSETS 739 740 testConcatMapAttrs = { 741 expr = concatMapAttrs 742 (name: value: { 743 ${name} = value; 744 ${name + value} = value; 745 }) 746 { 747 foo = "bar"; 748 foobar = "baz"; 749 }; 750 expected = { 751 foo = "bar"; 752 foobar = "baz"; 753 foobarbaz = "baz"; 754 }; 755 }; 756 757 # code from example 758 testFoldlAttrs = { 759 expr = { 760 example = foldlAttrs 761 (acc: name: value: { 762 sum = acc.sum + value; 763 names = acc.names ++ [ name ]; 764 }) 765 { sum = 0; names = [ ]; } 766 { 767 foo = 1; 768 bar = 10; 769 }; 770 # should just return the initial value 771 emptySet = foldlAttrs (throw "function not needed") 123 { }; 772 # should just evaluate to the last value 773 valuesNotNeeded = foldlAttrs (acc: _name: _v: acc) 3 { z = throw "value z not needed"; a = throw "value a not needed"; }; 774 # the accumulator doesnt have to be an attrset it can be as trivial as being just a number or string 775 trivialAcc = foldlAttrs (acc: _name: v: acc * 10 + v) 1 { z = 1; a = 2; }; 776 }; 777 expected = { 778 example = { 779 sum = 11; 780 names = [ "bar" "foo" ]; 781 }; 782 emptySet = 123; 783 valuesNotNeeded = 3; 784 trivialAcc = 121; 785 }; 786 }; 787 788 789 testMergeAttrsListExample1 = { 790 expr = attrsets.mergeAttrsList [ { a = 0; b = 1; } { c = 2; d = 3; } ]; 791 expected = { a = 0; b = 1; c = 2; d = 3; }; 792 }; 793 testMergeAttrsListExample2 = { 794 expr = attrsets.mergeAttrsList [ { a = 0; } { a = 1; } ]; 795 expected = { a = 1; }; 796 }; 797 testMergeAttrsListExampleMany = 798 let 799 list = genList (n: 800 listToAttrs (genList (m: 801 let 802 # Integer divide n by two to create duplicate attributes 803 str = "halfn${toString (n / 2)}m${toString m}"; 804 in 805 nameValuePair str str 806 ) 100) 807 ) 100; 808 in { 809 expr = attrsets.mergeAttrsList list; 810 expected = foldl' mergeAttrs { } list; 811 }; 812 813 # code from the example 814 testRecursiveUpdateUntil = { 815 expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) { 816 # first attribute set 817 foo.bar = 1; 818 foo.baz = 2; 819 bar = 3; 820 } { 821 #second attribute set 822 foo.bar = 1; 823 foo.quz = 2; 824 baz = 4; 825 }; 826 expected = { 827 foo.bar = 1; # 'foo.*' from the second set 828 foo.quz = 2; # 829 bar = 3; # 'bar' from the first set 830 baz = 4; # 'baz' from the second set 831 }; 832 }; 833 834 testOverrideExistingEmpty = { 835 expr = overrideExisting {} { a = 1; }; 836 expected = {}; 837 }; 838 839 testOverrideExistingDisjoint = { 840 expr = overrideExisting { b = 2; } { a = 1; }; 841 expected = { b = 2; }; 842 }; 843 844 testOverrideExistingOverride = { 845 expr = overrideExisting { a = 3; b = 2; } { a = 1; }; 846 expected = { a = 1; b = 2; }; 847 }; 848 849 testListAttrsReverse = let 850 exampleAttrs = {foo=1; bar="asdf"; baz = [1 3 3 7]; fnord=null;}; 851 exampleSingletonList = [{name="foo"; value=1;}]; 852 in { 853 expr = { 854 isReverseToListToAttrs = builtins.listToAttrs (attrsToList exampleAttrs) == exampleAttrs; 855 isReverseToAttrsToList = attrsToList (builtins.listToAttrs exampleSingletonList) == exampleSingletonList; 856 testDuplicatePruningBehaviour = attrsToList (builtins.listToAttrs [{name="a"; value=2;} {name="a"; value=1;}]); 857 }; 858 expected = { 859 isReverseToAttrsToList = true; 860 isReverseToListToAttrs = true; 861 testDuplicatePruningBehaviour = [{name="a"; value=2;}]; 862 }; 863 }; 864 865 testAttrsToListsCanDealWithFunctions = testingEval ( 866 attrsToList { someFunc= a: a + 1;} 867 ); 868 869# GENERATORS 870# these tests assume attributes are converted to lists 871# in alphabetical order 872 873 testMkKeyValueDefault = { 874 expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar"; 875 expected = ''f\:oo:bar''; 876 }; 877 878 testMkValueString = { 879 expr = let 880 vals = { 881 int = 42; 882 string = ''fo"o''; 883 bool = true; 884 bool2 = false; 885 null = null; 886 # float = 42.23; # floats are strange 887 }; 888 in mapAttrs 889 (const (generators.mkValueStringDefault {})) 890 vals; 891 expected = { 892 int = "42"; 893 string = ''fo"o''; 894 bool = "true"; 895 bool2 = "false"; 896 null = "null"; 897 # float = "42.23" true false [ "bar" ] ]''; 898 }; 899 }; 900 901 testToKeyValue = { 902 expr = generators.toKeyValue {} { 903 key = "value"; 904 "other=key" = "baz"; 905 }; 906 expected = '' 907 key=value 908 other\=key=baz 909 ''; 910 }; 911 912 testToINIEmpty = { 913 expr = generators.toINI {} {}; 914 expected = ""; 915 }; 916 917 testToINIEmptySection = { 918 expr = generators.toINI {} { foo = {}; bar = {}; }; 919 expected = '' 920 [bar] 921 922 [foo] 923 ''; 924 }; 925 926 testToINIDuplicateKeys = { 927 expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; }; 928 expected = '' 929 [baz] 930 qux=1 931 qux=false 932 933 [foo] 934 bar=true 935 ''; 936 }; 937 938 testToINIDefaultEscapes = { 939 expr = generators.toINI {} { 940 "no [ and ] allowed unescaped" = { 941 "and also no = in keys" = 42; 942 }; 943 }; 944 expected = '' 945 [no \[ and \] allowed unescaped] 946 and also no \= in keys=42 947 ''; 948 }; 949 950 testToINIDefaultFull = { 951 expr = generators.toINI {} { 952 "section 1" = { 953 attribute1 = 5; 954 x = "Me-se JarJar Binx"; 955 # booleans are converted verbatim by default 956 boolean = false; 957 }; 958 "foo[]" = { 959 "he\\h=he" = "this is okay"; 960 }; 961 }; 962 expected = '' 963 [foo\[\]] 964 he\h\=he=this is okay 965 966 [section 1] 967 attribute1=5 968 boolean=false 969 x=Me-se JarJar Binx 970 ''; 971 }; 972 973 testToINIWithGlobalSectionEmpty = { 974 expr = generators.toINIWithGlobalSection {} { 975 globalSection = { 976 }; 977 sections = { 978 }; 979 }; 980 expected = '' 981 ''; 982 }; 983 984 testToINIWithGlobalSectionGlobalEmptyIsTheSameAsToINI = 985 let 986 sections = { 987 "section 1" = { 988 attribute1 = 5; 989 x = "Me-se JarJar Binx"; 990 }; 991 "foo" = { 992 "he\\h=he" = "this is okay"; 993 }; 994 }; 995 in { 996 expr = 997 generators.toINIWithGlobalSection {} { 998 globalSection = {}; 999 sections = sections; 1000 }; 1001 expected = generators.toINI {} sections; 1002 }; 1003 1004 testToINIWithGlobalSectionFull = { 1005 expr = generators.toINIWithGlobalSection {} { 1006 globalSection = { 1007 foo = "bar"; 1008 test = false; 1009 }; 1010 sections = { 1011 "section 1" = { 1012 attribute1 = 5; 1013 x = "Me-se JarJar Binx"; 1014 }; 1015 "foo" = { 1016 "he\\h=he" = "this is okay"; 1017 }; 1018 }; 1019 }; 1020 expected = '' 1021 foo=bar 1022 test=false 1023 1024 [foo] 1025 he\h\=he=this is okay 1026 1027 [section 1] 1028 attribute1=5 1029 x=Me-se JarJar Binx 1030 ''; 1031 }; 1032 1033 testToGitINI = { 1034 expr = generators.toGitINI { 1035 user = { 1036 email = "user@example.org"; 1037 name = "John Doe"; 1038 signingKey = "00112233445566778899AABBCCDDEEFF"; 1039 }; 1040 gpg.program = "path-to-gpg"; 1041 tag.gpgSign = true; 1042 include.path = "~/path/to/config.inc"; 1043 includeIf."gitdif:~/src/dir".path = "~/path/to/conditional.inc"; 1044 extra = { 1045 boolean = true; 1046 integer = 38; 1047 name = "value"; 1048 subsection.value = "test"; 1049 };}; 1050 expected = '' 1051 [extra] 1052 ${"\t"}boolean = true 1053 ${"\t"}integer = 38 1054 ${"\t"}name = "value" 1055 1056 [extra "subsection"] 1057 ${"\t"}value = "test" 1058 1059 [gpg] 1060 ${"\t"}program = "path-to-gpg" 1061 1062 [include] 1063 ${"\t"}path = "~/path/to/config.inc" 1064 1065 [includeIf "gitdif:~/src/dir"] 1066 ${"\t"}path = "~/path/to/conditional.inc" 1067 1068 [tag] 1069 ${"\t"}gpgSign = true 1070 1071 [user] 1072 ${"\t"}email = "user@example.org" 1073 ${"\t"}name = "John Doe" 1074 ${"\t"}signingKey = "00112233445566778899AABBCCDDEEFF" 1075 ''; 1076 }; 1077 1078 /* right now only invocation check */ 1079 testToJSONSimple = 1080 let val = { 1081 foobar = [ "baz" 1 2 3 ]; 1082 }; 1083 in { 1084 expr = generators.toJSON {} val; 1085 # trivial implementation 1086 expected = builtins.toJSON val; 1087 }; 1088 1089 /* right now only invocation check */ 1090 testToYAMLSimple = 1091 let val = { 1092 list = [ { one = 1; } { two = 2; } ]; 1093 all = 42; 1094 }; 1095 in { 1096 expr = generators.toYAML {} val; 1097 # trivial implementation 1098 expected = builtins.toJSON val; 1099 }; 1100 1101 testToPretty = 1102 let 1103 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; }; 1104 in { 1105 expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec { 1106 int = 42; 1107 float = 0.1337; 1108 bool = true; 1109 emptystring = ""; 1110 string = "fn\${o}\"r\\d"; 1111 newlinestring = "\n"; 1112 path = /. + "/foo"; 1113 null_ = null; 1114 function = x: x; 1115 functionArgs = { arg ? 4, foo }: arg; 1116 list = [ 3 4 function [ false ] ]; 1117 emptylist = []; 1118 attrs = { foo = null; "foo b/ar" = "baz"; }; 1119 emptyattrs = {}; 1120 drv = deriv; 1121 }; 1122 expected = rec { 1123 int = "42"; 1124 float = "0.1337"; 1125 bool = "true"; 1126 emptystring = ''""''; 1127 string = ''"fn\''${o}\"r\\d"''; 1128 newlinestring = "\"\\n\""; 1129 path = "/foo"; 1130 null_ = "null"; 1131 function = "<function>"; 1132 functionArgs = "<function, args: {arg?, foo}>"; 1133 list = "[ 3 4 ${function} [ false ] ]"; 1134 emptylist = "[ ]"; 1135 attrs = "{ foo = null; \"foo b/ar\" = \"baz\"; }"; 1136 emptyattrs = "{ }"; 1137 drv = "<derivation ${deriv.name}>"; 1138 }; 1139 }; 1140 1141 testToPrettyLimit = 1142 let 1143 a.b = 1; 1144 a.c = a; 1145 in { 1146 expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a); 1147 expected = "{\n b = 1;\n c = {\n b = \"<unevaluated>\";\n c = {\n b = \"<unevaluated>\";\n c = \"<unevaluated>\";\n };\n };\n}"; 1148 }; 1149 1150 testToPrettyLimitThrow = 1151 let 1152 a.b = 1; 1153 a.c = a; 1154 in { 1155 expr = (builtins.tryEval 1156 (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success; 1157 expected = false; 1158 }; 1159 1160 testWithRecursionDealsWithFunctors = 1161 let 1162 functor = { 1163 __functor = self: { a, b, }: null; 1164 }; 1165 a = { 1166 value = "1234"; 1167 b = functor; 1168 c.d = functor; 1169 }; 1170 in { 1171 expr = generators.toPretty { } (generators.withRecursion { depthLimit = 1; throwOnDepthLimit = false; } a); 1172 expected = "{\n b = <function, args: {a, b}>;\n c = {\n d = \"<unevaluated>\";\n };\n value = \"<unevaluated>\";\n}"; 1173 }; 1174 1175 testToPrettyMultiline = { 1176 expr = mapAttrs (const (generators.toPretty { })) rec { 1177 list = [ 3 4 [ false ] ]; 1178 attrs = { foo = null; bar.foo = "baz"; }; 1179 newlinestring = "\n"; 1180 multilinestring = '' 1181 hello 1182 ''${there} 1183 te'''st 1184 ''; 1185 multilinestring' = '' 1186 hello 1187 there 1188 test''; 1189 }; 1190 expected = rec { 1191 list = '' 1192 [ 1193 3 1194 4 1195 [ 1196 false 1197 ] 1198 ]''; 1199 attrs = '' 1200 { 1201 bar = { 1202 foo = "baz"; 1203 }; 1204 foo = null; 1205 }''; 1206 newlinestring = "''\n \n''"; 1207 multilinestring = '' 1208 ''' 1209 hello 1210 '''''${there} 1211 te''''st 1212 '''''; 1213 multilinestring' = '' 1214 ''' 1215 hello 1216 there 1217 test'''''; 1218 1219 }; 1220 }; 1221 1222 testToPrettyAllowPrettyValues = { 1223 expr = generators.toPretty { allowPrettyValues = true; } 1224 { __pretty = v: "«" + v + "»"; val = "foo"; }; 1225 expected = "«foo»"; 1226 }; 1227 1228 testToPlist = 1229 let 1230 deriv = derivation { name = "test"; builder = "/bin/sh"; system = "aarch64-linux"; }; 1231 in { 1232 expr = mapAttrs (const (generators.toPlist { })) { 1233 value = { 1234 nested.values = rec { 1235 int = 42; 1236 float = 0.1337; 1237 bool = true; 1238 emptystring = ""; 1239 string = "fn\${o}\"r\\d"; 1240 newlinestring = "\n"; 1241 path = /. + "/foo"; 1242 null_ = null; 1243 list = [ 3 4 "test" ]; 1244 emptylist = []; 1245 attrs = { foo = null; "foo b/ar" = "baz"; }; 1246 emptyattrs = {}; 1247 }; 1248 }; 1249 }; 1250 expected = { value = builtins.readFile ./test-to-plist-expected.plist; }; 1251 }; 1252 1253 testToLuaEmptyAttrSet = { 1254 expr = generators.toLua {} {}; 1255 expected = ''{}''; 1256 }; 1257 1258 testToLuaEmptyList = { 1259 expr = generators.toLua {} []; 1260 expected = ''{}''; 1261 }; 1262 1263 testToLuaListOfVariousTypes = { 1264 expr = generators.toLua {} [ null 43 3.14159 true ]; 1265 expected = '' 1266 { 1267 nil, 1268 43, 1269 3.14159, 1270 true 1271 }''; 1272 }; 1273 1274 testToLuaString = { 1275 expr = generators.toLua {} ''double-quote (") and single quotes (')''; 1276 expected = ''"double-quote (\") and single quotes (')"''; 1277 }; 1278 1279 testToLuaAttrsetWithLuaInline = { 1280 expr = generators.toLua {} { x = generators.mkLuaInline ''"abc" .. "def"''; }; 1281 expected = '' 1282 { 1283 ["x"] = ("abc" .. "def") 1284 }''; 1285 }; 1286 1287 testToLuaAttrsetWithSpaceInKey = { 1288 expr = generators.toLua {} { "some space and double-quote (\")" = 42; }; 1289 expected = '' 1290 { 1291 ["some space and double-quote (\")"] = 42 1292 }''; 1293 }; 1294 1295 testToLuaWithoutMultiline = { 1296 expr = generators.toLua { multiline = false; } [ 41 43 ]; 1297 expected = ''{ 41, 43 }''; 1298 }; 1299 1300 testToLuaEmptyBindings = { 1301 expr = generators.toLua { asBindings = true; } {}; 1302 expected = ""; 1303 }; 1304 1305 testToLuaBindings = { 1306 expr = generators.toLua { asBindings = true; } { x1 = 41; _y = { a = 43; }; }; 1307 expected = '' 1308 _y = { 1309 ["a"] = 43 1310 } 1311 x1 = 41 1312 ''; 1313 }; 1314 1315 testToLuaPartialTableBindings = { 1316 expr = generators.toLua { asBindings = true; } { "x.y" = 42; }; 1317 expected = '' 1318 x.y = 42 1319 ''; 1320 }; 1321 1322 testToLuaIndentedBindings = { 1323 expr = generators.toLua { asBindings = true; indent = " "; } { x = { y = 42; }; }; 1324 expected = " x = {\n [\"y\"] = 42\n }\n"; 1325 }; 1326 1327 testToLuaBindingsWithSpace = testingThrow ( 1328 generators.toLua { asBindings = true; } { "with space" = 42; } 1329 ); 1330 1331 testToLuaBindingsWithLeadingDigit = testingThrow ( 1332 generators.toLua { asBindings = true; } { "11eleven" = 42; } 1333 ); 1334 1335 testToLuaBasicExample = { 1336 expr = generators.toLua {} { 1337 cmd = [ "typescript-language-server" "--stdio" ]; 1338 settings.workspace.library = generators.mkLuaInline ''vim.api.nvim_get_runtime_file("", true)''; 1339 }; 1340 expected = '' 1341 { 1342 ["cmd"] = { 1343 "typescript-language-server", 1344 "--stdio" 1345 }, 1346 ["settings"] = { 1347 ["workspace"] = { 1348 ["library"] = (vim.api.nvim_get_runtime_file("", true)) 1349 } 1350 } 1351 }''; 1352 }; 1353 1354# CLI 1355 1356 testToGNUCommandLine = { 1357 expr = cli.toGNUCommandLine {} { 1358 data = builtins.toJSON { id = 0; }; 1359 X = "PUT"; 1360 retry = 3; 1361 retry-delay = null; 1362 url = [ "https://example.com/foo" "https://example.com/bar" ]; 1363 silent = false; 1364 verbose = true; 1365 }; 1366 1367 expected = [ 1368 "-X" "PUT" 1369 "--data" "{\"id\":0}" 1370 "--retry" "3" 1371 "--url" "https://example.com/foo" 1372 "--url" "https://example.com/bar" 1373 "--verbose" 1374 ]; 1375 }; 1376 1377 testToGNUCommandLineShell = { 1378 expr = cli.toGNUCommandLineShell {} { 1379 data = builtins.toJSON { id = 0; }; 1380 X = "PUT"; 1381 retry = 3; 1382 retry-delay = null; 1383 url = [ "https://example.com/foo" "https://example.com/bar" ]; 1384 silent = false; 1385 verbose = true; 1386 }; 1387 1388 expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; 1389 }; 1390 1391 testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName { 1392 name = "..foo"; 1393 expected = "foo"; 1394 }; 1395 1396 testSanitizeDerivationNameUnicode = testSanitizeDerivationName { 1397 name = "fö"; 1398 expected = "f-"; 1399 }; 1400 1401 testSanitizeDerivationNameAscii = testSanitizeDerivationName { 1402 name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; 1403 expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-"; 1404 }; 1405 1406 testSanitizeDerivationNameTooLong = testSanitizeDerivationName { 1407 name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; 1408 expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; 1409 }; 1410 1411 testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName { 1412 name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&"; 1413 expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-"; 1414 }; 1415 1416 testSanitizeDerivationNameEmpty = testSanitizeDerivationName { 1417 name = ""; 1418 expected = "unknown"; 1419 }; 1420 1421 testFreeformOptions = { 1422 expr = 1423 let 1424 submodule = { lib, ... }: { 1425 freeformType = lib.types.attrsOf (lib.types.submodule { 1426 options.bar = lib.mkOption {}; 1427 }); 1428 options.bar = lib.mkOption {}; 1429 }; 1430 1431 module = { lib, ... }: { 1432 options.foo = lib.mkOption { 1433 type = lib.types.submodule submodule; 1434 }; 1435 }; 1436 1437 options = (evalModules { 1438 modules = [ module ]; 1439 }).options; 1440 1441 locs = filter (o: ! o.internal) (optionAttrSetToDocList options); 1442 in map (o: o.loc) locs; 1443 expected = [ [ "_module" "args" ] [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ]; 1444 }; 1445 1446 testCartesianProductOfEmptySet = { 1447 expr = cartesianProductOfSets {}; 1448 expected = [ {} ]; 1449 }; 1450 1451 testCartesianProductOfOneSet = { 1452 expr = cartesianProductOfSets { a = [ 1 2 3 ]; }; 1453 expected = [ { a = 1; } { a = 2; } { a = 3; } ]; 1454 }; 1455 1456 testCartesianProductOfTwoSets = { 1457 expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; }; 1458 expected = [ 1459 { a = 1; b = 10; } 1460 { a = 1; b = 20; } 1461 ]; 1462 }; 1463 1464 testCartesianProductOfTwoSetsWithOneEmpty = { 1465 expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; }; 1466 expected = [ ]; 1467 }; 1468 1469 testCartesianProductOfThreeSets = { 1470 expr = cartesianProductOfSets { 1471 a = [ 1 2 3 ]; 1472 b = [ 10 20 30 ]; 1473 c = [ 100 200 300 ]; 1474 }; 1475 expected = [ 1476 { a = 1; b = 10; c = 100; } 1477 { a = 1; b = 10; c = 200; } 1478 { a = 1; b = 10; c = 300; } 1479 1480 { a = 1; b = 20; c = 100; } 1481 { a = 1; b = 20; c = 200; } 1482 { a = 1; b = 20; c = 300; } 1483 1484 { a = 1; b = 30; c = 100; } 1485 { a = 1; b = 30; c = 200; } 1486 { a = 1; b = 30; c = 300; } 1487 1488 { a = 2; b = 10; c = 100; } 1489 { a = 2; b = 10; c = 200; } 1490 { a = 2; b = 10; c = 300; } 1491 1492 { a = 2; b = 20; c = 100; } 1493 { a = 2; b = 20; c = 200; } 1494 { a = 2; b = 20; c = 300; } 1495 1496 { a = 2; b = 30; c = 100; } 1497 { a = 2; b = 30; c = 200; } 1498 { a = 2; b = 30; c = 300; } 1499 1500 { a = 3; b = 10; c = 100; } 1501 { a = 3; b = 10; c = 200; } 1502 { a = 3; b = 10; c = 300; } 1503 1504 { a = 3; b = 20; c = 100; } 1505 { a = 3; b = 20; c = 200; } 1506 { a = 3; b = 20; c = 300; } 1507 1508 { a = 3; b = 30; c = 100; } 1509 { a = 3; b = 30; c = 200; } 1510 { a = 3; b = 30; c = 300; } 1511 ]; 1512 }; 1513 1514 # The example from the showAttrPath documentation 1515 testShowAttrPathExample = { 1516 expr = showAttrPath [ "foo" "10" "bar" ]; 1517 expected = "foo.\"10\".bar"; 1518 }; 1519 1520 testShowAttrPathEmpty = { 1521 expr = showAttrPath []; 1522 expected = "<root attribute path>"; 1523 }; 1524 1525 testShowAttrPathVarious = { 1526 expr = showAttrPath [ 1527 "." 1528 "foo" 1529 "2" 1530 "a2-b" 1531 "_bc'de" 1532 ]; 1533 expected = ''".".foo."2".a2-b._bc'de''; 1534 }; 1535 1536 testGroupBy = { 1537 expr = groupBy (n: toString (mod n 5)) (range 0 16); 1538 expected = { 1539 "0" = [ 0 5 10 15 ]; 1540 "1" = [ 1 6 11 16 ]; 1541 "2" = [ 2 7 12 ]; 1542 "3" = [ 3 8 13 ]; 1543 "4" = [ 4 9 14 ]; 1544 }; 1545 }; 1546 1547 testGroupBy' = { 1548 expr = groupBy' builtins.add 0 (x: boolToString (x > 2)) [ 5 1 2 3 4 ]; 1549 expected = { false = 3; true = 12; }; 1550 }; 1551 1552 # The example from the updateManyAttrsByPath documentation 1553 testUpdateManyAttrsByPathExample = { 1554 expr = updateManyAttrsByPath [ 1555 { 1556 path = [ "a" "b" ]; 1557 update = old: { d = old.c; }; 1558 } 1559 { 1560 path = [ "a" "b" "c" ]; 1561 update = old: old + 1; 1562 } 1563 { 1564 path = [ "x" "y" ]; 1565 update = old: "xy"; 1566 } 1567 ] { a.b.c = 0; }; 1568 expected = { a = { b = { d = 1; }; }; x = { y = "xy"; }; }; 1569 }; 1570 1571 # If there are no updates, the value is passed through 1572 testUpdateManyAttrsByPathNone = { 1573 expr = updateManyAttrsByPath [] "something"; 1574 expected = "something"; 1575 }; 1576 1577 # A single update to the root path is just like applying the function directly 1578 testUpdateManyAttrsByPathSingleIncrement = { 1579 expr = updateManyAttrsByPath [ 1580 { 1581 path = [ ]; 1582 update = old: old + 1; 1583 } 1584 ] 0; 1585 expected = 1; 1586 }; 1587 1588 # Multiple updates can be applied are done in order 1589 testUpdateManyAttrsByPathMultipleIncrements = { 1590 expr = updateManyAttrsByPath [ 1591 { 1592 path = [ ]; 1593 update = old: old + "a"; 1594 } 1595 { 1596 path = [ ]; 1597 update = old: old + "b"; 1598 } 1599 { 1600 path = [ ]; 1601 update = old: old + "c"; 1602 } 1603 ] ""; 1604 expected = "abc"; 1605 }; 1606 1607 # If an update doesn't use the value, all previous updates are not evaluated 1608 testUpdateManyAttrsByPathLazy = { 1609 expr = updateManyAttrsByPath [ 1610 { 1611 path = [ ]; 1612 update = old: old + throw "nope"; 1613 } 1614 { 1615 path = [ ]; 1616 update = old: "untainted"; 1617 } 1618 ] (throw "start"); 1619 expected = "untainted"; 1620 }; 1621 1622 # Deeply nested attributes can be updated without affecting others 1623 testUpdateManyAttrsByPathDeep = { 1624 expr = updateManyAttrsByPath [ 1625 { 1626 path = [ "a" "b" "c" ]; 1627 update = old: old + 1; 1628 } 1629 ] { 1630 a.b.c = 0; 1631 1632 a.b.z = 0; 1633 a.y.z = 0; 1634 x.y.z = 0; 1635 }; 1636 expected = { 1637 a.b.c = 1; 1638 1639 a.b.z = 0; 1640 a.y.z = 0; 1641 x.y.z = 0; 1642 }; 1643 }; 1644 1645 # Nested attributes are updated first 1646 testUpdateManyAttrsByPathNestedBeforehand = { 1647 expr = updateManyAttrsByPath [ 1648 { 1649 path = [ "a" ]; 1650 update = old: old // { x = old.b; }; 1651 } 1652 { 1653 path = [ "a" "b" ]; 1654 update = old: old + 1; 1655 } 1656 ] { 1657 a.b = 0; 1658 }; 1659 expected = { 1660 a.b = 1; 1661 a.x = 1; 1662 }; 1663 }; 1664 1665 ## Levenshtein distance functions and co. 1666 testCommonPrefixLengthEmpty = { 1667 expr = strings.commonPrefixLength "" "hello"; 1668 expected = 0; 1669 }; 1670 1671 testCommonPrefixLengthSame = { 1672 expr = strings.commonPrefixLength "hello" "hello"; 1673 expected = 5; 1674 }; 1675 1676 testCommonPrefixLengthDiffering = { 1677 expr = strings.commonPrefixLength "hello" "hey"; 1678 expected = 2; 1679 }; 1680 1681 testCommonSuffixLengthEmpty = { 1682 expr = strings.commonSuffixLength "" "hello"; 1683 expected = 0; 1684 }; 1685 1686 testCommonSuffixLengthSame = { 1687 expr = strings.commonSuffixLength "hello" "hello"; 1688 expected = 5; 1689 }; 1690 1691 testCommonSuffixLengthDiffering = { 1692 expr = strings.commonSuffixLength "test" "rest"; 1693 expected = 3; 1694 }; 1695 1696 testLevenshteinEmpty = { 1697 expr = strings.levenshtein "" ""; 1698 expected = 0; 1699 }; 1700 1701 testLevenshteinOnlyAdd = { 1702 expr = strings.levenshtein "" "hello there"; 1703 expected = 11; 1704 }; 1705 1706 testLevenshteinOnlyRemove = { 1707 expr = strings.levenshtein "hello there" ""; 1708 expected = 11; 1709 }; 1710 1711 testLevenshteinOnlyTransform = { 1712 expr = strings.levenshtein "abcdef" "ghijkl"; 1713 expected = 6; 1714 }; 1715 1716 testLevenshteinMixed = { 1717 expr = strings.levenshtein "kitchen" "sitting"; 1718 expected = 5; 1719 }; 1720 1721 testLevenshteinAtMostZeroFalse = { 1722 expr = strings.levenshteinAtMost 0 "foo" "boo"; 1723 expected = false; 1724 }; 1725 1726 testLevenshteinAtMostZeroTrue = { 1727 expr = strings.levenshteinAtMost 0 "foo" "foo"; 1728 expected = true; 1729 }; 1730 1731 testLevenshteinAtMostOneFalse = { 1732 expr = strings.levenshteinAtMost 1 "car" "ct"; 1733 expected = false; 1734 }; 1735 1736 testLevenshteinAtMostOneTrue = { 1737 expr = strings.levenshteinAtMost 1 "car" "cr"; 1738 expected = true; 1739 }; 1740 1741 # We test levenshteinAtMost 2 particularly well because it uses a complicated 1742 # implementation 1743 testLevenshteinAtMostTwoIsEmpty = { 1744 expr = strings.levenshteinAtMost 2 "" ""; 1745 expected = true; 1746 }; 1747 1748 testLevenshteinAtMostTwoIsZero = { 1749 expr = strings.levenshteinAtMost 2 "abcdef" "abcdef"; 1750 expected = true; 1751 }; 1752 1753 testLevenshteinAtMostTwoIsOne = { 1754 expr = strings.levenshteinAtMost 2 "abcdef" "abddef"; 1755 expected = true; 1756 }; 1757 1758 testLevenshteinAtMostTwoDiff0False = { 1759 expr = strings.levenshteinAtMost 2 "abcdef" "aczyef"; 1760 expected = false; 1761 }; 1762 1763 testLevenshteinAtMostTwoDiff0Outer = { 1764 expr = strings.levenshteinAtMost 2 "abcdef" "zbcdez"; 1765 expected = true; 1766 }; 1767 1768 testLevenshteinAtMostTwoDiff0DelLeft = { 1769 expr = strings.levenshteinAtMost 2 "abcdef" "bcdefz"; 1770 expected = true; 1771 }; 1772 1773 testLevenshteinAtMostTwoDiff0DelRight = { 1774 expr = strings.levenshteinAtMost 2 "abcdef" "zabcde"; 1775 expected = true; 1776 }; 1777 1778 testLevenshteinAtMostTwoDiff1False = { 1779 expr = strings.levenshteinAtMost 2 "abcdef" "bddez"; 1780 expected = false; 1781 }; 1782 1783 testLevenshteinAtMostTwoDiff1DelLeft = { 1784 expr = strings.levenshteinAtMost 2 "abcdef" "bcdez"; 1785 expected = true; 1786 }; 1787 1788 testLevenshteinAtMostTwoDiff1DelRight = { 1789 expr = strings.levenshteinAtMost 2 "abcdef" "zbcde"; 1790 expected = true; 1791 }; 1792 1793 testLevenshteinAtMostTwoDiff2False = { 1794 expr = strings.levenshteinAtMost 2 "hello" "hxo"; 1795 expected = false; 1796 }; 1797 1798 testLevenshteinAtMostTwoDiff2True = { 1799 expr = strings.levenshteinAtMost 2 "hello" "heo"; 1800 expected = true; 1801 }; 1802 1803 testLevenshteinAtMostTwoDiff3 = { 1804 expr = strings.levenshteinAtMost 2 "hello" "ho"; 1805 expected = false; 1806 }; 1807 1808 testLevenshteinAtMostThreeFalse = { 1809 expr = strings.levenshteinAtMost 3 "hello" "Holla!"; 1810 expected = false; 1811 }; 1812 1813 testLevenshteinAtMostThreeTrue = { 1814 expr = strings.levenshteinAtMost 3 "hello" "Holla"; 1815 expected = true; 1816 }; 1817 1818 # lazyDerivation 1819 1820 testLazyDerivationIsLazyInDerivationForAttrNames = { 1821 expr = attrNames (lazyDerivation { 1822 derivation = throw "not lazy enough"; 1823 }); 1824 # It's ok to add attribute names here when lazyDerivation is improved 1825 # in accordance with its inline comments. 1826 expected = [ "drvPath" "meta" "name" "out" "outPath" "outputName" "outputs" "system" "type" ]; 1827 }; 1828 1829 testLazyDerivationIsLazyInDerivationForPassthruAttr = { 1830 expr = (lazyDerivation { 1831 derivation = throw "not lazy enough"; 1832 passthru.tests = "whatever is in tests"; 1833 }).tests; 1834 expected = "whatever is in tests"; 1835 }; 1836 1837 testLazyDerivationIsLazyInDerivationForPassthruAttr2 = { 1838 # passthru.tests is not a special case. It works for any attr. 1839 expr = (lazyDerivation { 1840 derivation = throw "not lazy enough"; 1841 passthru.foo = "whatever is in foo"; 1842 }).foo; 1843 expected = "whatever is in foo"; 1844 }; 1845 1846 testLazyDerivationIsLazyInDerivationForMeta = { 1847 expr = (lazyDerivation { 1848 derivation = throw "not lazy enough"; 1849 meta = "whatever is in meta"; 1850 }).meta; 1851 expected = "whatever is in meta"; 1852 }; 1853 1854 testLazyDerivationReturnsDerivationAttrs = let 1855 derivation = { 1856 type = "derivation"; 1857 outputs = ["out"]; 1858 out = "test out"; 1859 outPath = "test outPath"; 1860 outputName = "out"; 1861 drvPath = "test drvPath"; 1862 name = "test name"; 1863 system = "test system"; 1864 meta = "test meta"; 1865 }; 1866 in { 1867 expr = lazyDerivation { inherit derivation; }; 1868 expected = derivation; 1869 }; 1870 1871 testTypeDescriptionInt = { 1872 expr = (with types; int).description; 1873 expected = "signed integer"; 1874 }; 1875 testTypeDescriptionListOfInt = { 1876 expr = (with types; listOf int).description; 1877 expected = "list of signed integer"; 1878 }; 1879 testTypeDescriptionListOfListOfInt = { 1880 expr = (with types; listOf (listOf int)).description; 1881 expected = "list of list of signed integer"; 1882 }; 1883 testTypeDescriptionListOfEitherStrOrBool = { 1884 expr = (with types; listOf (either str bool)).description; 1885 expected = "list of (string or boolean)"; 1886 }; 1887 testTypeDescriptionEitherListOfStrOrBool = { 1888 expr = (with types; either (listOf bool) str).description; 1889 expected = "(list of boolean) or string"; 1890 }; 1891 testTypeDescriptionEitherStrOrListOfBool = { 1892 expr = (with types; either str (listOf bool)).description; 1893 expected = "string or list of boolean"; 1894 }; 1895 testTypeDescriptionOneOfListOfStrOrBool = { 1896 expr = (with types; oneOf [ (listOf bool) str ]).description; 1897 expected = "(list of boolean) or string"; 1898 }; 1899 testTypeDescriptionOneOfListOfStrOrBoolOrNumber = { 1900 expr = (with types; oneOf [ (listOf bool) str number ]).description; 1901 expected = "(list of boolean) or string or signed integer or floating point number"; 1902 }; 1903 testTypeDescriptionEitherListOfBoolOrEitherStringOrNumber = { 1904 expr = (with types; either (listOf bool) (either str number)).description; 1905 expected = "(list of boolean) or string or signed integer or floating point number"; 1906 }; 1907 testTypeDescriptionEitherEitherListOfBoolOrStringOrNumber = { 1908 expr = (with types; either (either (listOf bool) str) number).description; 1909 expected = "(list of boolean) or string or signed integer or floating point number"; 1910 }; 1911 testTypeDescriptionEitherNullOrBoolOrString = { 1912 expr = (with types; either (nullOr bool) str).description; 1913 expected = "null or boolean or string"; 1914 }; 1915 testTypeDescriptionEitherListOfEitherBoolOrStrOrInt = { 1916 expr = (with types; either (listOf (either bool str)) int).description; 1917 expected = "(list of (boolean or string)) or signed integer"; 1918 }; 1919 testTypeDescriptionEitherIntOrListOrEitherBoolOrStr = { 1920 expr = (with types; either int (listOf (either bool str))).description; 1921 expected = "signed integer or list of (boolean or string)"; 1922 }; 1923 1924# Meta 1925 testGetExe'Output = { 1926 expr = getExe' { 1927 type = "derivation"; 1928 out = "somelonghash"; 1929 bin = "somelonghash"; 1930 } "executable"; 1931 expected = "somelonghash/bin/executable"; 1932 }; 1933 1934 testGetExeOutput = { 1935 expr = getExe { 1936 type = "derivation"; 1937 out = "somelonghash"; 1938 bin = "somelonghash"; 1939 meta.mainProgram = "mainProgram"; 1940 }; 1941 expected = "somelonghash/bin/mainProgram"; 1942 }; 1943 1944 testGetExe'FailureFirstArg = testingThrow ( 1945 getExe' "not a derivation" "executable" 1946 ); 1947 1948 testGetExe'FailureSecondArg = testingThrow ( 1949 getExe' { type = "derivation"; } "dir/executable" 1950 ); 1951}