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