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