nixpkgs mirror (for testing)
github.com/NixOS/nixpkgs
nix
1# To run these tests:
2# nix-build -A tests.stdenv
3
4{
5 stdenv,
6 pkgs,
7 lib,
8 testers,
9}:
10
11let
12 # tests can be based on builtins.derivation and stage0 or bootstrapTools directly to minimize rebuilds
13 # see test 'make-symlinks-relative' in ./hooks.nix as an example.
14 initialBash = if stdenv ? stage0 then stdenv.stage0.bash else stdenv.bootstrapTools;
15 initialPath = if stdenv ? stage0 then stdenv.stage0.initialPath else [ stdenv.bootstrapTools ];
16 # early enough not to rebuild gcc but late enough to have patchelf
17 earlyPkgs = stdenv.__bootPackages.stdenv.__bootPackages or pkgs;
18 earlierPkgs =
19 stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages
20 or earlyPkgs;
21 # use a early stdenv so when hacking on stdenv this test can be run quickly
22 bootStdenv = earlyPkgs.stdenv.__bootPackages.stdenv.__bootPackages.stdenv or earlyPkgs.stdenv;
23 pkgsStructured = import pkgs.path {
24 config = {
25 structuredAttrsByDefault = true;
26 };
27 inherit (stdenv.hostPlatform) system;
28 };
29 bootStdenvStructuredAttrsByDefault =
30 pkgsStructured.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv.__bootPackages.stdenv
31 or pkgsStructured.stdenv;
32
33 runCommand = earlierPkgs.runCommand;
34
35 ccWrapperSubstitutionsTest =
36 {
37 name,
38 stdenv',
39 extraAttrs ? { },
40 }:
41
42 stdenv'.cc.overrideAttrs (
43 previousAttrs:
44 (
45 {
46 inherit name;
47
48 postFixup = previousAttrs.postFixup + ''
49 declare -p wrapperName
50 echo "env.wrapperName = $wrapperName"
51 [[ $wrapperName == "CC_WRAPPER" ]] || (echo "'\$wrapperName' was not 'CC_WRAPPER'" && false)
52 declare -p suffixSalt
53 echo "env.suffixSalt = $suffixSalt"
54 [[ $suffixSalt == "${stdenv'.cc.suffixSalt}" ]] || (echo "'\$suffxSalt' was not '${stdenv'.cc.suffixSalt}'" && false)
55
56 grep -q "@out@" $out/bin/cc || echo "@out@ in $out/bin/cc was substituted"
57 grep -q "@suffixSalt@" $out/bin/cc && (echo "$out/bin/cc contains unsubstituted variables" && false)
58
59 touch $out
60 '';
61 }
62 // extraAttrs
63 )
64 );
65
66 testEnvAttrset =
67 {
68 name,
69 stdenv',
70 extraAttrs ? { },
71 }:
72 stdenv'.mkDerivation (
73 {
74 inherit name;
75 env = {
76 string = "testing-string";
77 };
78
79 passAsFile = [ "buildCommand" ];
80 buildCommand = ''
81 declare -p string
82 echo "env.string = $string"
83 [[ $string == "testing-string" ]] || (echo "'\$string' was not 'testing-string'" && false)
84 [[ "$(declare -p string)" == 'declare -x string="testing-string"' ]] || (echo "'\$string' was not exported" && false)
85 touch $out
86 '';
87 }
88 // extraAttrs
89 );
90
91 testPrependAndAppendToVar =
92 {
93 name,
94 stdenv',
95 extraAttrs ? { },
96 }:
97 stdenv'.mkDerivation (
98 {
99 inherit name;
100 env = {
101 string = "testing-string";
102 };
103
104 passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ];
105 buildCommand = ''
106 declare -p string
107 appendToVar string hello
108 # test that quoted strings work
109 prependToVar string "world"
110 declare -p string
111
112 declare -A associativeArray=(["X"]="Y")
113 [[ $(appendToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "appendToVar did not throw appending to associativeArray" && false)
114 [[ $(prependToVar associativeArray "fail" 2>&1) =~ "trying to use" ]] || (echo "prependToVar did not throw prepending associativeArray" && false)
115
116 [[ $string == "world testing-string hello" ]] || (echo "'\$string' was not 'world testing-string hello'" && false)
117
118 # test appending to a unset variable
119 appendToVar nonExistant created hello
120 declare -p nonExistant
121 if [[ -n $__structuredAttrs ]]; then
122 [[ "''${nonExistant[@]}" == "created hello" ]]
123 else
124 # there's a extra " " in front here and a extra " " in the end of prependToVar
125 # shouldn't matter because these functions will mostly be used for $*Flags and the Flag variable will in most cases already exist
126 [[ "$nonExistant" == " created hello" ]]
127 fi
128
129 eval "$extraTest"
130
131 touch $out
132 '';
133 }
134 // extraAttrs
135 );
136
137 testConcatTo =
138 {
139 name,
140 stdenv',
141 extraAttrs ? { },
142 }:
143 stdenv'.mkDerivation (
144 {
145 inherit name;
146
147 string = "a *";
148 list = [
149 "c"
150 "d"
151 ];
152
153 passAsFile = [ "buildCommand" ] ++ lib.optionals (extraAttrs ? extraTest) [ "extraTest" ];
154 buildCommand = ''
155 declare -A associativeArray=(["X"]="Y")
156 [[ $(concatTo nowhere associativeArray 2>&1) =~ "trying to use" ]] || (echo "concatTo did not throw concatenating associativeArray" && false)
157
158 empty_array=()
159 empty_string=""
160
161 declare -a flagsArray
162 concatTo flagsArray string list notset=e=f empty_array=g empty_string=h
163 declare -p flagsArray
164 [[ "''${flagsArray[0]}" == "a" ]] || (echo "'\$flagsArray[0]' was not 'a'" && false)
165 [[ "''${flagsArray[1]}" == "*" ]] || (echo "'\$flagsArray[1]' was not '*'" && false)
166 [[ "''${flagsArray[2]}" == "c" ]] || (echo "'\$flagsArray[2]' was not 'c'" && false)
167 [[ "''${flagsArray[3]}" == "d" ]] || (echo "'\$flagsArray[3]' was not 'd'" && false)
168 [[ "''${flagsArray[4]}" == "e=f" ]] || (echo "'\$flagsArray[4]' was not 'e=f'" && false)
169 [[ "''${flagsArray[5]}" == "g" ]] || (echo "'\$flagsArray[5]' was not 'g'" && false)
170 [[ "''${flagsArray[6]}" == "h" ]] || (echo "'\$flagsArray[6]' was not 'h'" && false)
171
172 # test concatenating to unset variable
173 concatTo nonExistant string list notset=e=f empty_array=g empty_string=h
174 declare -p nonExistant
175 [[ "''${nonExistant[0]}" == "a" ]] || (echo "'\$nonExistant[0]' was not 'a'" && false)
176 [[ "''${nonExistant[1]}" == "*" ]] || (echo "'\$nonExistant[1]' was not '*'" && false)
177 [[ "''${nonExistant[2]}" == "c" ]] || (echo "'\$nonExistant[2]' was not 'c'" && false)
178 [[ "''${nonExistant[3]}" == "d" ]] || (echo "'\$nonExistant[3]' was not 'd'" && false)
179 [[ "''${nonExistant[4]}" == "e=f" ]] || (echo "'\$nonExistant[4]' was not 'e=f'" && false)
180 [[ "''${nonExistant[5]}" == "g" ]] || (echo "'\$nonExistant[5]' was not 'g'" && false)
181 [[ "''${nonExistant[6]}" == "h" ]] || (echo "'\$nonExistant[6]' was not 'h'" && false)
182
183 eval "$extraTest"
184
185 touch $out
186 '';
187 }
188 // extraAttrs
189 );
190
191 testConcatStringsSep =
192 { name, stdenv' }:
193 stdenv'.mkDerivation {
194 inherit name;
195
196 # NOTE: Testing with "&" as separator is intentional, because unquoted
197 # "&" has a special meaning in the "${var//pattern/replacement}" syntax.
198 # Cf. https://github.com/NixOS/nixpkgs/pull/318614#discussion_r1706191919
199 passAsFile = [ "buildCommand" ];
200 buildCommand = ''
201 declare -A associativeArray=(["X"]="Y")
202 [[ $(concatStringsSep ";" associativeArray 2>&1) =~ "trying to use" ]] || (echo "concatStringsSep did not throw concatenating associativeArray" && false)
203
204 string="lorem ipsum dolor sit amet"
205 stringWithSep="$(concatStringsSep "&" string)"
206 [[ "$stringWithSep" == "lorem&ipsum&dolor&sit&amet" ]] || (echo "'\$stringWithSep' was not 'lorem&ipsum&dolor&sit&amet'" && false)
207
208 array=("lorem ipsum" "dolor" "sit amet")
209 arrayWithSep="$(concatStringsSep "&" array)"
210 [[ "$arrayWithSep" == "lorem ipsum&dolor&sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum&dolor&sit amet'" && false)
211
212 array=("lorem ipsum" "dolor" "sit amet")
213 arrayWithSep="$(concatStringsSep "++" array)"
214 [[ "$arrayWithSep" == "lorem ipsum++dolor++sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum++dolor++sit amet'" && false)
215
216 array=("lorem ipsum" "dolor" "sit amet")
217 arrayWithSep="$(concatStringsSep " and " array)"
218 [[ "$arrayWithSep" == "lorem ipsum and dolor and sit amet" ]] || (echo "'\$arrayWithSep' was not 'lorem ipsum and dolor and sit amet'" && false)
219
220 touch $out
221 '';
222 };
223
224 testInputDerivationDep = stdenv.mkDerivation {
225 name = "test-input-derivation-dependency";
226 buildCommand = "touch $out";
227 };
228 testInputDerivation =
229 attrs:
230 (stdenv.mkDerivation (
231 attrs
232 // {
233 buildInputs = [ testInputDerivationDep ];
234 }
235 )).inputDerivation
236 // {
237 meta = { };
238 };
239in
240
241{
242 # tests for hooks in `stdenv.defaultNativeBuildInputs`
243 hooks = lib.recurseIntoAttrs (
244 import ./hooks.nix {
245 stdenv = bootStdenv;
246 pkgs = earlyPkgs;
247 inherit initialPath initialBash lib;
248 }
249 );
250
251 outputs-no-out =
252 runCommand "outputs-no-out-assert"
253 {
254 result = earlierPkgs.testers.testBuildFailure (
255 bootStdenv.mkDerivation {
256 NIX_DEBUG = 1;
257 name = "outputs-no-out";
258 outputs = [ "foo" ];
259 buildPhase = ":";
260 installPhase = ''
261 touch $foo
262 '';
263 }
264 );
265
266 # Assumption: the first output* variable to be configured is
267 # _overrideFirst outputDev "dev" "out"
268 expectedMsg = "error: _assignFirst: could not find a non-empty variable whose name to assign to outputDev.\n The following variables were all unset or empty:\n dev out";
269 }
270 ''
271 grep -F "$expectedMsg" $result/testBuildFailure.log >/dev/null
272 touch $out
273 '';
274
275 test-env-attrset = testEnvAttrset {
276 name = "test-env-attrset";
277 stdenv' = bootStdenv;
278 };
279
280 # Check that mkDerivation rejects MD5 hashes
281 rejectedHashes = lib.recurseIntoAttrs {
282 md5 =
283 let
284 drv = runCommand "md5 outputHash rejected" {
285 outputHash = "md5-fPt7dxVVP7ffY3MxkQdwVw==";
286 } "true";
287 in
288 assert !(builtins.tryEval drv).success;
289 { };
290 };
291
292 test-inputDerivation =
293 let
294 inherit
295 (stdenv.mkDerivation {
296 dep1 = derivation {
297 name = "dep1";
298 builder = "/bin/sh";
299 args = [
300 "-c"
301 ": > $out"
302 ];
303 inherit (stdenv.buildPlatform) system;
304 };
305 dep2 = derivation {
306 name = "dep2";
307 builder = "/bin/sh";
308 args = [
309 "-c"
310 ": > $out"
311 ];
312 inherit (stdenv.buildPlatform) system;
313 };
314 passAsFile = [ "dep2" ];
315 })
316 inputDerivation
317 ;
318 in
319 runCommand "test-inputDerivation"
320 {
321 exportReferencesGraph = [
322 "graph"
323 inputDerivation
324 ];
325 }
326 ''
327 grep ${inputDerivation.dep1} graph
328 grep ${inputDerivation.dep2} graph
329 touch $out
330 '';
331
332 test-inputDerivation-fixed-output =
333 let
334 inherit
335 (stdenv.mkDerivation {
336 dep1 = derivation {
337 name = "dep1";
338 builder = "/bin/sh";
339 args = [
340 "-c"
341 ": > $out"
342 ];
343 inherit (stdenv.buildPlatform) system;
344 };
345 dep2 = derivation {
346 name = "dep2";
347 builder = "/bin/sh";
348 args = [
349 "-c"
350 ": > $out"
351 ];
352 inherit (stdenv.buildPlatform) system;
353 };
354 name = "meow";
355 outputHash = "sha256-47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=";
356 outputHashMode = "flat";
357 outputHashAlgo = "sha256";
358 buildCommand = ''
359 touch $out
360 '';
361 passAsFile = [ "dep2" ];
362 })
363 inputDerivation
364 ;
365 in
366 runCommand "test-inputDerivation"
367 {
368 exportReferencesGraph = [
369 "graph"
370 inputDerivation
371 ];
372 }
373 ''
374 grep ${inputDerivation.dep1} graph
375 grep ${inputDerivation.dep2} graph
376 touch $out
377 '';
378
379 test-inputDerivation-structured = testInputDerivation {
380 name = "test-inDrv-structured";
381 __structuredAttrs = true;
382 };
383
384 test-inputDerivation-allowedReferences = testInputDerivation {
385 name = "test-inDrv-allowedReferences";
386 allowedReferences = [ ];
387 };
388
389 test-inputDerivation-disallowedReferences = testInputDerivation {
390 name = "test-inDrv-disallowedReferences";
391 disallowedReferences = [ "${testInputDerivationDep}" ];
392 };
393
394 test-inputDerivation-allowedRequisites = testInputDerivation {
395 name = "test-inDrv-allowedRequisites";
396 allowedRequisites = [ ];
397 };
398
399 test-inputDerivation-disallowedRequisites = testInputDerivation {
400 name = "test-inDrv-disallowedRequisites";
401 disallowedRequisites = [ "${testInputDerivationDep}" ];
402 };
403
404 test-inputDerivation-structured-allowedReferences = testInputDerivation {
405 name = "test-inDrv-structured-allowedReferences";
406 __structuredAttrs = true;
407 outputChecks.out.allowedReferences = [ ];
408 };
409
410 test-inputDerivation-structured-disallowedReferences = testInputDerivation {
411 name = "test-inDrv-structured-disallowedReferences";
412 __structuredAttrs = true;
413 outputChecks.out.disallowedReferences = [ "${testInputDerivationDep}" ];
414 };
415
416 test-inputDerivation-structured-allowedRequisites = testInputDerivation {
417 name = "test-inDrv-structured-allowedRequisites";
418 __structuredAttrs = true;
419 outputChecks.out.allowedRequisites = [ ];
420 };
421
422 test-inputDerivation-structured-disallowedRequisites = testInputDerivation {
423 name = "test-inDrv-structured-disallowedRequisites";
424 __structuredAttrs = true;
425 outputChecks.out.disallowedRequisites = [ "${testInputDerivationDep}" ];
426 };
427
428 test-prepend-append-to-var = testPrependAndAppendToVar {
429 name = "test-prepend-append-to-var";
430 stdenv' = bootStdenv;
431 };
432
433 test-concat-to = testConcatTo {
434 name = "test-concat-to";
435 stdenv' = bootStdenv;
436 };
437
438 test-concat-strings-sep = testConcatStringsSep {
439 name = "test-concat-strings-sep";
440 stdenv' = bootStdenv;
441 };
442
443 test-structured-env-attrset = testEnvAttrset {
444 name = "test-structured-env-attrset";
445 stdenv' = bootStdenv;
446 extraAttrs = {
447 __structuredAttrs = true;
448 };
449 };
450
451 test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest {
452 name = "test-cc-wrapper-substitutions";
453 stdenv' = bootStdenv;
454 };
455
456 tests-stdenv-gcc-stageCompare = pkgs.callPackage ./gcc-stageCompare.nix { };
457
458 ensure-no-execve-in-setup-sh =
459 derivation {
460 name = "ensure-no-execve-in-setup-sh";
461 inherit (stdenv.hostPlatform) system;
462 builder = "${initialBash}/bin/bash";
463 PATH = "${pkgs.strace}/bin:${lib.strings.makeSearchPath "bin" initialPath}";
464 initialPath = initialPath ++ [
465 pkgs.strace
466 ];
467 args = [
468 "-c"
469 ''
470 countCall() {
471 echo "$stats" | tr -s ' ' | grep "$1" | cut -d ' ' -f5
472 }
473
474 # prevent setup.sh from running `nproc` when cores=0
475 # (this would mess up the syscall stats)
476 export NIX_BUILD_CORES=1
477
478 echo "Analyzing setup.sh with strace"
479 stats=$(strace -fc bash -c ". ${../../stdenv/generic/setup.sh}" 2>&1)
480 echo "$stats" | head -n15
481
482 # fail if execve calls is > 1
483 stats=$(strace -fc bash -c ". ${../../stdenv/generic/setup.sh}" 2>&1)
484 execveCalls=$(countCall execve)
485 if [ "$execveCalls" -gt 1 ]; then
486 echo "execve calls: $execveCalls; expected: 1"
487 echo "ERROR: setup.sh should not launch additional processes when being sourced"
488 exit 1
489 else
490 echo "setup.sh doesn't launch extra processes when sourcing, as expected"
491 fi
492
493 touch $out
494 ''
495 ];
496 }
497 // {
498 meta = { };
499 };
500
501 structuredAttrsByDefault = lib.recurseIntoAttrs {
502
503 hooks = lib.recurseIntoAttrs (
504 import ./hooks.nix {
505 stdenv = bootStdenvStructuredAttrsByDefault;
506 pkgs = earlyPkgs;
507 inherit initialBash initialPath lib;
508 }
509 );
510
511 test-cc-wrapper-substitutions = ccWrapperSubstitutionsTest {
512 name = "test-cc-wrapper-substitutions-structuredAttrsByDefault";
513 stdenv' = bootStdenvStructuredAttrsByDefault;
514 };
515
516 test-structured-env-attrset = testEnvAttrset {
517 name = "test-structured-env-attrset-structuredAttrsByDefault";
518 stdenv' = bootStdenvStructuredAttrsByDefault;
519 };
520
521 test-prepend-append-to-var = testPrependAndAppendToVar {
522 name = "test-prepend-append-to-var-structuredAttrsByDefault";
523 stdenv' = bootStdenvStructuredAttrsByDefault;
524 extraAttrs = {
525 # will be a bash indexed array in attrs.sh
526 # declare -a list=('a' 'b' )
527 # and a json array in attrs.json
528 # "list":["a","b"]
529 list = [
530 "a"
531 "b"
532 ];
533 # will be a bash associative array(dictionary) in attrs.sh
534 # declare -A array=(['a']='1' ['b']='2' )
535 # and a json object in attrs.json
536 # {"array":{"a":"1","b":"2"}
537 array = {
538 a = "1";
539 b = "2";
540 };
541 extraTest = ''
542 declare -p array
543 array+=(["c"]="3")
544 declare -p array
545
546 [[ "''${array[c]}" == "3" ]] || (echo "c element of '\$array' was not '3'" && false)
547
548 declare -p list
549 prependToVar list hello
550 # test that quoted strings work
551 appendToVar list "world"
552 declare -p list
553
554 [[ "''${list[0]}" == "hello" ]] || (echo "first element of '\$list' was not 'hello'" && false)
555 [[ "''${list[1]}" == "a" ]] || (echo "first element of '\$list' was not 'a'" && false)
556 [[ "''${list[-1]}" == "world" ]] || (echo "last element of '\$list' was not 'world'" && false)
557 '';
558 };
559 };
560
561 test-concat-to = testConcatTo {
562 name = "test-concat-to-structuredAttrsByDefault";
563 stdenv' = bootStdenvStructuredAttrsByDefault;
564 extraAttrs = {
565 # test that whitespace is kept in the bash array for structuredAttrs
566 listWithSpaces = [
567 "c c"
568 "d d"
569 ];
570 extraTest = ''
571 declare -a flagsWithSpaces
572 concatTo flagsWithSpaces string listWithSpaces
573 declare -p flagsWithSpaces
574 [[ "''${flagsWithSpaces[0]}" == "a" ]] || (echo "'\$flagsWithSpaces[0]' was not 'a'" && false)
575 [[ "''${flagsWithSpaces[1]}" == "*" ]] || (echo "'\$flagsWithSpaces[1]' was not '*'" && false)
576 [[ "''${flagsWithSpaces[2]}" == "c c" ]] || (echo "'\$flagsWithSpaces[2]' was not 'c c'" && false)
577 [[ "''${flagsWithSpaces[3]}" == "d d" ]] || (echo "'\$flagsWithSpaces[3]' was not 'd d'" && false)
578 '';
579 };
580 };
581
582 test-concat-strings-sep = testConcatStringsSep {
583 name = "test-concat-strings-sep-structuredAttrsByDefault";
584 stdenv' = bootStdenvStructuredAttrsByDefault;
585 };
586
587 test-golden-example-structuredAttrs =
588 let
589 goldenSh = earlyPkgs.writeText "goldenSh" ''
590 declare -A EXAMPLE_ATTRS=(['foo']='bar' )
591 declare EXAMPLE_BOOL_FALSE=
592 declare EXAMPLE_BOOL_TRUE=1
593 declare EXAMPLE_INT=123
594 declare EXAMPLE_INT_NEG=-123
595 declare -a EXAMPLE_LIST=('foo' 'bar' )
596 declare EXAMPLE_STR='foo bar'
597 '';
598 goldenJson = earlyPkgs.writeText "goldenSh" ''
599 {
600 "EXAMPLE_ATTRS": {
601 "foo": "bar"
602 },
603 "EXAMPLE_BOOL_FALSE": false,
604 "EXAMPLE_BOOL_TRUE": true,
605 "EXAMPLE_INT": 123,
606 "EXAMPLE_INT_NEG": -123,
607 "EXAMPLE_LIST": [
608 "foo",
609 "bar"
610 ],
611 "EXAMPLE_NESTED_ATTRS": {
612 "foo": {
613 "bar": "baz"
614 }
615 },
616 "EXAMPLE_NESTED_LIST": [
617 [
618 "foo",
619 "bar"
620 ],
621 [
622 "baz"
623 ]
624 ],
625 "EXAMPLE_STR": "foo bar"
626 }
627 '';
628 in
629 bootStdenvStructuredAttrsByDefault.mkDerivation {
630 name = "test-golden-example-structuredAttrsByDefault";
631 nativeBuildInputs = [ earlyPkgs.jq ];
632
633 EXAMPLE_BOOL_TRUE = true;
634 EXAMPLE_BOOL_FALSE = false;
635 EXAMPLE_INT = 123;
636 EXAMPLE_INT_NEG = -123;
637 EXAMPLE_STR = "foo bar";
638 EXAMPLE_LIST = [
639 "foo"
640 "bar"
641 ];
642 EXAMPLE_NESTED_LIST = [
643 [
644 "foo"
645 "bar"
646 ]
647 [ "baz" ]
648 ];
649 EXAMPLE_ATTRS = {
650 foo = "bar";
651 };
652 EXAMPLE_NESTED_ATTRS = {
653 foo.bar = "baz";
654 };
655
656 inherit goldenSh;
657 inherit goldenJson;
658
659 buildCommand = ''
660 mkdir -p $out
661 cat $NIX_ATTRS_SH_FILE | grep "EXAMPLE" | grep -v -E 'installPhase|jq' > $out/sh
662 jq 'with_entries(select(.key|match("EXAMPLE")))' $NIX_ATTRS_JSON_FILE > $out/json
663 diff $out/sh $goldenSh
664 diff $out/json $goldenJson
665 '';
666 };
667 };
668}