at 22.05-pre 19 kB view raw
1# to run these tests: 2# nix-instantiate --eval --strict nixpkgs/lib/tests/misc.nix 3# if the resulting list is empty, all tests passed 4with import ../default.nix; 5 6let 7 8 testSanitizeDerivationName = { name, expected }: 9 let 10 drv = derivation { 11 name = strings.sanitizeDerivationName name; 12 builder = "x"; 13 system = "x"; 14 }; 15 in { 16 # Evaluate the derivation so an invalid name would be caught 17 expr = builtins.seq drv.drvPath drv.name; 18 inherit expected; 19 }; 20 21in 22 23runTests { 24 25 26# TRIVIAL 27 28 testId = { 29 expr = id 1; 30 expected = 1; 31 }; 32 33 testConst = { 34 expr = const 2 3; 35 expected = 2; 36 }; 37 38 testPipe = { 39 expr = pipe 2 [ 40 (x: x + 2) # 2 + 2 = 4 41 (x: x * 2) # 4 * 2 = 8 42 ]; 43 expected = 8; 44 }; 45 46 testPipeEmpty = { 47 expr = pipe 2 []; 48 expected = 2; 49 }; 50 51 testPipeStrings = { 52 expr = pipe [ 3 4 ] [ 53 (map toString) 54 (map (s: s + "\n")) 55 concatStrings 56 ]; 57 expected = '' 58 3 59 4 60 ''; 61 }; 62 63 /* 64 testOr = { 65 expr = or true false; 66 expected = true; 67 }; 68 */ 69 70 testAnd = { 71 expr = and true false; 72 expected = false; 73 }; 74 75 testFix = { 76 expr = fix (x: {a = if x ? a then "a" else "b";}); 77 expected = {a = "a";}; 78 }; 79 80 testComposeExtensions = { 81 expr = let obj = makeExtensible (self: { foo = self.bar; }); 82 f = self: super: { bar = false; baz = true; }; 83 g = self: super: { bar = super.baz or false; }; 84 f_o_g = composeExtensions f g; 85 composed = obj.extend f_o_g; 86 in composed.foo; 87 expected = true; 88 }; 89 90 testComposeManyExtensions0 = { 91 expr = let obj = makeExtensible (self: { foo = true; }); 92 emptyComposition = composeManyExtensions []; 93 composed = obj.extend emptyComposition; 94 in composed.foo; 95 expected = true; 96 }; 97 98 testComposeManyExtensions = 99 let f = self: super: { bar = false; baz = true; }; 100 g = self: super: { bar = super.baz or false; }; 101 h = self: super: { qux = super.bar or false; }; 102 obj = makeExtensible (self: { foo = self.qux; }); 103 in { 104 expr = let composition = composeManyExtensions [f g h]; 105 composed = obj.extend composition; 106 in composed.foo; 107 expected = (obj.extend (composeExtensions f (composeExtensions g h))).foo; 108 }; 109 110 testBitAnd = { 111 expr = (bitAnd 3 10); 112 expected = 2; 113 }; 114 115 testBitOr = { 116 expr = (bitOr 3 10); 117 expected = 11; 118 }; 119 120 testBitXor = { 121 expr = (bitXor 3 10); 122 expected = 9; 123 }; 124 125 testToHexString = { 126 expr = toHexString 250; 127 expected = "FA"; 128 }; 129 130 testToBaseDigits = { 131 expr = toBaseDigits 2 6; 132 expected = [ 1 1 0 ]; 133 }; 134 135 testFunctionArgsFunctor = { 136 expr = functionArgs { __functor = self: { a, b }: null; }; 137 expected = { a = false; b = false; }; 138 }; 139 140 testFunctionArgsSetFunctionArgs = { 141 expr = functionArgs (setFunctionArgs (args: args.x) { x = false; }); 142 expected = { x = false; }; 143 }; 144 145# STRINGS 146 147 testConcatMapStrings = { 148 expr = concatMapStrings (x: x + ";") ["a" "b" "c"]; 149 expected = "a;b;c;"; 150 }; 151 152 testConcatStringsSep = { 153 expr = concatStringsSep "," ["a" "b" "c"]; 154 expected = "a,b,c"; 155 }; 156 157 testSplitStringsSimple = { 158 expr = strings.splitString "." "a.b.c.d"; 159 expected = [ "a" "b" "c" "d" ]; 160 }; 161 162 testSplitStringsEmpty = { 163 expr = strings.splitString "." "a..b"; 164 expected = [ "a" "" "b" ]; 165 }; 166 167 testSplitStringsOne = { 168 expr = strings.splitString ":" "a.b"; 169 expected = [ "a.b" ]; 170 }; 171 172 testSplitStringsNone = { 173 expr = strings.splitString "." ""; 174 expected = [ "" ]; 175 }; 176 177 testSplitStringsFirstEmpty = { 178 expr = strings.splitString "/" "/a/b/c"; 179 expected = [ "" "a" "b" "c" ]; 180 }; 181 182 testSplitStringsLastEmpty = { 183 expr = strings.splitString ":" "2001:db8:0:0042::8a2e:370:"; 184 expected = [ "2001" "db8" "0" "0042" "" "8a2e" "370" "" ]; 185 }; 186 187 testSplitStringsRegex = { 188 expr = strings.splitString "\\[{}]()^$?*+|." "A\\[{}]()^$?*+|.B"; 189 expected = [ "A" "B" ]; 190 }; 191 192 testSplitStringsDerivation = { 193 expr = take 3 (strings.splitString "/" (derivation { 194 name = "name"; 195 builder = "builder"; 196 system = "system"; 197 })); 198 expected = ["" "nix" "store"]; 199 }; 200 201 testSplitVersionSingle = { 202 expr = versions.splitVersion "1"; 203 expected = [ "1" ]; 204 }; 205 206 testSplitVersionDouble = { 207 expr = versions.splitVersion "1.2"; 208 expected = [ "1" "2" ]; 209 }; 210 211 testSplitVersionTriple = { 212 expr = versions.splitVersion "1.2.3"; 213 expected = [ "1" "2" "3" ]; 214 }; 215 216 testIsStorePath = { 217 expr = 218 let goodPath = 219 "${builtins.storeDir}/d945ibfx9x185xf04b890y4f9g3cbb63-python-2.7.11"; 220 in { 221 storePath = isStorePath goodPath; 222 storePathDerivation = isStorePath (import ../.. { system = "x86_64-linux"; }).hello; 223 storePathAppendix = isStorePath 224 "${goodPath}/bin/python"; 225 nonAbsolute = isStorePath (concatStrings (tail (stringToCharacters goodPath))); 226 asPath = isStorePath (/. + goodPath); 227 otherPath = isStorePath "/something/else"; 228 otherVals = { 229 attrset = isStorePath {}; 230 list = isStorePath []; 231 int = isStorePath 42; 232 }; 233 }; 234 expected = { 235 storePath = true; 236 storePathDerivation = true; 237 storePathAppendix = false; 238 nonAbsolute = false; 239 asPath = true; 240 otherPath = false; 241 otherVals = { 242 attrset = false; 243 list = false; 244 int = false; 245 }; 246 }; 247 }; 248 249 testEscapeXML = { 250 expr = escapeXML ''"test" 'test' < & >''; 251 expected = "&quot;test&quot; &apos;test&apos; &lt; &amp; &gt;"; 252 }; 253 254# LISTS 255 256 testFilter = { 257 expr = filter (x: x != "a") ["a" "b" "c" "a"]; 258 expected = ["b" "c"]; 259 }; 260 261 testFold = 262 let 263 f = op: fold: fold op 0 (range 0 100); 264 # fold with associative operator 265 assoc = f builtins.add; 266 # fold with non-associative operator 267 nonAssoc = f builtins.sub; 268 in { 269 expr = { 270 assocRight = assoc foldr; 271 # right fold with assoc operator is same as left fold 272 assocRightIsLeft = assoc foldr == assoc foldl; 273 nonAssocRight = nonAssoc foldr; 274 nonAssocLeft = nonAssoc foldl; 275 # with non-assoc operator the fold results are not the same 276 nonAssocRightIsNotLeft = nonAssoc foldl != nonAssoc foldr; 277 # fold is an alias for foldr 278 foldIsRight = nonAssoc fold == nonAssoc foldr; 279 }; 280 expected = { 281 assocRight = 5050; 282 assocRightIsLeft = true; 283 nonAssocRight = 50; 284 nonAssocLeft = (-5050); 285 nonAssocRightIsNotLeft = true; 286 foldIsRight = true; 287 }; 288 }; 289 290 testTake = testAllTrue [ 291 ([] == (take 0 [ 1 2 3 ])) 292 ([1] == (take 1 [ 1 2 3 ])) 293 ([ 1 2 ] == (take 2 [ 1 2 3 ])) 294 ([ 1 2 3 ] == (take 3 [ 1 2 3 ])) 295 ([ 1 2 3 ] == (take 4 [ 1 2 3 ])) 296 ]; 297 298 testFoldAttrs = { 299 expr = foldAttrs (n: a: [n] ++ a) [] [ 300 { a = 2; b = 7; } 301 { a = 3; c = 8; } 302 ]; 303 expected = { a = [ 2 3 ]; b = [7]; c = [8];}; 304 }; 305 306 testSort = { 307 expr = sort builtins.lessThan [ 40 2 30 42 ]; 308 expected = [2 30 40 42]; 309 }; 310 311 testToIntShouldConvertStringToInt = { 312 expr = toInt "27"; 313 expected = 27; 314 }; 315 316 testToIntShouldThrowErrorIfItCouldNotConvertToInt = { 317 expr = builtins.tryEval (toInt "\"foo\""); 318 expected = { success = false; value = false; }; 319 }; 320 321 testHasAttrByPathTrue = { 322 expr = hasAttrByPath ["a" "b"] { a = { b = "yey"; }; }; 323 expected = true; 324 }; 325 326 testHasAttrByPathFalse = { 327 expr = hasAttrByPath ["a" "b"] { a = { c = "yey"; }; }; 328 expected = false; 329 }; 330 331 332# ATTRSETS 333 334 # code from the example 335 testRecursiveUpdateUntil = { 336 expr = recursiveUpdateUntil (path: l: r: path == ["foo"]) { 337 # first attribute set 338 foo.bar = 1; 339 foo.baz = 2; 340 bar = 3; 341 } { 342 #second attribute set 343 foo.bar = 1; 344 foo.quz = 2; 345 baz = 4; 346 }; 347 expected = { 348 foo.bar = 1; # 'foo.*' from the second set 349 foo.quz = 2; # 350 bar = 3; # 'bar' from the first set 351 baz = 4; # 'baz' from the second set 352 }; 353 }; 354 355 testOverrideExistingEmpty = { 356 expr = overrideExisting {} { a = 1; }; 357 expected = {}; 358 }; 359 360 testOverrideExistingDisjoint = { 361 expr = overrideExisting { b = 2; } { a = 1; }; 362 expected = { b = 2; }; 363 }; 364 365 testOverrideExistingOverride = { 366 expr = overrideExisting { a = 3; b = 2; } { a = 1; }; 367 expected = { a = 1; b = 2; }; 368 }; 369 370# GENERATORS 371# these tests assume attributes are converted to lists 372# in alphabetical order 373 374 testMkKeyValueDefault = { 375 expr = generators.mkKeyValueDefault {} ":" "f:oo" "bar"; 376 expected = ''f\:oo:bar''; 377 }; 378 379 testMkValueString = { 380 expr = let 381 vals = { 382 int = 42; 383 string = ''fo"o''; 384 bool = true; 385 bool2 = false; 386 null = null; 387 # float = 42.23; # floats are strange 388 }; 389 in mapAttrs 390 (const (generators.mkValueStringDefault {})) 391 vals; 392 expected = { 393 int = "42"; 394 string = ''fo"o''; 395 bool = "true"; 396 bool2 = "false"; 397 null = "null"; 398 # float = "42.23" true false [ "bar" ] ]''; 399 }; 400 }; 401 402 testToKeyValue = { 403 expr = generators.toKeyValue {} { 404 key = "value"; 405 "other=key" = "baz"; 406 }; 407 expected = '' 408 key=value 409 other\=key=baz 410 ''; 411 }; 412 413 testToINIEmpty = { 414 expr = generators.toINI {} {}; 415 expected = ""; 416 }; 417 418 testToINIEmptySection = { 419 expr = generators.toINI {} { foo = {}; bar = {}; }; 420 expected = '' 421 [bar] 422 423 [foo] 424 ''; 425 }; 426 427 testToINIDuplicateKeys = { 428 expr = generators.toINI { listsAsDuplicateKeys = true; } { foo.bar = true; baz.qux = [ 1 false ]; }; 429 expected = '' 430 [baz] 431 qux=1 432 qux=false 433 434 [foo] 435 bar=true 436 ''; 437 }; 438 439 testToINIDefaultEscapes = { 440 expr = generators.toINI {} { 441 "no [ and ] allowed unescaped" = { 442 "and also no = in keys" = 42; 443 }; 444 }; 445 expected = '' 446 [no \[ and \] allowed unescaped] 447 and also no \= in keys=42 448 ''; 449 }; 450 451 testToINIDefaultFull = { 452 expr = generators.toINI {} { 453 "section 1" = { 454 attribute1 = 5; 455 x = "Me-se JarJar Binx"; 456 # booleans are converted verbatim by default 457 boolean = false; 458 }; 459 "foo[]" = { 460 "he\\h=he" = "this is okay"; 461 }; 462 }; 463 expected = '' 464 [foo\[\]] 465 he\h\=he=this is okay 466 467 [section 1] 468 attribute1=5 469 boolean=false 470 x=Me-se JarJar Binx 471 ''; 472 }; 473 474 /* right now only invocation check */ 475 testToJSONSimple = 476 let val = { 477 foobar = [ "baz" 1 2 3 ]; 478 }; 479 in { 480 expr = generators.toJSON {} val; 481 # trivial implementation 482 expected = builtins.toJSON val; 483 }; 484 485 /* right now only invocation check */ 486 testToYAMLSimple = 487 let val = { 488 list = [ { one = 1; } { two = 2; } ]; 489 all = 42; 490 }; 491 in { 492 expr = generators.toYAML {} val; 493 # trivial implementation 494 expected = builtins.toJSON val; 495 }; 496 497 testToPretty = 498 let 499 deriv = derivation { name = "test"; builder = "/bin/sh"; system = builtins.currentSystem; }; 500 in { 501 expr = mapAttrs (const (generators.toPretty { multiline = false; })) rec { 502 int = 42; 503 float = 0.1337; 504 bool = true; 505 emptystring = ""; 506 string = ''fno"rd''; 507 newlinestring = "\n"; 508 path = /. + "/foo"; 509 null_ = null; 510 function = x: x; 511 functionArgs = { arg ? 4, foo }: arg; 512 list = [ 3 4 function [ false ] ]; 513 emptylist = []; 514 attrs = { foo = null; "foo bar" = "baz"; }; 515 emptyattrs = {}; 516 drv = deriv; 517 }; 518 expected = rec { 519 int = "42"; 520 float = "~0.133700"; 521 bool = "true"; 522 emptystring = ''""''; 523 string = ''"fno\"rd"''; 524 newlinestring = "\"\\n\""; 525 path = "/foo"; 526 null_ = "null"; 527 function = "<function>"; 528 functionArgs = "<function, args: {arg?, foo}>"; 529 list = "[ 3 4 ${function} [ false ] ]"; 530 emptylist = "[ ]"; 531 attrs = "{ foo = null; \"foo bar\" = \"baz\"; }"; 532 emptyattrs = "{ }"; 533 drv = "<derivation ${deriv.drvPath}>"; 534 }; 535 }; 536 537 testToPrettyLimit = 538 let 539 a.b = 1; 540 a.c = a; 541 in { 542 expr = generators.toPretty { } (generators.withRecursion { throwOnDepthLimit = false; depthLimit = 2; } a); 543 expected = "{\n b = 1;\n c = {\n b = \"<unevaluated>\";\n c = {\n b = \"<unevaluated>\";\n c = \"<unevaluated>\";\n };\n };\n}"; 544 }; 545 546 testToPrettyLimitThrow = 547 let 548 a.b = 1; 549 a.c = a; 550 in { 551 expr = (builtins.tryEval 552 (generators.toPretty { } (generators.withRecursion { depthLimit = 2; } a))).success; 553 expected = false; 554 }; 555 556 testToPrettyMultiline = { 557 expr = mapAttrs (const (generators.toPretty { })) rec { 558 list = [ 3 4 [ false ] ]; 559 attrs = { foo = null; bar.foo = "baz"; }; 560 newlinestring = "\n"; 561 multilinestring = '' 562 hello 563 there 564 test 565 ''; 566 multilinestring' = '' 567 hello 568 there 569 test''; 570 }; 571 expected = rec { 572 list = '' 573 [ 574 3 575 4 576 [ 577 false 578 ] 579 ]''; 580 attrs = '' 581 { 582 bar = { 583 foo = "baz"; 584 }; 585 foo = null; 586 }''; 587 newlinestring = "''\n \n''"; 588 multilinestring = '' 589 ''' 590 hello 591 there 592 test 593 '''''; 594 multilinestring' = '' 595 ''' 596 hello 597 there 598 test'''''; 599 600 }; 601 }; 602 603 testToPrettyAllowPrettyValues = { 604 expr = generators.toPretty { allowPrettyValues = true; } 605 { __pretty = v: "«" + v + "»"; val = "foo"; }; 606 expected = "«foo»"; 607 }; 608 609 610# CLI 611 612 testToGNUCommandLine = { 613 expr = cli.toGNUCommandLine {} { 614 data = builtins.toJSON { id = 0; }; 615 X = "PUT"; 616 retry = 3; 617 retry-delay = null; 618 url = [ "https://example.com/foo" "https://example.com/bar" ]; 619 silent = false; 620 verbose = true; 621 }; 622 623 expected = [ 624 "-X" "PUT" 625 "--data" "{\"id\":0}" 626 "--retry" "3" 627 "--url" "https://example.com/foo" 628 "--url" "https://example.com/bar" 629 "--verbose" 630 ]; 631 }; 632 633 testToGNUCommandLineShell = { 634 expr = cli.toGNUCommandLineShell {} { 635 data = builtins.toJSON { id = 0; }; 636 X = "PUT"; 637 retry = 3; 638 retry-delay = null; 639 url = [ "https://example.com/foo" "https://example.com/bar" ]; 640 silent = false; 641 verbose = true; 642 }; 643 644 expected = "'-X' 'PUT' '--data' '{\"id\":0}' '--retry' '3' '--url' 'https://example.com/foo' '--url' 'https://example.com/bar' '--verbose'"; 645 }; 646 647 testSanitizeDerivationNameLeadingDots = testSanitizeDerivationName { 648 name = "..foo"; 649 expected = "foo"; 650 }; 651 652 testSanitizeDerivationNameAscii = testSanitizeDerivationName { 653 name = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; 654 expected = "-+--.-0123456789-=-?-ABCDEFGHIJKLMNOPQRSTUVWXYZ-_-abcdefghijklmnopqrstuvwxyz-"; 655 }; 656 657 testSanitizeDerivationNameTooLong = testSanitizeDerivationName { 658 name = "This string is loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; 659 expected = "loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong"; 660 }; 661 662 testSanitizeDerivationNameTooLongWithInvalid = testSanitizeDerivationName { 663 name = "Hello there aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&&&&&&&"; 664 expected = "there-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-"; 665 }; 666 667 testSanitizeDerivationNameEmpty = testSanitizeDerivationName { 668 name = ""; 669 expected = "unknown"; 670 }; 671 672 testFreeformOptions = { 673 expr = 674 let 675 submodule = { lib, ... }: { 676 freeformType = lib.types.attrsOf (lib.types.submodule { 677 options.bar = lib.mkOption {}; 678 }); 679 options.bar = lib.mkOption {}; 680 }; 681 682 module = { lib, ... }: { 683 options.foo = lib.mkOption { 684 type = lib.types.submodule submodule; 685 }; 686 }; 687 688 options = (evalModules { 689 modules = [ module ]; 690 }).options; 691 692 locs = filter (o: ! o.internal) (optionAttrSetToDocList options); 693 in map (o: o.loc) locs; 694 expected = [ [ "foo" ] [ "foo" "<name>" "bar" ] [ "foo" "bar" ] ]; 695 }; 696 697 testCartesianProductOfEmptySet = { 698 expr = cartesianProductOfSets {}; 699 expected = [ {} ]; 700 }; 701 702 testCartesianProductOfOneSet = { 703 expr = cartesianProductOfSets { a = [ 1 2 3 ]; }; 704 expected = [ { a = 1; } { a = 2; } { a = 3; } ]; 705 }; 706 707 testCartesianProductOfTwoSets = { 708 expr = cartesianProductOfSets { a = [ 1 ]; b = [ 10 20 ]; }; 709 expected = [ 710 { a = 1; b = 10; } 711 { a = 1; b = 20; } 712 ]; 713 }; 714 715 testCartesianProductOfTwoSetsWithOneEmpty = { 716 expr = cartesianProductOfSets { a = [ ]; b = [ 10 20 ]; }; 717 expected = [ ]; 718 }; 719 720 testCartesianProductOfThreeSets = { 721 expr = cartesianProductOfSets { 722 a = [ 1 2 3 ]; 723 b = [ 10 20 30 ]; 724 c = [ 100 200 300 ]; 725 }; 726 expected = [ 727 { a = 1; b = 10; c = 100; } 728 { a = 1; b = 10; c = 200; } 729 { a = 1; b = 10; c = 300; } 730 731 { a = 1; b = 20; c = 100; } 732 { a = 1; b = 20; c = 200; } 733 { a = 1; b = 20; c = 300; } 734 735 { a = 1; b = 30; c = 100; } 736 { a = 1; b = 30; c = 200; } 737 { a = 1; b = 30; c = 300; } 738 739 { a = 2; b = 10; c = 100; } 740 { a = 2; b = 10; c = 200; } 741 { a = 2; b = 10; c = 300; } 742 743 { a = 2; b = 20; c = 100; } 744 { a = 2; b = 20; c = 200; } 745 { a = 2; b = 20; c = 300; } 746 747 { a = 2; b = 30; c = 100; } 748 { a = 2; b = 30; c = 200; } 749 { a = 2; b = 30; c = 300; } 750 751 { a = 3; b = 10; c = 100; } 752 { a = 3; b = 10; c = 200; } 753 { a = 3; b = 10; c = 300; } 754 755 { a = 3; b = 20; c = 100; } 756 { a = 3; b = 20; c = 200; } 757 { a = 3; b = 20; c = 300; } 758 759 { a = 3; b = 30; c = 100; } 760 { a = 3; b = 30; c = 200; } 761 { a = 3; b = 30; c = 300; } 762 ]; 763 }; 764}