diff options
author | John Ericson <John.Ericson@Obsidian.Systems> | 2023-12-01 13:53:13 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-12-01 13:53:13 -0500 |
commit | 211b6e18556e03829ae7bba38c7272af2900a2e8 (patch) | |
tree | bfcfef3efeb73cb0c3c6d812280191d03bda1233 /tests/functional | |
parent | 72425212657d795dc215b334b7c8c8cd36d06b72 (diff) | |
parent | f7f37035c81fa825a4dfc2df1ad2589013ac6380 (diff) |
Merge pull request #9517 from NixOS/2.18-flatten-tests
[Backport 2.18-maintanence] Backport test source layout reorgs
Diffstat (limited to 'tests/functional')
589 files changed, 13947 insertions, 0 deletions
diff --git a/tests/functional/add.sh b/tests/functional/add.sh new file mode 100644 index 000000000..5c3eed793 --- /dev/null +++ b/tests/functional/add.sh @@ -0,0 +1,28 @@ +source common.sh + +path1=$(nix-store --add ./dummy) +echo $path1 + +path2=$(nix-store --add-fixed sha256 --recursive ./dummy) +echo $path2 + +if test "$path1" != "$path2"; then + echo "nix-store --add and --add-fixed mismatch" + exit 1 +fi + +path3=$(nix-store --add-fixed sha256 ./dummy) +echo $path3 +test "$path1" != "$path3" || exit 1 + +path4=$(nix-store --add-fixed sha1 --recursive ./dummy) +echo $path4 +test "$path1" != "$path4" || exit 1 + +hash1=$(nix-store -q --hash $path1) +echo $hash1 + +hash2=$(nix-hash --type sha256 --base32 ./dummy) +echo $hash2 + +test "$hash1" = "sha256:$hash2" diff --git a/tests/functional/bad.tar.xz b/tests/functional/bad.tar.xz Binary files differnew file mode 100644 index 000000000..250a5ad1a --- /dev/null +++ b/tests/functional/bad.tar.xz diff --git a/tests/functional/bash-profile.sh b/tests/functional/bash-profile.sh new file mode 100644 index 000000000..3faeaaba1 --- /dev/null +++ b/tests/functional/bash-profile.sh @@ -0,0 +1,9 @@ +source common.sh + +sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh + +user=$(whoami) +rm -rf $TEST_HOME $TEST_ROOT/profile-var +mkdir -p $TEST_HOME +USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set" +USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency diff --git a/tests/functional/big-derivation-attr.nix b/tests/functional/big-derivation-attr.nix new file mode 100644 index 000000000..35c1187f6 --- /dev/null +++ b/tests/functional/big-derivation-attr.nix @@ -0,0 +1,13 @@ +let + sixteenBytes = "0123456789abcdef"; + times16 = s: builtins.concatStringsSep "" [s s s s s s s s s s s s s s s s]; + exp = n: x: if n == 1 then x else times16 (exp (n - 1) x); + sixteenMegabyte = exp 6 sixteenBytes; +in +assert builtins.stringLength sixteenMegabyte == 16777216; +derivation { + name = "big-derivation-attr"; + builder = "/x"; + system = "y"; + bigAttr = sixteenMegabyte; +} diff --git a/tests/functional/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh new file mode 100644 index 000000000..81cd21a4a --- /dev/null +++ b/tests/functional/binary-cache-build-remote.sh @@ -0,0 +1,16 @@ +source common.sh + +clearStore +clearCacheCache + +# Fails without remote builders +(! nix-build --store "file://$cacheDir" dependencies.nix) + +# Succeeds with default store as build remote. +outPath=$(nix-build --store "file://$cacheDir" --builders 'auto - - 1 1' -j0 dependencies.nix) + +# Test that the path exactly exists in the destination store. +nix path-info --store "file://$cacheDir" $outPath + +# Succeeds without any build capability because no-op +nix-build --store "file://$cacheDir" -j0 dependencies.nix diff --git a/tests/functional/binary-cache.sh b/tests/functional/binary-cache.sh new file mode 100644 index 000000000..7c64a115c --- /dev/null +++ b/tests/functional/binary-cache.sh @@ -0,0 +1,286 @@ +source common.sh + +needLocalStore "'--no-require-sigs' can’t be used with the daemon" + +# We can produce drvs directly into the binary cache +clearStore +clearCacheCache +nix-instantiate --store "file://$cacheDir" dependencies.nix + +# Create the binary cache. +clearStore +clearCache +outPath=$(nix-build dependencies.nix --no-out-link) + +nix copy --to file://$cacheDir $outPath + +# Test copying build logs to the binary cache. +expect 1 nix log --store file://$cacheDir $outPath 2>&1 | grep 'is not available' +nix store copy-log --to file://$cacheDir $outPath +nix log --store file://$cacheDir $outPath | grep FOO +rm -rf $TEST_ROOT/var/log/nix +expect 1 nix log $outPath 2>&1 | grep 'is not available' +nix log --substituters file://$cacheDir $outPath | grep FOO + +# Test copying build logs from the binary cache. +nix store copy-log --from file://$cacheDir $(nix-store -qd $outPath)^'*' +nix log $outPath | grep FOO + +basicDownloadTests() { + # No uploading tests bcause upload with force HTTP doesn't work. + + # By default, a binary cache doesn't support "nix-env -qas", but does + # support installation. + clearStore + clearCacheCache + + nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---" + + nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath + + [ -x $outPath/program ] + + + # But with the right configuration, "nix-env -qas" should also work. + clearStore + clearCacheCache + echo "WantMassQuery: 1" >> $cacheDir/nix-cache-info + + nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S" + nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S" + + x=$(nix-env -f dependencies.nix -qas \* --prebuilt-only) + [ -z "$x" ] + + nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath + + nix-store --check-validity $outPath + nix-store -qR $outPath | grep input-2 + + echo "WantMassQuery: 0" >> $cacheDir/nix-cache-info +} + + +# Test LocalBinaryCacheStore. +basicDownloadTests + + +# Test HttpBinaryCacheStore. +export _NIX_FORCE_HTTP=1 +basicDownloadTests + + +# Test whether Nix notices if the NAR doesn't match the hash in the NAR info. +clearStore + +nar=$(ls $cacheDir/nar/*.nar.xz | head -n1) +mv $nar $nar.good +mkdir -p $TEST_ROOT/empty +nix-store --dump $TEST_ROOT/empty | xz > $nar + +expect 1 nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log +grepQuiet "hash mismatch" $TEST_ROOT/log + +mv $nar.good $nar + + +# Test whether this unsigned cache is rejected if the user requires signed caches. +clearStore +clearCacheCache + +if nix-store --substituters "file://$cacheDir" -r $outPath; then + echo "unsigned binary cache incorrectly accepted" + exit 1 +fi + + +# Test whether fallback works if a NAR has disappeared. This does not require --fallback. +clearStore + +mv $cacheDir/nar $cacheDir/nar2 + +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result + +mv $cacheDir/nar2 $cacheDir/nar + + +# Test whether fallback works if a NAR is corrupted. This does require --fallback. +clearStore + +mv $cacheDir/nar $cacheDir/nar2 +mkdir $cacheDir/nar +for i in $(cd $cacheDir/nar2 && echo *); do touch $cacheDir/nar/$i; done + +(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result) + +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result --fallback + +rm -rf $cacheDir/nar +mv $cacheDir/nar2 $cacheDir/nar + + +# Test whether building works if the binary cache contains an +# incomplete closure. +clearStore + +rm -v $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo) + +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log +grepQuiet "copying path.*input-0" $TEST_ROOT/log +grepQuiet "copying path.*input-2" $TEST_ROOT/log +grepQuiet "copying path.*top" $TEST_ROOT/log + + +# Idem, but without cached .narinfo. +clearStore +clearCacheCache + +nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log +grepQuiet "don't know how to build" $TEST_ROOT/log +grepQuiet "building.*input-1" $TEST_ROOT/log +grepQuiet "building.*input-2" $TEST_ROOT/log +grepQuiet "copying path.*input-0" $TEST_ROOT/log +grepQuiet "copying path.*top" $TEST_ROOT/log + + +# Create a signed binary cache. +clearCache +clearCacheCache + +nix key generate-secret --key-name test.nixos.org-1 > $TEST_ROOT/sk1 +publicKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk1) + +nix key generate-secret --key-name test.nixos.org-1 > $TEST_ROOT/sk2 +badKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk2) + +nix key generate-secret --key-name foo.nixos.org-1 > $TEST_ROOT/sk3 +otherKey=$(nix key convert-secret-to-public < $TEST_ROOT/sk3) + +_NIX_FORCE_HTTP= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath + + +# Downloading should fail if we don't provide a key. +clearStore +clearCacheCache + +(! nix-store -r $outPath --substituters "file://$cacheDir") + + +# And it should fail if we provide an incorrect key. +clearStore +clearCacheCache + +(! nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$badKey") + + +# It should succeed if we provide the correct key. +nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey" + + +# It should fail if we corrupt the .narinfo. +clearStore + +cacheDir2=$TEST_ROOT/binary-cache-2 +rm -rf $cacheDir2 +cp -r $cacheDir $cacheDir2 + +for i in $cacheDir2/*.narinfo; do + grep -v References $i > $i.tmp + mv $i.tmp $i +done + +clearCacheCache + +(! nix-store -r $outPath --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey") + +# If we provide a bad and a good binary cache, it should succeed. + +nix-store -r $outPath --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey" + + +unset _NIX_FORCE_HTTP + + +# Test 'nix verify --all' on a binary cache. +nix store verify -vvvvv --all --store file://$cacheDir --no-trust + + +# Test local NAR caching. +narCache=$TEST_ROOT/nar-cache +rm -rf $narCache +mkdir $narCache + +[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" $outPath/foobar) = FOOBAR ]] + +rm -rfv "$cacheDir/nar" + +[[ $(nix store cat --store "file://$cacheDir?local-nar-cache=$narCache" $outPath/foobar) = FOOBAR ]] + +(! nix store cat --store file://$cacheDir $outPath/foobar) + + +# Test NAR listing generation. +clearCache + +outPath=$(nix-build --no-out-link -E ' + with import ./config.nix; + mkDerivation { + name = "nar-listing"; + buildCommand = "mkdir $out; echo foo > $out/bar; ln -s xyzzy $out/link"; + } +') + +nix copy --to file://$cacheDir?write-nar-listing=1 $outPath + +diff -u \ + <(jq -S < $cacheDir/$(basename $outPath | cut -c1-32).ls) \ + <(echo '{"version":1,"root":{"type":"directory","entries":{"bar":{"type":"regular","size":4,"narOffset":232},"link":{"type":"symlink","target":"xyzzy"}}}}' | jq -S) + + +# Test debug info index generation. +clearCache + +outPath=$(nix-build --no-out-link -E ' + with import ./config.nix; + mkDerivation { + name = "debug-info"; + buildCommand = "mkdir -p $out/lib/debug/.build-id/02; echo foo > $out/lib/debug/.build-id/02/623eda209c26a59b1a8638ff7752f6b945c26b.debug"; + } +') + +nix copy --to "file://$cacheDir?index-debug-info=1&compression=none" $outPath + +diff -u \ + <(cat $cacheDir/debuginfo/02623eda209c26a59b1a8638ff7752f6b945c26b.debug | jq -S) \ + <(echo '{"archive":"../nar/100vxs724qr46phz8m24iswmg9p3785hsyagz0kchf6q6gf06sw6.nar","member":"lib/debug/.build-id/02/623eda209c26a59b1a8638ff7752f6b945c26b.debug"}' | jq -S) + +# Test against issue https://github.com/NixOS/nix/issues/3964 +# +expr=' + with import ./config.nix; + mkDerivation { + name = "multi-output"; + buildCommand = "mkdir -p $out; echo foo > $doc; echo $doc > $out/docref"; + outputs = ["out" "doc"]; + } +' +outPath=$(nix-build --no-out-link -E "$expr") +docPath=$(nix-store -q --references $outPath) + +# $ nix-store -q --tree $outPath +# ...-multi-output +# +---...-multi-output-doc + +nix copy --to "file://$cacheDir" $outPath + +hashpart() { + basename "$1" | cut -c1-32 +} + +# break the closure of out by removing doc +rm $cacheDir/$(hashpart $docPath).narinfo + +nix-store --delete $outPath $docPath +# -vvv is the level that logs during the loop +timeout 60 nix-build --no-out-link -E "$expr" --option substituters "file://$cacheDir" \ + --option trusted-binary-caches "file://$cacheDir" --no-require-sigs diff --git a/tests/functional/brotli.sh b/tests/functional/brotli.sh new file mode 100644 index 000000000..dc9bbdb66 --- /dev/null +++ b/tests/functional/brotli.sh @@ -0,0 +1,21 @@ +source common.sh + +clearStore +clearCache + +cacheURI="file://$cacheDir?compression=br" + +outPath=$(nix-build dependencies.nix --no-out-link) + +nix copy --to $cacheURI $outPath + +HASH=$(nix hash path $outPath) + +clearStore +clearCacheCache + +nix copy --from $cacheURI $outPath --no-check-sigs + +HASH2=$(nix hash path $outPath) + +[[ $HASH = $HASH2 ]] diff --git a/tests/functional/build-delete.sh b/tests/functional/build-delete.sh new file mode 100644 index 000000000..9c56b00e8 --- /dev/null +++ b/tests/functional/build-delete.sh @@ -0,0 +1,54 @@ +source common.sh + +clearStore + +# https://github.com/NixOS/nix/issues/6572 +issue_6572_independent_outputs() { + nix build -f multiple-outputs.nix --json independent --no-link > $TEST_ROOT/independent.json + + # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation. + p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) + nix-store --delete "$p" # Clean up for next test + + # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. + nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.first)" + p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) + cmp $p <<EOF +first +second +EOF + nix-store --delete "$p" # Clean up for next test + + # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. + nix-store --delete "$(jq -r <$TEST_ROOT/independent.json .[0].outputs.second)" + p=$(nix build -f multiple-outputs.nix use-independent --no-link --print-out-paths) + cmp $p <<EOF +first +second +EOF + nix-store --delete "$p" # Clean up for next test +} +issue_6572_independent_outputs + + +# https://github.com/NixOS/nix/issues/6572 +issue_6572_dependent_outputs() { + + nix build -f multiple-outputs.nix --json a --no-link > $TEST_ROOT/a.json + + # # Make sure that 'nix build' can build a derivation that depends on both outputs of another derivation. + p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths) + nix-store --delete "$p" # Clean up for next test + + # Make sure that 'nix build' tracks input-outputs correctly when a single output is already present. + nix-store --delete "$(jq -r <$TEST_ROOT/a.json .[0].outputs.second)" + p=$(nix build -f multiple-outputs.nix use-a --no-link --print-out-paths) + cmp $p <<EOF +first +second +EOF + nix-store --delete "$p" # Clean up for next test +} +if isDaemonNewer "2.12pre0"; then + issue_6572_dependent_outputs +fi diff --git a/tests/functional/build-dry.sh b/tests/functional/build-dry.sh new file mode 100644 index 000000000..6d1754af5 --- /dev/null +++ b/tests/functional/build-dry.sh @@ -0,0 +1,67 @@ +source common.sh + +################################################### +# Check that --dry-run isn't confused with read-only mode +# https://github.com/NixOS/nix/issues/1795 + +clearStore +clearCache + +# Ensure this builds successfully first +nix build --no-link -f dependencies.nix + +clearStore +clearCache + +# Try --dry-run using old command first +nix-build --no-out-link dependencies.nix --dry-run 2>&1 | grep "will be built" +# Now new command: +nix build -f dependencies.nix --dry-run 2>&1 | grep "will be built" + +clearStore +clearCache + +# Try --dry-run using new command first +nix build -f dependencies.nix --dry-run 2>&1 | grep "will be built" +# Now old command: +nix-build --no-out-link dependencies.nix --dry-run 2>&1 | grep "will be built" + +################################################### +# Check --dry-run doesn't create links with --dry-run +# https://github.com/NixOS/nix/issues/1849 +clearStore +clearCache + +RESULT=$TEST_ROOT/result-link +rm -f $RESULT + +nix-build dependencies.nix -o $RESULT --dry-run + +[[ ! -h $RESULT ]] || fail "nix-build --dry-run created output link" + +nix build -f dependencies.nix -o $RESULT --dry-run + +[[ ! -h $RESULT ]] || fail "nix build --dry-run created output link" + +nix build -f dependencies.nix -o $RESULT + +[[ -h $RESULT ]] + +################################################### +# Check the JSON output +clearStore +clearCache + +RES=$(nix build -f dependencies.nix --dry-run --json) + +if [[ -z "${NIX_TESTS_CA_BY_DEFAULT-}" ]]; then + echo "$RES" | jq '.[0] | [ + (.drvPath | test("'$NIX_STORE_DIR'.*\\.drv")), + (.outputs.out | test("'$NIX_STORE_DIR'")) + ] | all' +else + echo "$RES" | jq '.[0] | [ + (.drvPath | test("'$NIX_STORE_DIR'.*\\.drv")), + .outputs.out == null + ] | all' +fi diff --git a/tests/functional/build-hook-ca-fixed.nix b/tests/functional/build-hook-ca-fixed.nix new file mode 100644 index 000000000..4cb9e85d1 --- /dev/null +++ b/tests/functional/build-hook-ca-fixed.nix @@ -0,0 +1,58 @@ +{ busybox }: + +with import ./config.nix; + +let + + mkDerivation = args: + derivation ({ + inherit system; + builder = busybox; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + } // removeAttrs args ["builder" "meta" "passthru"]) + // { meta = args.meta or {}; passthru = args.passthru or {}; }; + + input1 = mkDerivation { + shell = busybox; + name = "build-remote-input-1"; + buildCommand = "echo hi-input1; echo FOO > $out"; + requiredSystemFeatures = ["foo"]; + outputHash = "sha256-FePFYIlMuycIXPZbWi7LGEiMmZSX9FMbaQenWBzm1Sc="; + }; + + input2 = mkDerivation { + shell = busybox; + name = "build-remote-input-2"; + buildCommand = "echo hi; echo BAR > $out"; + requiredSystemFeatures = ["bar"]; + outputHash = "sha256-XArauVH91AVwP9hBBQNlkX9ccuPpSYx9o0zeIHb6e+Q="; + }; + + input3 = mkDerivation { + shell = busybox; + name = "build-remote-input-3"; + buildCommand = '' + echo hi-input3 + read x < ${input2} + echo $x BAZ > $out + ''; + requiredSystemFeatures = ["baz"]; + outputHash = "sha256-daKAcPp/+BYMQsVi/YYMlCKoNAxCNDsaivwSHgQqD2s="; + }; + +in + + mkDerivation { + shell = busybox; + name = "build-remote"; + passthru = { inherit input1 input2 input3; }; + buildCommand = + '' + read x < ${input1} + read y < ${input3} + echo "$x $y" > $out + ''; + outputHash = "sha256-5SxbkUw6xe2l9TE1uwCvTtTDysD1vhRor38OtDF0LqQ="; + } diff --git a/tests/functional/build-hook-ca-floating.nix b/tests/functional/build-hook-ca-floating.nix new file mode 100644 index 000000000..dfdbb82da --- /dev/null +++ b/tests/functional/build-hook-ca-floating.nix @@ -0,0 +1,6 @@ +{ busybox }: + +import ./build-hook.nix { + inherit busybox; + contentAddressed = true; +} diff --git a/tests/functional/build-hook.nix b/tests/functional/build-hook.nix new file mode 100644 index 000000000..7effd7903 --- /dev/null +++ b/tests/functional/build-hook.nix @@ -0,0 +1,59 @@ +{ busybox, contentAddressed ? false }: + +with import ./config.nix; + +let + + caArgs = if contentAddressed then { + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + __contentAddressed = true; + } else {}; + + mkDerivation = args: + derivation ({ + inherit system; + builder = busybox; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + } // removeAttrs args ["builder" "meta" "passthru"] + // caArgs) + // { meta = args.meta or {}; passthru = args.passthru or {}; }; + + input1 = mkDerivation { + shell = busybox; + name = "build-remote-input-1"; + buildCommand = "echo hi-input1; echo FOO > $out"; + requiredSystemFeatures = ["foo"]; + }; + + input2 = mkDerivation { + shell = busybox; + name = "build-remote-input-2"; + buildCommand = "echo hi; echo BAR > $out"; + requiredSystemFeatures = ["bar"]; + }; + + input3 = mkDerivation { + shell = busybox; + name = "build-remote-input-3"; + buildCommand = '' + echo hi-input3 + read x < ${input2} + echo $x BAZ > $out + ''; + requiredSystemFeatures = ["baz"]; + }; + +in + + mkDerivation { + shell = busybox; + name = "build-remote"; + passthru = { inherit input1 input2 input3; }; + buildCommand = + '' + read x < ${input1} + read y < ${input3} + echo "$x $y" > $out + ''; + } diff --git a/tests/functional/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh new file mode 100644 index 000000000..ae7441591 --- /dev/null +++ b/tests/functional/build-remote-content-addressed-fixed.sh @@ -0,0 +1,5 @@ +source common.sh + +file=build-hook-ca-fixed.nix + +source build-remote.sh diff --git a/tests/functional/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh new file mode 100644 index 000000000..e83b42b41 --- /dev/null +++ b/tests/functional/build-remote-content-addressed-floating.sh @@ -0,0 +1,9 @@ +source common.sh + +file=build-hook-ca-floating.nix + +enableFeatures "ca-derivations" + +CONTENT_ADDRESSED=true + +source build-remote.sh diff --git a/tests/functional/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh new file mode 100644 index 000000000..49d15c389 --- /dev/null +++ b/tests/functional/build-remote-input-addressed.sh @@ -0,0 +1,33 @@ +source common.sh + +file=build-hook.nix + +source build-remote.sh + +# Add a `post-build-hook` option to the nix conf. +# This hook will be executed both for the local machine and the remote builders +# (because they share the same config). +registerBuildHook () { + # Dummy post-build-hook just to ensure that it's executed correctly. + # (we can't reuse the one from `$PWD/push-to-store.sh` because of + # https://github.com/NixOS/nix/issues/4341) + cat <<EOF > $TEST_ROOT/post-build-hook.sh +#!/bin/sh + +echo "Post hook ran successfully" +# Add an empty line to a counter file, just to check that this hook ran properly +echo "" >> $TEST_ROOT/post-hook-counter +EOF + chmod +x $TEST_ROOT/post-build-hook.sh + rm -f $TEST_ROOT/post-hook-counter + + echo "post-build-hook = $TEST_ROOT/post-build-hook.sh" >> $NIX_CONF_DIR/nix.conf +} + +registerBuildHook +source build-remote.sh + +# `build-hook.nix` has four derivations to build, and the hook runs twice for +# each derivation (once on the builder and once on the host), so the counter +# should contain eight lines now +[[ $(cat $TEST_ROOT/post-hook-counter | wc -l) -eq 8 ]] diff --git a/tests/functional/build-remote-trustless-after.sh b/tests/functional/build-remote-trustless-after.sh new file mode 100644 index 000000000..19f59e6ae --- /dev/null +++ b/tests/functional/build-remote-trustless-after.sh @@ -0,0 +1,2 @@ +outPath=$(readlink -f $TEST_ROOT/result) +grep 'FOO BAR BAZ' ${remoteDir}/${outPath} diff --git a/tests/functional/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh new file mode 100644 index 000000000..fad1def59 --- /dev/null +++ b/tests/functional/build-remote-trustless-should-fail-0.sh @@ -0,0 +1,29 @@ +source common.sh + +enableFeatures "daemon-trust-override" + +restartDaemon + +[[ $busybox =~ busybox ]] || skipTest "no busybox" + +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +# We first build a dependency of the derivation we eventually want to +# build. +nix-build build-hook.nix -A passthru.input2 \ + -o "$TEST_ROOT/input2" \ + --arg busybox "$busybox" \ + --store "$TEST_ROOT/local" \ + --option system-features bar + +# Now when we go to build that downstream derivation, Nix will try to +# copy our already-build `input2` to the remote store. That store object +# is input-addressed, so this will fail. + +file=build-hook.nix +prog=$(readlink -e ./nix-daemon-untrusting.sh) +proto=ssh-ng + +expectStderr 1 source build-remote-trustless.sh \ + | grepQuiet "cannot add path '[^ ]*' because it lacks a signature by a trusted key" diff --git a/tests/functional/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh new file mode 100644 index 000000000..2a7ebd8c6 --- /dev/null +++ b/tests/functional/build-remote-trustless-should-pass-0.sh @@ -0,0 +1,9 @@ +source common.sh + +# Remote trusts us +file=build-hook.nix +prog=nix-store +proto=ssh + +source build-remote-trustless.sh +source build-remote-trustless-after.sh diff --git a/tests/functional/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh new file mode 100644 index 000000000..516bdf092 --- /dev/null +++ b/tests/functional/build-remote-trustless-should-pass-1.sh @@ -0,0 +1,9 @@ +source common.sh + +# Remote trusts us +file=build-hook.nix +prog=nix-daemon +proto=ssh-ng + +source build-remote-trustless.sh +source build-remote-trustless-after.sh diff --git a/tests/functional/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh new file mode 100644 index 000000000..b769a88f0 --- /dev/null +++ b/tests/functional/build-remote-trustless-should-pass-2.sh @@ -0,0 +1,13 @@ +source common.sh + +enableFeatures "daemon-trust-override" + +restartDaemon + +# Remote doesn't trust us +file=build-hook.nix +prog=$(readlink -e ./nix-daemon-untrusting.sh) +proto=ssh-ng + +source build-remote-trustless.sh +source build-remote-trustless-after.sh diff --git a/tests/functional/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh new file mode 100644 index 000000000..40f81da5a --- /dev/null +++ b/tests/functional/build-remote-trustless-should-pass-3.sh @@ -0,0 +1,14 @@ +source common.sh + +enableFeatures "daemon-trust-override" + +restartDaemon + +# Remote doesn't trusts us, but this is fine because we are only +# building (fixed) CA derivations. +file=build-hook-ca-fixed.nix +prog=$(readlink -e ./nix-daemon-untrusting.sh) +proto=ssh-ng + +source build-remote-trustless.sh +source build-remote-trustless-after.sh diff --git a/tests/functional/build-remote-trustless.sh b/tests/functional/build-remote-trustless.sh new file mode 100644 index 000000000..81e5253bf --- /dev/null +++ b/tests/functional/build-remote-trustless.sh @@ -0,0 +1,14 @@ +requireSandboxSupport +[[ $busybox =~ busybox ]] || skipTest "no busybox" + +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +remoteDir=$TEST_ROOT/remote + +# Note: ssh{-ng}://localhost bypasses ssh. See tests/functional/build-remote.sh for +# more details. +nix-build $file -o $TEST_ROOT/result --max-jobs 0 \ + --arg busybox $busybox \ + --store $TEST_ROOT/local \ + --builders "$proto://localhost?remote-program=$prog&remote-store=${remoteDir}%3Fsystem-features=foo%20bar%20baz - - 1 1 foo,bar,baz" diff --git a/tests/functional/build-remote.sh b/tests/functional/build-remote.sh new file mode 100644 index 000000000..d2a2132c1 --- /dev/null +++ b/tests/functional/build-remote.sh @@ -0,0 +1,84 @@ +requireSandboxSupport +[[ $busybox =~ busybox ]] || skipTest "no busybox" + +# Avoid store dir being inside sandbox build-dir +unset NIX_STORE_DIR +unset NIX_STATE_DIR + +function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; } + +EXTRA_SYSTEM_FEATURES=() +if [[ -n "${CONTENT_ADDRESSED-}" ]]; then + EXTRA_SYSTEM_FEATURES=("ca-derivations") +fi + +builders=( + # system-features will automatically be added to the outer URL, but not inner + # remote-store URL. + "ssh://localhost?remote-store=$TEST_ROOT/machine1?system-features=$(join_by "%20" foo ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," foo ${EXTRA_SYSTEM_FEATURES[@]})" + "$TEST_ROOT/machine2 - - 1 1 $(join_by "," bar ${EXTRA_SYSTEM_FEATURES[@]})" + "ssh-ng://localhost?remote-store=$TEST_ROOT/machine3?system-features=$(join_by "%20" baz ${EXTRA_SYSTEM_FEATURES[@]}) - - 1 1 $(join_by "," baz ${EXTRA_SYSTEM_FEATURES[@]})" +) + +chmod -R +w $TEST_ROOT/machine* || true +rm -rf $TEST_ROOT/machine* || true + +# Note: ssh://localhost bypasses ssh, directly invoking nix-store as a +# child process. This allows us to test LegacySSHStore::buildDerivation(). +# ssh-ng://... likewise allows us to test RemoteStore::buildDerivation(). +nix build -L -v -f $file -o $TEST_ROOT/result --max-jobs 0 \ + --arg busybox $busybox \ + --store $TEST_ROOT/machine0 \ + --builders "$(join_by '; ' "${builders[@]}")" + +outPath=$(readlink -f $TEST_ROOT/result) + +grep 'FOO BAR BAZ' $TEST_ROOT/machine0/$outPath + +testPrintOutPath=$(nix build -L -v -f $file --no-link --print-out-paths --max-jobs 0 \ + --arg busybox $busybox \ + --store $TEST_ROOT/machine0 \ + --builders "$(join_by '; ' "${builders[@]}")" +) + +[[ $testPrintOutPath =~ store.*build-remote ]] + +# Ensure that input1 was built on store1 due to the required feature. +output=$(nix path-info --store $TEST_ROOT/machine1 --all) +echo "$output" | grepQuiet builder-build-remote-input-1.sh +echo "$output" | grepQuietInverse builder-build-remote-input-2.sh +echo "$output" | grepQuietInverse builder-build-remote-input-3.sh +unset output + +# Ensure that input2 was built on store2 due to the required feature. +output=$(nix path-info --store $TEST_ROOT/machine2 --all) +echo "$output" | grepQuietInverse builder-build-remote-input-1.sh +echo "$output" | grepQuiet builder-build-remote-input-2.sh +echo "$output" | grepQuietInverse builder-build-remote-input-3.sh +unset output + +# Ensure that input3 was built on store3 due to the required feature. +output=$(nix path-info --store $TEST_ROOT/machine3 --all) +echo "$output" | grepQuietInverse builder-build-remote-input-1.sh +echo "$output" | grepQuietInverse builder-build-remote-input-2.sh +echo "$output" | grepQuiet builder-build-remote-input-3.sh +unset output + + +for i in input1 input3; do +nix log --store $TEST_ROOT/machine0 --file "$file" --arg busybox $busybox passthru."$i" | grep hi-$i +done + +# Behavior of keep-failed +out="$(nix-build 2>&1 failing.nix \ + --no-out-link \ + --builders "$(join_by '; ' "${builders[@]}")" \ + --keep-failed \ + --store $TEST_ROOT/machine0 \ + -j0 \ + --arg busybox $busybox)" || true + +[[ "$out" =~ .*"note: keeping build directory".* ]] + +build_dir="$(grep "note: keeping build" <<< "$out" | sed -E "s/^(.*)note: keeping build directory '(.*)'(.*)$/\2/")" +[[ "foo" = $(<"$build_dir"/bar) ]] diff --git a/tests/functional/build.sh b/tests/functional/build.sh new file mode 100644 index 000000000..7fbdb0f07 --- /dev/null +++ b/tests/functional/build.sh @@ -0,0 +1,135 @@ +source common.sh + +clearStore + +# Make sure that 'nix build' returns all outputs by default. +nix build -f multiple-outputs.nix --json a b --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | + (keys | length == 2) and + (.first | match(".*multiple-outputs-a-first")) and + (.second | match(".*multiple-outputs-a-second")))) + and (.[1] | + (.drvPath | match(".*multiple-outputs-b.drv")) and + (.outputs | + (keys | length == 1) and + (.out | match(".*multiple-outputs-b")))) +' + +# Test output selection using the '^' syntax. +nix build -f multiple-outputs.nix --json a^first --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | keys == ["first"])) +' + +nix build -f multiple-outputs.nix --json a^second,first --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | keys == ["first", "second"])) +' + +nix build -f multiple-outputs.nix --json 'a^*' --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | keys == ["first", "second"])) +' + +# Test that 'outputsToInstall' is respected by default. +nix build -f multiple-outputs.nix --json e --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a", "b"])) +' + +# But not when it's overriden. +nix build -f multiple-outputs.nix --json e^a_a --no-link +nix build -f multiple-outputs.nix --json e^a_a --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a"])) +' + +nix build -f multiple-outputs.nix --json 'e^*' --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a", "b", "c"])) +' + +# test buidling from non-drv attr path + +nix build -f multiple-outputs.nix --json 'e.a_a.outPath' --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a"])) +' + +# Illegal type of string context +expectStderr 1 nix build -f multiple-outputs.nix 'e.a_a.drvPath' \ + | grepQuiet "has a context which refers to a complete source and binary closure." + +# No string context +expectStderr 1 nix build --expr '""' --no-link \ + | grepQuiet "has 0 entries in its context. It should only have exactly one entry" + +# Too much string context +expectStderr 1 nix build --impure --expr 'with (import ./multiple-outputs.nix).e.a_a; "${drvPath}${outPath}"' --no-link \ + | grepQuiet "has 2 entries in its context. It should only have exactly one entry" + +nix build --impure --json --expr 'builtins.unsafeDiscardOutputDependency (import ./multiple-outputs.nix).e.a_a.drvPath' --no-link | jq --exit-status ' + (.[0] | match(".*multiple-outputs-e.drv")) +' + +# Test building from raw store path to drv not expression. + +drv=$(nix eval -f multiple-outputs.nix --raw a.drvPath) +if nix build "$drv^not-an-output" --no-link --json; then + fail "'not-an-output' should fail to build" +fi + +if nix build "$drv^" --no-link --json; then + fail "'empty outputs list' should fail to build" +fi + +if nix build "$drv^*nope" --no-link --json; then + fail "'* must be entire string' should fail to build" +fi + +nix build "$drv^first" --no-link --json | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | + (keys | length == 1) and + (.first | match(".*multiple-outputs-a-first")) and + (has("second") | not))) +' + +nix build "$drv^first,second" --no-link --json | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | + (keys | length == 2) and + (.first | match(".*multiple-outputs-a-first")) and + (.second | match(".*multiple-outputs-a-second")))) +' + +nix build "$drv^*" --no-link --json | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-a.drv")) and + (.outputs | + (keys | length == 2) and + (.first | match(".*multiple-outputs-a-first")) and + (.second | match(".*multiple-outputs-a-second")))) +' + +# Make sure that `--impure` works (regression test for https://github.com/NixOS/nix/issues/6488) +nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status ' + (.[0] | + (.drvPath | match(".*multiple-outputs-e.drv")) and + (.outputs | keys == ["a_a", "b"])) +' + +# Make sure that `--stdin` works and does not apply any defaults +printf "" | nix build --no-link --stdin --json | jq --exit-status '. == []' +printf "%s\n" "$drv^*" | nix build --no-link --stdin --json | jq --exit-status '.[0]|has("drvPath")' diff --git a/tests/functional/ca-shell.nix b/tests/functional/ca-shell.nix new file mode 100644 index 000000000..36e1d1526 --- /dev/null +++ b/tests/functional/ca-shell.nix @@ -0,0 +1 @@ +{ inNixShell ? false, ... }@args: import ./shell.nix (args // { contentAddressed = true; }) diff --git a/tests/functional/ca/build-cache.sh b/tests/functional/ca/build-cache.sh new file mode 100644 index 000000000..6a4080fec --- /dev/null +++ b/tests/functional/ca/build-cache.sh @@ -0,0 +1,51 @@ +#!/usr/bin/env bash + +source common.sh + +# The substituters didn't work prior to this time. +requireDaemonNewerThan "2.18.0pre20230808" + +drv=$(nix-instantiate ./content-addressed.nix -A rootCA --arg seed 1)^out +nix derivation show "$drv" --arg seed 1 + +buildAttr () { + local derivationPath=$1 + local seedValue=$2 + shift; shift + local args=("./content-addressed.nix" "-A" "$derivationPath" --arg seed "$seedValue" "--no-out-link") + args+=("$@") + nix-build "${args[@]}" +} + +copyAttr () { + local derivationPath=$1 + local seedValue=$2 + shift; shift + local args=("-f" "./content-addressed.nix" "$derivationPath" --arg seed "$seedValue") + args+=("$@") + # Note: to copy CA derivations, we need to copy the realisations, which + # currently requires naming the installables, not just the derivation output + # path. + nix copy --to file://$cacheDir "${args[@]}" +} + +testRemoteCacheFor () { + local derivationPath=$1 + clearCache + copyAttr "$derivationPath" 1 + clearStore + # Check nothing gets built. + buildAttr "$derivationPath" 1 --option substituters file://$cacheDir --no-require-sigs |& grepQuietInverse " will be built:" +} + +testRemoteCache () { + testRemoteCacheFor rootCA + testRemoteCacheFor dependentCA + testRemoteCacheFor dependentNonCA + testRemoteCacheFor dependentFixedOutput + testRemoteCacheFor dependentForBuildCA + testRemoteCacheFor dependentForBuildNonCA +} + +clearStore +testRemoteCache
\ No newline at end of file diff --git a/tests/functional/ca/build-dry.sh b/tests/functional/ca/build-dry.sh new file mode 100644 index 000000000..9a72075ec --- /dev/null +++ b/tests/functional/ca/build-dry.sh @@ -0,0 +1,6 @@ +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 + +cd .. && source build-dry.sh + diff --git a/tests/functional/ca/build-with-garbage-path.sh b/tests/functional/ca/build-with-garbage-path.sh new file mode 100755 index 000000000..884cd2802 --- /dev/null +++ b/tests/functional/ca/build-with-garbage-path.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +# Regression test for https://github.com/NixOS/nix/issues/4858 + +source common.sh + +requireDaemonNewerThan "2.4pre20210621" + +# Get the output path of `rootCA`, and put some garbage instead +outPath="$(nix-build ./content-addressed.nix -A rootCA --no-out-link)" +nix-store --delete $(nix-store -q --referrers-closure "$outPath") +touch "$outPath" + +# The build should correctly remove the garbage and put the expected path instead +nix-build ./content-addressed.nix -A rootCA --no-out-link + +# Rebuild it. This shouldn’t overwrite the existing path +oldInode=$(stat -c '%i' "$outPath") +nix-build ./content-addressed.nix -A rootCA --no-out-link --arg seed 2 +newInode=$(stat -c '%i' "$outPath") +[[ "$oldInode" == "$newInode" ]] diff --git a/tests/functional/ca/build.sh b/tests/functional/ca/build.sh new file mode 100644 index 000000000..e1a8a7625 --- /dev/null +++ b/tests/functional/ca/build.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash + +source common.sh + +drv=$(nix-instantiate ./content-addressed.nix -A rootCA --arg seed 1)^out +nix derivation show "$drv" --arg seed 1 + +buildAttr () { + local derivationPath=$1 + local seedValue=$2 + shift; shift + local args=("./content-addressed.nix" "-A" "$derivationPath" --arg seed "$seedValue" "--no-out-link") + args+=("$@") + nix-build "${args[@]}" +} + +testDeterministicCA () { + [[ $(buildAttr rootCA 1) = $(buildAttr rootCA 2) ]] +} + +testCutoffFor () { + local out1 out2 + out1=$(buildAttr $1 1) + # The seed only changes the root derivation, and not it's output, so the + # dependent derivations should only need to be built once. + buildAttr rootCA 2 + out2=$(buildAttr $1 2 -j0) + test "$out1" == "$out2" +} + +testCutoff () { + # Don't directly build dependentCA, that way we'll make sure we don't rely on + # dependent derivations always being already built. + #testDerivation dependentCA + testCutoffFor transitivelyDependentCA + testCutoffFor dependentNonCA + testCutoffFor dependentFixedOutput +} + +testGC () { + nix-instantiate ./content-addressed.nix -A rootCA --arg seed 5 + nix-collect-garbage --option keep-derivations true + clearStore + buildAttr rootCA 1 --out-link $TEST_ROOT/rootCA + nix-collect-garbage + buildAttr rootCA 1 -j0 +} + +testNixCommand () { + clearStore + nix build --file ./content-addressed.nix --no-link +} + +# Regression test for https://github.com/NixOS/nix/issues/4775 +testNormalization () { + clearStore + outPath=$(buildAttr rootCA 1) + test "$(stat -c %Y $outPath)" -eq 1 +} + +clearStore +testNormalization +testDeterministicCA +clearStore +testCutoff +testGC +testNixCommand diff --git a/tests/functional/ca/common.sh b/tests/functional/ca/common.sh new file mode 100644 index 000000000..b104b5a78 --- /dev/null +++ b/tests/functional/ca/common.sh @@ -0,0 +1,5 @@ +source ../common.sh + +enableFeatures "ca-derivations" + +restartDaemon diff --git a/tests/functional/ca/concurrent-builds.sh b/tests/functional/ca/concurrent-builds.sh new file mode 100755 index 000000000..b442619e2 --- /dev/null +++ b/tests/functional/ca/concurrent-builds.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash + +# Ensure that we can’t build twice the same derivation concurrently. +# Regression test for https://github.com/NixOS/nix/issues/5029 + +source common.sh + +buggyNeedLocalStore "For some reason, this deadlocks with the daemon" + +export NIX_TESTS_CA_BY_DEFAULT=1 + +clearStore + +for i in {0..5}; do + nix build --no-link --file ./racy.nix & +done + +wait diff --git a/tests/functional/ca/config.nix.in b/tests/functional/ca/config.nix.in new file mode 120000 index 000000000..af24ddb30 --- /dev/null +++ b/tests/functional/ca/config.nix.in @@ -0,0 +1 @@ +../config.nix.in
\ No newline at end of file diff --git a/tests/functional/ca/content-addressed.nix b/tests/functional/ca/content-addressed.nix new file mode 100644 index 000000000..2559c562f --- /dev/null +++ b/tests/functional/ca/content-addressed.nix @@ -0,0 +1,100 @@ +with import ./config.nix; + +let mkCADerivation = args: mkDerivation ({ + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; +} // args); +in + +{ seed ? 0 }: +# A simple content-addressed derivation. +# The derivation can be arbitrarily modified by passing a different `seed`, +# but the output will always be the same +rec { + rootLegacy = mkDerivation { + name = "simple-input-addressed"; + buildCommand = '' + set -x + echo "Building a legacy derivation" + mkdir -p $out + echo "Hello World" > $out/hello + ''; + }; + rootCA = mkCADerivation { + name = "rootCA"; + outputs = [ "out" "dev" "foo" ]; + buildCommand = '' + echo "building a CA derivation" + echo "The seed is ${toString seed}" + mkdir -p $out + echo ${rootLegacy}/hello > $out/dep + ln -s $out $out/self + # test symlinks at root + ln -s $out $dev + ln -s $out $foo + ''; + }; + dependentCA = mkCADerivation { + name = "dependent"; + buildCommand = '' + echo "building a dependent derivation" + mkdir -p $out + cat ${rootCA}/self/dep + echo ${rootCA}/self/dep > $out/dep + ''; + }; + transitivelyDependentCA = mkCADerivation { + name = "transitively-dependent"; + buildCommand = '' + echo "building transitively-dependent" + cat ${dependentCA}/dep + echo ${dependentCA} > $out + ''; + }; + dependentNonCA = mkDerivation { + name = "dependent-non-ca"; + buildCommand = '' + echo "Didn't cut-off" + echo "building dependent-non-ca" + mkdir -p $out + echo ${rootCA}/non-ca-hello > $out/dep + ''; + }; + dependentForBuildCA = mkCADerivation { + name = "dependent-for-build-ca"; + buildCommand = '' + echo "Depends on rootCA for building only" + mkdir -p $out + echo ${rootCA} + touch $out + ''; + }; + dependentForBuildNonCA = mkDerivation { + name = "dependent-for-build-non-ca"; + buildCommand = '' + echo "Depends on rootCA for building only" + mkdir -p $out + echo ${rootCA} + touch $out + ''; + }; + dependentFixedOutput = mkDerivation { + name = "dependent-fixed-output"; + outputHashMode = "recursive"; + outputHash = "sha512-7aJcmSuEuYP5tGKcmGY8bRr/lrCjJlOxP2mIUjO/vMQeg6gx/65IbzRWES8EKiPDOs9z+wF30lEfcwxM/cT4pw=="; + buildCommand = '' + cat ${dependentCA}/dep + echo foo > $out + ''; + }; + runnable = mkCADerivation rec { + name = "runnable-thing"; + buildCommand = '' + mkdir -p $out/bin + echo ${rootCA} # Just to make it depend on it + echo "#! ${shell}" > $out/bin/${name} + chmod +x $out/bin/${name} + ''; + }; +} diff --git a/tests/functional/ca/derivation-json.sh b/tests/functional/ca/derivation-json.sh new file mode 100644 index 000000000..c1480fd17 --- /dev/null +++ b/tests/functional/ca/derivation-json.sh @@ -0,0 +1,29 @@ +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 + +drvPath=$(nix-instantiate ../simple.nix) + +nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json + +drvPath2=$(nix derivation add < $TEST_HOME/simple.json) + +[[ "$drvPath" = "$drvPath2" ]] + +# Content-addressed derivations can be renamed. +jq '.name = "foo"' < $TEST_HOME/simple.json > $TEST_HOME/foo.json +drvPath3=$(nix derivation add --dry-run < $TEST_HOME/foo.json) +# With --dry-run nothing is actually written +[[ ! -e "$drvPath3" ]] + +# But the JSON is rejected without the experimental feature +expectStderr 1 nix derivation add < $TEST_HOME/foo.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'ca-derivations' is disabled" + +# Without --dry-run it is actually written +drvPath4=$(nix derivation add < $TEST_HOME/foo.json) +[[ "$drvPath4" = "$drvPath3" ]] +[[ -e "$drvPath3" ]] + +# The modified derivation read back as JSON matches +nix derivation show $drvPath3 | jq .[] > $TEST_HOME/foo-read.json +diff $TEST_HOME/foo.json $TEST_HOME/foo-read.json diff --git a/tests/functional/ca/duplicate-realisation-in-closure.sh b/tests/functional/ca/duplicate-realisation-in-closure.sh new file mode 100644 index 000000000..da9cd8fb4 --- /dev/null +++ b/tests/functional/ca/duplicate-realisation-in-closure.sh @@ -0,0 +1,26 @@ +source ./common.sh + +requireDaemonNewerThan "2.4pre20210625" + +export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" +export REMOTE_STORE="file://$REMOTE_STORE_DIR" + +rm -rf $REMOTE_STORE_DIR +clearStore + +# Build dep1 and push that to the binary cache. +# This entails building (and pushing) current-time. +nix copy --to "$REMOTE_STORE" -f nondeterministic.nix dep1 +clearStore +sleep 2 # To make sure that `$(date)` will be different +# Build dep2. +# As we’ve cleared the cache, we’ll have to rebuild current-time. And because +# the current time isn’t the same as before, this will yield a new (different) +# realisation +nix build -f nondeterministic.nix dep2 --no-link + +# Build something that depends both on dep1 and dep2. +# If everything goes right, we should rebuild dep2 rather than fetch it from +# the cache (because that would mean duplicating `current-time` in the closure), +# and have `dep1 == dep2`. +nix build --substituters "$REMOTE_STORE" -f nondeterministic.nix toplevel --no-require-sigs --no-link diff --git a/tests/functional/ca/flake.nix b/tests/functional/ca/flake.nix new file mode 100644 index 000000000..332c92a67 --- /dev/null +++ b/tests/functional/ca/flake.nix @@ -0,0 +1,3 @@ +{ + outputs = { self }: import ./content-addressed.nix {}; +} diff --git a/tests/functional/ca/gc.sh b/tests/functional/ca/gc.sh new file mode 100755 index 000000000..e9b6c5ab5 --- /dev/null +++ b/tests/functional/ca/gc.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Ensure that garbage collection works properly with ca derivations + +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 + +cd .. +source gc.sh diff --git a/tests/functional/ca/import-derivation.sh b/tests/functional/ca/import-derivation.sh new file mode 100644 index 000000000..e98e0fbd0 --- /dev/null +++ b/tests/functional/ca/import-derivation.sh @@ -0,0 +1,6 @@ +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 + +cd .. && source import-derivation.sh + diff --git a/tests/functional/ca/local.mk b/tests/functional/ca/local.mk new file mode 100644 index 000000000..fd87b8d1f --- /dev/null +++ b/tests/functional/ca/local.mk @@ -0,0 +1,28 @@ +ca-tests := \ + $(d)/build-with-garbage-path.sh \ + $(d)/build.sh \ + $(d)/build-cache.sh \ + $(d)/concurrent-builds.sh \ + $(d)/derivation-json.sh \ + $(d)/duplicate-realisation-in-closure.sh \ + $(d)/gc.sh \ + $(d)/import-derivation.sh \ + $(d)/new-build-cmd.sh \ + $(d)/nix-copy.sh \ + $(d)/nix-run.sh \ + $(d)/nix-shell.sh \ + $(d)/post-hook.sh \ + $(d)/recursive.sh \ + $(d)/repl.sh \ + $(d)/selfref-gc.sh \ + $(d)/signatures.sh \ + $(d)/substitute.sh \ + $(d)/why-depends.sh + +install-tests-groups += ca + +clean-files += \ + $(d)/config.nix + +test-deps += \ + tests/functional/ca/config.nix diff --git a/tests/functional/ca/new-build-cmd.sh b/tests/functional/ca/new-build-cmd.sh new file mode 100644 index 000000000..432d4d132 --- /dev/null +++ b/tests/functional/ca/new-build-cmd.sh @@ -0,0 +1,5 @@ +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 +cd .. +source ./build.sh diff --git a/tests/functional/ca/nix-copy.sh b/tests/functional/ca/nix-copy.sh new file mode 100755 index 000000000..7a8307a4e --- /dev/null +++ b/tests/functional/ca/nix-copy.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash + +source common.sh + +export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" +export REMOTE_STORE="file://$REMOTE_STORE_DIR" + +ensureCorrectlyCopied () { + attrPath="$1" + nix build --store "$REMOTE_STORE" --file ./content-addressed.nix "$attrPath" +} + +testOneCopy () { + clearStore + rm -rf "$REMOTE_STORE_DIR" + + attrPath="$1" + nix copy --to $REMOTE_STORE "$attrPath" --file ./content-addressed.nix + + ensureCorrectlyCopied "$attrPath" + + # Ensure that we can copy back what we put in the store + clearStore + nix copy --from $REMOTE_STORE \ + --file ./content-addressed.nix "$attrPath" \ + --no-check-sigs +} + +for attrPath in rootCA dependentCA transitivelyDependentCA dependentNonCA dependentFixedOutput; do + testOneCopy "$attrPath" +done diff --git a/tests/functional/ca/nix-run.sh b/tests/functional/ca/nix-run.sh new file mode 100755 index 000000000..5f46518e8 --- /dev/null +++ b/tests/functional/ca/nix-run.sh @@ -0,0 +1,7 @@ +#!/usr/bin/env bash + +source common.sh + +FLAKE_PATH=path:$PWD + +nix run --no-write-lock-file $FLAKE_PATH#runnable diff --git a/tests/functional/ca/nix-shell.sh b/tests/functional/ca/nix-shell.sh new file mode 100755 index 000000000..1c5a6639f --- /dev/null +++ b/tests/functional/ca/nix-shell.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +source common.sh + +CONTENT_ADDRESSED=true +cd .. +source ./nix-shell.sh + diff --git a/tests/functional/ca/nondeterministic.nix b/tests/functional/ca/nondeterministic.nix new file mode 100644 index 000000000..d6d099a3e --- /dev/null +++ b/tests/functional/ca/nondeterministic.nix @@ -0,0 +1,35 @@ +with import ./config.nix; + +let mkCADerivation = args: mkDerivation ({ + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; +} // args); +in + +rec { + currentTime = mkCADerivation { + name = "current-time"; + buildCommand = '' + mkdir $out + echo $(date) > $out/current-time + ''; + }; + dep = seed: mkCADerivation { + name = "dep"; + inherit seed; + buildCommand = '' + echo ${currentTime} > $out + ''; + }; + dep1 = dep 1; + dep2 = dep 2; + toplevel = mkCADerivation { + name = "toplevel"; + buildCommand = '' + test ${dep1} == ${dep2} + touch $out + ''; + }; +} + diff --git a/tests/functional/ca/post-hook.sh b/tests/functional/ca/post-hook.sh new file mode 100755 index 000000000..705bde9d4 --- /dev/null +++ b/tests/functional/ca/post-hook.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +source common.sh + +requireDaemonNewerThan "2.4pre20210626" + +export NIX_TESTS_CA_BY_DEFAULT=1 +cd .. +source ./post-hook.sh + + diff --git a/tests/functional/ca/racy.nix b/tests/functional/ca/racy.nix new file mode 100644 index 000000000..555a15484 --- /dev/null +++ b/tests/functional/ca/racy.nix @@ -0,0 +1,15 @@ +# A derivation that would certainly fail if several builders tried to +# build it at once. + + +with import ./config.nix; + +mkDerivation { + name = "simple"; + buildCommand = '' + mkdir $out + echo bar >> $out/foo + sleep 3 + [[ "$(cat $out/foo)" == bar ]] + ''; +} diff --git a/tests/functional/ca/recursive.sh b/tests/functional/ca/recursive.sh new file mode 100755 index 000000000..cd6736b24 --- /dev/null +++ b/tests/functional/ca/recursive.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +source common.sh + +requireDaemonNewerThan "2.4pre20210623" + +export NIX_TESTS_CA_BY_DEFAULT=1 +cd .. +source ./recursive.sh diff --git a/tests/functional/ca/repl.sh b/tests/functional/ca/repl.sh new file mode 100644 index 000000000..3808c7cb2 --- /dev/null +++ b/tests/functional/ca/repl.sh @@ -0,0 +1,5 @@ +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 + +cd .. && source repl.sh diff --git a/tests/functional/ca/selfref-gc.sh b/tests/functional/ca/selfref-gc.sh new file mode 100755 index 000000000..248778894 --- /dev/null +++ b/tests/functional/ca/selfref-gc.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +source common.sh + +requireDaemonNewerThan "2.4pre20210626" + +enableFeatures "ca-derivations nix-command flakes" + +export NIX_TESTS_CA_BY_DEFAULT=1 +cd .. +source ./selfref-gc.sh diff --git a/tests/functional/ca/signatures.sh b/tests/functional/ca/signatures.sh new file mode 100644 index 000000000..eb18a4130 --- /dev/null +++ b/tests/functional/ca/signatures.sh @@ -0,0 +1,36 @@ +source common.sh + +clearStore +clearCache + +nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 +pk1=$(cat $TEST_ROOT/pk1) + +export REMOTE_STORE_DIR="$TEST_ROOT/remote_store" +export REMOTE_STORE="file://$REMOTE_STORE_DIR" + +ensureCorrectlyCopied () { + attrPath="$1" + nix build --store "$REMOTE_STORE" --file ./content-addressed.nix "$attrPath" +} + +testOneCopy () { + clearStore + rm -rf "$REMOTE_STORE_DIR" + + attrPath="$1" + nix copy -vvvv --to $REMOTE_STORE "$attrPath" --file ./content-addressed.nix \ + --secret-key-files "$TEST_ROOT/sk1" --show-trace + + ensureCorrectlyCopied "$attrPath" + + # Ensure that we can copy back what we put in the store + clearStore + nix copy --from $REMOTE_STORE \ + --file ./content-addressed.nix "$attrPath" \ + --trusted-public-keys $pk1 +} + +for attrPath in rootCA dependentCA transitivelyDependentCA dependentNonCA dependentFixedOutput; do + testOneCopy "$attrPath" +done diff --git a/tests/functional/ca/substitute.sh b/tests/functional/ca/substitute.sh new file mode 100644 index 000000000..ea981adc4 --- /dev/null +++ b/tests/functional/ca/substitute.sh @@ -0,0 +1,71 @@ +#!/usr/bin/env bash + +# Ensure that binary substitution works properly with ca derivations + +source common.sh + +needLocalStore "“--no-require-sigs” can’t be used with the daemon" + +rm -rf $TEST_ROOT/binary_cache + +export REMOTE_STORE_DIR=$TEST_ROOT/binary_cache +export REMOTE_STORE=file://$REMOTE_STORE_DIR + +buildDrvs () { + nix build --file ./content-addressed.nix -L --no-link "$@" +} + +# Populate the remote cache +clearStore +nix copy --to $REMOTE_STORE --file ./content-addressed.nix + +# Restart the build on an empty store, ensuring that we don't build +clearStore +buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 transitivelyDependentCA +# Check that the thing we’ve just substituted has its realisation stored +nix realisation info --file ./content-addressed.nix transitivelyDependentCA +# Check that its dependencies have it too +nix realisation info --file ./content-addressed.nix dependentCA +# nix realisation info --file ./content-addressed.nix rootCA --outputs out + +if isDaemonNewer "2.13"; then + pushToStore="../push-to-store.sh" +else + pushToStore="../push-to-store-old.sh" +fi + +# Same thing, but +# 1. With non-ca derivations +# 2. Erasing the realisations on the remote store +# +# Even in that case, realising the derivations should still produce the right +# realisations on the local store +# +# Regression test for #4725 +clearStore +nix build --file ../simple.nix -L --no-link --post-build-hook "$pushToStore" +clearStore +rm -r "$REMOTE_STORE_DIR/realisations" +nix build --file ../simple.nix -L --no-link --substitute --substituters "$REMOTE_STORE" --no-require-sigs -j0 +# There's no easy way to check whether a realisation is present on the local +# store − short of manually querying the db, but the build environment doesn't +# have the sqlite binary − so we instead push things again, and check that the +# realisations have correctly been pushed to the remote store +nix copy --to "$REMOTE_STORE" --file ../simple.nix +if [[ -z "$(ls "$REMOTE_STORE_DIR/realisations")" ]]; then + echo "Realisations not rebuilt" + exit 1 +fi + +# Test the local realisation disk cache +buildDrvs --post-build-hook "$pushToStore" +clearStore +# Add the realisations of rootCA to the cachecache +clearCacheCache +export _NIX_FORCE_HTTP=1 +buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 +# Try rebuilding, but remove the realisations from the remote cache to force +# using the cachecache +clearStore +rm $REMOTE_STORE_DIR/realisations/* +buildDrvs --substitute --substituters $REMOTE_STORE --no-require-sigs -j0 diff --git a/tests/functional/ca/why-depends.sh b/tests/functional/ca/why-depends.sh new file mode 100644 index 000000000..0c079f63b --- /dev/null +++ b/tests/functional/ca/why-depends.sh @@ -0,0 +1,5 @@ +source common.sh + +export NIX_TESTS_CA_BY_DEFAULT=1 + +cd .. && source why-depends.sh diff --git a/tests/functional/case-hack.sh b/tests/functional/case-hack.sh new file mode 100644 index 000000000..61bf9b94b --- /dev/null +++ b/tests/functional/case-hack.sh @@ -0,0 +1,19 @@ +source common.sh + +clearStore + +rm -rf $TEST_ROOT/case + +opts="--option use-case-hack true" + +# Check whether restoring and dumping a NAR that contains case +# collisions is round-tripping, even on a case-insensitive system. +nix-store $opts --restore $TEST_ROOT/case < case.nar +nix-store $opts --dump $TEST_ROOT/case > $TEST_ROOT/case.nar +cmp case.nar $TEST_ROOT/case.nar +[ "$(nix-hash $opts --type sha256 $TEST_ROOT/case)" = "$(nix-hash --flat --type sha256 case.nar)" ] + +# Check whether we detect true collisions (e.g. those remaining after +# removal of the suffix). +touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3" +(! nix-store $opts --dump $TEST_ROOT/case > /dev/null) diff --git a/tests/functional/case.nar b/tests/functional/case.nar Binary files differnew file mode 100644 index 000000000..22ff26db5 --- /dev/null +++ b/tests/functional/case.nar diff --git a/tests/functional/check-refs.nix b/tests/functional/check-refs.nix new file mode 100644 index 000000000..89690e456 --- /dev/null +++ b/tests/functional/check-refs.nix @@ -0,0 +1,77 @@ +with import ./config.nix; + +rec { + + dep = import ./dependencies.nix {}; + + makeTest = nr: args: mkDerivation ({ + name = "check-refs-" + toString nr; + } // args); + + src = builtins.toFile "aux-ref" "bla bla"; + + test1 = makeTest 1 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link"; + inherit dep; + }; + + test2 = makeTest 2 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link"; + inherit dep; + }; + + test3 = makeTest 3 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link"; + allowedReferences = []; + inherit dep; + }; + + test4 = makeTest 4 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link"; + allowedReferences = [dep]; + inherit dep; + }; + + test5 = makeTest 5 { + builder = builtins.toFile "builder.sh" "mkdir $out"; + allowedReferences = []; + inherit dep; + }; + + test6 = makeTest 6 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link"; + allowedReferences = []; + inherit dep; + }; + + test7 = makeTest 7 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link"; + allowedReferences = ["out"]; + inherit dep; + }; + + test8 = makeTest 8 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link"; + inherit dep; + }; + + test9 = makeTest 9 { + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link"; + inherit dep; + disallowedReferences = [dep]; + }; + + test10 = makeTest 10 { + builder = builtins.toFile "builder.sh" "mkdir $out; echo $test5; ln -s $dep $out/link"; + inherit dep test5; + disallowedReferences = [test5]; + }; + + test11 = makeTest 11 { + __structuredAttrs = true; + unsafeDiscardReferences.out = true; + outputChecks.out.allowedReferences = []; + buildCommand = ''echo ${dep} > "''${outputs[out]}"''; + }; + +} diff --git a/tests/functional/check-refs.sh b/tests/functional/check-refs.sh new file mode 100644 index 000000000..3b587d1e5 --- /dev/null +++ b/tests/functional/check-refs.sh @@ -0,0 +1,53 @@ +source common.sh + +clearStore + +RESULT=$TEST_ROOT/result + +dep=$(nix-build -o $RESULT check-refs.nix -A dep) + +# test1 references dep, not itself. +test1=$(nix-build -o $RESULT check-refs.nix -A test1) +nix-store -q --references $test1 | grepQuietInverse $test1 +nix-store -q --references $test1 | grepQuiet $dep + +# test2 references src, not itself nor dep. +test2=$(nix-build -o $RESULT check-refs.nix -A test2) +nix-store -q --references $test2 | grepQuietInverse $test2 +nix-store -q --references $test2 | grepQuietInverse $dep +nix-store -q --references $test2 | grepQuiet aux-ref + +# test3 should fail (unallowed ref). +(! nix-build -o $RESULT check-refs.nix -A test3) + +# test4 should succeed. +nix-build -o $RESULT check-refs.nix -A test4 + +# test5 should succeed. +nix-build -o $RESULT check-refs.nix -A test5 + +# test6 should fail (unallowed self-ref). +(! nix-build -o $RESULT check-refs.nix -A test6) + +# test7 should succeed (allowed self-ref). +nix-build -o $RESULT check-refs.nix -A test7 + +# test8 should fail (toFile depending on derivation output). +(! nix-build -o $RESULT check-refs.nix -A test8) + +# test9 should fail (disallowed reference). +(! nix-build -o $RESULT check-refs.nix -A test9) + +# test10 should succeed (no disallowed references). +nix-build -o $RESULT check-refs.nix -A test10 + +if isDaemonNewer 2.12pre20230103; then + if ! isDaemonNewer 2.16.0; then + enableFeatures discard-references + restartDaemon + fi + + # test11 should succeed. + test11=$(nix-build -o $RESULT check-refs.nix -A test11) + [[ -z $(nix-store -q --references "$test11") ]] +fi diff --git a/tests/functional/check-reqs.nix b/tests/functional/check-reqs.nix new file mode 100644 index 000000000..41436cb48 --- /dev/null +++ b/tests/functional/check-reqs.nix @@ -0,0 +1,57 @@ +with import ./config.nix; + +rec { + dep1 = mkDerivation { + name = "check-reqs-dep1"; + builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file1"; + }; + + dep2 = mkDerivation { + name = "check-reqs-dep2"; + builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file2"; + }; + + deps = mkDerivation { + name = "check-reqs-deps"; + dep1 = dep1; + dep2 = dep2; + builder = builtins.toFile "builder.sh" '' + mkdir $out + ln -s $dep1/file1 $out/file1 + ln -s $dep2/file2 $out/file2 + ''; + }; + + makeTest = nr: allowreqs: mkDerivation { + name = "check-reqs-" + toString nr; + inherit deps; + builder = builtins.toFile "builder.sh" '' + mkdir $out + ln -s $deps $out/depdir1 + ''; + allowedRequisites = allowreqs; + }; + + # When specifying all the requisites, the build succeeds. + test1 = makeTest 1 [ dep1 dep2 deps ]; + + # But missing anything it fails. + test2 = makeTest 2 [ dep2 deps ]; + test3 = makeTest 3 [ dep1 deps ]; + test4 = makeTest 4 [ deps ]; + test5 = makeTest 5 []; + + test6 = mkDerivation { + name = "check-reqs"; + inherit deps; + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1"; + disallowedRequisites = [dep1]; + }; + + test7 = mkDerivation { + name = "check-reqs"; + inherit deps; + builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1"; + disallowedRequisites = [test1]; + }; +} diff --git a/tests/functional/check-reqs.sh b/tests/functional/check-reqs.sh new file mode 100644 index 000000000..856c94cec --- /dev/null +++ b/tests/functional/check-reqs.sh @@ -0,0 +1,16 @@ +source common.sh + +clearStore + +RESULT=$TEST_ROOT/result + +nix-build -o $RESULT check-reqs.nix -A test1 + +(! nix-build -o $RESULT check-reqs.nix -A test2) +(! nix-build -o $RESULT check-reqs.nix -A test3) +(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep1' +(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep2' +(! nix-build -o $RESULT check-reqs.nix -A test5) +(! nix-build -o $RESULT check-reqs.nix -A test6) + +nix-build -o $RESULT check-reqs.nix -A test7 diff --git a/tests/functional/check.nix b/tests/functional/check.nix new file mode 100644 index 000000000..ddab8eea9 --- /dev/null +++ b/tests/functional/check.nix @@ -0,0 +1,55 @@ +{checkBuildId ? 0}: + +with import ./config.nix; + +{ + nondeterministic = mkDerivation { + inherit checkBuildId; + name = "nondeterministic"; + buildCommand = + '' + mkdir $out + date +%s.%N > $out/date + echo "CHECK_TMPDIR=$TMPDIR" + echo "checkBuildId=$checkBuildId" + echo "$checkBuildId" > $TMPDIR/checkBuildId + ''; + }; + + deterministic = mkDerivation { + inherit checkBuildId; + name = "deterministic"; + buildCommand = + '' + mkdir $out + echo date > $out/date + echo "CHECK_TMPDIR=$TMPDIR" + echo "checkBuildId=$checkBuildId" + echo "$checkBuildId" > $TMPDIR/checkBuildId + ''; + }; + + failed = mkDerivation { + inherit checkBuildId; + name = "failed"; + buildCommand = + '' + mkdir $out + echo date > $out/date + echo "CHECK_TMPDIR=$TMPDIR" + echo "checkBuildId=$checkBuildId" + echo "$checkBuildId" > $TMPDIR/checkBuildId + false + ''; + }; + + hashmismatch = import <nix/fetchurl.nix> { + url = "file://" + builtins.getEnv "TEST_ROOT" + "/dummy"; + sha256 = "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73"; + }; + + fetchurl = import <nix/fetchurl.nix> { + url = "file://" + toString ./lang/eval-okay-xml.exp.xml; + sha256 = "sha256-behBlX+DQK/Pjvkuc8Tx68Jwi4E5v86wDq+ZLaHyhQE="; + }; +} diff --git a/tests/functional/check.sh b/tests/functional/check.sh new file mode 100644 index 000000000..e13abf747 --- /dev/null +++ b/tests/functional/check.sh @@ -0,0 +1,91 @@ +source common.sh + +# XXX: This shouldn’t be, but #4813 cause this test to fail +buggyNeedLocalStore "see #4813" + +checkBuildTempDirRemoved () +{ + buildDir=$(sed -n 's/CHECK_TMPDIR=//p' $1 | head -1) + checkBuildIdFile=${buildDir}/checkBuildId + [[ ! -f $checkBuildIdFile ]] || ! grep $checkBuildId $checkBuildIdFile +} + +# written to build temp directories to verify created by this instance +checkBuildId=$(date +%s%N) + +clearStore + +nix-build dependencies.nix --no-out-link +nix-build dependencies.nix --no-out-link --check + +# Build failure exit codes (100, 104, etc.) are from +# doc/manual/src/command-ref/status-build-failure.md + +# check for dangling temporary build directories +# only retain if build fails and --keep-failed is specified, or... +# ...build is non-deterministic and --check and --keep-failed are both specified +nix-build check.nix -A failed --argstr checkBuildId $checkBuildId \ + --no-out-link 2> $TEST_ROOT/log || status=$? +[ "$status" = "100" ] +checkBuildTempDirRemoved $TEST_ROOT/log + +nix-build check.nix -A failed --argstr checkBuildId $checkBuildId \ + --no-out-link --keep-failed 2> $TEST_ROOT/log || status=$? +[ "$status" = "100" ] +if checkBuildTempDirRemoved $TEST_ROOT/log; then false; fi + +nix-build check.nix -A deterministic --argstr checkBuildId $checkBuildId \ + --no-out-link 2> $TEST_ROOT/log +checkBuildTempDirRemoved $TEST_ROOT/log + +nix-build check.nix -A deterministic --argstr checkBuildId $checkBuildId \ + --no-out-link --check --keep-failed 2> $TEST_ROOT/log +if grepQuiet 'may not be deterministic' $TEST_ROOT/log; then false; fi +checkBuildTempDirRemoved $TEST_ROOT/log + +nix-build check.nix -A nondeterministic --argstr checkBuildId $checkBuildId \ + --no-out-link 2> $TEST_ROOT/log +checkBuildTempDirRemoved $TEST_ROOT/log + +nix-build check.nix -A nondeterministic --argstr checkBuildId $checkBuildId \ + --no-out-link --check 2> $TEST_ROOT/log || status=$? +grep 'may not be deterministic' $TEST_ROOT/log +[ "$status" = "104" ] +checkBuildTempDirRemoved $TEST_ROOT/log + +nix-build check.nix -A nondeterministic --argstr checkBuildId $checkBuildId \ + --no-out-link --check --keep-failed 2> $TEST_ROOT/log || status=$? +grep 'may not be deterministic' $TEST_ROOT/log +[ "$status" = "104" ] +if checkBuildTempDirRemoved $TEST_ROOT/log; then false; fi + +clearStore + +path=$(nix-build check.nix -A fetchurl --no-out-link) + +chmod +w $path +echo foo > $path +chmod -w $path + +nix-build check.nix -A fetchurl --no-out-link --check +# Note: "check" doesn't repair anything, it just compares to the hash stored in the database. +[[ $(cat $path) = foo ]] + +nix-build check.nix -A fetchurl --no-out-link --repair +[[ $(cat $path) != foo ]] + +echo 'Hello World' > $TEST_ROOT/dummy +nix-build check.nix -A hashmismatch --no-out-link || status=$? +[ "$status" = "102" ] + +echo -n > $TEST_ROOT/dummy +nix-build check.nix -A hashmismatch --no-out-link +echo 'Hello World' > $TEST_ROOT/dummy + +nix-build check.nix -A hashmismatch --no-out-link --check || status=$? +[ "$status" = "102" ] + +# Multiple failures with --keep-going +nix-build check.nix -A nondeterministic --no-out-link +nix-build check.nix -A nondeterministic -A hashmismatch --no-out-link --check --keep-going || status=$? +[ "$status" = "110" ] diff --git a/tests/functional/common.sh b/tests/functional/common.sh new file mode 100644 index 000000000..7b0922c9f --- /dev/null +++ b/tests/functional/common.sh @@ -0,0 +1,12 @@ +set -eu -o pipefail + +if [[ -z "${COMMON_SH_SOURCED-}" ]]; then + +COMMON_SH_SOURCED=1 + +source "$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")/common/vars-and-functions.sh" +if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + startDaemon +fi + +fi # COMMON_SH_SOURCED diff --git a/tests/functional/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in new file mode 100644 index 000000000..967d6be54 --- /dev/null +++ b/tests/functional/common/vars-and-functions.sh.in @@ -0,0 +1,276 @@ +set -eu -o pipefail + +if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then + +COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1 + +export PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) ' + +export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//} +export NIX_STORE_DIR +if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then + # Maybe the build directory is symlinked. + export NIX_IGNORE_SYMLINK_STORE=1 + NIX_STORE_DIR=$TEST_ROOT/store +fi +export NIX_LOCALSTATE_DIR=$TEST_ROOT/var +export NIX_LOG_DIR=$TEST_ROOT/var/log/nix +export NIX_STATE_DIR=$TEST_ROOT/var/nix +export NIX_CONF_DIR=$TEST_ROOT/etc +export NIX_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket +unset NIX_USER_CONF_FILES +export _NIX_TEST_SHARED=$TEST_ROOT/shared +if [[ -n $NIX_STORE ]]; then + export _NIX_TEST_NO_SANDBOX=1 +fi +export _NIX_IN_TEST=$TEST_ROOT/shared +export _NIX_TEST_NO_LSOF=1 +export NIX_REMOTE=${NIX_REMOTE_-} +unset NIX_PATH +export TEST_HOME=$TEST_ROOT/test-home +export HOME=$TEST_HOME +unset XDG_STATE_HOME +unset XDG_DATA_HOME +unset XDG_CONFIG_HOME +unset XDG_CONFIG_DIRS +unset XDG_CACHE_HOME +mkdir -p $TEST_HOME + +export PATH=@bindir@:$PATH +if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then + export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH +fi +DAEMON_PATH="$PATH" +if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" +fi +coreutils=@coreutils@ + +export dot=@dot@ +export SHELL="@bash@" +export PAGER=cat +export busybox="@sandbox_shell@" + +export version=@PACKAGE_VERSION@ +export system=@system@ + +export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@ + +export IMPURE_VAR1=foo +export IMPURE_VAR2=bar + +cacheDir=$TEST_ROOT/binary-cache + +readLink() { + ls -l "$1" | sed 's/.*->\ //' +} + +clearProfiles() { + profiles="$HOME"/.local/state/nix/profiles + rm -rf "$profiles" +} + +clearStore() { + echo "clearing store..." + chmod -R +w "$NIX_STORE_DIR" + rm -rf "$NIX_STORE_DIR" + mkdir "$NIX_STORE_DIR" + rm -rf "$NIX_STATE_DIR" + mkdir "$NIX_STATE_DIR" + clearProfiles +} + +clearCache() { + rm -rf "$cacheDir" +} + +clearCacheCache() { + rm -f $TEST_HOME/.cache/nix/binary-cache* +} + +startDaemon() { + # Don’t start the daemon twice, as this would just make it loop indefinitely + if [[ "${_NIX_TEST_DAEMON_PID-}" != '' ]]; then + return + fi + # Start the daemon, wait for the socket to appear. + rm -f $NIX_DAEMON_SOCKET_PATH + PATH=$DAEMON_PATH nix-daemon & + _NIX_TEST_DAEMON_PID=$! + export _NIX_TEST_DAEMON_PID + for ((i = 0; i < 300; i++)); do + if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then + DAEMON_STARTED=1 + break; + fi + sleep 0.1 + done + if [[ -z ${DAEMON_STARTED+x} ]]; then + fail "Didn’t manage to start the daemon" + fi + trap "killDaemon" EXIT + # Save for if daemon is killed + NIX_REMOTE_OLD=$NIX_REMOTE + export NIX_REMOTE=daemon +} + +killDaemon() { + # Don’t fail trying to stop a non-existant daemon twice + if [[ "${_NIX_TEST_DAEMON_PID-}" == '' ]]; then + return + fi + kill $_NIX_TEST_DAEMON_PID + for i in {0..100}; do + kill -0 $_NIX_TEST_DAEMON_PID 2> /dev/null || break + sleep 0.1 + done + kill -9 $_NIX_TEST_DAEMON_PID 2> /dev/null || true + wait $_NIX_TEST_DAEMON_PID || true + rm -f $NIX_DAEMON_SOCKET_PATH + # Indicate daemon is stopped + unset _NIX_TEST_DAEMON_PID + # Restore old nix remote + NIX_REMOTE=$NIX_REMOTE_OLD + trap "" EXIT +} + +restartDaemon() { + [[ -z "${_NIX_TEST_DAEMON_PID:-}" ]] && return 0 + + killDaemon + startDaemon +} + +if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then + _canUseSandbox=1 +fi + +isDaemonNewer () { + [[ -n "${NIX_DAEMON_PACKAGE:-}" ]] || return 0 + local requiredVersion="$1" + local daemonVersion=$($NIX_DAEMON_PACKAGE/bin/nix-daemon --version | cut -d' ' -f3) + [[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]] +} + +skipTest () { + echo "$1, skipping this test..." >&2 + exit 99 +} + +requireDaemonNewerThan () { + isDaemonNewer "$1" || skipTest "Daemon is too old" +} + +canUseSandbox() { + [[ ${_canUseSandbox-} ]] +} + +requireSandboxSupport () { + canUseSandbox || skipTest "Sandboxing not supported" +} + +requireGit() { + [[ $(type -p git) ]] || skipTest "Git not installed" +} + +fail() { + echo "$1" >&2 + exit 1 +} + +# Run a command failing if it didn't exit with the expected exit code. +# +# Has two advantages over the built-in `!`: +# +# 1. `!` conflates all non-0 codes. `expect` allows testing for an exact +# code. +# +# 2. `!` unexpectedly negates `set -e`, and cannot be used on individual +# pipeline stages with `set -o pipefail`. It only works on the entire +# pipeline, which is useless if we want, say, `nix ...` invocation to +# *fail*, but a grep on the error message it outputs to *succeed*. +expect() { + local expected res + expected="$1" + shift + "$@" && res=0 || res="$?" + if [[ $res -ne $expected ]]; then + echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 + return 1 + fi + return 0 +} + +# Better than just doing `expect ... >&2` because the "Expected..." +# message below will *not* be redirected. +expectStderr() { + local expected res + expected="$1" + shift + "$@" 2>&1 && res=0 || res="$?" + if [[ $res -ne $expected ]]; then + echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2 + return 1 + fi + return 0 +} + +needLocalStore() { + if [[ "$NIX_REMOTE" == "daemon" ]]; then + skipTest "Can’t run through the daemon ($1)" + fi +} + +# Just to make it easy to find which tests should be fixed +buggyNeedLocalStore() { + needLocalStore "$1" +} + +enableFeatures() { + local features="$1" + sed -i 's/experimental-features .*/& '"$features"'/' "$NIX_CONF_DIR"/nix.conf +} + +set -x + +onError() { + set +x + echo "$0: test failed at:" >&2 + for ((i = 1; i < ${#BASH_SOURCE[@]}; i++)); do + if [[ -z ${BASH_SOURCE[i]} ]]; then break; fi + echo " ${FUNCNAME[i]} in ${BASH_SOURCE[i]}:${BASH_LINENO[i-1]}" >&2 + done +} + +# `grep -v` doesn't work well for exit codes. We want `!(exist line l. l +# matches)`. It gives us `exist line l. !(l matches)`. +# +# `!` normally doesn't work well with `set -e`, but when we wrap in a +# function it *does*. +grepInverse() { + ! grep "$@" +} + +# A shorthand, `> /dev/null` is a bit noisy. +# +# `grep -q` would seem to do this, no function necessary, but it is a +# bad fit with pipes and `set -o pipefail`: `-q` will exit after the +# first match, and then subsequent writes will result in broken pipes. +# +# Note that reproducing the above is a bit tricky as it depends on +# non-deterministic properties such as the timing between the match and +# the closing of the pipe, the buffering of the pipe, and the speed of +# the producer into the pipe. But rest assured we've seen it happen in +# CI reliably. +grepQuiet() { + grep "$@" > /dev/null +} + +# The previous two, combined +grepQuietInverse() { + ! grep "$@" > /dev/null +} + +trap onError ERR + +fi # COMMON_VARS_AND_FUNCTIONS_SH_SOURCED diff --git a/tests/functional/completions.sh b/tests/functional/completions.sh new file mode 100644 index 000000000..19dc61098 --- /dev/null +++ b/tests/functional/completions.sh @@ -0,0 +1,68 @@ +source common.sh + +cd "$TEST_ROOT" + +mkdir -p dep +cat <<EOF > dep/flake.nix +{ + outputs = i: { }; +} +EOF +mkdir -p foo +cat <<EOF > foo/flake.nix +{ + inputs.a.url = "path:$(realpath dep)"; + + outputs = i: { + sampleOutput = 1; + }; +} +EOF +mkdir -p bar +cat <<EOF > bar/flake.nix +{ + inputs.b.url = "path:$(realpath dep)"; + + outputs = i: { + sampleOutput = 1; + }; +} +EOF +mkdir -p err +cat <<EOF > err/flake.nix +throw "error" +EOF + +# Test the completion of a subcommand +[[ "$(NIX_GET_COMPLETIONS=1 nix buil)" == $'normal\nbuild\t' ]] +[[ "$(NIX_GET_COMPLETIONS=2 nix flake metad)" == $'normal\nmetadata\t' ]] + +# Filename completion +[[ "$(NIX_GET_COMPLETIONS=2 nix build ./f)" == $'filenames\n./foo\t' ]] +[[ "$(NIX_GET_COMPLETIONS=2 nix build ./nonexistent)" == $'filenames' ]] + +# Input override completion +[[ "$(NIX_GET_COMPLETIONS=4 nix build ./foo --override-input '')" == $'normal\na\t' ]] +[[ "$(NIX_GET_COMPLETIONS=5 nix flake show ./foo --override-input '')" == $'normal\na\t' ]] +## With multiple input flakes +[[ "$(NIX_GET_COMPLETIONS=5 nix build ./foo ./bar --override-input '')" == $'normal\na\t\nb\t' ]] +## With tilde expansion +[[ "$(HOME=$PWD NIX_GET_COMPLETIONS=4 nix build '~/foo' --override-input '')" == $'normal\na\t' ]] +## Out of order +[[ "$(NIX_GET_COMPLETIONS=3 nix build --update-input '' ./foo)" == $'normal\na\t' ]] +[[ "$(NIX_GET_COMPLETIONS=4 nix build ./foo --update-input '' ./bar)" == $'normal\na\t\nb\t' ]] + +# Cli flag completion +NIX_GET_COMPLETIONS=2 nix build --log-form | grep -- "--log-format" + +# Config option completion +## With `--option` +NIX_GET_COMPLETIONS=3 nix build --option allow-import-from | grep -- "allow-import-from-derivation" +## As a cli flag – not working atm +# NIX_GET_COMPLETIONS=2 nix build --allow-import-from | grep -- "allow-import-from-derivation" + +# Attr path completions +[[ "$(NIX_GET_COMPLETIONS=2 nix eval ./foo\#sam)" == $'attrs\n./foo#sampleOutput\t' ]] +[[ "$(NIX_GET_COMPLETIONS=4 nix eval --file ./foo/flake.nix outp)" == $'attrs\noutputs\t' ]] +[[ "$(NIX_GET_COMPLETIONS=4 nix eval --file ./err/flake.nix outp 2>&1)" == $'attrs' ]] +[[ "$(NIX_GET_COMPLETIONS=2 nix eval ./err\# 2>&1)" == $'attrs' ]] diff --git a/tests/functional/compression-levels.sh b/tests/functional/compression-levels.sh new file mode 100644 index 000000000..85f12974a --- /dev/null +++ b/tests/functional/compression-levels.sh @@ -0,0 +1,22 @@ +source common.sh + +clearStore +clearCache + +outPath=$(nix-build dependencies.nix --no-out-link) + +cacheURI="file://$cacheDir?compression=xz&compression-level=0" + +nix copy --to $cacheURI $outPath + +FILESIZES=$(cat ${cacheDir}/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') + +clearCache + +cacheURI="file://$cacheDir?compression=xz&compression-level=5" + +nix copy --to $cacheURI $outPath + +FILESIZES2=$(cat ${cacheDir}/*.narinfo | awk '/FileSize: /{sum+=$2}END{print sum}') + +[[ $FILESIZES -gt $FILESIZES2 ]] diff --git a/tests/functional/compute-levels.sh b/tests/functional/compute-levels.sh new file mode 100644 index 000000000..de3da2ebd --- /dev/null +++ b/tests/functional/compute-levels.sh @@ -0,0 +1,7 @@ +source common.sh + +if [[ $(uname -ms) = "Linux x86_64" ]]; then + # x86_64 CPUs must always support the baseline + # microarchitecture level. + nix -vv --version | grepQuiet "x86_64-v1-linux" +fi diff --git a/tests/functional/config.nix.in b/tests/functional/config.nix.in new file mode 100644 index 000000000..7facbdcbc --- /dev/null +++ b/tests/functional/config.nix.in @@ -0,0 +1,27 @@ +let + contentAddressedByDefault = builtins.getEnv "NIX_TESTS_CA_BY_DEFAULT" == "1"; + caArgs = if contentAddressedByDefault then { + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + } else {}; +in + +rec { + shell = "@bash@"; + + path = "@coreutils@"; + + system = "@system@"; + + shared = builtins.getEnv "_NIX_TEST_SHARED"; + + mkDerivation = args: + derivation ({ + inherit system; + builder = shell; + args = ["-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + PATH = path; + } // caArgs // removeAttrs args ["builder" "meta"]) + // { meta = args.meta or {}; }; +} diff --git a/tests/functional/config.sh b/tests/functional/config.sh new file mode 100644 index 000000000..723f575ed --- /dev/null +++ b/tests/functional/config.sh @@ -0,0 +1,58 @@ +source common.sh + +# Isolate the home for this test. +# Other tests (e.g. flake registry tests) could be writing to $HOME in parallel. +export HOME=$TEST_ROOT/userhome + +# Test that using XDG_CONFIG_HOME works +# Assert the config folder didn't exist initially. +[ ! -e "$HOME/.config" ] +# Without XDG_CONFIG_HOME, creates $HOME/.config +unset XDG_CONFIG_HOME +# Run against the nix registry to create the config dir +# (Tip: this relies on removing non-existent entries being a no-op!) +nix registry remove userhome-without-xdg +# Verifies it created it +[ -e "$HOME/.config" ] +# Remove the directory it created +rm -rf "$HOME/.config" +# Run the same test, but with XDG_CONFIG_HOME +export XDG_CONFIG_HOME=$TEST_ROOT/confighome +# Assert the XDG_CONFIG_HOME/nix path does not exist yet. +[ ! -e "$TEST_ROOT/confighome/nix" ] +nix registry remove userhome-with-xdg +# Verifies the confighome path has been created +[ -e "$TEST_ROOT/confighome/nix" ] +# Assert the .config folder hasn't been created. +[ ! -e "$HOME/.config" ] + +# Test that files are loaded from XDG by default +export XDG_CONFIG_HOME=$TEST_ROOT/confighome +export XDG_CONFIG_DIRS=$TEST_ROOT/dir1:$TEST_ROOT/dir2 +files=$(nix-build --verbose --version | grep "User config" | cut -d ':' -f2- | xargs) +[[ $files == "$TEST_ROOT/confighome/nix/nix.conf:$TEST_ROOT/dir1/nix/nix.conf:$TEST_ROOT/dir2/nix/nix.conf" ]] + +# Test that setting NIX_USER_CONF_FILES overrides all the default user config files +export NIX_USER_CONF_FILES=$TEST_ROOT/file1.conf:$TEST_ROOT/file2.conf +files=$(nix-build --verbose --version | grep "User config" | cut -d ':' -f2- | xargs) +[[ $files == "$TEST_ROOT/file1.conf:$TEST_ROOT/file2.conf" ]] + +# Test that it's possible to load the config from a custom location +here=$(readlink -f "$(dirname "${BASH_SOURCE[0]}")") +export NIX_USER_CONF_FILES=$here/config/nix-with-substituters.conf +var=$(nix show-config | grep '^substituters =' | cut -d '=' -f 2 | xargs) +[[ $var == https://example.com ]] + +# Test that it's possible to load config from the environment +prev=$(nix show-config | grep '^cores' | cut -d '=' -f 2 | xargs) +export NIX_CONFIG="cores = 4242"$'\n'"experimental-features = nix-command flakes" +exp_cores=$(nix show-config | grep '^cores' | cut -d '=' -f 2 | xargs) +exp_features=$(nix show-config | grep '^experimental-features' | cut -d '=' -f 2 | xargs) +[[ $prev != $exp_cores ]] +[[ $exp_cores == "4242" ]] +[[ $exp_features == "flakes nix-command" ]] + +# Test that it's possible to retrieve a single setting's value +val=$(nix show-config | grep '^warn-dirty' | cut -d '=' -f 2 | xargs) +val2=$(nix show-config warn-dirty) +[[ $val == $val2 ]] diff --git a/tests/functional/config/nix-with-substituters.conf b/tests/functional/config/nix-with-substituters.conf new file mode 100644 index 000000000..90f359a6f --- /dev/null +++ b/tests/functional/config/nix-with-substituters.conf @@ -0,0 +1,2 @@ +experimental-features = nix-command +substituters = https://example.com diff --git a/tests/functional/db-migration.sh b/tests/functional/db-migration.sh new file mode 100644 index 000000000..44cd16bc0 --- /dev/null +++ b/tests/functional/db-migration.sh @@ -0,0 +1,28 @@ +# Test that we can successfully migrate from an older db schema + +source common.sh + +# Only run this if we have an older Nix available +# XXX: This assumes that the `daemon` package is older than the `client` one +if [[ -z "${NIX_DAEMON_PACKAGE-}" ]]; then + skipTest "not using the Nix daemon" +fi + +killDaemon + +# Fill the db using the older Nix +PATH_WITH_NEW_NIX="$PATH" +export PATH="${NIX_DAEMON_PACKAGE}/bin:$PATH" +clearStore +nix-build simple.nix --no-out-link +nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 +dependenciesOutPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROOT/sk1") +fixedOutPath=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build fixed.nix -A good.0 --no-out-link) + +# Migrate to the new schema and ensure that everything's there +export PATH="$PATH_WITH_NEW_NIX" +info=$(nix path-info --json $dependenciesOutPath) +[[ $info =~ '"ultimate":true' ]] +[[ $info =~ 'cache1.example.org' ]] +nix verify -r "$fixedOutPath" +nix verify -r "$dependenciesOutPath" --sigs-needed 1 --trusted-public-keys $(cat $TEST_ROOT/pk1) diff --git a/tests/functional/dependencies.builder0.sh b/tests/functional/dependencies.builder0.sh new file mode 100644 index 000000000..9b11576e0 --- /dev/null +++ b/tests/functional/dependencies.builder0.sh @@ -0,0 +1,16 @@ +[ "${input1: -2}" = /. ] +[ "${input2: -2}" = /. ] + +mkdir $out +echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar + +ln -s $input2 $out/reference-to-input-2 + +# Self-reference. +ln -s $out $out/self + +# Executable. +echo program > $out/program +chmod +x $out/program + +echo FOO diff --git a/tests/functional/dependencies.nix b/tests/functional/dependencies.nix new file mode 100644 index 000000000..be1a7ae9a --- /dev/null +++ b/tests/functional/dependencies.nix @@ -0,0 +1,48 @@ +{ hashInvalidator ? "" }: +with import ./config.nix; + +let { + + input0 = mkDerivation { + name = "dependencies-input-0"; + buildCommand = "mkdir $out; echo foo > $out/bar"; + }; + + input1 = mkDerivation { + name = "dependencies-input-1"; + buildCommand = "mkdir $out; echo FOO > $out/foo"; + }; + + input2 = mkDerivation { + name = "dependencies-input-2"; + buildCommand = '' + mkdir $out + echo BAR > $out/bar + echo ${input0} > $out/input0 + ''; + }; + + fod_input = mkDerivation { + name = "fod-input"; + buildCommand = '' + echo ${hashInvalidator} + echo FOD > $out + ''; + outputHashMode = "flat"; + outputHashAlgo = "sha256"; + outputHash = "1dq9p0hnm1y75q2x40fws5887bq1r840hzdxak0a9djbwvx0b16d"; + }; + + body = mkDerivation { + name = "dependencies-top"; + builder = ./dependencies.builder0.sh + "/FOOBAR/../."; + input1 = input1 + "/."; + input2 = "${input2}/."; + input1_drv = input1; + input2_drv = input2; + input0_drv = input0; + fod_input_drv = fod_input; + meta.description = "Random test package"; + }; + +} diff --git a/tests/functional/dependencies.sh b/tests/functional/dependencies.sh new file mode 100644 index 000000000..b93dacac0 --- /dev/null +++ b/tests/functional/dependencies.sh @@ -0,0 +1,72 @@ +source common.sh + +clearStore + +drvPath=$(nix-instantiate dependencies.nix) + +echo "derivation is $drvPath" + +nix-store -q --tree "$drvPath" | grep '───.*builder-dependencies-input-1.sh' + +# Test Graphviz graph generation. +nix-store -q --graph "$drvPath" > $TEST_ROOT/graph +if test -n "$dot"; then + # Does it parse? + $dot < $TEST_ROOT/graph +fi + +# Test GraphML graph generation +nix-store -q --graphml "$drvPath" > $TEST_ROOT/graphml + +outPath=$(nix-store -rvv "$drvPath") || fail "build failed" + +# Test Graphviz graph generation. +nix-store -q --graph "$outPath" > $TEST_ROOT/graph +if test -n "$dot"; then + # Does it parse? + $dot < $TEST_ROOT/graph +fi + +nix-store -q --tree "$outPath" | grep '───.*dependencies-input-2' + +echo "output path is $outPath" + +text=$(cat "$outPath"/foobar) +if test "$text" != "FOOBAR"; then exit 1; fi + +deps=$(nix-store -quR "$drvPath") + +echo "output closure contains $deps" + +# The output path should be in the closure. +echo "$deps" | grepQuiet "$outPath" + +# Input-1 is not retained. +if echo "$deps" | grepQuiet "dependencies-input-1"; then exit 1; fi + +# Input-2 is retained. +input2OutPath=$(echo "$deps" | grep "dependencies-input-2") + +# The referrers closure of input-2 should include outPath. +nix-store -q --referrers-closure "$input2OutPath" | grep "$outPath" + +# Check that the derivers are set properly. +test $(nix-store -q --deriver "$outPath") = "$drvPath" +nix-store -q --deriver "$input2OutPath" | grepQuiet -- "-input-2.drv" + +# --valid-derivers returns the currently single valid .drv file +test "$(nix-store -q --valid-derivers "$outPath")" = "$drvPath" + +# instantiate a different drv with the same output +drvPath2=$(nix-instantiate dependencies.nix --argstr hashInvalidator yay) + +# now --valid-derivers returns both +test "$(nix-store -q --valid-derivers "$outPath" | sort)" = "$(sort <<< "$drvPath"$'\n'"$drvPath2")" + +# check that nix-store --valid-derivers only returns existing drv +nix-store --delete "$drvPath" +test "$(nix-store -q --valid-derivers "$outPath")" = "$drvPath2" + +# check that --valid-derivers returns nothing when there are no valid derivers +nix-store --delete "$drvPath2" +test -z "$(nix-store -q --valid-derivers "$outPath")" diff --git a/tests/functional/derivation-json.sh b/tests/functional/derivation-json.sh new file mode 100644 index 000000000..b6be5d977 --- /dev/null +++ b/tests/functional/derivation-json.sh @@ -0,0 +1,12 @@ +source common.sh + +drvPath=$(nix-instantiate simple.nix) + +nix derivation show $drvPath | jq .[] > $TEST_HOME/simple.json + +drvPath2=$(nix derivation add < $TEST_HOME/simple.json) + +[[ "$drvPath" = "$drvPath2" ]] + +# Input addressed derivations cannot be renamed. +jq '.name = "foo"' < $TEST_HOME/simple.json | expectStderr 1 nix derivation add | grepQuiet "has incorrect output" diff --git a/tests/functional/dummy b/tests/functional/dummy new file mode 100644 index 000000000..557db03de --- /dev/null +++ b/tests/functional/dummy @@ -0,0 +1 @@ +Hello World diff --git a/tests/functional/dump-db.sh b/tests/functional/dump-db.sh new file mode 100644 index 000000000..48647f403 --- /dev/null +++ b/tests/functional/dump-db.sh @@ -0,0 +1,22 @@ +source common.sh + +needLocalStore "--dump-db requires a local store" + +clearStore + +path=$(nix-build dependencies.nix -o $TEST_ROOT/result) + +deps="$(nix-store -qR $TEST_ROOT/result)" + +nix-store --dump-db > $TEST_ROOT/dump + +rm -rf $NIX_STATE_DIR/db + +nix-store --load-db < $TEST_ROOT/dump + +deps2="$(nix-store -qR $TEST_ROOT/result)" + +[ "$deps" = "$deps2" ]; + +nix-store --dump-db > $TEST_ROOT/dump2 +cmp $TEST_ROOT/dump $TEST_ROOT/dump2 diff --git a/tests/functional/dyn-drv/build-built-drv.sh b/tests/functional/dyn-drv/build-built-drv.sh new file mode 100644 index 000000000..647be9457 --- /dev/null +++ b/tests/functional/dyn-drv/build-built-drv.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +source common.sh + +# In the corresponding nix file, we have two derivations: the first, named `hello`, +# is a normal recursive derivation, while the second, named dependent, has the +# new outputHashMode "text". Note that in "dependent", we don't refer to the +# build output of `hello`, but only to the path of the drv file. For this reason, +# we only need to: +# +# - instantiate `hello` +# - build `producingDrv` +# - check that the path of the output coincides with that of the original derivation + +out1=$(nix build -f ./text-hashed-output.nix hello --no-link) + +clearStore + +drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv) + +expectStderr 1 nix build "${drvDep}^out^out" --no-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented" diff --git a/tests/functional/dyn-drv/common.sh b/tests/functional/dyn-drv/common.sh new file mode 100644 index 000000000..c786f6925 --- /dev/null +++ b/tests/functional/dyn-drv/common.sh @@ -0,0 +1,8 @@ +source ../common.sh + +# Need backend to support text-hashing too +requireDaemonNewerThan "2.16.0pre20230419" + +enableFeatures "ca-derivations dynamic-derivations" + +restartDaemon diff --git a/tests/functional/dyn-drv/config.nix.in b/tests/functional/dyn-drv/config.nix.in new file mode 120000 index 000000000..af24ddb30 --- /dev/null +++ b/tests/functional/dyn-drv/config.nix.in @@ -0,0 +1 @@ +../config.nix.in
\ No newline at end of file diff --git a/tests/functional/dyn-drv/dep-built-drv.sh b/tests/functional/dyn-drv/dep-built-drv.sh new file mode 100644 index 000000000..4f6e9b080 --- /dev/null +++ b/tests/functional/dyn-drv/dep-built-drv.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +source common.sh + +out1=$(nix-build ./text-hashed-output.nix -A hello --no-out-link) + +clearStore + +expectStderr 1 nix-build ./text-hashed-output.nix -A wrapper --no-out-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented" + +# diff -r $out1 $out2 diff --git a/tests/functional/dyn-drv/eval-outputOf.sh b/tests/functional/dyn-drv/eval-outputOf.sh new file mode 100644 index 000000000..9467feb8d --- /dev/null +++ b/tests/functional/dyn-drv/eval-outputOf.sh @@ -0,0 +1,80 @@ +#!/usr/bin/env bash + +source ./common.sh + +# Without the dynamic-derivations XP feature, we don't have the builtin. +nix --experimental-features 'nix-command' eval --impure --expr \ + 'assert ! (builtins ? outputOf); ""' + +# Test that a string is required. +# +# We currently require a string to be passed, rather than a derivation +# object that could be coerced to a string. We might liberalise this in +# the future so it does work, but there are some design questions to +# resolve first. Adding a test so we don't liberalise it by accident. +expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \ + 'builtins.outputOf (import ../dependencies.nix {}) "out"' \ + | grepQuiet "value is a set while a string was expected" + +# Test that "DrvDeep" string contexts are not supported at this time +# +# Like the above, this is a restriction we could relax later. +expectStderr 1 nix --experimental-features 'nix-command dynamic-derivations' eval --impure --expr \ + 'builtins.outputOf (import ../dependencies.nix {}).drvPath "out"' \ + | grepQuiet "has a context which refers to a complete source and binary closure. This is not supported at this time" + +# Test using `builtins.outputOf` with static derivations +testStaticHello () { + nix eval --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = hello.outPath; + b = builtins.outputOf (builtins.unsafeDiscardOutputDependency hello.drvPath) "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' +} + +# Test with a regular old input-addresed derivation +# +# `builtins.outputOf` works without ca-derivations and doesn't create a +# placeholder but just returns the output path. +testStaticHello + +# Test with content addressed derivation. +NIX_TESTS_CA_BY_DEFAULT=1 testStaticHello + +# Test with derivation-producing derivation +# +# This is hardly different from the preceding cases, except that we're +# only taking 1 outputOf out of 2 possible outputOfs. Note that +# `.outPath` could be defined as `outputOf drvPath`, which is what we're +# testing here. The other `outputOf` that we're not testing here is the +# use of _dynamic_ derivations. +nix eval --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = producingDrv.outPath; + b = builtins.outputOf (builtins.builtins.unsafeDiscardOutputDependency producingDrv.drvPath) "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' + +# Test with unbuilt output of derivation-producing derivation. +# +# This function similar to `testStaticHello` used above, but instead of +# checking the property on a constant derivation, we check it on a +# derivation that's from another derivation's output (outPath). +testDynamicHello () { + nix eval --impure --expr \ + 'with (import ./text-hashed-output.nix); let + a = builtins.outputOf producingDrv.outPath "out"; + b = builtins.outputOf (builtins.outputOf (builtins.unsafeDiscardOutputDependency producingDrv.drvPath) "out") "out"; + in builtins.trace a + (builtins.trace b + (assert a == b; null))' +} + +# inner dynamic derivation is input-addressed +testDynamicHello + +# inner dynamic derivation is content-addressed +NIX_TESTS_CA_BY_DEFAULT=1 testDynamicHello diff --git a/tests/functional/dyn-drv/local.mk b/tests/functional/dyn-drv/local.mk new file mode 100644 index 000000000..c87534944 --- /dev/null +++ b/tests/functional/dyn-drv/local.mk @@ -0,0 +1,15 @@ +dyn-drv-tests := \ + $(d)/text-hashed-output.sh \ + $(d)/recursive-mod-json.sh \ + $(d)/build-built-drv.sh \ + $(d)/eval-outputOf.sh \ + $(d)/dep-built-drv.sh \ + $(d)/old-daemon-error-hack.sh + +install-tests-groups += dyn-drv + +clean-files += \ + $(d)/config.nix + +test-deps += \ + tests/functional/dyn-drv/config.nix diff --git a/tests/functional/dyn-drv/old-daemon-error-hack.nix b/tests/functional/dyn-drv/old-daemon-error-hack.nix new file mode 100644 index 000000000..c9d4a62d4 --- /dev/null +++ b/tests/functional/dyn-drv/old-daemon-error-hack.nix @@ -0,0 +1,20 @@ +with import ./config.nix; + +# A simple content-addressed derivation. +# The derivation can be arbitrarily modified by passing a different `seed`, +# but the output will always be the same +rec { + stub = mkDerivation { + name = "stub"; + buildCommand = '' + echo stub > $out + ''; + }; + wrapper = mkDerivation { + name = "has-dynamic-drv-dep"; + buildCommand = '' + exit 1 # we're not building this derivation + ${builtins.outputOf stub.outPath "out"} + ''; + }; +} diff --git a/tests/functional/dyn-drv/old-daemon-error-hack.sh b/tests/functional/dyn-drv/old-daemon-error-hack.sh new file mode 100644 index 000000000..43b049973 --- /dev/null +++ b/tests/functional/dyn-drv/old-daemon-error-hack.sh @@ -0,0 +1,11 @@ +# Purposely bypassing our usual common for this subgroup +source ../common.sh + +# Need backend to support text-hashing too +isDaemonNewer "2.18.0pre20230906" && skipTest "Daemon is too new" + +enableFeatures "ca-derivations dynamic-derivations" + +restartDaemon + +expectStderr 1 nix-instantiate --read-write-mode ./old-daemon-error-hack.nix | grepQuiet "the daemon is too old to understand dependencies on dynamic derivations" diff --git a/tests/functional/dyn-drv/recursive-mod-json.nix b/tests/functional/dyn-drv/recursive-mod-json.nix new file mode 100644 index 000000000..c6a24ca4f --- /dev/null +++ b/tests/functional/dyn-drv/recursive-mod-json.nix @@ -0,0 +1,33 @@ +with import ./config.nix; + +let innerName = "foo"; in + +mkDerivation rec { + name = "${innerName}.drv"; + SHELL = shell; + + requiredSystemFeatures = [ "recursive-nix" ]; + + drv = builtins.unsafeDiscardOutputDependency (import ./text-hashed-output.nix).hello.drvPath; + + buildCommand = '' + export NIX_CONFIG='experimental-features = nix-command ca-derivations' + + PATH=${builtins.getEnv "EXTRA_PATH"}:$PATH + + # JSON of pre-existing drv + nix derivation show $drv | jq .[] > drv0.json + + # Fix name + jq < drv0.json '.name = "${innerName}"' > drv1.json + + # Extend `buildCommand` + jq < drv1.json '.env.buildCommand += "echo \"I am alive!\" >> $out/hello\n"' > drv0.json + + # Used as our output + cp $(nix derivation add < drv0.json) $out + ''; + __contentAddressed = true; + outputHashMode = "text"; + outputHashAlgo = "sha256"; +} diff --git a/tests/functional/dyn-drv/recursive-mod-json.sh b/tests/functional/dyn-drv/recursive-mod-json.sh new file mode 100644 index 000000000..0698b81bd --- /dev/null +++ b/tests/functional/dyn-drv/recursive-mod-json.sh @@ -0,0 +1,27 @@ +source common.sh + +# FIXME +if [[ $(uname) != Linux ]]; then skipTest "Not running Linux"; fi + +export NIX_TESTS_CA_BY_DEFAULT=1 + +enableFeatures 'recursive-nix' +restartDaemon + +clearStore + +rm -f $TEST_ROOT/result + +EXTRA_PATH=$(dirname $(type -p nix)):$(dirname $(type -p jq)) +export EXTRA_PATH + +# Will produce a drv +metaDrv=$(nix-instantiate ./recursive-mod-json.nix) + +# computed "dynamic" derivation +drv=$(nix-store -r $metaDrv) + +# build that dyn drv +res=$(nix-store -r $drv) + +grep 'I am alive!' $res/hello diff --git a/tests/functional/dyn-drv/text-hashed-output.nix b/tests/functional/dyn-drv/text-hashed-output.nix new file mode 100644 index 000000000..99203b518 --- /dev/null +++ b/tests/functional/dyn-drv/text-hashed-output.nix @@ -0,0 +1,33 @@ +with import ./config.nix; + +# A simple content-addressed derivation. +# The derivation can be arbitrarily modified by passing a different `seed`, +# but the output will always be the same +rec { + hello = mkDerivation { + name = "hello"; + buildCommand = '' + set -x + echo "Building a CA derivation" + mkdir -p $out + echo "Hello World" > $out/hello + ''; + }; + producingDrv = mkDerivation { + name = "hello.drv"; + buildCommand = '' + echo "Copying the derivation" + cp ${builtins.unsafeDiscardOutputDependency hello.drvPath} $out + ''; + __contentAddressed = true; + outputHashMode = "text"; + outputHashAlgo = "sha256"; + }; + wrapper = mkDerivation { + name = "use-dynamic-drv-in-non-dynamic-drv"; + buildCommand = '' + echo "Copying the output of the dynamic derivation" + cp -r ${builtins.outputOf producingDrv.outPath "out"} $out + ''; + }; +} diff --git a/tests/functional/dyn-drv/text-hashed-output.sh b/tests/functional/dyn-drv/text-hashed-output.sh new file mode 100644 index 000000000..f3e5aa93b --- /dev/null +++ b/tests/functional/dyn-drv/text-hashed-output.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +source common.sh + +# In the corresponding nix file, we have two derivations: the first, named root, +# is a normal recursive derivation, while the second, named dependent, has the +# new outputHashMode "text". Note that in "dependent", we don't refer to the +# build output of root, but only to the path of the drv file. For this reason, +# we only need to: +# +# - instantiate the root derivation +# - build the dependent derivation +# - check that the path of the output coincides with that of the original derivation + +drv=$(nix-instantiate ./text-hashed-output.nix -A hello) +nix show-derivation "$drv" + +drvProducingDrv=$(nix-instantiate ./text-hashed-output.nix -A producingDrv) +nix show-derivation "$drvProducingDrv" + +out1=$(nix-build ./text-hashed-output.nix -A producingDrv --no-out-link) + +nix path-info $drv --derivation --json | jq +nix path-info $out1 --derivation --json | jq + +test $out1 == $drv diff --git a/tests/functional/eval-store.sh b/tests/functional/eval-store.sh new file mode 100644 index 000000000..8fc859730 --- /dev/null +++ b/tests/functional/eval-store.sh @@ -0,0 +1,30 @@ +source common.sh + +# Using `--eval-store` with the daemon will eventually copy everything +# to the build store, invalidating most of the tests here +needLocalStore "“--eval-store” doesn't achieve much with the daemon" + +eval_store=$TEST_ROOT/eval-store + +clearStore +rm -rf "$eval_store" + +nix build -f dependencies.nix --eval-store "$eval_store" -o "$TEST_ROOT/result" +[[ -e $TEST_ROOT/result/foobar ]] +(! ls $NIX_STORE_DIR/*.drv) +ls $eval_store/nix/store/*.drv + +clearStore +rm -rf "$eval_store" + +nix-instantiate dependencies.nix --eval-store "$eval_store" +(! ls $NIX_STORE_DIR/*.drv) +ls $eval_store/nix/store/*.drv + +clearStore +rm -rf "$eval_store" + +nix-build dependencies.nix --eval-store "$eval_store" -o "$TEST_ROOT/result" +[[ -e $TEST_ROOT/result/foobar ]] +(! ls $NIX_STORE_DIR/*.drv) +ls $eval_store/nix/store/*.drv diff --git a/tests/functional/eval.nix b/tests/functional/eval.nix new file mode 100644 index 000000000..befbd17a9 --- /dev/null +++ b/tests/functional/eval.nix @@ -0,0 +1,5 @@ +{ + int = 123; + str = "foo"; + attr.foo = "bar"; +} diff --git a/tests/functional/eval.sh b/tests/functional/eval.sh new file mode 100644 index 000000000..b81bb1e2c --- /dev/null +++ b/tests/functional/eval.sh @@ -0,0 +1,43 @@ +source common.sh + +clearStore + +testStdinHeredoc=$(nix eval -f - <<EOF +{ + bar = 3 + 1; + foo = 2 + 2; +} +EOF +) +[[ $testStdinHeredoc == '{ bar = 4; foo = 4; }' ]] + +nix eval --expr 'assert 1 + 2 == 3; true' + +[[ $(nix eval int -f "./eval.nix") == 123 ]] +[[ $(nix eval str -f "./eval.nix") == '"foo"' ]] +[[ $(nix eval str --raw -f "./eval.nix") == 'foo' ]] +[[ "$(nix eval attr -f "./eval.nix")" == '{ foo = "bar"; }' ]] +[[ $(nix eval attr --json -f "./eval.nix") == '{"foo":"bar"}' ]] +[[ $(nix eval int -f - < "./eval.nix") == 123 ]] +[[ "$(nix eval --expr '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]] + +# Check if toFile can be utilized during restricted eval +[[ $(nix eval --restrict-eval --expr 'import (builtins.toFile "source" "42")') == 42 ]] + +nix-instantiate --eval -E 'assert 1 + 2 == 3; true' +[[ $(nix-instantiate -A int --eval "./eval.nix") == 123 ]] +[[ $(nix-instantiate -A str --eval "./eval.nix") == '"foo"' ]] +[[ "$(nix-instantiate -A attr --eval "./eval.nix")" == '{ foo = "bar"; }' ]] +[[ $(nix-instantiate -A attr --eval --json "./eval.nix") == '{"foo":"bar"}' ]] +[[ $(nix-instantiate -A int --eval - < "./eval.nix") == 123 ]] +[[ "$(nix-instantiate --eval -E '{"assert"=1;bar=2;}')" == '{ "assert" = 1; bar = 2; }' ]] + +# Check that symlink cycles don't cause a hang. +ln -sfn cycle.nix $TEST_ROOT/cycle.nix +(! nix eval --file $TEST_ROOT/cycle.nix) + +# Check that relative symlinks are resolved correctly. +mkdir -p $TEST_ROOT/xyzzy $TEST_ROOT/foo +ln -sfn ../xyzzy $TEST_ROOT/foo/bar +printf 123 > $TEST_ROOT/xyzzy/default.nix +[[ $(nix eval --impure --expr "import $TEST_ROOT/foo/bar") = 123 ]] diff --git a/tests/functional/experimental-features.sh b/tests/functional/experimental-features.sh new file mode 100644 index 000000000..607bf0a8e --- /dev/null +++ b/tests/functional/experimental-features.sh @@ -0,0 +1,86 @@ +source common.sh + +# Skipping these two for now, because we actually *do* want flags and +# config settings to always show up in the manual, just be marked +# experimental. Will reenable once the manual generation takes advantage +# of the JSON metadata on this. +# +# # Without flakes, flake options should not show up +# # With flakes, flake options should show up +# +# function grep_both_ways { +# nix --experimental-features 'nix-command' "$@" | grepQuietInverse flake +# nix --experimental-features 'nix-command flakes' "$@" | grepQuiet flake +# +# # Also, the order should not matter +# nix "$@" --experimental-features 'nix-command' | grepQuietInverse flake +# nix "$@" --experimental-features 'nix-command flakes' | grepQuiet flake +# } +# +# # Simple case, the configuration effects the running command +# grep_both_ways show-config +# +# # Medium case, the configuration effects --help +# grep_both_ways store gc --help + +# Test settings that are gated on experimental features; the setting is ignored +# with a warning if the experimental feature is not enabled. The order of the +# `setting = value` lines in the configuration should not matter. + +# 'flakes' experimental-feature is disabled before, ignore and warn +NIX_CONFIG=' + experimental-features = nix-command + accept-flake-config = true +' nix show-config accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr +grepQuiet "false" $TEST_ROOT/stdout +grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr + +# 'flakes' experimental-feature is disabled after, ignore and warn +NIX_CONFIG=' + accept-flake-config = true + experimental-features = nix-command +' nix show-config accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr +grepQuiet "false" $TEST_ROOT/stdout +grepQuiet "Ignoring setting 'accept-flake-config' because experimental feature 'flakes' is not enabled" $TEST_ROOT/stderr + +# 'flakes' experimental-feature is enabled before, process +NIX_CONFIG=' + experimental-features = nix-command flakes + accept-flake-config = true +' nix show-config accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr +grepQuiet "true" $TEST_ROOT/stdout +grepQuietInverse "Ignoring setting 'accept-flake-config'" $TEST_ROOT/stderr + +# 'flakes' experimental-feature is enabled after, process +NIX_CONFIG=' + accept-flake-config = true + experimental-features = nix-command flakes +' nix show-config accept-flake-config 1>$TEST_ROOT/stdout 2>$TEST_ROOT/stderr +grepQuiet "true" $TEST_ROOT/stdout +grepQuietInverse "Ignoring setting 'accept-flake-config'" $TEST_ROOT/stderr + +function exit_code_both_ways { + expect 1 nix --experimental-features 'nix-command' "$@" 1>/dev/null + nix --experimental-features 'nix-command flakes' "$@" 1>/dev/null + + # Also, the order should not matter + expect 1 nix "$@" --experimental-features 'nix-command' 1>/dev/null + nix "$@" --experimental-features 'nix-command flakes' 1>/dev/null +} + +exit_code_both_ways show-config --flake-registry 'https://no' + +# Double check these are stable +nix --experimental-features '' --help 1>/dev/null +nix --experimental-features '' doctor --help 1>/dev/null +nix --experimental-features '' repl --help 1>/dev/null +nix --experimental-features '' upgrade-nix --help 1>/dev/null + +# These 3 arguments are currently given to all commands, which is wrong (as not +# all care). To deal with fixing later, we simply make them require the +# nix-command experimental features --- it so happens that the commands we wish +# stabilizing to do not need them anyways. +for arg in '--print-build-logs' '--offline' '--refresh'; do + nix --experimental-features 'nix-command' "$arg" --help 1>/dev/null + expect 1 nix --experimental-features '' "$arg" --help 1>/dev/null +done diff --git a/tests/functional/export-graph.nix b/tests/functional/export-graph.nix new file mode 100644 index 000000000..64fe36bd1 --- /dev/null +++ b/tests/functional/export-graph.nix @@ -0,0 +1,29 @@ +with import ./config.nix; + +rec { + + printRefs = + '' + echo $exportReferencesGraph + while read path; do + read drv + read nrRefs + echo "$path has $nrRefs references" + echo "$path" >> $out + for ((n = 0; n < $nrRefs; n++)); do read ref; echo "ref $ref"; test -e "$ref"; done + done < refs + ''; + + foo."bar.runtimeGraph" = mkDerivation { + name = "dependencies"; + builder = builtins.toFile "build-graph-builder" "${printRefs}"; + exportReferencesGraph = ["refs" (import ./dependencies.nix {})]; + }; + + foo."bar.buildGraph" = mkDerivation { + name = "dependencies"; + builder = builtins.toFile "build-graph-builder" "${printRefs}"; + exportReferencesGraph = ["refs" (import ./dependencies.nix {}).drvPath]; + }; + +} diff --git a/tests/functional/export-graph.sh b/tests/functional/export-graph.sh new file mode 100644 index 000000000..1f6232a40 --- /dev/null +++ b/tests/functional/export-graph.sh @@ -0,0 +1,30 @@ +source common.sh + +clearStore +clearProfiles + +checkRef() { + nix-store -q --references $TEST_ROOT/result | grepQuiet "$1"'$' || fail "missing reference $1" +} + +# Test the export of the runtime dependency graph. + +outPath=$(nix-build ./export-graph.nix -A 'foo."bar.runtimeGraph"' -o $TEST_ROOT/result) + +test $(nix-store -q --references $TEST_ROOT/result | wc -l) = 3 || fail "bad nr of references" + +checkRef input-2 +for i in $(cat $outPath); do checkRef $i; done + +# Test the export of the build-time dependency graph. + +nix-store --gc # should force rebuild of input-1 + +outPath=$(nix-build ./export-graph.nix -A 'foo."bar.buildGraph"' -o $TEST_ROOT/result) + +checkRef input-1 +checkRef input-1.drv +checkRef input-2 +checkRef input-2.drv + +for i in $(cat $outPath); do checkRef $i; done diff --git a/tests/functional/export.sh b/tests/functional/export.sh new file mode 100644 index 000000000..2238539bc --- /dev/null +++ b/tests/functional/export.sh @@ -0,0 +1,36 @@ +source common.sh + +clearStore + +outPath=$(nix-build dependencies.nix --no-out-link) + +nix-store --export $outPath > $TEST_ROOT/exp + +nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all + +if nix-store --export $outPath >/dev/full ; then + echo "exporting to a bad file descriptor should fail" + exit 1 +fi + + +clearStore + +if nix-store --import < $TEST_ROOT/exp; then + echo "importing a non-closure should fail" + exit 1 +fi + + +clearStore + +nix-store --import < $TEST_ROOT/exp_all + +nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all2 + + +clearStore + +# Regression test: the derivers in exp_all2 are empty, which shouldn't +# cause a failure. +nix-store --import < $TEST_ROOT/exp_all2 diff --git a/tests/functional/failing.nix b/tests/functional/failing.nix new file mode 100644 index 000000000..2a0350d4d --- /dev/null +++ b/tests/functional/failing.nix @@ -0,0 +1,22 @@ +{ busybox }: +with import ./config.nix; +let + + mkDerivation = args: + derivation ({ + inherit system; + builder = busybox; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + } // removeAttrs args ["builder" "meta"]) + // { meta = args.meta or {}; }; +in +{ + + failing = mkDerivation { + name = "failing"; + buildCommand = '' + echo foo > bar + exit 1 + ''; + }; +} diff --git a/tests/functional/fetchClosure.sh b/tests/functional/fetchClosure.sh new file mode 100644 index 000000000..a02d1ce7a --- /dev/null +++ b/tests/functional/fetchClosure.sh @@ -0,0 +1,150 @@ +source common.sh + +enableFeatures "fetch-closure" + +clearStore +clearCacheCache + +# Old daemons don't properly zero out the self-references when +# calculating the CA hashes, so this breaks `nix store +# make-content-addressed` which expects the client and the daemon to +# compute the same hash +requireDaemonNewerThan "2.16.0pre20230524" + +# Initialize binary cache. +nonCaPath=$(nix build --json --file ./dependencies.nix --no-link | jq -r .[].outputs.out) +caPath=$(nix store make-content-addressed --json $nonCaPath | jq -r '.rewrites | map(.) | .[]') +nix copy --to file://$cacheDir $nonCaPath + +# Test basic fetchClosure rewriting from non-CA to CA. +clearStore + +[ ! -e $nonCaPath ] +[ ! -e $caPath ] + +[[ $(nix eval -v --raw --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $nonCaPath; + toPath = $caPath; + } +") = $caPath ]] + +[ ! -e $nonCaPath ] +[ -e $caPath ] + +clearStore + +# The daemon will reject input addressed paths unless configured to trust the +# cache key or the user. This behavior should be covered by another test, so we +# skip this part when using the daemon. +if [[ "$NIX_REMOTE" != "daemon" ]]; then + + # If we want to return a non-CA path, we have to be explicit about it. + expectStderr 1 nix eval --raw --no-require-sigs --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $nonCaPath; + } + " | grepQuiet -E "The .fromPath. value .* is input-addressed, but .inputAddressed. is set to .false." + + # TODO: Should the closure be rejected, despite single user mode? + # [ ! -e $nonCaPath ] + + [ ! -e $caPath ] + + # We can use non-CA paths when we ask explicitly. + [[ $(nix eval --raw --no-require-sigs --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $nonCaPath; + inputAddressed = true; + } + ") = $nonCaPath ]] + + [ -e $nonCaPath ] + [ ! -e $caPath ] + + +fi + +[ ! -e $caPath ] + +# 'toPath' set to empty string should fail but print the expected path. +expectStderr 1 nix eval -v --json --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $nonCaPath; + toPath = \"\"; + } +" | grep "error: rewriting.*$nonCaPath.*yielded.*$caPath" + +# If fromPath is CA, then toPath isn't needed. +nix copy --to file://$cacheDir $caPath + +clearStore + +[ ! -e $caPath ] + +[[ $(nix eval -v --raw --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $caPath; + } +") = $caPath ]] + +[ -e $caPath ] + +# Check that URL query parameters aren't allowed. +clearStore +narCache=$TEST_ROOT/nar-cache +rm -rf $narCache +(! nix eval -v --raw --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir?local-nar-cache=$narCache\"; + fromPath = $caPath; + } +") +(! [ -e $narCache ]) + +# If toPath is specified but wrong, we check it (only) when the path is missing. +clearStore + +badPath=$(echo $caPath | sed -e 's!/store/................................-!/store/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-!') + +[ ! -e $badPath ] + +expectStderr 1 nix eval -v --raw --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $nonCaPath; + toPath = $badPath; + } +" | grep "error: rewriting.*$nonCaPath.*yielded.*$caPath.*while.*$badPath.*was expected" + +[ ! -e $badPath ] + +# We only check it when missing, as a performance optimization similar to what we do for fixed output derivations. So if it's already there, we don't check it. +# It would be nice for this to fail, but checking it would be too(?) slow. +[ -e $caPath ] + +[[ $(nix eval -v --raw --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $badPath; + toPath = $caPath; + } +") = $caPath ]] + + +# However, if the output address is unexpected, we can report it + + +expectStderr 1 nix eval -v --raw --expr " + builtins.fetchClosure { + fromStore = \"file://$cacheDir\"; + fromPath = $caPath; + inputAddressed = true; + } +" | grepQuiet 'error.*The store object referred to by.*fromPath.* at .* is not input-addressed, but .*inputAddressed.* is set to .*true.*' + diff --git a/tests/functional/fetchGit.sh b/tests/functional/fetchGit.sh new file mode 100644 index 000000000..418b4f63f --- /dev/null +++ b/tests/functional/fetchGit.sh @@ -0,0 +1,256 @@ +source common.sh + +requireGit + +clearStore + +# Intentionally not in a canonical form +# See https://github.com/NixOS/nix/issues/6195 +repo=$TEST_ROOT/./git + +export _NIX_FORCE_HTTP=1 + +rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix $TEST_ROOT/worktree $TEST_ROOT/shallow $TEST_ROOT/minimal + +git init $repo +git -C $repo config user.email "foobar@example.com" +git -C $repo config user.name "Foobar" + +echo utrecht > $repo/hello +touch $repo/.gitignore +git -C $repo add hello .gitignore +git -C $repo commit -m 'Bla1' +rev1=$(git -C $repo rev-parse HEAD) +git -C $repo tag -a tag1 -m tag1 + +echo world > $repo/hello +git -C $repo commit -m 'Bla2' -a +git -C $repo worktree add $TEST_ROOT/worktree +echo hello >> $TEST_ROOT/worktree/hello +rev2=$(git -C $repo rev-parse HEAD) +git -C $repo tag -a tag2 -m tag2 + +# Fetch a worktree +unset _NIX_FORCE_HTTP +path0=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$TEST_ROOT/worktree).outPath") +path0_=$(nix eval --impure --raw --expr "(builtins.fetchTree { type = \"git\"; url = file://$TEST_ROOT/worktree; }).outPath") +[[ $path0 = $path0_ ]] +export _NIX_FORCE_HTTP=1 +[[ $(tail -n 1 $path0/hello) = "hello" ]] + +# Fetch the default branch. +path=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath") +[[ $(cat $path/hello) = world ]] + +# Fetch a rev from another branch +git -C $repo checkout -b devtest +echo "different file" >> $TEST_ROOT/git/differentbranch +git -C $repo add differentbranch +git -C $repo commit -m 'Test2' +git -C $repo checkout master +devrev=$(git -C $repo rev-parse devtest) +out=$(nix eval --impure --raw --expr "builtins.fetchGit { url = file://$repo; rev = \"$devrev\"; }" 2>&1) || status=$? +[[ $status == 1 ]] +[[ $out =~ 'Cannot find Git revision' ]] + +[[ $(nix eval --raw --expr "builtins.readFile (builtins.fetchGit { url = file://$repo; rev = \"$devrev\"; allRefs = true; } + \"/differentbranch\")") = 'different file' ]] + +# In pure eval mode, fetchGit without a revision should fail. +[[ $(nix eval --impure --raw --expr "builtins.readFile (fetchGit file://$repo + \"/hello\")") = world ]] +(! nix eval --raw --expr "builtins.readFile (fetchGit file://$repo + \"/hello\")") + +# Fetch using an explicit revision hash. +path2=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path2 ]] + +# In pure eval mode, fetchGit with a revision should succeed. +[[ $(nix eval --raw --expr "builtins.readFile (fetchGit { url = file://$repo; rev = \"$rev2\"; } + \"/hello\")") = world ]] + +# Fetch again. This should be cached. +mv $repo ${repo}-tmp +path2=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath") +[[ $path = $path2 ]] + +[[ $(nix eval --impure --expr "(builtins.fetchGit file://$repo).revCount") = 2 ]] +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).rev") = $rev2 ]] +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).shortRev") = ${rev2:0:7} ]] + +# Fetching with a explicit hash should succeed. +path2=$(nix eval --refresh --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path2 ]] + +path2=$(nix eval --refresh --raw --expr "(builtins.fetchGit { url = file://$repo; rev = \"$rev1\"; }).outPath") +[[ $(cat $path2/hello) = utrecht ]] + +mv ${repo}-tmp $repo + +# Using a clean working tree should produce the same result. +path2=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath") +[[ $path = $path2 ]] + +# Using an unclean tree should yield the tracked but uncommitted changes. +mkdir $repo/dir1 $repo/dir2 +echo foo > $repo/dir1/foo +echo bar > $repo/bar +echo bar > $repo/dir2/bar +git -C $repo add dir1/foo +git -C $repo rm hello + +unset _NIX_FORCE_HTTP +path2=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath") +[ ! -e $path2/hello ] +[ ! -e $path2/bar ] +[ ! -e $path2/dir2/bar ] +[ ! -e $path2/.git ] +[[ $(cat $path2/dir1/foo) = foo ]] + +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]] +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).dirtyRev") = "${rev2}-dirty" ]] +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).dirtyShortRev") = "${rev2:0:7}-dirty" ]] + +# ... unless we're using an explicit ref or rev. +path3=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = $repo; ref = \"master\"; }).outPath") +[[ $path = $path3 ]] + +path3=$(nix eval --raw --expr "(builtins.fetchGit { url = $repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path3 ]] + +# Committing should not affect the store path. +git -C $repo commit -m 'Bla3' -a + +path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchGit file://$repo).outPath") +[[ $path2 = $path4 ]] + +[[ $(nix eval --impure --expr "builtins.hasAttr \"rev\" (builtins.fetchGit $repo)") == "true" ]] +[[ $(nix eval --impure --expr "builtins.hasAttr \"dirtyRev\" (builtins.fetchGit $repo)") == "false" ]] +[[ $(nix eval --impure --expr "builtins.hasAttr \"dirtyShortRev\" (builtins.fetchGit $repo)") == "false" ]] + +status=0 +nix eval --impure --raw --expr "(builtins.fetchGit { url = $repo; rev = \"$rev2\"; narHash = \"sha256-B5yIPHhEm0eysJKEsO7nqxprh9vcblFxpJG11gXJus1=\"; }).outPath" || status=$? +[[ "$status" = "102" ]] + +path5=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = $repo; rev = \"$rev2\"; narHash = \"sha256-Hr8g6AqANb3xqX28eu1XnjK/3ab8Gv6TJSnkb1LezG9=\"; }).outPath") +[[ $path = $path5 ]] + +# tarball-ttl should be ignored if we specify a rev +echo delft > $repo/hello +git -C $repo add hello +git -C $repo commit -m 'Bla4' +rev3=$(git -C $repo rev-parse HEAD) +nix eval --tarball-ttl 3600 --expr "builtins.fetchGit { url = $repo; rev = \"$rev3\"; }" >/dev/null + +# Update 'path' to reflect latest master +path=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath") + +# Check behavior when non-master branch is used +git -C $repo checkout $rev2 -b dev +echo dev > $repo/hello + +# File URI uses dirty tree unless specified otherwise +path2=$(nix eval --impure --raw --expr "(builtins.fetchGit file://$repo).outPath") +[ $(cat $path2/hello) = dev ] + +# Using local path with branch other than 'master' should work when clean or dirty +path3=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath") +# (check dirty-tree handling was used) +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]] +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).shortRev") = 0000000 ]] +# Making a dirty tree clean again and fetching it should +# record correct revision information. See: #4140 +echo world > $repo/hello +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit $repo).rev") = $rev2 ]] + +# Committing shouldn't change store path, or switch to using 'master' +echo dev > $repo/hello +git -C $repo commit -m 'Bla5' -a +path4=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath") +[[ $(cat $path4/hello) = dev ]] +[[ $path3 = $path4 ]] + +# Using remote path with branch other than 'master' should fetch the HEAD revision. +# (--tarball-ttl 0 to prevent using the cached repo above) +export _NIX_FORCE_HTTP=1 +path4=$(nix eval --tarball-ttl 0 --impure --raw --expr "(builtins.fetchGit $repo).outPath") +[[ $(cat $path4/hello) = dev ]] +[[ $path3 = $path4 ]] +unset _NIX_FORCE_HTTP + +# Confirm same as 'dev' branch +path5=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath") +[[ $path3 = $path5 ]] + + +# Nuke the cache +rm -rf $TEST_HOME/.cache/nix + +# Try again, but without 'git' on PATH. This should fail. +NIX=$(command -v nix) +(! PATH= $NIX eval --impure --raw --expr "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath" ) + +# Try again, with 'git' available. This should work. +path5=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath") +[[ $path3 = $path5 ]] + +# Fetching from a repo with only a specific revision and no branches should +# not fall back to copying files and record correct revision information. See: #5302 +mkdir $TEST_ROOT/minimal +git -C $TEST_ROOT/minimal init +git -C $TEST_ROOT/minimal fetch $repo $rev2 +git -C $TEST_ROOT/minimal checkout $rev2 +[[ $(nix eval --impure --raw --expr "(builtins.fetchGit { url = $TEST_ROOT/minimal; }).rev") = $rev2 ]] + +# Fetching a shallow repo shouldn't work by default, because we can't +# return a revCount. +git clone --depth 1 file://$repo $TEST_ROOT/shallow +(! nix eval --impure --raw --expr "(builtins.fetchGit { url = $TEST_ROOT/shallow; ref = \"dev\"; }).outPath") + +# But you can request a shallow clone, which won't return a revCount. +path6=$(nix eval --impure --raw --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow\"; ref = \"dev\"; shallow = true; }).outPath") +[[ $path3 = $path6 ]] +[[ $(nix eval --impure --expr "(builtins.fetchTree { type = \"git\"; url = \"file://$TEST_ROOT/shallow\"; ref = \"dev\"; shallow = true; }).revCount or 123") == 123 ]] + +# Explicit ref = "HEAD" should work, and produce the same outPath as without ref +path7=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; }).outPath") +path8=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; }).outPath") +[[ $path7 = $path8 ]] + +# ref = "HEAD" should fetch the HEAD revision +rev4=$(git -C $repo rev-parse HEAD) +rev4_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; }).rev") +[[ $rev4 = $rev4_nix ]] + +# The name argument should be handled +path9=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"HEAD\"; name = \"foo\"; }).outPath") +[[ $path9 =~ -foo$ ]] + +# Specifying a ref without a rev shouldn't pick a cached rev for a different ref +export _NIX_FORCE_HTTP=1 +rev_tag1_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"refs/tags/tag1\"; }).rev") +rev_tag1=$(git -C $repo rev-parse refs/tags/tag1) +[[ $rev_tag1_nix = $rev_tag1 ]] +rev_tag2_nix=$(nix eval --impure --raw --expr "(builtins.fetchGit { url = \"file://$repo\"; ref = \"refs/tags/tag2\"; }).rev") +rev_tag2=$(git -C $repo rev-parse refs/tags/tag2) +[[ $rev_tag2_nix = $rev_tag2 ]] +unset _NIX_FORCE_HTTP + +# should fail if there is no repo +rm -rf $repo/.git +(! nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath") + +# should succeed for a repo without commits +git init $repo +path10=$(nix eval --impure --raw --expr "(builtins.fetchGit \"file://$repo\").outPath") + +# should succeed for a path with a space +# regression test for #7707 +repo="$TEST_ROOT/a b" +git init "$repo" +git -C "$repo" config user.email "foobar@example.com" +git -C "$repo" config user.name "Foobar" + +echo utrecht > "$repo/hello" +touch "$repo/.gitignore" +git -C "$repo" add hello .gitignore +git -C "$repo" commit -m 'Bla1' +cd "$repo" +path11=$(nix eval --impure --raw --expr "(builtins.fetchGit ./.).outPath") diff --git a/tests/functional/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh new file mode 100644 index 000000000..d643fea04 --- /dev/null +++ b/tests/functional/fetchGitRefs.sh @@ -0,0 +1,108 @@ +source common.sh + +requireGit + +clearStore + +repo="$TEST_ROOT/git" + +rm -rf "$repo" "${repo}-tmp" "$TEST_HOME/.cache/nix" + +git init "$repo" +git -C "$repo" config user.email "foobar@example.com" +git -C "$repo" config user.name "Foobar" + +echo utrecht > "$repo"/hello +git -C "$repo" add hello +git -C "$repo" commit -m 'Bla1' + +path=$(nix eval --raw --impure --expr "(builtins.fetchGit { url = $repo; ref = \"master\"; }).outPath") + +# Test various combinations of ref names +# (taken from the git project) + +# git help check-ref-format +# Git imposes the following rules on how references are named: +# +# 1. They can include slash / for hierarchical (directory) grouping, but no slash-separated component can begin with a dot . or end with the sequence .lock. +# 2. They must contain at least one /. This enforces the presence of a category like heads/, tags/ etc. but the actual names are not restricted. If the --allow-onelevel option is used, this rule is waived. +# 3. They cannot have two consecutive dots .. anywhere. +# 4. They cannot have ASCII control characters (i.e. bytes whose values are lower than \040, or \177 DEL), space, tilde ~, caret ^, or colon : anywhere. +# 5. They cannot have question-mark ?, asterisk *, or open bracket [ anywhere. See the --refspec-pattern option below for an exception to this rule. +# 6. They cannot begin or end with a slash / or contain multiple consecutive slashes (see the --normalize option below for an exception to this rule) +# 7. They cannot end with a dot .. +# 8. They cannot contain a sequence @{. +# 9. They cannot be the single character @. +# 10. They cannot contain a \. + +valid_ref() { + { set +x; printf >&2 '\n>>>>>>>>>> valid_ref %s\b <<<<<<<<<<\n' $(printf %s "$1" | sed -n -e l); set -x; } + git check-ref-format --branch "$1" >/dev/null + git -C "$repo" branch "$1" master >/dev/null + path1=$(nix eval --raw --impure --expr "(builtins.fetchGit { url = $repo; ref = ''$1''; }).outPath") + [[ $path1 = $path ]] + git -C "$repo" branch -D "$1" >/dev/null +} + +invalid_ref() { + { set +x; printf >&2 '\n>>>>>>>>>> invalid_ref %s\b <<<<<<<<<<\n' $(printf %s "$1" | sed -n -e l); set -x; } + # special case for a sole @: + # --branch @ will try to interpret @ as a branch reference and not fail. Thus we need --allow-onelevel + if [ "$1" = "@" ]; then + (! git check-ref-format --allow-onelevel "$1" >/dev/null 2>&1) + else + (! git check-ref-format --branch "$1" >/dev/null 2>&1) + fi + expect 1 nix --debug eval --raw --impure --expr "(builtins.fetchGit { url = $repo; ref = ''$1''; }).outPath" 2>&1 | grep 'invalid Git branch/tag name' >/dev/null +} + + +valid_ref 'foox' +valid_ref '1337' +valid_ref 'foo.baz' +valid_ref 'foo/bar/baz' +valid_ref 'foo./bar' +valid_ref 'heads/foo@bar' +valid_ref "$(printf 'heads/fu\303\237')" +valid_ref 'foo-bar-baz' +valid_ref '$1' +valid_ref 'foo.locke' + +invalid_ref 'refs///heads/foo' +invalid_ref 'heads/foo/' +invalid_ref '///heads/foo' +invalid_ref '.foo' +invalid_ref './foo' +invalid_ref './foo/bar' +invalid_ref 'foo/./bar' +invalid_ref 'foo/bar/.' +invalid_ref 'foo bar' +invalid_ref 'foo?bar' +invalid_ref 'foo^bar' +invalid_ref 'foo~bar' +invalid_ref 'foo:bar' +invalid_ref 'foo[bar' +invalid_ref 'foo/bar/.' +invalid_ref '.refs/foo' +invalid_ref 'refs/heads/foo.' +invalid_ref 'heads/foo..bar' +invalid_ref 'heads/foo?bar' +invalid_ref 'heads/foo.lock' +invalid_ref 'heads///foo.lock' +invalid_ref 'foo.lock/bar' +invalid_ref 'foo.lock///bar' +invalid_ref 'heads/v@{ation' +invalid_ref 'heads/foo\.ar' # should fail due to \ +invalid_ref 'heads/foo\bar' # should fail due to \ +invalid_ref "$(printf 'heads/foo\t')" # should fail because it has a TAB +invalid_ref "$(printf 'heads/foo\177')" +invalid_ref '@' + +invalid_ref 'foo/*' +invalid_ref '*/foo' +invalid_ref 'foo/*/bar' +invalid_ref '*' +invalid_ref 'foo/*/*' +invalid_ref '*/foo/*' +invalid_ref '/foo' +invalid_ref '' diff --git a/tests/functional/fetchGitSubmodules.sh b/tests/functional/fetchGitSubmodules.sh new file mode 100644 index 000000000..df81232e5 --- /dev/null +++ b/tests/functional/fetchGitSubmodules.sh @@ -0,0 +1,128 @@ +source common.sh + +set -u + +requireGit + +clearStore + +rootRepo=$TEST_ROOT/gitSubmodulesRoot +subRepo=$TEST_ROOT/gitSubmodulesSub + +rm -rf ${rootRepo} ${subRepo} $TEST_HOME/.cache/nix + +# Submodules can't be fetched locally by default, which can cause +# information leakage vulnerabilities, but for these tests our +# submodule is intentionally local and it's all trusted, so we +# disable this restriction. Setting it per repo is not sufficient, as +# the repo-local config does not apply to the commands run from +# outside the repos by Nix. +export XDG_CONFIG_HOME=$TEST_HOME/.config +git config --global protocol.file.allow always + +initGitRepo() { + git init $1 + git -C $1 config user.email "foobar@example.com" + git -C $1 config user.name "Foobar" +} + +addGitContent() { + echo "lorem ipsum" > $1/content + git -C $1 add content + git -C $1 commit -m "Initial commit" +} + +initGitRepo $subRepo +addGitContent $subRepo + +initGitRepo $rootRepo + +git -C $rootRepo submodule init +git -C $rootRepo submodule add $subRepo sub +git -C $rootRepo add sub +git -C $rootRepo commit -m "Add submodule" + +rev=$(git -C $rootRepo rev-parse HEAD) + +r1=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; }).outPath") +r2=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = false; }).outPath") +r3=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = true; }).outPath") + +[[ $r1 == $r2 ]] +[[ $r2 != $r3 ]] + +r4=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; ref = \"master\"; rev = \"$rev\"; }).outPath") +r5=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; ref = \"master\"; rev = \"$rev\"; submodules = false; }).outPath") +r6=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; ref = \"master\"; rev = \"$rev\"; submodules = true; }).outPath") +r7=$(nix eval --raw --expr "(builtins.fetchGit { url = $rootRepo; ref = \"master\"; rev = \"$rev\"; submodules = true; }).outPath") +r8=$(nix eval --raw --expr "(builtins.fetchGit { url = $rootRepo; rev = \"$rev\"; submodules = true; }).outPath") + +[[ $r1 == $r4 ]] +[[ $r4 == $r5 ]] +[[ $r3 == $r6 ]] +[[ $r6 == $r7 ]] +[[ $r7 == $r8 ]] + +have_submodules=$(nix eval --expr "(builtins.fetchGit { url = $rootRepo; rev = \"$rev\"; }).submodules") +[[ $have_submodules == false ]] + +have_submodules=$(nix eval --expr "(builtins.fetchGit { url = $rootRepo; rev = \"$rev\"; submodules = false; }).submodules") +[[ $have_submodules == false ]] + +have_submodules=$(nix eval --expr "(builtins.fetchGit { url = $rootRepo; rev = \"$rev\"; submodules = true; }).submodules") +[[ $have_submodules == true ]] + +pathWithoutSubmodules=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; }).outPath") +pathWithSubmodules=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = true; }).outPath") +pathWithSubmodulesAgain=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev\"; submodules = true; }).outPath") +pathWithSubmodulesAgainWithRef=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; ref = \"master\"; rev = \"$rev\"; submodules = true; }).outPath") + +# The resulting store path cannot be the same. +[[ $pathWithoutSubmodules != $pathWithSubmodules ]] + +# Checking out the same repo with submodules returns in the same store path. +[[ $pathWithSubmodules == $pathWithSubmodulesAgain ]] + +# Checking out the same repo with submodules returns in the same store path. +[[ $pathWithSubmodulesAgain == $pathWithSubmodulesAgainWithRef ]] + +# The submodules flag is actually honored. +[[ ! -e $pathWithoutSubmodules/sub/content ]] +[[ -e $pathWithSubmodules/sub/content ]] + +[[ -e $pathWithSubmodulesAgainWithRef/sub/content ]] + +# No .git directory or submodule reference files must be left +test "$(find "$pathWithSubmodules" -name .git)" = "" + +# Git repos without submodules can be fetched with submodules = true. +subRev=$(git -C $subRepo rev-parse HEAD) +noSubmoduleRepoBaseline=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$subRepo; rev = \"$subRev\"; }).outPath") +noSubmoduleRepo=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$subRepo; rev = \"$subRev\"; submodules = true; }).outPath") + +[[ $noSubmoduleRepoBaseline == $noSubmoduleRepo ]] + +# Test relative submodule URLs. +rm $TEST_HOME/.cache/nix/fetcher-cache* +rm -rf $rootRepo/.git $rootRepo/.gitmodules $rootRepo/sub +initGitRepo $rootRepo +git -C $rootRepo submodule add ../gitSubmodulesSub sub +git -C $rootRepo commit -m "Add submodule" +rev2=$(git -C $rootRepo rev-parse HEAD) +pathWithRelative=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$rootRepo; rev = \"$rev2\"; submodules = true; }).outPath") +diff -r -x .gitmodules $pathWithSubmodules $pathWithRelative + +# Test clones that have an upstream with relative submodule URLs. +rm $TEST_HOME/.cache/nix/fetcher-cache* +cloneRepo=$TEST_ROOT/a/b/gitSubmodulesClone # NB /a/b to make the relative path not work relative to $cloneRepo +git clone $rootRepo $cloneRepo +pathIndirect=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$cloneRepo; rev = \"$rev2\"; submodules = true; }).outPath") +[[ $pathIndirect = $pathWithRelative ]] + +# Test that if the clone has the submodule already, we're not fetching +# it again. +git -C $cloneRepo submodule update --init +rm $TEST_HOME/.cache/nix/fetcher-cache* +rm -rf $subRepo +pathSubmoduleGone=$(nix eval --raw --expr "(builtins.fetchGit { url = file://$cloneRepo; rev = \"$rev2\"; submodules = true; }).outPath") +[[ $pathSubmoduleGone = $pathWithRelative ]] diff --git a/tests/functional/fetchMercurial.sh b/tests/functional/fetchMercurial.sh new file mode 100644 index 000000000..e6f8525c6 --- /dev/null +++ b/tests/functional/fetchMercurial.sh @@ -0,0 +1,106 @@ +source common.sh + +[[ $(type -p hq) ]] || skipTest "Mercurial not installed" + +clearStore + +# Intentionally not in a canonical form +# See https://github.com/NixOS/nix/issues/6195 +repo=$TEST_ROOT/./hg + +rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix + +hg init $repo +echo '[ui]' >> $repo/.hg/hgrc +echo 'username = Foobar <foobar@example.org>' >> $repo/.hg/hgrc + +# Set ui.tweakdefaults to ensure HGPLAIN is being set. +echo 'tweakdefaults = True' >> $repo/.hg/hgrc + +echo utrecht > $repo/hello +touch $repo/.hgignore +hg add --cwd $repo hello .hgignore +hg commit --cwd $repo -m 'Bla1' +rev1=$(hg log --cwd $repo -r tip --template '{node}') + +echo world > $repo/hello +hg commit --cwd $repo -m 'Bla2' +rev2=$(hg log --cwd $repo -r tip --template '{node}') + +# Fetch an unclean branch. +echo unclean > $repo/hello +path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath") +[[ $(cat $path/hello) = unclean ]] +hg revert --cwd $repo --all + +# Fetch the default branch. +path=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath") +[[ $(cat $path/hello) = world ]] + +# In pure eval mode, fetchGit without a revision should fail. +[[ $(nix eval --impure --raw --expr "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))") = world ]] +(! nix eval --raw --expr "builtins.readFile (fetchMercurial file://$repo + \"/hello\")") + +# Fetch using an explicit revision hash. +path2=$(nix eval --impure --raw --expr "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path2 ]] + +# In pure eval mode, fetchGit with a revision should succeed. +[[ $(nix eval --raw --expr "builtins.readFile (fetchMercurial { url = file://$repo; rev = \"$rev2\"; } + \"/hello\")") = world ]] + +# Fetch again. This should be cached. +mv $repo ${repo}-tmp +path2=$(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).outPath") +[[ $path = $path2 ]] + +[[ $(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).branch") = default ]] +[[ $(nix eval --impure --expr "(builtins.fetchMercurial file://$repo).revCount") = 1 ]] +[[ $(nix eval --impure --raw --expr "(builtins.fetchMercurial file://$repo).rev") = $rev2 ]] + +# But with TTL 0, it should fail. +(! nix eval --impure --refresh --expr "builtins.fetchMercurial file://$repo") + +# Fetching with a explicit hash should succeed. +path2=$(nix eval --refresh --raw --expr "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath") +[[ $path = $path2 ]] + +path2=$(nix eval --refresh --raw --expr "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev1\"; }).outPath") +[[ $(cat $path2/hello) = utrecht ]] + +mv ${repo}-tmp $repo + +# Using a clean working tree should produce the same result. +path2=$(nix eval --impure --raw --expr "(builtins.fetchMercurial $repo).outPath") +[[ $path = $path2 ]] + +# Using an unclean tree should yield the tracked but uncommitted changes. +mkdir $repo/dir1 $repo/dir2 +echo foo > $repo/dir1/foo +echo bar > $repo/bar +echo bar > $repo/dir2/bar +hg add --cwd $repo dir1/foo +hg rm --cwd $repo hello + +path2=$(nix eval --impure --raw --expr "(builtins.fetchMercurial $repo).outPath") +[ ! -e $path2/hello ] +[ ! -e $path2/bar ] +[ ! -e $path2/dir2/bar ] +[ ! -e $path2/.hg ] +[[ $(cat $path2/dir1/foo) = foo ]] + +[[ $(nix eval --impure --raw --expr "(builtins.fetchMercurial $repo).rev") = 0000000000000000000000000000000000000000 ]] + +# ... unless we're using an explicit ref. +path3=$(nix eval --impure --raw --expr "(builtins.fetchMercurial { url = $repo; rev = \"default\"; }).outPath") +[[ $path = $path3 ]] + +# Committing should not affect the store path. +hg commit --cwd $repo -m 'Bla3' + +path4=$(nix eval --impure --refresh --raw --expr "(builtins.fetchMercurial file://$repo).outPath") +[[ $path2 = $path4 ]] + +echo paris > $repo/hello +# Passing a `name` argument should be reflected in the output path +path5=$(nix eval -vvvvv --impure --refresh --raw --expr "(builtins.fetchMercurial { url = \"file://$repo\"; name = \"foo\"; } ).outPath") +[[ $path5 =~ -foo$ ]] diff --git a/tests/functional/fetchPath.sh b/tests/functional/fetchPath.sh new file mode 100644 index 000000000..29be38ce2 --- /dev/null +++ b/tests/functional/fetchPath.sh @@ -0,0 +1,6 @@ +source common.sh + +touch $TEST_ROOT/foo -t 202211111111 +# We only check whether 2022-11-1* **:**:** is the last modified date since +# `lastModified` is transformed into UTC in `builtins.fetchTarball`. +[[ "$(nix eval --impure --raw --expr "(builtins.fetchTree \"path://$TEST_ROOT/foo\").lastModifiedDate")" =~ 2022111.* ]] diff --git a/tests/functional/fetchTree-file.sh b/tests/functional/fetchTree-file.sh new file mode 100644 index 000000000..6395c133d --- /dev/null +++ b/tests/functional/fetchTree-file.sh @@ -0,0 +1,117 @@ +source common.sh + +clearStore + +cd "$TEST_ROOT" + +test_fetch_file () { + echo foo > test_input + + input_hash="$(nix hash path test_input)" + + nix eval --impure --file - <<EOF + let + tree = builtins.fetchTree { type = "file"; url = "file://$PWD/test_input"; }; + in + assert (tree.narHash == "$input_hash"); + tree +EOF +} + +# Make sure that `http(s)` and `file` flake inputs are properly extracted when +# they should be, and treated as opaque files when they should be +test_file_flake_input () { + rm -fr "$TEST_ROOT/testFlake"; + mkdir "$TEST_ROOT/testFlake"; + pushd testFlake + + mkdir inputs + echo foo > inputs/test_input_file + echo '{ outputs = { self }: { }; }' > inputs/flake.nix + tar cfa test_input.tar.gz inputs + cp test_input.tar.gz test_input_no_ext + input_tarball_hash="$(nix hash path test_input.tar.gz)" + input_directory_hash="$(nix hash path inputs)" + + cat <<EOF > flake.nix + { + inputs.no_ext_default_no_unpack = { + url = "file://$PWD/test_input_no_ext"; + flake = false; + }; + inputs.no_ext_explicit_unpack = { + url = "tarball+file://$PWD/test_input_no_ext"; + flake = false; + }; + inputs.tarball_default_unpack = { + url = "file://$PWD/test_input.tar.gz"; + flake = false; + }; + inputs.tarball_explicit_no_unpack = { + url = "file+file://$PWD/test_input.tar.gz"; + flake = false; + }; + inputs.flake_no_ext = { + url = "file://$PWD/test_input_no_ext"; + }; + outputs = { ... }: {}; + } +EOF + + nix flake update + nix eval --file - <<EOF + with (builtins.fromJSON (builtins.readFile ./flake.lock)); + + # Non-flake inputs whose extension doesn’t match a known archive format should + # not be unpacked by default + assert (nodes.no_ext_default_no_unpack.locked.type == "file"); + assert (nodes.no_ext_default_no_unpack.locked.unpack or false == false); + assert (nodes.no_ext_default_no_unpack.locked.narHash == "$input_tarball_hash"); + + # For backwards compatibility, flake inputs that correspond to the + # old 'tarball' fetcher should still have their type set to 'tarball' + assert (nodes.tarball_default_unpack.locked.type == "tarball"); + # Unless explicitely specified, the 'unpack' parameter shouldn’t appear here + # because that would break older Nix versions + assert (!nodes.tarball_default_unpack.locked ? unpack); + assert (nodes.tarball_default_unpack.locked.narHash == "$input_directory_hash"); + + # Explicitely passing the unpack parameter should enforce the desired behavior + assert (nodes.no_ext_explicit_unpack.locked.narHash == nodes.tarball_default_unpack.locked.narHash); + assert (nodes.tarball_explicit_no_unpack.locked.narHash == nodes.no_ext_default_no_unpack.locked.narHash); + + # Flake inputs should always be tarballs + assert (nodes.flake_no_ext.locked.type == "tarball"); + + true +EOF + + # Test tarball URLs on the command line. + [[ $(nix flake metadata --json file://$PWD/test_input_no_ext | jq -r .resolved.type) = tarball ]] + + popd + + [[ -z "${NIX_DAEMON_PACKAGE-}" ]] && return 0 + + # Ensure that a lockfile generated by the current Nix for tarball inputs + # can still be read by an older Nix + + cat <<EOF > flake.nix + { + inputs.tarball = { + url = "file://$PWD/test_input.tar.gz"; + flake = false; + }; + outputs = { self, tarball }: { + foo = builtins.readFile "\${tarball}/test_input_file"; + }; + } + nix flake update + + clearStore + "$NIX_DAEMON_PACKAGE/bin/nix" eval .#foo +EOF +} + +test_fetch_file +test_file_flake_input diff --git a/tests/functional/fetchurl.sh b/tests/functional/fetchurl.sh new file mode 100644 index 000000000..8cd40c09f --- /dev/null +++ b/tests/functional/fetchurl.sh @@ -0,0 +1,80 @@ +source common.sh + +clearStore + +# Test fetching a flat file. +hash=$(nix-hash --flat --type sha256 ./fetchurl.sh) + +outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha256 $hash --no-out-link) + +cmp $outPath fetchurl.sh + +# Do not re-fetch paths already present. +outPath2=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file:///does-not-exist/must-remain-unused/fetchurl.sh --argstr sha256 $hash --no-out-link) +test "$outPath" == "$outPath2" + +# Now using a base-64 hash. +clearStore + +hash=$(nix hash file --type sha512 --base64 ./fetchurl.sh) + +outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha512 $hash --no-out-link) + +cmp $outPath fetchurl.sh + +# Now using an SRI hash. +clearStore + +hash=$(nix hash file ./fetchurl.sh) + +[[ $hash =~ ^sha256- ]] + +outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr hash $hash --no-out-link) + +cmp $outPath fetchurl.sh + +# Test that we can substitute from a different store dir. +clearStore + +other_store=file://$TEST_ROOT/other_store?store=/fnord/store + +hash=$(nix hash file --type sha256 --base16 ./fetchurl.sh) + +storePath=$(nix --store $other_store store add-file ./fetchurl.sh) + +outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr sha256 $hash --no-out-link --substituters $other_store) + +# Test hashed mirrors with an SRI hash. +nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr hash $(nix hash to-sri --type sha256 $hash) \ + --no-out-link --substituters $other_store + +# Test unpacking a NAR. +rm -rf $TEST_ROOT/archive +mkdir -p $TEST_ROOT/archive +cp ./fetchurl.sh $TEST_ROOT/archive +chmod +x $TEST_ROOT/archive/fetchurl.sh +ln -s foo $TEST_ROOT/archive/symlink +nar=$TEST_ROOT/archive.nar +nix-store --dump $TEST_ROOT/archive > $nar + +hash=$(nix-hash --flat --type sha256 $nar) + +outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file://$nar --argstr sha256 $hash \ + --arg unpack true --argstr name xyzzy --no-out-link) + +echo $outPath | grepQuiet 'xyzzy' + +test -x $outPath/fetchurl.sh +test -L $outPath/symlink + +nix-store --delete $outPath + +# Test unpacking a compressed NAR. +narxz=$TEST_ROOT/archive.nar.xz +rm -f $narxz +xz --keep $nar +outPath=$(nix-build -vvvvv --expr 'import <nix/fetchurl.nix>' --argstr url file://$narxz --argstr sha256 $hash \ + --arg unpack true --argstr name xyzzy --no-out-link) + +test -x $outPath/fetchurl.sh +test -L $outPath/symlink diff --git a/tests/functional/filter-source.nix b/tests/functional/filter-source.nix new file mode 100644 index 000000000..907163639 --- /dev/null +++ b/tests/functional/filter-source.nix @@ -0,0 +1,12 @@ +with import ./config.nix; + +mkDerivation { + name = "filter"; + builder = builtins.toFile "builder" "ln -s $input $out"; + input = + let filter = path: type: + type != "symlink" + && baseNameOf path != "foo" + && !((import ./lang/lib.nix).hasSuffix ".bak" (baseNameOf path)); + in builtins.filterSource filter ((builtins.getEnv "TEST_ROOT") + "/filterin"); +} diff --git a/tests/functional/filter-source.sh b/tests/functional/filter-source.sh new file mode 100644 index 000000000..ba34d2eac --- /dev/null +++ b/tests/functional/filter-source.sh @@ -0,0 +1,25 @@ +source common.sh + +rm -rf $TEST_ROOT/filterin +mkdir $TEST_ROOT/filterin +mkdir $TEST_ROOT/filterin/foo +touch $TEST_ROOT/filterin/foo/bar +touch $TEST_ROOT/filterin/xyzzy +touch $TEST_ROOT/filterin/b +touch $TEST_ROOT/filterin/bak +touch $TEST_ROOT/filterin/bla.c.bak +ln -s xyzzy $TEST_ROOT/filterin/link + +checkFilter() { + test ! -e $1/foo/bar + test -e $1/xyzzy + test -e $1/bak + test ! -e $1/bla.c.bak + test ! -L $1/link +} + +nix-build ./filter-source.nix -o $TEST_ROOT/filterout1 +checkFilter $TEST_ROOT/filterout1 + +nix-build ./path.nix -o $TEST_ROOT/filterout2 +checkFilter $TEST_ROOT/filterout2 diff --git a/tests/functional/fixed.builder1.sh b/tests/functional/fixed.builder1.sh new file mode 100644 index 000000000..c41bb2b9a --- /dev/null +++ b/tests/functional/fixed.builder1.sh @@ -0,0 +1,3 @@ +if test "$IMPURE_VAR1" != "foo"; then exit 1; fi +if test "$IMPURE_VAR2" != "bar"; then exit 1; fi +echo "Hello World!" > $out diff --git a/tests/functional/fixed.builder2.sh b/tests/functional/fixed.builder2.sh new file mode 100644 index 000000000..31ea1579a --- /dev/null +++ b/tests/functional/fixed.builder2.sh @@ -0,0 +1,6 @@ +echo dummy: $dummy +if test -n "$dummy"; then sleep 2; fi +mkdir $out +mkdir $out/bla +echo "Hello World!" > $out/foo +ln -s foo $out/bar diff --git a/tests/functional/fixed.nix b/tests/functional/fixed.nix new file mode 100644 index 000000000..babe71504 --- /dev/null +++ b/tests/functional/fixed.nix @@ -0,0 +1,58 @@ +with import ./config.nix; + +rec { + + f2 = dummy: builder: mode: algo: hash: mkDerivation { + name = "fixed"; + inherit builder; + outputHashMode = mode; + outputHashAlgo = algo; + outputHash = hash; + inherit dummy; + impureEnvVars = ["IMPURE_VAR1" "IMPURE_VAR2"]; + }; + + f = f2 ""; + + good = [ + (f ./fixed.builder1.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858") + (f ./fixed.builder1.sh "flat" "sha1" "a0b65939670bc2c010f4d5d6a0b3e4e4590fb92b") + (f ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42") + (f ./fixed.builder2.sh "recursive" "sha1" "vw46m23bizj4n8afrc0fj19wrp7mj3c0") + ]; + + # Expression to test that `nix-build --check` also throws an error if the hash of + # fixed-output derivation has changed even if the hash exists in the store (in this + # case the hash exists because of `fixed.builder2.sh`, but building a derivation + # with the same hash and a different result must throw an error). + check = [ + (f ./fixed.builder1.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42") + ]; + + good2 = [ + # Yes, this looks fscked up: builder2 doesn't have that result. + # But Nix sees that an output with the desired hash already + # exists, and will refrain from building it. + (f ./fixed.builder2.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858") + ]; + + sameAsAdd = + f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik"; + + bad = [ + (f ./fixed.builder1.sh "flat" "md5" "0ddd8be4b179a529afa5f2ffae4b9858") + ]; + + reallyBad = [ + # Hash too short, and not base-32 either. + (f ./fixed.builder1.sh "flat" "md5" "ddd8be4b179a529afa5f2ffae4b9858") + ]; + + # Test for building two derivations in parallel that produce the + # same output path because they're fixed-output derivations. + parallelSame = [ + (f2 "foo" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42") + (f2 "bar" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42") + ]; + +} diff --git a/tests/functional/fixed.sh b/tests/functional/fixed.sh new file mode 100644 index 000000000..f1e1ce420 --- /dev/null +++ b/tests/functional/fixed.sh @@ -0,0 +1,58 @@ +source common.sh + +clearStore + +path=$(nix-store -q $(nix-instantiate fixed.nix -A good.0)) + +echo 'testing bad...' +nix-build fixed.nix -A bad --no-out-link && fail "should fail" + +# Building with the bad hash should produce the "good" output path as +# a side-effect. +[[ -e $path ]] +nix path-info --json $path | grep fixed:md5:2qk15sxzzjlnpjk9brn7j8ppcd + +echo 'testing good...' +nix-build fixed.nix -A good --no-out-link + +if isDaemonNewer "2.4pre20210927"; then + echo 'testing --check...' + nix-build fixed.nix -A check --check && fail "should fail" +fi + +echo 'testing good2...' +nix-build fixed.nix -A good2 --no-out-link + +echo 'testing reallyBad...' +nix-instantiate fixed.nix -A reallyBad && fail "should fail" + +# While we're at it, check attribute selection a bit more. +echo 'testing attribute selection...' +test $(nix-instantiate fixed.nix -A good.1 | wc -l) = 1 + +# Test parallel builds of derivations that produce the same output. +# Only one should run at the same time. +echo 'testing parallelSame...' +clearStore +nix-build fixed.nix -A parallelSame --no-out-link -j2 + +# Fixed-output derivations with a recursive SHA-256 hash should +# produce the same path as "nix-store --add". +echo 'testing sameAsAdd...' +out=$(nix-build fixed.nix -A sameAsAdd --no-out-link) + +# This is what fixed.builder2 produces... +rm -rf $TEST_ROOT/fixed +mkdir $TEST_ROOT/fixed +mkdir $TEST_ROOT/fixed/bla +echo "Hello World!" > $TEST_ROOT/fixed/foo +ln -s foo $TEST_ROOT/fixed/bar + +out2=$(nix-store --add $TEST_ROOT/fixed) +[ "$out" = "$out2" ] + +out3=$(nix-store --add-fixed --recursive sha256 $TEST_ROOT/fixed) +[ "$out" = "$out3" ] + +out4=$(nix-store --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed) +[ "$out" = "$out4" ] diff --git a/tests/functional/flakes/absolute-paths.sh b/tests/functional/flakes/absolute-paths.sh new file mode 100644 index 000000000..e7bfba12d --- /dev/null +++ b/tests/functional/flakes/absolute-paths.sh @@ -0,0 +1,17 @@ +source ./common.sh + +requireGit + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 + +createGitRepo $flake1Dir +cat > $flake1Dir/flake.nix <<EOF +{ + outputs = { self }: { x = builtins.readFile $(pwd)/absolute-paths.sh; }; +} +EOF +git -C $flake1Dir add flake.nix +git -C $flake1Dir commit -m Initial + +nix eval --impure --json $flake1Dir#x diff --git a/tests/functional/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh new file mode 100644 index 000000000..ff012e1b3 --- /dev/null +++ b/tests/functional/flakes/build-paths.sh @@ -0,0 +1,96 @@ +source ./common.sh + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 + +mkdir -p $flake1Dir $flake2Dir + +writeSimpleFlake $flake2Dir +tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake2 +hash=$(nix hash path $flake2Dir) + +dep=$(nix store add-path ./common.sh) + +cat > $flake1Dir/flake.nix <<EOF +{ + inputs.flake2.url = "file://$TEST_ROOT/flake.tar.gz"; + + outputs = { self, flake2 }: { + + a1 = builtins.fetchTarball { + #type = "tarball"; + url = "file://$TEST_ROOT/flake.tar.gz"; + sha256 = "$hash"; + }; + + a2 = ./foo; + + a3 = ./.; + + a4 = self.outPath; + + # FIXME + a5 = self; + + a6 = flake2.outPath; + + # FIXME + a7 = "\${flake2}/config.nix"; + + # This is only allowed in impure mode. + a8 = builtins.storePath $dep; + + a9 = "$dep"; + + drvCall = with import ./config.nix; mkDerivation { + name = "simple"; + builder = ./simple.builder.sh; + PATH = ""; + goodPath = path; + }; + + a10 = builtins.unsafeDiscardOutputDependency self.drvCall.drvPath; + + a11 = self.drvCall.drvPath; + + a12 = self.drvCall.outPath; + + a13 = "\${self.drvCall.drvPath}\${self.drvCall.outPath}"; + }; +} +EOF + +cp ../simple.nix ../simple.builder.sh ../config.nix $flake1Dir/ + +echo bar > $flake1Dir/foo + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a1 +[[ -e $TEST_ROOT/result/simple.nix ]] + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a2 +[[ $(cat $TEST_ROOT/result) = bar ]] + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a3 + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a4 + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a6 +[[ -e $TEST_ROOT/result/simple.nix ]] + +nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a8 +diff common.sh $TEST_ROOT/result + +expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a9 \ + | grepQuiet "has 0 entries in its context. It should only have exactly one entry" + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a10 +[[ $(readlink -e $TEST_ROOT/result) = *simple.drv ]] + +expectStderr 1 nix build --json --out-link $TEST_ROOT/result $flake1Dir#a11 \ + | grepQuiet "has a context which refers to a complete source and binary closure" + +nix build --json --out-link $TEST_ROOT/result $flake1Dir#a12 +[[ -e $TEST_ROOT/result/hello ]] + +expectStderr 1 nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a13 \ + | grepQuiet "has 2 entries in its context. It should only have exactly one entry" diff --git a/tests/functional/flakes/bundle.sh b/tests/functional/flakes/bundle.sh new file mode 100644 index 000000000..67bbb05ac --- /dev/null +++ b/tests/functional/flakes/bundle.sh @@ -0,0 +1,32 @@ +source common.sh + +cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME + +cd $TEST_HOME + +cat <<EOF > flake.nix +{ + outputs = {self}: { + bundlers.$system = rec { + simple = drv: + if drv?type && drv.type == "derivation" + then drv + else self.packages.$system.default; + default = simple; + }; + packages.$system.default = import ./simple.nix; + apps.$system.default = { + type = "app"; + program = "\${import ./simple.nix}/hello"; + }; + }; +} +EOF + +nix build .# +nix bundle --bundler .# .# +nix bundle --bundler .#bundlers.$system.default .#packages.$system.default +nix bundle --bundler .#bundlers.$system.simple .#packages.$system.default + +nix bundle --bundler .#bundlers.$system.default .#apps.$system.default +nix bundle --bundler .#bundlers.$system.simple .#apps.$system.default diff --git a/tests/functional/flakes/check.sh b/tests/functional/flakes/check.sh new file mode 100644 index 000000000..0433e5335 --- /dev/null +++ b/tests/functional/flakes/check.sh @@ -0,0 +1,91 @@ +source common.sh + +flakeDir=$TEST_ROOT/flake3 +mkdir -p $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + overlay = final: prev: { + }; + }; +} +EOF + +nix flake check $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + overlay = finalll: prev: { + }; + }; +} +EOF + +(! nix flake check $flakeDir) + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self, ... }: { + overlays.x86_64-linux.foo = final: prev: { + }; + }; +} +EOF + +checkRes=$(nix flake check $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true) +echo "$checkRes" | grepQuiet "error: overlay is not a function, but a set instead" + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + nixosModules.foo = { + a.b.c = 123; + foo = true; + }; + }; +} +EOF + +nix flake check $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + nixosModules.foo = assert false; { + a.b.c = 123; + foo = true; + }; + }; +} +EOF + +(! nix flake check $flakeDir) + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + nixosModule = { config, pkgs, ... }: { + a.b.c = 123; + }; + }; +} +EOF + +nix flake check $flakeDir + +cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + packages.system-1.default = "foo"; + packages.system-2.default = "bar"; + }; +} +EOF + +nix flake check $flakeDir + +checkRes=$(nix flake check --all-systems --keep-going $flakeDir 2>&1 && fail "nix flake check --all-systems should have failed" || true) +echo "$checkRes" | grepQuiet "packages.system-1.default" +echo "$checkRes" | grepQuiet "packages.system-2.default" diff --git a/tests/functional/flakes/circular.sh b/tests/functional/flakes/circular.sh new file mode 100644 index 000000000..09cd02edf --- /dev/null +++ b/tests/functional/flakes/circular.sh @@ -0,0 +1,49 @@ +# Test circular flake dependencies. +source ./common.sh + +requireGit + +flakeA=$TEST_ROOT/flakeA +flakeB=$TEST_ROOT/flakeB + +createGitRepo $flakeA +createGitRepo $flakeB + +cat > $flakeA/flake.nix <<EOF +{ + inputs.b.url = git+file://$flakeB; + inputs.b.inputs.a.follows = "/"; + + outputs = { self, b }: { + foo = 123 + b.bar; + xyzzy = 1000; + }; +} +EOF + +git -C $flakeA add flake.nix + +cat > $flakeB/flake.nix <<EOF +{ + inputs.a.url = git+file://$flakeA; + + outputs = { self, a }: { + bar = 456 + a.xyzzy; + }; +} +EOF + +git -C $flakeB add flake.nix +git -C $flakeB commit -a -m 'Foo' + +[[ $(nix eval $flakeA#foo) = 1579 ]] +[[ $(nix eval $flakeA#foo) = 1579 ]] + +sed -i $flakeB/flake.nix -e 's/456/789/' +git -C $flakeB commit -a -m 'Foo' + +[[ $(nix eval --update-input b $flakeA#foo) = 1912 ]] + +# Test list-inputs with circular dependencies +nix flake metadata $flakeA + diff --git a/tests/functional/flakes/common.sh b/tests/functional/flakes/common.sh new file mode 100644 index 000000000..427abcdde --- /dev/null +++ b/tests/functional/flakes/common.sh @@ -0,0 +1,70 @@ +source ../common.sh + +registry=$TEST_ROOT/registry.json + +writeSimpleFlake() { + local flakeDir="$1" + cat > $flakeDir/flake.nix <<EOF +{ + description = "Bla bla"; + + outputs = inputs: rec { + packages.$system = rec { + foo = import ./simple.nix; + default = foo; + }; + packages.someOtherSystem = rec { + foo = import ./simple.nix; + default = foo; + }; + + # To test "nix flake init". + legacyPackages.$system.hello = import ./simple.nix; + }; +} +EOF + + cp ../simple.nix ../simple.builder.sh ../config.nix $flakeDir/ +} + +createSimpleGitFlake() { + local flakeDir="$1" + writeSimpleFlake $flakeDir + git -C $flakeDir add flake.nix simple.nix simple.builder.sh config.nix + git -C $flakeDir commit -m 'Initial' +} + +writeDependentFlake() { + local flakeDir="$1" + cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self, flake1 }: { + packages.$system.default = flake1.packages.$system.default; + expr = assert builtins.pathExists ./flake.lock; 123; + }; +} +EOF +} + +writeTrivialFlake() { + local flakeDir="$1" + cat > $flakeDir/flake.nix <<EOF +{ + outputs = { self }: { + expr = 123; + }; +} +EOF +} + +createGitRepo() { + local repo="$1" + local extraArgs="${2-}" + + rm -rf $repo $repo.tmp + mkdir -p $repo + + git -C $repo init $extraArgs + git -C $repo config user.email "foobar@example.com" + git -C $repo config user.name "Foobar" +} diff --git a/tests/functional/flakes/config.sh b/tests/functional/flakes/config.sh new file mode 100644 index 000000000..d1941a6be --- /dev/null +++ b/tests/functional/flakes/config.sh @@ -0,0 +1,42 @@ +source common.sh + +cp ../simple.nix ../simple.builder.sh ../config.nix $TEST_HOME + +cd $TEST_HOME + +rm -f post-hook-ran +cat <<EOF > echoing-post-hook.sh +#!/bin/sh + +echo "ThePostHookRan as \$0" > $PWD/post-hook-ran +EOF +chmod +x echoing-post-hook.sh + +cat <<EOF > flake.nix +{ + nixConfig.post-build-hook = ./echoing-post-hook.sh; + nixConfig.allow-dirty = false; # See #5621 + + outputs = a: { + packages.$system.default = import ./simple.nix; + }; +} +EOF + +# Without --accept-flake-config, the post hook should not run. +nix build < /dev/null +(! [[ -f post-hook-ran ]]) +clearStore + +nix build --accept-flake-config +test -f post-hook-ran || fail "The post hook should have ran" + +# Make sure that the path to the post hook doesn’t change if we change +# something in the flake. +# Otherwise the user would have to re-validate the setting each time. +mv post-hook-ran previous-post-hook-run +echo "# Dummy comment" >> flake.nix +clearStore +nix build --accept-flake-config +diff -q post-hook-ran previous-post-hook-run || \ + fail "Both post hook runs should report the same filename" diff --git a/tests/functional/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh new file mode 100644 index 000000000..21a4b52de --- /dev/null +++ b/tests/functional/flakes/flake-in-submodule.sh @@ -0,0 +1,52 @@ +source common.sh + +# Tests that: +# - flake.nix may reside inside of a git submodule +# - the flake can access content outside of the submodule +# +# rootRepo +# ├── root.nix +# └── submodule +# ├── flake.nix +# └── sub.nix + + +requireGit + +clearStore + +# Submodules can't be fetched locally by default. +# See fetchGitSubmodules.sh +export XDG_CONFIG_HOME=$TEST_HOME/.config +git config --global protocol.file.allow always + + +rootRepo=$TEST_ROOT/rootRepo +subRepo=$TEST_ROOT/submodule + + +createGitRepo $subRepo +cat > $subRepo/flake.nix <<EOF +{ + outputs = { self }: { + sub = import ./sub.nix; + root = import ../root.nix; + }; +} +EOF +echo '"expression in submodule"' > $subRepo/sub.nix +git -C $subRepo add flake.nix sub.nix +git -C $subRepo commit -m Initial + +createGitRepo $rootRepo + +git -C $rootRepo submodule init +git -C $rootRepo submodule add $subRepo submodule +echo '"expression in root repo"' > $rootRepo/root.nix +git -C $rootRepo add root.nix +git -C $rootRepo commit -m "Add root.nix" + +# Flake can live inside a submodule and can be accessed via ?dir=submodule +[[ $(nix eval --json git+file://$rootRepo\?submodules=1\&dir=submodule#sub ) = '"expression in submodule"' ]] +# The flake can access content outside of the submodule +[[ $(nix eval --json git+file://$rootRepo\?submodules=1\&dir=submodule#root ) = '"expression in root repo"' ]] diff --git a/tests/functional/flakes/flakes.sh b/tests/functional/flakes/flakes.sh new file mode 100644 index 000000000..128f759ea --- /dev/null +++ b/tests/functional/flakes/flakes.sh @@ -0,0 +1,511 @@ +source ./common.sh + +requireGit + +clearStore +rm -rf $TEST_HOME/.cache $TEST_HOME/.config + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 +flake3Dir=$TEST_ROOT/flake3 +flake5Dir=$TEST_ROOT/flake5 +flake7Dir=$TEST_ROOT/flake7 +nonFlakeDir=$TEST_ROOT/nonFlake +badFlakeDir=$TEST_ROOT/badFlake +flakeGitBare=$TEST_ROOT/flakeGitBare + +for repo in $flake1Dir $flake2Dir $flake3Dir $flake7Dir $nonFlakeDir; do + # Give one repo a non-main initial branch. + extraArgs= + if [[ $repo == $flake2Dir ]]; then + extraArgs="--initial-branch=main" + fi + + createGitRepo "$repo" "$extraArgs" +done + +createSimpleGitFlake $flake1Dir + +cat > $flake2Dir/flake.nix <<EOF +{ + description = "Fnord"; + + outputs = { self, flake1 }: rec { + packages.$system.bar = flake1.packages.$system.foo; + }; +} +EOF + +git -C $flake2Dir add flake.nix +git -C $flake2Dir commit -m 'Initial' + +cat > $flake3Dir/flake.nix <<EOF +{ + description = "Fnord"; + + outputs = { self, flake2 }: rec { + packages.$system.xyzzy = flake2.packages.$system.bar; + + checks = { + xyzzy = packages.$system.xyzzy; + }; + }; +} +EOF + +cat > $flake3Dir/default.nix <<EOF +{ x = 123; } +EOF + +git -C $flake3Dir add flake.nix default.nix +git -C $flake3Dir commit -m 'Initial' + +cat > $nonFlakeDir/README.md <<EOF +FNORD +EOF + +git -C $nonFlakeDir add README.md +git -C $nonFlakeDir commit -m 'Initial' + +# Construct a custom registry, additionally test the --registry flag +nix registry add --registry $registry flake1 git+file://$flake1Dir +nix registry add --registry $registry flake2 git+file://$flake2Dir +nix registry add --registry $registry flake3 git+file://$flake3Dir +nix registry add --registry $registry flake4 flake3 +nix registry add --registry $registry nixpkgs flake1 + +# Test 'nix registry list'. +[[ $(nix registry list | wc -l) == 5 ]] +nix registry list | grep '^global' +nix registry list | grepInverse '^user' # nothing in user registry + +# Test 'nix flake metadata'. +nix flake metadata flake1 +nix flake metadata flake1 | grepQuiet 'Locked URL:.*flake1.*' + +# Test 'nix flake metadata' on a local flake. +(cd $flake1Dir && nix flake metadata) | grepQuiet 'URL:.*flake1.*' +(cd $flake1Dir && nix flake metadata .) | grepQuiet 'URL:.*flake1.*' +nix flake metadata $flake1Dir | grepQuiet 'URL:.*flake1.*' + +# Test 'nix flake metadata --json'. +json=$(nix flake metadata flake1 --json | jq .) +[[ $(echo "$json" | jq -r .description) = 'Bla bla' ]] +[[ -d $(echo "$json" | jq -r .path) ]] +[[ $(echo "$json" | jq -r .lastModified) = $(git -C $flake1Dir log -n1 --format=%ct) ]] +hash1=$(echo "$json" | jq -r .revision) + +echo foo > $flake1Dir/foo +git -C $flake1Dir add $flake1Dir/foo +[[ $(nix flake metadata flake1 --json --refresh | jq -r .dirtyRevision) == "$hash1-dirty" ]] + +echo -n '# foo' >> $flake1Dir/flake.nix +flake1OriginalCommit=$(git -C $flake1Dir rev-parse HEAD) +git -C $flake1Dir commit -a -m 'Foo' +flake1NewCommit=$(git -C $flake1Dir rev-parse HEAD) +hash2=$(nix flake metadata flake1 --json --refresh | jq -r .revision) +[[ $(nix flake metadata flake1 --json --refresh | jq -r .dirtyRevision) == "null" ]] +[[ $hash1 != $hash2 ]] + +# Test 'nix build' on a flake. +nix build -o $TEST_ROOT/result flake1#foo +[[ -e $TEST_ROOT/result/hello ]] + +# Test packages.default. +nix build -o $TEST_ROOT/result flake1 +[[ -e $TEST_ROOT/result/hello ]] + +nix build -o $TEST_ROOT/result $flake1Dir +nix build -o $TEST_ROOT/result git+file://$flake1Dir + +# Check that store symlinks inside a flake are not interpreted as flakes. +nix build -o $flake1Dir/result git+file://$flake1Dir +nix path-info $flake1Dir/result + +# 'getFlake' on an unlocked flakeref should fail in pure mode, but +# succeed in impure mode. +(! nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default") +nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"$flake1Dir\").packages.$system.default" --impure + +# 'getFlake' on a locked flakeref should succeed even in pure mode. +nix build -o $TEST_ROOT/result --expr "(builtins.getFlake \"git+file://$flake1Dir?rev=$hash2\").packages.$system.default" + +# Building a flake with an unlocked dependency should fail in pure mode. +(! nix build -o $TEST_ROOT/result flake2#bar --no-registries) +(! nix build -o $TEST_ROOT/result flake2#bar --no-use-registries) +(! nix eval --expr "builtins.getFlake \"$flake2Dir\"") + +# But should succeed in impure mode. +(! nix build -o $TEST_ROOT/result flake2#bar --impure) +nix build -o $TEST_ROOT/result flake2#bar --impure --no-write-lock-file +nix eval --expr "builtins.getFlake \"$flake2Dir\"" --impure + +# Building a local flake with an unlocked dependency should fail with --no-update-lock-file. +expect 1 nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes' + +# But it should succeed without that flag. +nix build -o $TEST_ROOT/result $flake2Dir#bar --no-write-lock-file +expect 1 nix build -o $TEST_ROOT/result $flake2Dir#bar --no-update-lock-file 2>&1 | grep 'requires lock file changes' +nix build -o $TEST_ROOT/result $flake2Dir#bar --commit-lock-file +[[ -e $flake2Dir/flake.lock ]] +[[ -z $(git -C $flake2Dir diff main || echo failed) ]] + +# Rerunning the build should not change the lockfile. +nix build -o $TEST_ROOT/result $flake2Dir#bar +[[ -z $(git -C $flake2Dir diff main || echo failed) ]] + +# Building with a lockfile should not require a fetch of the registry. +nix build -o $TEST_ROOT/result --flake-registry file:///no-registry.json $flake2Dir#bar --refresh +nix build -o $TEST_ROOT/result --no-registries $flake2Dir#bar --refresh +nix build -o $TEST_ROOT/result --no-use-registries $flake2Dir#bar --refresh + +# Updating the flake should not change the lockfile. +nix flake lock $flake2Dir +[[ -z $(git -C $flake2Dir diff main || echo failed) ]] + +# Now we should be able to build the flake in pure mode. +nix build -o $TEST_ROOT/result flake2#bar + +# Or without a registry. +nix build -o $TEST_ROOT/result --no-registries git+file://$flake2Dir#bar --refresh +nix build -o $TEST_ROOT/result --no-use-registries git+file://$flake2Dir#bar --refresh + +# Test whether indirect dependencies work. +nix build -o $TEST_ROOT/result $flake3Dir#xyzzy +git -C $flake3Dir add flake.lock + +# Add dependency to flake3. +rm $flake3Dir/flake.nix + +cat > $flake3Dir/flake.nix <<EOF +{ + description = "Fnord"; + + outputs = { self, flake1, flake2 }: rec { + packages.$system.xyzzy = flake2.packages.$system.bar; + packages.$system."sth sth" = flake1.packages.$system.foo; + }; +} +EOF + +git -C $flake3Dir add flake.nix +git -C $flake3Dir commit -m 'Update flake.nix' + +# Check whether `nix build` works with an incomplete lockfile +nix build -o $TEST_ROOT/result $flake3Dir#"sth sth" +nix build -o $TEST_ROOT/result $flake3Dir#"sth%20sth" + +# Check whether it saved the lockfile +[[ -n $(git -C $flake3Dir diff master) ]] + +git -C $flake3Dir add flake.lock + +git -C $flake3Dir commit -m 'Add lockfile' + +# Test whether registry caching works. +nix registry list --flake-registry file://$registry | grepQuiet flake3 +mv $registry $registry.tmp +nix store gc +nix registry list --flake-registry file://$registry --refresh | grepQuiet flake3 +mv $registry.tmp $registry + +# Test whether flakes are registered as GC roots for offline use. +# FIXME: use tarballs rather than git. +rm -rf $TEST_HOME/.cache +nix store gc # get rid of copies in the store to ensure they get fetched to our git cache +_NIX_FORCE_HTTP=1 nix build -o $TEST_ROOT/result git+file://$flake2Dir#bar +mv $flake1Dir $flake1Dir.tmp +mv $flake2Dir $flake2Dir.tmp +nix store gc +_NIX_FORCE_HTTP=1 nix build -o $TEST_ROOT/result git+file://$flake2Dir#bar +_NIX_FORCE_HTTP=1 nix build -o $TEST_ROOT/result git+file://$flake2Dir#bar --refresh +mv $flake1Dir.tmp $flake1Dir +mv $flake2Dir.tmp $flake2Dir + +# Add nonFlakeInputs to flake3. +rm $flake3Dir/flake.nix + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs = { + flake1 = {}; + flake2 = {}; + nonFlake = { + url = git+file://$nonFlakeDir; + flake = false; + }; + nonFlakeFile = { + url = path://$nonFlakeDir/README.md; + flake = false; + }; + nonFlakeFile2 = { + url = "$nonFlakeDir/README.md"; + flake = false; + }; + }; + + description = "Fnord"; + + outputs = inputs: rec { + packages.$system.xyzzy = inputs.flake2.packages.$system.bar; + packages.$system.sth = inputs.flake1.packages.$system.foo; + packages.$system.fnord = + with import ./config.nix; + mkDerivation { + inherit system; + name = "fnord"; + dummy = builtins.readFile (builtins.path { name = "source"; path = ./.; filter = path: type: baseNameOf path == "config.nix"; } + "/config.nix"); + dummy2 = builtins.readFile (builtins.path { name = "source"; path = inputs.flake1; filter = path: type: baseNameOf path == "simple.nix"; } + "/simple.nix"); + buildCommand = '' + cat \${inputs.nonFlake}/README.md > \$out + [[ \$(cat \${inputs.nonFlake}/README.md) = \$(cat \${inputs.nonFlakeFile}) ]] + [[ \${inputs.nonFlakeFile} = \${inputs.nonFlakeFile2} ]] + ''; + }; + }; +} +EOF + +cp ../config.nix $flake3Dir + +git -C $flake3Dir add flake.nix config.nix +git -C $flake3Dir commit -m 'Add nonFlakeInputs' + +# Check whether `nix build` works with a lockfile which is missing a +# nonFlakeInputs. +nix build -o $TEST_ROOT/result $flake3Dir#sth --commit-lock-file + +nix build -o $TEST_ROOT/result flake3#fnord +[[ $(cat $TEST_ROOT/result) = FNORD ]] + +# Check whether flake input fetching is lazy: flake3#sth does not +# depend on flake2, so this shouldn't fail. +rm -rf $TEST_HOME/.cache +clearStore +mv $flake2Dir $flake2Dir.tmp +mv $nonFlakeDir $nonFlakeDir.tmp +nix build -o $TEST_ROOT/result flake3#sth +(! nix build -o $TEST_ROOT/result flake3#xyzzy) +(! nix build -o $TEST_ROOT/result flake3#fnord) +mv $flake2Dir.tmp $flake2Dir +mv $nonFlakeDir.tmp $nonFlakeDir +nix build -o $TEST_ROOT/result flake3#xyzzy flake3#fnord + +# Test doing multiple `lookupFlake`s +nix build -o $TEST_ROOT/result flake4#xyzzy + +# Test 'nix flake update' and --override-flake. +nix flake lock $flake3Dir +[[ -z $(git -C $flake3Dir diff master || echo failed) ]] + +nix flake update $flake3Dir --override-flake flake2 nixpkgs +[[ ! -z $(git -C $flake3Dir diff master || echo failed) ]] + +# Make branch "removeXyzzy" where flake3 doesn't have xyzzy anymore +git -C $flake3Dir checkout -b removeXyzzy +rm $flake3Dir/flake.nix + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs = { + nonFlake = { + url = "$nonFlakeDir"; + flake = false; + }; + }; + + description = "Fnord"; + + outputs = { self, flake1, flake2, nonFlake }: rec { + packages.$system.sth = flake1.packages.$system.foo; + packages.$system.fnord = + with import ./config.nix; + mkDerivation { + inherit system; + name = "fnord"; + buildCommand = '' + cat \${nonFlake}/README.md > \$out + ''; + }; + }; +} +EOF +nix flake lock $flake3Dir +git -C $flake3Dir add flake.nix flake.lock +git -C $flake3Dir commit -m 'Remove packages.xyzzy' +git -C $flake3Dir checkout master + +# Test whether fuzzy-matching works for registry entries. +(! nix build -o $TEST_ROOT/result flake4/removeXyzzy#xyzzy) +nix build -o $TEST_ROOT/result flake4/removeXyzzy#sth + +# Testing the nix CLI +nix registry add flake1 flake3 +[[ $(nix registry list | wc -l) == 6 ]] +nix registry pin flake1 +[[ $(nix registry list | wc -l) == 6 ]] +nix registry pin flake1 flake3 +[[ $(nix registry list | wc -l) == 6 ]] +nix registry remove flake1 +[[ $(nix registry list | wc -l) == 5 ]] + +# Test 'nix registry list' with a disabled global registry. +nix registry add user-flake1 git+file://$flake1Dir +nix registry add user-flake2 git+file://$flake2Dir +[[ $(nix --flake-registry "" registry list | wc -l) == 2 ]] +nix --flake-registry "" registry list | grepQuietInverse '^global' # nothing in global registry +nix --flake-registry "" registry list | grepQuiet '^user' +nix registry remove user-flake1 +nix registry remove user-flake2 +[[ $(nix registry list | wc -l) == 5 ]] + +# Test 'nix flake clone'. +rm -rf $TEST_ROOT/flake1-v2 +nix flake clone flake1 --dest $TEST_ROOT/flake1-v2 +[ -e $TEST_ROOT/flake1-v2/flake.nix ] + +# Test 'follows' inputs. +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.foo = { + type = "indirect"; + id = "flake1"; + }; + inputs.bar.follows = "foo"; + + outputs = { self, foo, bar }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["foo"]' ]] + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.bar.follows = "flake2/flake1"; + + outputs = { self, flake2, bar }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["flake2","flake1"]' ]] + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.bar.follows = "flake2"; + + outputs = { self, flake2, bar }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq -c .nodes.root.inputs.bar $flake3Dir/flake.lock) = '["flake2"]' ]] + +# Test overriding inputs of inputs. +writeTrivialFlake $flake7Dir +git -C $flake7Dir add flake.nix +git -C $flake7Dir commit -m 'Initial' + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.flake2.inputs.flake1 = { + type = "git"; + url = file://$flake7Dir; + }; + + outputs = { self, flake2 }: { + }; +} +EOF + +nix flake lock $flake3Dir +[[ $(jq .nodes.flake1.locked.url $flake3Dir/flake.lock) =~ flake7 ]] + +cat > $flake3Dir/flake.nix <<EOF +{ + inputs.flake2.inputs.flake1.follows = "foo"; + inputs.foo.url = git+file://$flake7Dir; + + outputs = { self, flake2 }: { + }; +} +EOF + +nix flake update $flake3Dir +[[ $(jq -c .nodes.flake2.inputs.flake1 $flake3Dir/flake.lock) =~ '["foo"]' ]] +[[ $(jq .nodes.foo.locked.url $flake3Dir/flake.lock) =~ flake7 ]] + +# Test git+file with bare repo. +rm -rf $flakeGitBare +git clone --bare $flake1Dir $flakeGitBare +nix build -o $TEST_ROOT/result git+file://$flakeGitBare + +# Test path flakes. +mkdir -p $flake5Dir +writeDependentFlake $flake5Dir +nix flake lock path://$flake5Dir + +# Test tarball flakes. +tar cfz $TEST_ROOT/flake.tar.gz -C $TEST_ROOT flake5 + +nix build -o $TEST_ROOT/result file://$TEST_ROOT/flake.tar.gz + +# Building with a tarball URL containing a SRI hash should also work. +url=$(nix flake metadata --json file://$TEST_ROOT/flake.tar.gz | jq -r .url) +[[ $url =~ sha256- ]] + +nix build -o $TEST_ROOT/result $url + +# Building with an incorrect SRI hash should fail. +expectStderr 102 nix build -o $TEST_ROOT/result "file://$TEST_ROOT/flake.tar.gz?narHash=sha256-qQ2Zz4DNHViCUrp6gTS7EE4+RMqFQtUfWF2UNUtJKS0=" | grep 'NAR hash mismatch' + +# Test --override-input. +git -C $flake3Dir reset --hard +nix flake lock $flake3Dir --override-input flake2/flake1 file://$TEST_ROOT/flake.tar.gz -vvvvv +[[ $(jq .nodes.flake1_2.locked.url $flake3Dir/flake.lock) =~ flake.tar.gz ]] + +nix flake lock $flake3Dir --override-input flake2/flake1 flake1 +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]] + +nix flake lock $flake3Dir --override-input flake2/flake1 flake1/master/$hash1 +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash1 ]] + +# Test --update-input. +nix flake lock $flake3Dir +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) = $hash1 ]] + +nix flake lock $flake3Dir --update-input flake2/flake1 +[[ $(jq -r .nodes.flake1_2.locked.rev $flake3Dir/flake.lock) =~ $hash2 ]] + +# Test 'nix flake metadata --json'. +nix flake metadata $flake3Dir --json | jq . + +# Test flake in store does not evaluate. +rm -rf $badFlakeDir +mkdir $badFlakeDir +echo INVALID > $badFlakeDir/flake.nix +nix store delete $(nix store add-path $badFlakeDir) + +[[ $(nix path-info $(nix store add-path $flake1Dir)) =~ flake1 ]] +[[ $(nix path-info path:$(nix store add-path $flake1Dir)) =~ simple ]] + +# Test fetching flakerefs in the legacy CLI. +[[ $(nix-instantiate --eval flake:flake3 -A x) = 123 ]] +[[ $(nix-instantiate --eval flake:git+file://$flake3Dir -A x) = 123 ]] +[[ $(nix-instantiate -I flake3=flake:flake3 --eval '<flake3>' -A x) = 123 ]] +[[ $(NIX_PATH=flake3=flake:flake3 nix-instantiate --eval '<flake3>' -A x) = 123 ]] + +# Test alternate lockfile paths. +nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2.lock +cmp $flake2Dir/flake.lock $TEST_ROOT/flake2.lock >/dev/null # lockfiles should be identical, since we're referencing flake2's original one + +nix flake lock $flake2Dir --output-lock-file $TEST_ROOT/flake2-overridden.lock --override-input flake1 git+file://$flake1Dir?rev=$flake1OriginalCommit +expectStderr 1 cmp $flake2Dir/flake.lock $TEST_ROOT/flake2-overridden.lock +nix flake metadata $flake2Dir --reference-lock-file $TEST_ROOT/flake2-overridden.lock | grepQuiet $flake1OriginalCommit + +# reference-lock-file can only be used if allow-dirty is set. +expectStderr 1 nix flake metadata $flake2Dir --no-allow-dirty --reference-lock-file $TEST_ROOT/flake2-overridden.lock diff --git a/tests/functional/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh new file mode 100644 index 000000000..dc97027ac --- /dev/null +++ b/tests/functional/flakes/follow-paths.sh @@ -0,0 +1,232 @@ +source ./common.sh + +requireGit + +flakeFollowsA=$TEST_ROOT/follows/flakeA +flakeFollowsB=$TEST_ROOT/follows/flakeA/flakeB +flakeFollowsC=$TEST_ROOT/follows/flakeA/flakeB/flakeC +flakeFollowsD=$TEST_ROOT/follows/flakeA/flakeD +flakeFollowsE=$TEST_ROOT/follows/flakeA/flakeE + +# Test following path flakerefs. +createGitRepo $flakeFollowsA +mkdir -p $flakeFollowsB +mkdir -p $flakeFollowsC +mkdir -p $flakeFollowsD +mkdir -p $flakeFollowsE + +cat > $flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs = { + B = { + url = "path:./flakeB"; + inputs.foobar.follows = "foobar"; + }; + + foobar.url = "path:$flakeFollowsA/flakeE"; + }; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsB/flake.nix <<EOF +{ + description = "Flake B"; + inputs = { + foobar.url = "path:$flakeFollowsA/flakeE"; + goodoo.follows = "C/goodoo"; + C = { + url = "path:./flakeC"; + inputs.foobar.follows = "foobar"; + }; + }; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsC/flake.nix <<EOF +{ + description = "Flake C"; + inputs = { + foobar.url = "path:$flakeFollowsA/flakeE"; + goodoo.follows = "foobar"; + }; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsD/flake.nix <<EOF +{ + description = "Flake D"; + inputs = {}; + outputs = { ... }: {}; +} +EOF + +cat > $flakeFollowsE/flake.nix <<EOF +{ + description = "Flake E"; + inputs = {}; + outputs = { ... }: {}; +} +EOF + +git -C $flakeFollowsA add flake.nix flakeB/flake.nix \ + flakeB/flakeC/flake.nix flakeD/flake.nix flakeE/flake.nix + +nix flake metadata $flakeFollowsA + +nix flake update $flakeFollowsA + +nix flake lock $flakeFollowsA + +oldLock="$(cat "$flakeFollowsA/flake.lock")" + +# Ensure that locking twice doesn't change anything + +nix flake lock $flakeFollowsA + +newLock="$(cat "$flakeFollowsA/flake.lock")" + +diff <(echo "$newLock") <(echo "$oldLock") + +[[ $(jq -c .nodes.B.inputs.C $flakeFollowsA/flake.lock) = '"C"' ]] +[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '["foobar"]' ]] +[[ $(jq -c .nodes.C.inputs.foobar $flakeFollowsA/flake.lock) = '["B","foobar"]' ]] + +# Ensure removing follows from flake.nix removes them from the lockfile + +cat > $flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs = { + B = { + url = "path:./flakeB"; + }; + D.url = "path:./flakeD"; + }; + outputs = { ... }: {}; +} +EOF + +nix flake lock $flakeFollowsA + +[[ $(jq -c .nodes.B.inputs.foobar $flakeFollowsA/flake.lock) = '"foobar"' ]] +jq -r -c '.nodes | keys | .[]' $flakeFollowsA/flake.lock | grep "^foobar$" + +# Ensure a relative path is not allowed to go outside the store path +cat > $flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs = { + B.url = "path:../flakeB"; + }; + outputs = { ... }: {}; +} +EOF + +git -C $flakeFollowsA add flake.nix + +expect 1 nix flake lock $flakeFollowsA 2>&1 | grep 'points outside' + +# Non-existant follows should print a warning. +cat >$flakeFollowsA/flake.nix <<EOF +{ + description = "Flake A"; + inputs.B = { + url = "path:./flakeB"; + inputs.invalid.follows = "D"; + inputs.invalid2.url = "path:./flakeD"; + }; + inputs.D.url = "path:./flakeD"; + outputs = { ... }: {}; +} +EOF + +git -C $flakeFollowsA add flake.nix + +nix flake lock "$flakeFollowsA" 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid'" +nix flake lock "$flakeFollowsA" 2>&1 | grep "warning: input 'B' has an override for a non-existent input 'invalid2'" + +# Now test follow path overloading +# This tests a lockfile checking regression https://github.com/NixOS/nix/pull/8819 +# +# We construct the following graph, where p->q means p has input q. +# A double edge means that the edge gets overridden using `follows`. +# +# A +# / \ +# / \ +# v v +# B ==> C --- follows declared in A +# \\ / +# \\/ --- follows declared in B +# v +# D +# +# The message was +# error: input 'B/D' follows a non-existent input 'B/C/D' +# +# Note that for `B` to resolve its follow for `D`, it needs `C/D`, for which it needs to resolve the follow on `C` first. +flakeFollowsOverloadA="$TEST_ROOT/follows/overload/flakeA" +flakeFollowsOverloadB="$TEST_ROOT/follows/overload/flakeA/flakeB" +flakeFollowsOverloadC="$TEST_ROOT/follows/overload/flakeA/flakeB/flakeC" +flakeFollowsOverloadD="$TEST_ROOT/follows/overload/flakeA/flakeB/flakeC/flakeD" + +# Test following path flakerefs. +createGitRepo "$flakeFollowsOverloadA" +mkdir -p "$flakeFollowsOverloadB" +mkdir -p "$flakeFollowsOverloadC" +mkdir -p "$flakeFollowsOverloadD" + +cat > "$flakeFollowsOverloadD/flake.nix" <<EOF +{ + description = "Flake D"; + inputs = {}; + outputs = { ... }: {}; +} +EOF + +cat > "$flakeFollowsOverloadC/flake.nix" <<EOF +{ + description = "Flake C"; + inputs.D.url = "path:./flakeD"; + outputs = { ... }: {}; +} +EOF + +cat > "$flakeFollowsOverloadB/flake.nix" <<EOF +{ + description = "Flake B"; + inputs = { + C = { + url = "path:./flakeC"; + }; + D.follows = "C/D"; + }; + outputs = { ... }: {}; +} +EOF + +# input B/D should be able to be found... +cat > "$flakeFollowsOverloadA/flake.nix" <<EOF +{ + description = "Flake A"; + inputs = { + B = { + url = "path:./flakeB"; + inputs.C.follows = "C"; + }; + C.url = "path:./flakeB/flakeC"; + }; + outputs = { ... }: {}; +} +EOF + +git -C "$flakeFollowsOverloadA" add flake.nix flakeB/flake.nix \ + flakeB/flakeC/flake.nix flakeB/flakeC/flakeD/flake.nix + +nix flake metadata "$flakeFollowsOverloadA" +nix flake update "$flakeFollowsOverloadA" +nix flake lock "$flakeFollowsOverloadA" diff --git a/tests/functional/flakes/init.sh b/tests/functional/flakes/init.sh new file mode 100644 index 000000000..2d4c77ba1 --- /dev/null +++ b/tests/functional/flakes/init.sh @@ -0,0 +1,87 @@ +source ./common.sh + +requireGit + +templatesDir=$TEST_ROOT/templates +flakeDir=$TEST_ROOT/flake +nixpkgsDir=$TEST_ROOT/nixpkgs + +nix registry add --registry $registry templates git+file://$templatesDir +nix registry add --registry $registry nixpkgs git+file://$nixpkgsDir + +createGitRepo $nixpkgsDir +createSimpleGitFlake $nixpkgsDir + +# Test 'nix flake init'. +createGitRepo $templatesDir + +cat > $templatesDir/flake.nix <<EOF +{ + description = "Some templates"; + + outputs = { self }: { + templates = rec { + trivial = { + path = ./trivial; + description = "A trivial flake"; + welcomeText = '' + Welcome to my trivial flake + ''; + }; + default = trivial; + }; + }; +} +EOF + +mkdir $templatesDir/trivial + +cat > $templatesDir/trivial/flake.nix <<EOF +{ + description = "A flake for building Hello World"; + + outputs = { self, nixpkgs }: { + packages.$system = rec { + hello = nixpkgs.legacyPackages.$system.hello; + default = hello; + }; + }; +} +EOF +echo a > $templatesDir/trivial/a +echo b > $templatesDir/trivial/b + +git -C $templatesDir add flake.nix trivial/ +git -C $templatesDir commit -m 'Initial' + +nix flake check templates +nix flake show templates +nix flake show templates --json | jq + +createGitRepo $flakeDir +(cd $flakeDir && nix flake init) +(cd $flakeDir && nix flake init) # check idempotence +git -C $flakeDir add flake.nix +nix flake check $flakeDir +nix flake show $flakeDir +nix flake show $flakeDir --json | jq +git -C $flakeDir commit -a -m 'Initial' + +# Test 'nix flake init' with benign conflicts +createGitRepo "$flakeDir" +echo a > $flakeDir/a +(cd $flakeDir && nix flake init) # check idempotence + +# Test 'nix flake init' with conflicts +createGitRepo "$flakeDir" +echo b > $flakeDir/a +pushd $flakeDir +(! nix flake init) |& grep "refusing to overwrite existing file '$flakeDir/a'" +popd +git -C $flakeDir commit -a -m 'Changed' + +# Test 'nix flake new'. +rm -rf $flakeDir +nix flake new -t templates#trivial $flakeDir +nix flake new -t templates#trivial $flakeDir # check idempotence +nix flake check $flakeDir diff --git a/tests/functional/flakes/inputs.sh b/tests/functional/flakes/inputs.sh new file mode 100644 index 000000000..80620488a --- /dev/null +++ b/tests/functional/flakes/inputs.sh @@ -0,0 +1,80 @@ +source ./common.sh + +requireGit + + +test_subdir_self_path() { + baseDir=$TEST_ROOT/$RANDOM + flakeDir=$baseDir/b-low + mkdir -p $flakeDir + writeSimpleFlake $baseDir + writeSimpleFlake $flakeDir + + echo all good > $flakeDir/message + cat > $flakeDir/flake.nix <<EOF +{ + outputs = inputs: rec { + packages.$system = rec { + default = + assert builtins.readFile ./message == "all good\n"; + assert builtins.readFile (inputs.self + "/message") == "all good\n"; + import ./simple.nix; + }; + }; +} +EOF + ( + nix build $baseDir?dir=b-low --no-link + ) +} +test_subdir_self_path + + +test_git_subdir_self_path() { + repoDir=$TEST_ROOT/repo-$RANDOM + createGitRepo $repoDir + flakeDir=$repoDir/b-low + mkdir -p $flakeDir + writeSimpleFlake $repoDir + writeSimpleFlake $flakeDir + + echo all good > $flakeDir/message + cat > $flakeDir/flake.nix <<EOF +{ + outputs = inputs: rec { + packages.$system = rec { + default = + assert builtins.readFile ./message == "all good\n"; + assert builtins.readFile (inputs.self + "/message") == "all good\n"; + assert inputs.self.outPath == inputs.self.sourceInfo.outPath + "/b-low"; + import ./simple.nix; + }; + }; +} +EOF + ( + cd $flakeDir + git add . + git commit -m init + # nix build + ) + + clientDir=$TEST_ROOT/client-$RANDOM + mkdir -p $clientDir + cat > $clientDir/flake.nix <<EOF +{ + inputs.inp = { + type = "git"; + url = "file://$repoDir"; + dir = "b-low"; + }; + + outputs = inputs: rec { + packages = inputs.inp.packages; + }; +} +EOF + nix build $clientDir --no-link + +} +test_git_subdir_self_path diff --git a/tests/functional/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh new file mode 100644 index 000000000..0622c79b7 --- /dev/null +++ b/tests/functional/flakes/mercurial.sh @@ -0,0 +1,43 @@ +source ./common.sh + +[[ $(type -p hq) ]] || skipTest "Mercurial not installed" + +flake1Dir=$TEST_ROOT/flake-hg1 +mkdir -p $flake1Dir +writeSimpleFlake $flake1Dir +hg init $flake1Dir + +nix registry add --registry $registry flake1 hg+file://$flake1Dir + +flake2Dir=$TEST_ROOT/flake-hg2 +mkdir -p $flake2Dir +writeDependentFlake $flake2Dir +hg init $flake2Dir + +hg add $flake1Dir/* +hg commit --config ui.username=foobar@example.org $flake1Dir -m 'Initial commit' + +hg add $flake2Dir/flake.nix +hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Initial commit' + +nix build -o $TEST_ROOT/result hg+file://$flake2Dir +[[ -e $TEST_ROOT/result/hello ]] + +(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) + +nix eval hg+file://$flake2Dir#expr + +nix eval hg+file://$flake2Dir#expr + +(! nix eval hg+file://$flake2Dir#expr --no-allow-dirty) + +(! nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revision) + +hg commit --config ui.username=foobar@example.org $flake2Dir -m 'Add lock file' + +nix flake metadata --json hg+file://$flake2Dir --refresh | jq -e -r .revision +nix flake metadata --json hg+file://$flake2Dir +[[ $(nix flake metadata --json hg+file://$flake2Dir | jq -e -r .revCount) = 1 ]] + +nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-registries --no-allow-dirty +nix build -o $TEST_ROOT/result hg+file://$flake2Dir --no-use-registries --no-allow-dirty diff --git a/tests/functional/flakes/run.sh b/tests/functional/flakes/run.sh new file mode 100644 index 000000000..9fa51d1c7 --- /dev/null +++ b/tests/functional/flakes/run.sh @@ -0,0 +1,29 @@ +source ../common.sh + +clearStore +rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local +cp ../shell-hello.nix ../config.nix $TEST_HOME +cd $TEST_HOME + +cat <<EOF > flake.nix +{ + outputs = {self}: { + packages.$system.pkgAsPkg = (import ./shell-hello.nix).hello; + packages.$system.appAsApp = self.packages.$system.appAsApp; + + apps.$system.pkgAsApp = self.packages.$system.pkgAsPkg; + apps.$system.appAsApp = { + type = "app"; + program = "\${(import ./shell-hello.nix).hello}/bin/hello"; + }; + }; +} +EOF +nix run --no-write-lock-file .#appAsApp +nix run --no-write-lock-file .#pkgAsPkg + +! nix run --no-write-lock-file .#pkgAsApp || fail "'nix run' shouldn’t accept an 'app' defined under 'packages'" +! nix run --no-write-lock-file .#appAsPkg || fail "elements of 'apps' should be of type 'app'" + +clearStore + diff --git a/tests/functional/flakes/search-root.sh b/tests/functional/flakes/search-root.sh new file mode 100644 index 000000000..d8586dc8a --- /dev/null +++ b/tests/functional/flakes/search-root.sh @@ -0,0 +1,50 @@ +source common.sh + +clearStore + +writeSimpleFlake $TEST_HOME +cd $TEST_HOME +mkdir -p foo/subdir + +echo '{ outputs = _: {}; }' > foo/flake.nix +cat <<EOF > flake.nix +{ + inputs.foo.url = "$PWD/foo"; + outputs = a: { + packages.$system = rec { + test = import ./simple.nix; + default = test; + }; + }; +} +EOF +mkdir subdir +pushd subdir + +success=("" . .# .#test ../subdir ../subdir#test "$PWD") +failure=("path:$PWD") + +for i in "${success[@]}"; do + nix build $i || fail "flake should be found by searching up directories" +done + +for i in "${failure[@]}"; do + ! nix build $i || fail "flake should not search up directories when using 'path:'" +done + +popd + +nix build --override-input foo . || fail "flake should search up directories when not an installable" + +sed "s,$PWD/foo,$PWD/foo/subdir,g" -i flake.nix +! nix build || fail "flake should not search upwards when part of inputs" + +if [[ -n $(type -p git) ]]; then + pushd subdir + git init + for i in "${success[@]}" "${failure[@]}"; do + ! nix build $i || fail "flake should not search past a git repository" + done + rm -rf .git + popd +fi diff --git a/tests/functional/flakes/show.sh b/tests/functional/flakes/show.sh new file mode 100644 index 000000000..a3d300552 --- /dev/null +++ b/tests/functional/flakes/show.sh @@ -0,0 +1,87 @@ +source ./common.sh + +flakeDir=$TEST_ROOT/flake +mkdir -p "$flakeDir" + +writeSimpleFlake "$flakeDir" +cd "$flakeDir" + + +# By default: Only show the packages content for the current system and no +# legacyPackages at all +nix flake show --json > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.packages.someOtherSystem.default == {}; +assert show_output.packages.${builtins.currentSystem}.default.name == "simple"; +assert show_output.legacyPackages.${builtins.currentSystem} == {}; +true +' + +# With `--all-systems`, show the packages for all systems +nix flake show --json --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.packages.someOtherSystem.default.name == "simple"; +assert show_output.legacyPackages.${builtins.currentSystem} == {}; +true +' + +# With `--legacy`, show the legacy packages +nix flake show --json --legacy > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.legacyPackages.${builtins.currentSystem}.hello.name == "simple"; +true +' + +# Test that attributes are only reported when they have actual content +cat >flake.nix <<EOF +{ + description = "Bla bla"; + + outputs = inputs: rec { + apps.$system = { }; + checks.$system = { }; + devShells.$system = { }; + legacyPackages.$system = { }; + packages.$system = { }; + packages.someOtherSystem = { }; + + formatter = { }; + nixosConfigurations = { }; + nixosModules = { }; + }; +} +EOF +nix flake show --json --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output == { }; +true +' + +# Test that attributes with errors are handled correctly. +# nixpkgs.legacyPackages is a particularly prominent instance of this. +cat >flake.nix <<EOF +{ + outputs = inputs: { + legacyPackages.$system = { + AAAAAASomeThingsFailToEvaluate = throw "nooo"; + simple = import ./simple.nix; + }; + }; +} +EOF +nix flake show --json --legacy --all-systems > show-output.json +nix eval --impure --expr ' +let show_output = builtins.fromJSON (builtins.readFile ./show-output.json); +in +assert show_output.legacyPackages.${builtins.currentSystem}.AAAAAASomeThingsFailToEvaluate == { }; +assert show_output.legacyPackages.${builtins.currentSystem}.simple.name == "simple"; +true +' diff --git a/tests/functional/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh new file mode 100644 index 000000000..8abc8b7d3 --- /dev/null +++ b/tests/functional/flakes/unlocked-override.sh @@ -0,0 +1,30 @@ +source ./common.sh + +requireGit + +flake1Dir=$TEST_ROOT/flake1 +flake2Dir=$TEST_ROOT/flake2 + +createGitRepo $flake1Dir +cat > $flake1Dir/flake.nix <<EOF +{ + outputs = { self }: { x = import ./x.nix; }; +} +EOF +echo 123 > $flake1Dir/x.nix +git -C $flake1Dir add flake.nix x.nix +git -C $flake1Dir commit -m Initial + +createGitRepo $flake2Dir +cat > $flake2Dir/flake.nix <<EOF +{ + outputs = { self, flake1 }: { x = flake1.x; }; +} +EOF +git -C $flake2Dir add flake.nix + +[[ $(nix eval --json $flake2Dir#x --override-input flake1 $TEST_ROOT/flake1) = 123 ]] + +echo 456 > $flake1Dir/x.nix + +[[ $(nix eval --json $flake2Dir#x --override-input flake1 $TEST_ROOT/flake1) = 456 ]] diff --git a/tests/functional/fmt.sh b/tests/functional/fmt.sh new file mode 100644 index 000000000..3c1bd9989 --- /dev/null +++ b/tests/functional/fmt.sh @@ -0,0 +1,33 @@ +source common.sh + +clearStore +rm -rf $TEST_HOME/.cache $TEST_HOME/.config $TEST_HOME/.local + +cp ./simple.nix ./simple.builder.sh ./fmt.simple.sh ./config.nix $TEST_HOME + +cd $TEST_HOME + +nix fmt --help | grep "Format" + +cat << EOF > flake.nix +{ + outputs = _: { + formatter.$system = + with import ./config.nix; + mkDerivation { + name = "formatter"; + buildCommand = '' + mkdir -p \$out/bin + echo "#! ${shell}" > \$out/bin/formatter + cat \${./fmt.simple.sh} >> \$out/bin/formatter + chmod +x \$out/bin/formatter + ''; + }; + }; +} +EOF +nix fmt ./file ./folder | grep 'Formatting: ./file ./folder' +nix flake check +nix flake show | grep -P "package 'formatter'" + +clearStore diff --git a/tests/functional/fmt.simple.sh b/tests/functional/fmt.simple.sh new file mode 100755 index 000000000..4c8c67ebb --- /dev/null +++ b/tests/functional/fmt.simple.sh @@ -0,0 +1 @@ +echo Formatting: "${@}" diff --git a/tests/functional/function-trace.sh b/tests/functional/function-trace.sh new file mode 100755 index 000000000..bd804bf18 --- /dev/null +++ b/tests/functional/function-trace.sh @@ -0,0 +1,67 @@ +source common.sh + +set +x + +expect_trace() { + expr="$1" + expect="$2" + actual=$( + nix-instantiate \ + --trace-function-calls \ + --expr "$expr" 2>&1 \ + | grep "function-trace" \ + | sed -e 's/ [0-9]*$//' \ + || true + ) + + echo -n "Tracing expression '$expr'" + msg=$(diff -swB \ + <(echo "$expect") \ + <(echo "$actual") + ) && result=0 || result=$? + if [ $result -eq 0 ]; then + echo " ok." + else + echo " failed. difference:" + echo "$msg" + return $result + fi +} + +# failure inside a tryEval +expect_trace 'builtins.tryEval (throw "example")' " +function-trace entered «string»:1:1 at +function-trace entered «string»:1:19 at +function-trace exited «string»:1:19 at +function-trace exited «string»:1:1 at +" + +# Missing argument to a formal function +expect_trace '({ x }: x) { }' " +function-trace entered «string»:1:1 at +function-trace exited «string»:1:1 at +" + +# Too many arguments to a formal function +expect_trace '({ x }: x) { x = "x"; y = "y"; }' " +function-trace entered «string»:1:1 at +function-trace exited «string»:1:1 at +" + +# Not enough arguments to a lambda +expect_trace '(x: y: x + y) 1' " +function-trace entered «string»:1:1 at +function-trace exited «string»:1:1 at +" + +# Too many arguments to a lambda +expect_trace '(x: x) 1 2' " +function-trace entered «string»:1:1 at +function-trace exited «string»:1:1 at +" + +# Not a function +expect_trace '1 2' " +function-trace entered «string»:1:1 at +function-trace exited «string»:1:1 at +" diff --git a/tests/functional/gc-auto.sh b/tests/functional/gc-auto.sh new file mode 100644 index 000000000..521d9e539 --- /dev/null +++ b/tests/functional/gc-auto.sh @@ -0,0 +1,81 @@ +source common.sh + +needLocalStore "“min-free” and “max-free” are daemon options" + +clearStore + +garbage1=$(nix store add-path --name garbage1 ./nar-access.sh) +garbage2=$(nix store add-path --name garbage2 ./nar-access.sh) +garbage3=$(nix store add-path --name garbage3 ./nar-access.sh) + +ls -l $garbage3 +POSIXLY_CORRECT=1 du $garbage3 + +fake_free=$TEST_ROOT/fake-free +export _NIX_TEST_FREE_SPACE_FILE=$fake_free +echo 1100 > $fake_free + +fifoLock=$TEST_ROOT/fifoLock +mkfifo "$fifoLock" + +expr=$(cat <<EOF +with import ./config.nix; mkDerivation { + name = "gc-A"; + buildCommand = '' + set -x + [[ \$(ls \$NIX_STORE/*-garbage? | wc -l) = 3 ]] + + mkdir \$out + echo foo > \$out/bar + + # Pretend that we run out of space + echo 100 > ${fake_free}.tmp1 + mv ${fake_free}.tmp1 $fake_free + + # Wait for the GC to run + for i in {1..20}; do + echo ''\${i}... + if [[ \$(ls \$NIX_STORE/*-garbage? | wc -l) = 1 ]]; then + exit 0 + fi + sleep 1 + done + exit 1 + ''; +} +EOF +) + +expr2=$(cat <<EOF +with import ./config.nix; mkDerivation { + name = "gc-B"; + buildCommand = '' + set -x + mkdir \$out + echo foo > \$out/bar + + # Wait for the first build to finish + cat "$fifoLock" + ''; +} +EOF +) + +nix build --impure -v -o $TEST_ROOT/result-A -L --expr "$expr" \ + --min-free 1000 --max-free 2000 --min-free-check-interval 1 & +pid1=$! + +nix build --impure -v -o $TEST_ROOT/result-B -L --expr "$expr2" \ + --min-free 1000 --max-free 2000 --min-free-check-interval 1 & +pid2=$! + +# Once the first build is done, unblock the second one. +# If the first build fails, we need to postpone the failure to still allow +# the second one to finish +wait "$pid1" || FIRSTBUILDSTATUS=$? +echo "unlock" > $fifoLock +( exit ${FIRSTBUILDSTATUS:-0} ) +wait "$pid2" + +[[ foo = $(cat $TEST_ROOT/result-A/bar) ]] +[[ foo = $(cat $TEST_ROOT/result-B/bar) ]] diff --git a/tests/functional/gc-concurrent.builder.sh b/tests/functional/gc-concurrent.builder.sh new file mode 100644 index 000000000..bb6dcd4cf --- /dev/null +++ b/tests/functional/gc-concurrent.builder.sh @@ -0,0 +1,16 @@ +echo "Build started" > "$lockFifo" + +mkdir $out +echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar + +# Wait for someone to write on the fifo +cat "$lockFifo" + +# $out should not have been GC'ed while we were sleeping, but just in +# case... +mkdir -p $out + +# Check that the GC hasn't deleted the lock on our output. +test -e "$out.lock" + +ln -s $input2 $out/input-2 diff --git a/tests/functional/gc-concurrent.nix b/tests/functional/gc-concurrent.nix new file mode 100644 index 000000000..0aba1f983 --- /dev/null +++ b/tests/functional/gc-concurrent.nix @@ -0,0 +1,30 @@ +with import ./config.nix; + +{ lockFifo ? null }: + +rec { + + input1 = mkDerivation { + name = "dependencies-input-1"; + buildCommand = "mkdir $out; echo FOO > $out/foo"; + }; + + input2 = mkDerivation { + name = "dependencies-input-2"; + buildCommand = "mkdir $out; echo BAR > $out/bar"; + }; + + test1 = mkDerivation { + name = "gc-concurrent"; + builder = ./gc-concurrent.builder.sh; + inherit input1 input2; + inherit lockFifo; + }; + + test2 = mkDerivation { + name = "gc-concurrent2"; + builder = ./gc-concurrent2.builder.sh; + inherit input1 input2; + }; + +} diff --git a/tests/functional/gc-concurrent.sh b/tests/functional/gc-concurrent.sh new file mode 100644 index 000000000..2c6622c62 --- /dev/null +++ b/tests/functional/gc-concurrent.sh @@ -0,0 +1,59 @@ +source common.sh + +clearStore + +lockFifo1=$TEST_ROOT/test1.fifo +mkfifo "$lockFifo1" + +drvPath1=$(nix-instantiate gc-concurrent.nix -A test1 --argstr lockFifo "$lockFifo1") +outPath1=$(nix-store -q $drvPath1) + +drvPath2=$(nix-instantiate gc-concurrent.nix -A test2) +outPath2=$(nix-store -q $drvPath2) + +drvPath3=$(nix-instantiate simple.nix) +outPath3=$(nix-store -r $drvPath3) + +(! test -e $outPath3.lock) +touch $outPath3.lock + +rm -f "$NIX_STATE_DIR"/gcroots/foo* +ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo +ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2 + +# Start build #1 in the background. It starts immediately. +nix-store -rvv "$drvPath1" & +pid1=$! + +# Wait for the build of $drvPath1 to start +cat $lockFifo1 + +# Run the garbage collector while the build is running. +nix-collect-garbage + +# Unlock the build of $drvPath1 +echo "" > $lockFifo1 +echo waiting for pid $pid1 to finish... +wait $pid1 + +# Check that the root of build #1 and its dependencies haven't been +# deleted. The should not be deleted by the GC because they were +# being built during the GC. +cat $outPath1/foobar +cat $outPath1/input-2/bar + +# Check that the build build $drvPath2 succeeds. +# It should succeed because the derivation is a GC root. +nix-store -rvv "$drvPath2" +cat $outPath2/foobar + +rm -f "$NIX_STATE_DIR"/gcroots/foo* + +# The collector should have deleted lock files for paths that have +# been built previously. +(! test -e $outPath3.lock) + +# If we run the collector now, it should delete outPath1/2. +nix-collect-garbage +(! test -e $outPath1) +(! test -e $outPath2) diff --git a/tests/functional/gc-concurrent2.builder.sh b/tests/functional/gc-concurrent2.builder.sh new file mode 100644 index 000000000..4f6c58b96 --- /dev/null +++ b/tests/functional/gc-concurrent2.builder.sh @@ -0,0 +1,5 @@ +mkdir $out +echo $(cat $input1/foo)$(cat $input2/bar)xyzzy > $out/foobar + +# Check that the GC hasn't deleted the lock on our output. +test -e "$out.lock" diff --git a/tests/functional/gc-non-blocking.sh b/tests/functional/gc-non-blocking.sh new file mode 100644 index 000000000..0d781485d --- /dev/null +++ b/tests/functional/gc-non-blocking.sh @@ -0,0 +1,33 @@ +# Test whether the collector is non-blocking, i.e. a build can run in +# parallel with it. +source common.sh + +needLocalStore "the GC test needs a synchronisation point" + +clearStore + +fifo=$TEST_ROOT/test.fifo +mkfifo "$fifo" + +dummy=$(nix store add-path ./simple.nix) + +running=$TEST_ROOT/running +touch $running + +(_NIX_TEST_GC_SYNC=$fifo nix-store --gc -vvvvv; rm $running) & +pid=$! + +sleep 2 + +outPath=$(nix-build --max-silent-time 60 -o "$TEST_ROOT/result" -E " + with import ./config.nix; + mkDerivation { + name = \"non-blocking\"; + buildCommand = \"set -x; test -e $running; mkdir \$out; echo > $fifo\"; + }") + +wait $pid + +(! test -e $running) +(! test -e $dummy) +test -e $outPath diff --git a/tests/functional/gc-runtime.nix b/tests/functional/gc-runtime.nix new file mode 100644 index 000000000..ee5980bdf --- /dev/null +++ b/tests/functional/gc-runtime.nix @@ -0,0 +1,17 @@ +with import ./config.nix; + +mkDerivation { + name = "gc-runtime"; + builder = + # Test inline source file definitions. + builtins.toFile "builder.sh" '' + mkdir $out + + cat > $out/program <<EOF + #! ${shell} + sleep 10000 + EOF + + chmod +x $out/program + ''; +} diff --git a/tests/functional/gc-runtime.sh b/tests/functional/gc-runtime.sh new file mode 100644 index 000000000..dc1826a55 --- /dev/null +++ b/tests/functional/gc-runtime.sh @@ -0,0 +1,38 @@ +source common.sh + +case $system in + *linux*) + ;; + *) + skipTest "Not running Linux"; +esac + +set -m # enable job control, needed for kill + +profiles="$NIX_STATE_DIR"/profiles +rm -rf $profiles + +nix-env -p $profiles/test -f ./gc-runtime.nix -i gc-runtime + +outPath=$(nix-env -p $profiles/test -q --no-name --out-path gc-runtime) +echo $outPath + +echo "backgrounding program..." +$profiles/test/program & +sleep 2 # hack - wait for the program to get started +child=$! +echo PID=$child + +nix-env -p $profiles/test -e gc-runtime +nix-env -p $profiles/test --delete-generations old + +nix-store --gc + +kill -- -$child + +if ! test -e $outPath; then + echo "running program was garbage collected!" + exit 1 +fi + +exit 0 diff --git a/tests/functional/gc.sh b/tests/functional/gc.sh new file mode 100644 index 000000000..ad09a8b39 --- /dev/null +++ b/tests/functional/gc.sh @@ -0,0 +1,52 @@ +source common.sh + +clearStore + +drvPath=$(nix-instantiate dependencies.nix) +outPath=$(nix-store -rvv "$drvPath") + +# Set a GC root. +rm -f "$NIX_STATE_DIR"/gcroots/foo +ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo + +[ "$(nix-store -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ] + +nix-store --gc --print-roots | grep $outPath +nix-store --gc --print-live | grep $outPath +nix-store --gc --print-dead | grep $drvPath +if nix-store --gc --print-dead | grep -E $outPath$; then false; fi + +nix-store --gc --print-dead + +inUse=$(readLink $outPath/reference-to-input-2) +if nix-store --delete $inUse; then false; fi +test -e $inUse + +if nix-store --delete $outPath; then false; fi +test -e $outPath + +for i in $NIX_STORE_DIR/*; do + if [[ $i =~ /trash ]]; then continue; fi # compat with old daemon + touch $i.lock + touch $i.chroot +done + +nix-collect-garbage + +# Check that the root and its dependencies haven't been deleted. +cat $outPath/foobar +cat $outPath/reference-to-input-2/bar + +# Check that the derivation has been GC'd. +if test -e $drvPath; then false; fi + +rm "$NIX_STATE_DIR"/gcroots/foo + +nix-collect-garbage + +# Check that the output has been GC'd. +if test -e $outPath/foobar; then false; fi + +# Check that the store is empty. +rmdir $NIX_STORE_DIR/.links +rmdir $NIX_STORE_DIR diff --git a/tests/functional/hash-check.nix b/tests/functional/hash-check.nix new file mode 100644 index 000000000..4a8e9b8a8 --- /dev/null +++ b/tests/functional/hash-check.nix @@ -0,0 +1,29 @@ +let { + + input1 = derivation { + name = "dependencies-input-1"; + system = "i086-msdos"; + builder = "/bar/sh"; + args = ["-e" "-x" ./dummy]; + }; + + input2 = derivation { + name = "dependencies-input-2"; + system = "i086-msdos"; + builder = "/bar/sh"; + args = ["-e" "-x" ./dummy]; + outputHashMode = "recursive"; + outputHashAlgo = "md5"; + outputHash = "ffffffffffffffffffffffffffffffff"; + }; + + body = derivation { + name = "dependencies"; + system = "i086-msdos"; + builder = "/bar/sh"; + args = ["-e" "-x" (./dummy + "/FOOBAR/../.")]; + input1 = input1 + "/."; + inherit input2; + }; + +}
\ No newline at end of file diff --git a/tests/functional/hash.sh b/tests/functional/hash.sh new file mode 100644 index 000000000..34c1bb38a --- /dev/null +++ b/tests/functional/hash.sh @@ -0,0 +1,107 @@ +source common.sh + +try () { + printf "%s" "$2" > $TEST_ROOT/vector + hash="$(nix-hash --flat ${FORMAT_FLAG-} --type "$1" "$TEST_ROOT/vector")" + if ! (( "${NO_TEST_CLASSIC-}" )) && test "$hash" != "$3"; then + echo "try nix-hash: hash $1, expected $3, got $hash" + exit 1 + fi + hash="$(nix hash file ${FORMAT_FLAG-} --type "$1" "$TEST_ROOT/vector")" + if ! (( "${NO_TEST_NIX_COMMAND-}" )) && test "$hash" != "$3"; then + echo "try nix hash: hash $1, expected $3, got $hash" + exit 1 + fi +} + +FORMAT_FLAG=--base16 +try md5 "" "d41d8cd98f00b204e9800998ecf8427e" +try md5 "a" "0cc175b9c0f1b6a831c399e269772661" +try md5 "abc" "900150983cd24fb0d6963f7d28e17f72" +try md5 "message digest" "f96b697d7cb7938d525a2f31aaf161d0" +try md5 "abcdefghijklmnopqrstuvwxyz" "c3fcd3d76192e4007dfb496cca67e13b" +try md5 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" "d174ab98d277d9f5a5611c2c9f419d9f" +try md5 "12345678901234567890123456789012345678901234567890123456789012345678901234567890" "57edf4a22be3c955ac49da2e2107b67a" + +try sha1 "" "da39a3ee5e6b4b0d3255bfef95601890afd80709" +try sha1 "abc" "a9993e364706816aba3e25717850c26c9cd0d89d" +try sha1 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "84983e441c3bd26ebaae4aa1f95129e5e54670f1" + +try sha256 "" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" +try sha256 "abc" "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" +try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1" + +try sha512 "" "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" +try sha512 "abc" "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" +try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" +unset FORMAT_FLAG + +FORMAT_FLAG=--base32 +try sha256 "abc" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" +unset FORMAT_FLAG + +FORMAT_FLAG=--sri +try sha512 "" "sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg==" +try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw==" +try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha512-IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ==" +try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha256-JI1qYdIGOLjlwCaTDD5gOaM85Flk/yFn9uzt1BnbBsE=" +unset FORMAT_FLAG + +# nix-hash [--flat] defaults to the Base16 format +NO_TEST_NIX_COMMAND=1 try sha512 "abc" "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f" + +# nix hash [file|path] defaults to the SRI format +NO_TEST_CLASSIC=1 try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw==" + +try2 () { + hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path) + if test "$hash" != "$2"; then + echo "hash $1, expected $2, got $hash" + exit 1 + fi +} + +rm -rf $TEST_ROOT/hash-path +mkdir $TEST_ROOT/hash-path +echo "Hello World" > $TEST_ROOT/hash-path/hello + +try2 md5 "ea9b55537dd4c7e104515b2ccfaf4100" + +# Execute bit matters. +chmod +x $TEST_ROOT/hash-path/hello +try2 md5 "20f3ffe011d4cfa7d72bfabef7882836" + +# Mtime and other bits don't. +touch -r . $TEST_ROOT/hash-path/hello +chmod 744 $TEST_ROOT/hash-path/hello +try2 md5 "20f3ffe011d4cfa7d72bfabef7882836" + +# File type (e.g., symlink) does. +rm $TEST_ROOT/hash-path/hello +ln -s x $TEST_ROOT/hash-path/hello +try2 md5 "f78b733a68f5edbdf9413899339eaa4a" + +# Conversion. +try3() { + h64=$(nix-hash --type "$1" --to-base64 "$2") + [ "$h64" = "$4" ] + h64=$(nix hash to-base64 --type "$1" "$2") + [ "$h64" = "$4" ] + sri=$(nix-hash --type "$1" --to-sri "$2") + [ "$sri" = "$1-$4" ] + sri=$(nix hash to-sri --type "$1" "$2") + [ "$sri" = "$1-$4" ] + h32=$(nix-hash --type "$1" --to-base32 "$2") + [ "$h32" = "$3" ] + h32=$(nix hash to-base32 --type "$1" "$2") + [ "$h32" = "$3" ] + h16=$(nix-hash --type "$1" --to-base16 "$h32") + [ "$h16" = "$2" ] + h16=$(nix hash to-base16 --type "$1" "$h64") + [ "$h16" = "$2" ] + h16=$(nix hash to-base16 "$sri") + [ "$h16" = "$2" ] +} +try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8=" +try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=" +try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ==" diff --git a/tests/functional/hermetic.nix b/tests/functional/hermetic.nix new file mode 100644 index 000000000..4c9d7a51f --- /dev/null +++ b/tests/functional/hermetic.nix @@ -0,0 +1,56 @@ +{ busybox, seed }: + +with import ./config.nix; + +let + contentAddressedByDefault = builtins.getEnv "NIX_TESTS_CA_BY_DEFAULT" == "1"; + caArgs = if contentAddressedByDefault then { + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + } else {}; + + mkDerivation = args: + derivation ({ + inherit system; + builder = busybox; + args = ["sh" "-e" args.builder or (builtins.toFile "builder-${args.name}.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")]; + } // removeAttrs args ["builder" "meta" "passthru"] + // caArgs) + // { meta = args.meta or {}; passthru = args.passthru or {}; }; + + input1 = mkDerivation { + shell = busybox; + name = "hermetic-input-1"; + buildCommand = "echo hi-input1 seed=${toString seed}; echo FOO > $out"; + }; + + input2 = mkDerivation { + shell = busybox; + name = "hermetic-input-2"; + buildCommand = "echo hi; echo BAR > $out"; + }; + + input3 = mkDerivation { + shell = busybox; + name = "hermetic-input-3"; + buildCommand = '' + echo hi-input3 + read x < ${input2} + echo $x BAZ > $out + ''; + }; + +in + + mkDerivation { + shell = busybox; + name = "hermetic"; + passthru = { inherit input1 input2 input3; }; + buildCommand = + '' + read x < ${input1} + read y < ${input3} + echo "$x $y" > $out + ''; + } diff --git a/tests/functional/import-derivation.nix b/tests/functional/import-derivation.nix new file mode 100644 index 000000000..44fa9a45d --- /dev/null +++ b/tests/functional/import-derivation.nix @@ -0,0 +1,26 @@ +with import ./config.nix; + +let + + bar = mkDerivation { + name = "bar"; + builder = builtins.toFile "builder.sh" + '' + echo 'builtins.add 123 456' > $out + ''; + }; + + value = + # Test that pathExists can check the existence of /nix/store paths + assert builtins.pathExists bar; + import bar; + +in + +mkDerivation { + name = "foo"; + builder = builtins.toFile "builder.sh" + '' + echo -n FOO${toString value} > $out + ''; +} diff --git a/tests/functional/import-derivation.sh b/tests/functional/import-derivation.sh new file mode 100644 index 000000000..98d61ef49 --- /dev/null +++ b/tests/functional/import-derivation.sh @@ -0,0 +1,12 @@ +source common.sh + +clearStore + +if nix-instantiate --readonly-mode ./import-derivation.nix; then + echo "read-only evaluation of an imported derivation unexpectedly failed" + exit 1 +fi + +outPath=$(nix-build ./import-derivation.nix --no-out-link) + +[ "$(cat $outPath)" = FOO579 ] diff --git a/tests/functional/impure-derivations.nix b/tests/functional/impure-derivations.nix new file mode 100644 index 000000000..98547e6c1 --- /dev/null +++ b/tests/functional/impure-derivations.nix @@ -0,0 +1,63 @@ +with import ./config.nix; + +rec { + + impure = mkDerivation { + name = "impure"; + outputs = [ "out" "stuff" ]; + buildCommand = + '' + echo impure + x=$(< $TEST_ROOT/counter) + mkdir $out $stuff + echo $x > $out/n + ln -s $out/n $stuff/bla + printf $((x + 1)) > $TEST_ROOT/counter + ''; + __impure = true; + impureEnvVars = [ "TEST_ROOT" ]; + }; + + impureOnImpure = mkDerivation { + name = "impure-on-impure"; + buildCommand = + '' + echo impure-on-impure + x=$(< ${impure}/n) + mkdir $out + printf X$x > $out/n + ln -s ${impure.stuff} $out/symlink + ln -s $out $out/self + ''; + __impure = true; + }; + + # This is not allowed. + inputAddressed = mkDerivation { + name = "input-addressed"; + buildCommand = + '' + cat ${impure} > $out + ''; + }; + + contentAddressed = mkDerivation { + name = "content-addressed"; + buildCommand = + '' + echo content-addressed + x=$(< ${impureOnImpure}/n) + printf ''${x:0:1} > $out + ''; + outputHashMode = "recursive"; + outputHash = "sha256-eBYxcgkuWuiqs4cKNgKwkb3vY/HR0vVsJnqe8itJGcQ="; + }; + + inputAddressedAfterCA = mkDerivation { + name = "input-addressed-after-ca"; + buildCommand = + '' + cat ${contentAddressed} > $out + ''; + }; +} diff --git a/tests/functional/impure-derivations.sh b/tests/functional/impure-derivations.sh new file mode 100644 index 000000000..39d053a04 --- /dev/null +++ b/tests/functional/impure-derivations.sh @@ -0,0 +1,65 @@ +source common.sh + +requireDaemonNewerThan "2.8pre20220311" + +enableFeatures "ca-derivations impure-derivations" +restartDaemon + +clearStore + +# Basic test of impure derivations: building one a second time should not use the previous result. +printf 0 > $TEST_ROOT/counter + +# `nix derivation add` with impure derivations work +drvPath=$(nix-instantiate ./impure-derivations.nix -A impure) +nix derivation show $drvPath | jq .[] > $TEST_HOME/impure-drv.json +drvPath2=$(nix derivation add < $TEST_HOME/impure-drv.json) +[[ "$drvPath" = "$drvPath2" ]] + +# But only with the experimental feature! +expectStderr 1 nix derivation add < $TEST_HOME/impure-drv.json --experimental-features nix-command | grepQuiet "experimental Nix feature 'impure-derivations' is disabled" + +nix build --dry-run --json --file ./impure-derivations.nix impure.all +json=$(nix build -L --no-link --json --file ./impure-derivations.nix impure.all) +path1=$(echo $json | jq -r .[].outputs.out) +path1_stuff=$(echo $json | jq -r .[].outputs.stuff) +[[ $(< $path1/n) = 0 ]] +[[ $(< $path1_stuff/bla) = 0 ]] + +[[ $(nix path-info --json $path1 | jq .[].ca) =~ fixed:r:sha256: ]] + +path2=$(nix build -L --no-link --json --file ./impure-derivations.nix impure | jq -r .[].outputs.out) +[[ $(< $path2/n) = 1 ]] + +# Test impure derivations that depend on impure derivations. +path3=$(nix build -L --no-link --json --file ./impure-derivations.nix impureOnImpure | jq -r .[].outputs.out) +[[ $(< $path3/n) = X2 ]] + +path4=$(nix build -L --no-link --json --file ./impure-derivations.nix impureOnImpure | jq -r .[].outputs.out) +[[ $(< $path4/n) = X3 ]] + +# Test that (self-)references work. +[[ $(< $path4/symlink/bla) = 3 ]] +[[ $(< $path4/self/n) = X3 ]] + +# Input-addressed derivations cannot depend on impure derivations directly. +(! nix build -L --no-link --json --file ./impure-derivations.nix inputAddressed 2>&1) | grep 'depends on impure derivation' + +drvPath=$(nix eval --json --file ./impure-derivations.nix impure.drvPath | jq -r .) +[[ $(nix derivation show $drvPath | jq ".[\"$drvPath\"].outputs.out.impure") = true ]] +[[ $(nix derivation show $drvPath | jq ".[\"$drvPath\"].outputs.stuff.impure") = true ]] + +# Fixed-output derivations *can* depend on impure derivations. +path5=$(nix build -L --no-link --json --file ./impure-derivations.nix contentAddressed | jq -r .[].outputs.out) +[[ $(< $path5) = X ]] +[[ $(< $TEST_ROOT/counter) = 5 ]] + +# And they should not be rebuilt. +path5=$(nix build -L --no-link --json --file ./impure-derivations.nix contentAddressed | jq -r .[].outputs.out) +[[ $(< $path5) = X ]] +[[ $(< $TEST_ROOT/counter) = 5 ]] + +# Input-addressed derivations can depend on fixed-output derivations that depend on impure derivations. +path6=$(nix build -L --no-link --json --file ./impure-derivations.nix inputAddressedAfterCA | jq -r .[].outputs.out) +[[ $(< $path6) = X ]] +[[ $(< $TEST_ROOT/counter) = 5 ]] diff --git a/tests/functional/init.sh b/tests/functional/init.sh new file mode 100755 index 000000000..c420e8c9f --- /dev/null +++ b/tests/functional/init.sh @@ -0,0 +1,41 @@ +# Don't start the daemon +source common/vars-and-functions.sh + +test -n "$TEST_ROOT" +if test -d "$TEST_ROOT"; then + chmod -R u+w "$TEST_ROOT" + # We would delete any daemon socket, so let's stop the daemon first. + killDaemon + rm -rf "$TEST_ROOT" +fi +mkdir "$TEST_ROOT" + +mkdir "$NIX_STORE_DIR" +mkdir "$NIX_LOCALSTATE_DIR" +mkdir -p "$NIX_LOG_DIR"/drvs +mkdir "$NIX_STATE_DIR" +mkdir "$NIX_CONF_DIR" + +cat > "$NIX_CONF_DIR"/nix.conf <<EOF +build-users-group = +keep-derivations = false +sandbox = false +experimental-features = nix-command flakes +gc-reserved-space = 0 +substituters = +flake-registry = $TEST_ROOT/registry.json +show-trace = true +include nix.conf.extra +trusted-users = $(whoami) +EOF + +cat > "$NIX_CONF_DIR"/nix.conf.extra <<EOF +fsync-metadata = false +!include nix.conf.extra.not-there +EOF + +# Initialise the database. +nix-store --init + +# Did anything happen? +test -e "$NIX_STATE_DIR"/db/db.sqlite diff --git a/tests/functional/install-darwin.sh b/tests/functional/install-darwin.sh new file mode 100755 index 000000000..ea2b75323 --- /dev/null +++ b/tests/functional/install-darwin.sh @@ -0,0 +1,96 @@ +#!/bin/sh + +set -eux + +cleanup() { + PLIST="/Library/LaunchDaemons/org.nixos.nix-daemon.plist" + if sudo launchctl list | grepQuiet nix-daemon; then + sudo launchctl unload "$PLIST" + fi + + if [ -f "$PLIST" ]; then + sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist + fi + + profiles=(/etc/profile /etc/bashrc /etc/zshrc) + for profile in "${profiles[@]}"; do + if [ -f "${profile}.backup-before-nix" ]; then + sudo mv "${profile}.backup-before-nix" "${profile}" + fi + done + + for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do + if [ -e "$file" ]; then + cat "$file" | grep -v nix-profile > "$file.next" + mv "$file.next" "$file" + fi + done + + for i in $(seq 1 $(sysctl -n hw.ncpu)); do + sudo /usr/bin/dscl . -delete "/Users/nixbld$i" || true + done + sudo /usr/bin/dscl . -delete "/Groups/nixbld" || true + + sudo rm -rf /etc/nix \ + /nix \ + /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels \ + "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels" +} + +verify() { + set +e + output=$(echo "nix-shell -p bash --run 'echo toow | rev'" | bash -l) + set -e + + test "$output" = "woot" +} + +scratch=$(mktemp -d -t tmp.XXXXXXXXXX) +function finish { + rm -rf "$scratch" +} +trap finish EXIT + +# First setup Nix +cleanup +curl -L -o install https://nixos.org/nix/install +yes | bash ./install +verify + + +( + set +e + ( + echo "cd $(pwd)" + echo nix-build ./release.nix -A binaryTarball.x86_64-darwin + ) | bash -l + set -e + cp ./result/nix-*.tar.bz2 $scratch/nix.tar.bz2 +) + +( + cd $scratch + tar -xf ./nix.tar.bz2 + + cd nix-* + + set -eux + + cleanup + + yes | ./install + verify + cleanup + + echo -n "" | ./install + verify + cleanup + + sudo mkdir -p /nix/store + sudo touch /nix/store/.silly-hint + echo -n "" | ALLOW_PREEXISTING_INSTALLATION=true ./install + verify + test -e /nix/store/.silly-hint + + cleanup +) diff --git a/tests/functional/lang-test-infra.sh b/tests/functional/lang-test-infra.sh new file mode 100644 index 000000000..30da8977b --- /dev/null +++ b/tests/functional/lang-test-infra.sh @@ -0,0 +1,86 @@ +# Test the function for lang.sh +source common.sh + +source lang/framework.sh + +# We are testing this, so don't want outside world to affect us. +unset _NIX_TEST_ACCEPT + +# We'll only modify this in subshells so we don't need to reset it. +badDiff=0 + +# matches non-empty +echo Hi! > "$TEST_ROOT/got" +cp "$TEST_ROOT/got" "$TEST_ROOT/expected" +( + diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 0 )) +) + +# matches empty, non-existant file is the same as empty file +echo -n > "$TEST_ROOT/got" +( + diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/does-not-exist" + (( "$badDiff" == 0 )) +) + +# doesn't matches non-empty, non-existant file is the same as empty file +echo Hi! > "$TEST_ROOT/got" +( + diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/does-not-exist" + (( "$badDiff" == 1 )) +) + +# doesn't match, `badDiff` set, file unchanged +echo Hi! > "$TEST_ROOT/got" +echo Bye! > "$TEST_ROOT/expected" +( + diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 1 )) +) +[[ "$(echo Bye! )" == $(< "$TEST_ROOT/expected") ]] + +# _NIX_TEST_ACCEPT=1 matches non-empty +echo Hi! > "$TEST_ROOT/got" +cp "$TEST_ROOT/got" "$TEST_ROOT/expected" +( + _NIX_TEST_ACCEPT=1 diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 0 )) +) + +# _NIX_TEST_ACCEPT doesn't match, `badDiff=1` set, file changed (was previously non-empty) +echo Hi! > "$TEST_ROOT/got" +echo Bye! > "$TEST_ROOT/expected" +( + _NIX_TEST_ACCEPT=1 diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 1 )) +) +[[ "$(echo Hi! )" == $(< "$TEST_ROOT/expected") ]] +# second time succeeds +( + diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 0 )) +) + +# _NIX_TEST_ACCEPT matches empty, non-existant file not created +echo -n > "$TEST_ROOT/got" +( + _NIX_TEST_ACCEPT=1 diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/does-not-exists" + (( "$badDiff" == 0 )) +) +[[ ! -f "$TEST_ROOT/does-not-exist" ]] + +# _NIX_TEST_ACCEPT doesn't match, output empty, file deleted +echo -n > "$TEST_ROOT/got" +echo Bye! > "$TEST_ROOT/expected" +badDiff=0 +( + _NIX_TEST_ACCEPT=1 diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 1 )) +) +[[ ! -f "$TEST_ROOT/expected" ]] +# second time succeeds +( + diffAndAcceptInner test "$TEST_ROOT/got" "$TEST_ROOT/expected" + (( "$badDiff" == 0 )) +) diff --git a/tests/functional/lang.sh b/tests/functional/lang.sh new file mode 100755 index 000000000..f4760eced --- /dev/null +++ b/tests/functional/lang.sh @@ -0,0 +1,146 @@ +source common.sh + +set -o pipefail + +source lang/framework.sh + +# specialize function a bit +function diffAndAccept() { + local -r testName="$1" + local -r got="lang/$testName.$2" + local -r expected="lang/$testName.$3" + diffAndAcceptInner "$testName" "$got" "$expected" +} + +export TEST_VAR=foo # for eval-okay-getenv.nix +export NIX_REMOTE=dummy:// +export NIX_STORE_DIR=/nix/store + +nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>&1 | grepQuiet Hello +nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>/dev/null | grepQuiet 123 +nix-instantiate --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 +nix-instantiate --trace-verbose --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuiet Hello +nix-instantiate --eval -E 'builtins.traceVerbose "Hello" 123' 2>&1 | grepQuietInverse Hello +nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grepQuietInverse Hello +expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' | grepQuiet Hello + +nix-instantiate --eval -E 'let x = builtins.trace { x = x; } true; in x' \ + 2>&1 | grepQuiet -E 'trace: { x = «potential infinite recursion»; }' + +nix-instantiate --eval -E 'let x = { repeating = x; tracing = builtins.trace x true; }; in x.tracing'\ + 2>&1 | grepQuiet -F 'trace: { repeating = «repeated»; tracing = «potential infinite recursion»; }' + +set +x + +badDiff=0 +badExitCode=0 + +for i in lang/parse-fail-*.nix; do + echo "parsing $i (should fail)"; + i=$(basename "$i" .nix) + if expectStderr 1 nix-instantiate --parse - < "lang/$i.nix" > "lang/$i.err" + then + diffAndAccept "$i" err err.exp + else + echo "FAIL: $i shouldn't parse" + badExitCode=1 + fi +done + +for i in lang/parse-okay-*.nix; do + echo "parsing $i (should succeed)"; + i=$(basename "$i" .nix) + if + expect 0 nix-instantiate --parse - < "lang/$i.nix" \ + 1> "lang/$i.out" \ + 2> "lang/$i.err" + then + sed "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + diffAndAccept "$i" out exp + diffAndAccept "$i" err err.exp + else + echo "FAIL: $i should parse" + badExitCode=1 + fi +done + +for i in lang/eval-fail-*.nix; do + echo "evaluating $i (should fail)"; + i=$(basename "$i" .nix) + if + expectStderr 1 nix-instantiate --show-trace "lang/$i.nix" \ + | sed "s!$(pwd)!/pwd!g" > "lang/$i.err" + then + diffAndAccept "$i" err err.exp + else + echo "FAIL: $i shouldn't evaluate" + badExitCode=1 + fi +done + +for i in lang/eval-okay-*.nix; do + echo "evaluating $i (should succeed)"; + i=$(basename "$i" .nix) + + if test -e "lang/$i.exp.xml"; then + if expect 0 nix-instantiate --eval --xml --no-location --strict \ + "lang/$i.nix" > "lang/$i.out.xml" + then + diffAndAccept "$i" out.xml exp.xml + else + echo "FAIL: $i should evaluate" + badExitCode=1 + fi + elif test ! -e "lang/$i.exp-disabled"; then + declare -a flags=() + if test -e "lang/$i.flags"; then + read -r -a flags < "lang/$i.flags" + fi + + if + expect 0 env \ + NIX_PATH=lang/dir3:lang/dir4 \ + HOME=/fake-home \ + nix-instantiate "${flags[@]}" --eval --strict "lang/$i.nix" \ + 1> "lang/$i.out" \ + 2> "lang/$i.err" + then + sed -i "s!$(pwd)!/pwd!g" "lang/$i.out" "lang/$i.err" + diffAndAccept "$i" out exp + diffAndAccept "$i" err err.exp + else + echo "FAIL: $i should evaluate" + badExitCode=1 + fi + fi +done + +if test -n "${_NIX_TEST_ACCEPT-}"; then + if (( "$badDiff" )); then + echo 'Output did mot match, but accepted output as the persisted expected output.' + echo 'That means the next time the tests are run, they should pass.' + else + echo 'NOTE: Environment variable _NIX_TEST_ACCEPT is defined,' + echo 'indicating the unexpected output should be accepted as the expected output going forward,' + echo 'but no tests had unexpected output so there was no expected output to update.' + fi + if (( "$badExitCode" )); then + exit "$badExitCode" + else + skipTest "regenerating golden masters" + fi +else + if (( "$badDiff" )); then + echo '' + echo 'You can rerun this test with:' + echo '' + echo ' _NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test' + echo '' + echo 'to regenerate the files containing the expected output,' + echo 'and then view the git diff to decide whether a change is' + echo 'good/intentional or bad/unintentional.' + echo 'If the diff contains arbitrary or impure information,' + echo 'please improve the normalization that the test applies to the output.' + fi + exit $(( "$badExitCode" + "$badDiff" )) +fi diff --git a/tests/functional/lang/binary-data b/tests/functional/lang/binary-data Binary files differnew file mode 100644 index 000000000..06d740502 --- /dev/null +++ b/tests/functional/lang/binary-data diff --git a/tests/functional/lang/data b/tests/functional/lang/data new file mode 100644 index 000000000..257cc5642 --- /dev/null +++ b/tests/functional/lang/data @@ -0,0 +1 @@ +foo diff --git a/tests/functional/lang/dir1/a.nix b/tests/functional/lang/dir1/a.nix new file mode 100644 index 000000000..231f150c5 --- /dev/null +++ b/tests/functional/lang/dir1/a.nix @@ -0,0 +1 @@ +"a" diff --git a/tests/functional/lang/dir2/a.nix b/tests/functional/lang/dir2/a.nix new file mode 100644 index 000000000..170df520a --- /dev/null +++ b/tests/functional/lang/dir2/a.nix @@ -0,0 +1 @@ +"X" diff --git a/tests/functional/lang/dir2/b.nix b/tests/functional/lang/dir2/b.nix new file mode 100644 index 000000000..19010cc35 --- /dev/null +++ b/tests/functional/lang/dir2/b.nix @@ -0,0 +1 @@ +"b" diff --git a/tests/functional/lang/dir3/a.nix b/tests/functional/lang/dir3/a.nix new file mode 100644 index 000000000..170df520a --- /dev/null +++ b/tests/functional/lang/dir3/a.nix @@ -0,0 +1 @@ +"X" diff --git a/tests/functional/lang/dir3/b.nix b/tests/functional/lang/dir3/b.nix new file mode 100644 index 000000000..170df520a --- /dev/null +++ b/tests/functional/lang/dir3/b.nix @@ -0,0 +1 @@ +"X" diff --git a/tests/functional/lang/dir3/c.nix b/tests/functional/lang/dir3/c.nix new file mode 100644 index 000000000..cdf158597 --- /dev/null +++ b/tests/functional/lang/dir3/c.nix @@ -0,0 +1 @@ +"c" diff --git a/tests/functional/lang/dir4/a.nix b/tests/functional/lang/dir4/a.nix new file mode 100644 index 000000000..170df520a --- /dev/null +++ b/tests/functional/lang/dir4/a.nix @@ -0,0 +1 @@ +"X" diff --git a/tests/functional/lang/dir4/c.nix b/tests/functional/lang/dir4/c.nix new file mode 100644 index 000000000..170df520a --- /dev/null +++ b/tests/functional/lang/dir4/c.nix @@ -0,0 +1 @@ +"X" diff --git a/tests/functional/lang/empty.exp b/tests/functional/lang/empty.exp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/functional/lang/empty.exp diff --git a/tests/functional/lang/eval-fail-abort.err.exp b/tests/functional/lang/eval-fail-abort.err.exp new file mode 100644 index 000000000..345232d3f --- /dev/null +++ b/tests/functional/lang/eval-fail-abort.err.exp @@ -0,0 +1,10 @@ +error: + … while calling the 'abort' builtin + + at /pwd/lang/eval-fail-abort.nix:1:14: + + 1| if true then abort "this should fail" else 1 + | ^ + 2| + + error: evaluation aborted with the following error message: 'this should fail' diff --git a/tests/functional/lang/eval-fail-abort.nix b/tests/functional/lang/eval-fail-abort.nix new file mode 100644 index 000000000..75c51bceb --- /dev/null +++ b/tests/functional/lang/eval-fail-abort.nix @@ -0,0 +1 @@ +if true then abort "this should fail" else 1 diff --git a/tests/functional/lang/eval-fail-antiquoted-path.err.exp b/tests/functional/lang/eval-fail-antiquoted-path.err.exp new file mode 100644 index 000000000..425deba42 --- /dev/null +++ b/tests/functional/lang/eval-fail-antiquoted-path.err.exp @@ -0,0 +1 @@ +error: getting attributes of path ‘PWD/lang/fnord’: No such file or directory diff --git a/tests/functional/lang/eval-fail-assert.err.exp b/tests/functional/lang/eval-fail-assert.err.exp new file mode 100644 index 000000000..aeecd8167 --- /dev/null +++ b/tests/functional/lang/eval-fail-assert.err.exp @@ -0,0 +1,36 @@ +error: + … while evaluating the attribute 'body' + + at /pwd/lang/eval-fail-assert.nix:4:3: + + 3| + 4| body = x "x"; + | ^ + 5| } + + … from call site + + at /pwd/lang/eval-fail-assert.nix:4:10: + + 3| + 4| body = x "x"; + | ^ + 5| } + + … while calling 'x' + + at /pwd/lang/eval-fail-assert.nix:2:7: + + 1| let { + 2| x = arg: assert arg == "y"; 123; + | ^ + 3| + + error: assertion '(arg == "y")' failed + + at /pwd/lang/eval-fail-assert.nix:2:12: + + 1| let { + 2| x = arg: assert arg == "y"; 123; + | ^ + 3| diff --git a/tests/functional/lang/eval-fail-assert.nix b/tests/functional/lang/eval-fail-assert.nix new file mode 100644 index 000000000..3b7a1e8bf --- /dev/null +++ b/tests/functional/lang/eval-fail-assert.nix @@ -0,0 +1,5 @@ +let { + x = arg: assert arg == "y"; 123; + + body = x "x"; +}
\ No newline at end of file diff --git a/tests/functional/lang/eval-fail-bad-antiquote-1.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-1.err.exp new file mode 100644 index 000000000..cf94f53bc --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-antiquote-1.err.exp @@ -0,0 +1,10 @@ +error: + … while evaluating a path segment + + at /pwd/lang/eval-fail-bad-antiquote-1.nix:1:2: + + 1| "${x: x}" + | ^ + 2| + + error: cannot coerce a function to a string diff --git a/tests/functional/lang/eval-fail-bad-antiquote-2.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-2.err.exp new file mode 100644 index 000000000..c8fe39d12 --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-antiquote-2.err.exp @@ -0,0 +1 @@ +error: operation 'addToStoreFromDump' is not supported by store 'dummy' diff --git a/tests/functional/lang/eval-fail-bad-antiquote-3.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-3.err.exp new file mode 100644 index 000000000..fbefbc826 --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-antiquote-3.err.exp @@ -0,0 +1,10 @@ +error: + … while evaluating a path segment + + at /pwd/lang/eval-fail-bad-antiquote-3.nix:1:3: + + 1| ''${x: x}'' + | ^ + 2| + + error: cannot coerce a function to a string diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp new file mode 100644 index 000000000..eb73e9a52 --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp @@ -0,0 +1,10 @@ +error: + … while evaluating a path segment + + at /pwd/lang/eval-fail-bad-string-interpolation-1.nix:1:2: + + 1| "${x: x}" + | ^ + 2| + + error: cannot coerce a function to a string diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-1.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-1.nix new file mode 100644 index 000000000..ffe9c983c --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-1.nix @@ -0,0 +1 @@ +"${x: x}" diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp new file mode 100644 index 000000000..c8fe39d12 --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp @@ -0,0 +1 @@ +error: operation 'addToStoreFromDump' is not supported by store 'dummy' diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-2.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-2.nix new file mode 100644 index 000000000..3745235ce --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-2.nix @@ -0,0 +1 @@ +"${./fnord}" diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp new file mode 100644 index 000000000..ac14f329b --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp @@ -0,0 +1,10 @@ +error: + … while evaluating a path segment + + at /pwd/lang/eval-fail-bad-string-interpolation-3.nix:1:3: + + 1| ''${x: x}'' + | ^ + 2| + + error: cannot coerce a function to a string diff --git a/tests/functional/lang/eval-fail-bad-string-interpolation-3.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-3.nix new file mode 100644 index 000000000..65b9d4f50 --- /dev/null +++ b/tests/functional/lang/eval-fail-bad-string-interpolation-3.nix @@ -0,0 +1 @@ +''${x: x}'' diff --git a/tests/functional/lang/eval-fail-blackhole.err.exp b/tests/functional/lang/eval-fail-blackhole.err.exp new file mode 100644 index 000000000..f0618d8ac --- /dev/null +++ b/tests/functional/lang/eval-fail-blackhole.err.exp @@ -0,0 +1,18 @@ +error: + … while evaluating the attribute 'body' + + at /pwd/lang/eval-fail-blackhole.nix:2:3: + + 1| let { + 2| body = x; + | ^ + 3| x = y; + + error: infinite recursion encountered + + at /pwd/lang/eval-fail-blackhole.nix:3:7: + + 2| body = x; + 3| x = y; + | ^ + 4| y = x; diff --git a/tests/functional/lang/eval-fail-blackhole.nix b/tests/functional/lang/eval-fail-blackhole.nix new file mode 100644 index 000000000..81133b511 --- /dev/null +++ b/tests/functional/lang/eval-fail-blackhole.nix @@ -0,0 +1,5 @@ +let { + body = x; + x = y; + y = x; +} diff --git a/tests/functional/lang/eval-fail-deepseq.err.exp b/tests/functional/lang/eval-fail-deepseq.err.exp new file mode 100644 index 000000000..5e204ba73 --- /dev/null +++ b/tests/functional/lang/eval-fail-deepseq.err.exp @@ -0,0 +1,26 @@ +error: + … while calling the 'deepSeq' builtin + + at /pwd/lang/eval-fail-deepseq.nix:1:1: + + 1| builtins.deepSeq { x = abort "foo"; } 456 + | ^ + 2| + + … while evaluating the attribute 'x' + + at /pwd/lang/eval-fail-deepseq.nix:1:20: + + 1| builtins.deepSeq { x = abort "foo"; } 456 + | ^ + 2| + + … while calling the 'abort' builtin + + at /pwd/lang/eval-fail-deepseq.nix:1:24: + + 1| builtins.deepSeq { x = abort "foo"; } 456 + | ^ + 2| + + error: evaluation aborted with the following error message: 'foo' diff --git a/tests/functional/lang/eval-fail-deepseq.nix b/tests/functional/lang/eval-fail-deepseq.nix new file mode 100644 index 000000000..9baa49b06 --- /dev/null +++ b/tests/functional/lang/eval-fail-deepseq.nix @@ -0,0 +1 @@ +builtins.deepSeq { x = abort "foo"; } 456 diff --git a/tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp b/tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp new file mode 100644 index 000000000..e01f8e6d0 --- /dev/null +++ b/tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp @@ -0,0 +1,8 @@ +error: dynamic attribute 'b' already defined at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:2:11 + + at /pwd/lang/eval-fail-dup-dynamic-attrs.nix:3:11: + + 2| set = { "${"" + "b"}" = 1; }; + 3| set = { "${"b" + ""}" = 2; }; + | ^ + 4| } diff --git a/tests/functional/lang/eval-fail-dup-dynamic-attrs.nix b/tests/functional/lang/eval-fail-dup-dynamic-attrs.nix new file mode 100644 index 000000000..7ea17f6c8 --- /dev/null +++ b/tests/functional/lang/eval-fail-dup-dynamic-attrs.nix @@ -0,0 +1,4 @@ +{ + set = { "${"" + "b"}" = 1; }; + set = { "${"b" + ""}" = 2; }; +} diff --git a/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp new file mode 100644 index 000000000..0069285fb --- /dev/null +++ b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp @@ -0,0 +1,38 @@ +error: + … while calling the 'foldl'' builtin + + at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:2:1: + + 1| # Tests that the result of applying op is forced even if the value is never used + 2| builtins.foldl' + | ^ + 3| (_: f: f null) + + … while calling anonymous lambda + + at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:7: + + 2| builtins.foldl' + 3| (_: f: f null) + | ^ + 4| null + + … from call site + + at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:3:10: + + 2| builtins.foldl' + 3| (_: f: f null) + | ^ + 4| null + + … while calling anonymous lambda + + at /pwd/lang/eval-fail-foldlStrict-strict-op-application.nix:5:6: + + 4| null + 5| [ (_: throw "Not the final value, but is still forced!") (_: 23) ] + | ^ + 6| + + error: Not the final value, but is still forced! diff --git a/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix new file mode 100644 index 000000000..1620cc76e --- /dev/null +++ b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix @@ -0,0 +1,5 @@ +# Tests that the result of applying op is forced even if the value is never used +builtins.foldl' + (_: f: f null) + null + [ (_: throw "Not the final value, but is still forced!") (_: 23) ] diff --git a/tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp b/tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp new file mode 100644 index 000000000..f6bd19f5a --- /dev/null +++ b/tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp @@ -0,0 +1,12 @@ +error: + … while calling the 'fromTOML' builtin + + at /pwd/lang/eval-fail-fromTOML-timestamps.nix:1:1: + + 1| builtins.fromTOML '' + | ^ + 2| key = "value" + + error: while parsing a TOML string: Dates and times are not supported + + at «none»:0: (source not available) diff --git a/tests/functional/lang/eval-fail-fromTOML-timestamps.nix b/tests/functional/lang/eval-fail-fromTOML-timestamps.nix new file mode 100644 index 000000000..74cff9470 --- /dev/null +++ b/tests/functional/lang/eval-fail-fromTOML-timestamps.nix @@ -0,0 +1,130 @@ +builtins.fromTOML '' + key = "value" + bare_key = "value" + bare-key = "value" + 1234 = "value" + + "127.0.0.1" = "value" + "character encoding" = "value" + "ʎǝʞ" = "value" + 'key2' = "value" + 'quoted "value"' = "value" + + name = "Orange" + + physical.color = "orange" + physical.shape = "round" + site."google.com" = true + + # This is legal according to the spec, but cpptoml doesn't handle it. + #a.b.c = 1 + #a.d = 2 + + str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." + + int1 = +99 + int2 = 42 + int3 = 0 + int4 = -17 + int5 = 1_000 + int6 = 5_349_221 + int7 = 1_2_3_4_5 + + hex1 = 0xDEADBEEF + hex2 = 0xdeadbeef + hex3 = 0xdead_beef + + oct1 = 0o01234567 + oct2 = 0o755 + + bin1 = 0b11010110 + + flt1 = +1.0 + flt2 = 3.1415 + flt3 = -0.01 + flt4 = 5e+22 + flt5 = 1e6 + flt6 = -2E-2 + flt7 = 6.626e-34 + flt8 = 9_224_617.445_991_228_313 + + bool1 = true + bool2 = false + + odt1 = 1979-05-27T07:32:00Z + odt2 = 1979-05-27T00:32:00-07:00 + odt3 = 1979-05-27T00:32:00.999999-07:00 + odt4 = 1979-05-27 07:32:00Z + ldt1 = 1979-05-27T07:32:00 + ldt2 = 1979-05-27T00:32:00.999999 + ld1 = 1979-05-27 + lt1 = 07:32:00 + lt2 = 00:32:00.999999 + + arr1 = [ 1, 2, 3 ] + arr2 = [ "red", "yellow", "green" ] + arr3 = [ [ 1, 2 ], [3, 4, 5] ] + arr4 = [ "all", 'strings', """are the same""", ''''type''''] + arr5 = [ [ 1, 2 ], ["a", "b", "c"] ] + + arr7 = [ + 1, 2, 3 + ] + + arr8 = [ + 1, + 2, # this is ok + ] + + [table-1] + key1 = "some string" + key2 = 123 + + + [table-2] + key1 = "another string" + key2 = 456 + + [dog."tater.man"] + type.name = "pug" + + [a.b.c] + [ d.e.f ] + [ g . h . i ] + [ j . "ʞ" . 'l' ] + [x.y.z.w] + + name = { first = "Tom", last = "Preston-Werner" } + point = { x = 1, y = 2 } + animal = { type.name = "pug" } + + [[products]] + name = "Hammer" + sku = 738594937 + + [[products]] + + [[products]] + name = "Nail" + sku = 284758393 + color = "gray" + + [[fruit]] + name = "apple" + + [fruit.physical] + color = "red" + shape = "round" + + [[fruit.variety]] + name = "red delicious" + + [[fruit.variety]] + name = "granny smith" + + [[fruit]] + name = "banana" + + [[fruit.variety]] + name = "plantain" +'' diff --git a/tests/functional/lang/eval-fail-hashfile-missing.err.exp b/tests/functional/lang/eval-fail-hashfile-missing.err.exp new file mode 100644 index 000000000..8e77dec1e --- /dev/null +++ b/tests/functional/lang/eval-fail-hashfile-missing.err.exp @@ -0,0 +1,19 @@ +error: + … while calling the 'toString' builtin + + at /pwd/lang/eval-fail-hashfile-missing.nix:4:3: + + 3| in + 4| toString (builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"])) + | ^ + 5| + + … while evaluating the first argument passed to builtins.toString + + at «none»:0: (source not available) + + … while calling the 'hashFile' builtin + + at «none»:0: (source not available) + + error: opening file '/pwd/lang/this-file-is-definitely-not-there-7392097': No such file or directory diff --git a/tests/functional/lang/eval-fail-hashfile-missing.nix b/tests/functional/lang/eval-fail-hashfile-missing.nix new file mode 100644 index 000000000..ce098b823 --- /dev/null +++ b/tests/functional/lang/eval-fail-hashfile-missing.nix @@ -0,0 +1,5 @@ +let + paths = [ ./this-file-is-definitely-not-there-7392097 "/and/neither/is/this/37293620" ]; +in + toString (builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"])) + diff --git a/tests/functional/lang/eval-fail-list.err.exp b/tests/functional/lang/eval-fail-list.err.exp new file mode 100644 index 000000000..24d682118 --- /dev/null +++ b/tests/functional/lang/eval-fail-list.err.exp @@ -0,0 +1,10 @@ +error: + … while evaluating one of the elements to concatenate + + at /pwd/lang/eval-fail-list.nix:1:2: + + 1| 8++1 + | ^ + 2| + + error: value is an integer while a list was expected diff --git a/tests/functional/lang/eval-fail-list.nix b/tests/functional/lang/eval-fail-list.nix new file mode 100644 index 000000000..fa749f2f7 --- /dev/null +++ b/tests/functional/lang/eval-fail-list.nix @@ -0,0 +1 @@ +8++1 diff --git a/tests/functional/lang/eval-fail-missing-arg.err.exp b/tests/functional/lang/eval-fail-missing-arg.err.exp new file mode 100644 index 000000000..61fabf0d5 --- /dev/null +++ b/tests/functional/lang/eval-fail-missing-arg.err.exp @@ -0,0 +1,16 @@ +error: + … from call site + + at /pwd/lang/eval-fail-missing-arg.nix:1:1: + + 1| ({x, y, z}: x + y + z) {x = "foo"; z = "bar";} + | ^ + 2| + + error: function 'anonymous lambda' called without required argument 'y' + + at /pwd/lang/eval-fail-missing-arg.nix:1:2: + + 1| ({x, y, z}: x + y + z) {x = "foo"; z = "bar";} + | ^ + 2| diff --git a/tests/functional/lang/eval-fail-missing-arg.nix b/tests/functional/lang/eval-fail-missing-arg.nix new file mode 100644 index 000000000..c4be9797c --- /dev/null +++ b/tests/functional/lang/eval-fail-missing-arg.nix @@ -0,0 +1 @@ +({x, y, z}: x + y + z) {x = "foo"; z = "bar";} diff --git a/tests/functional/lang/eval-fail-nonexist-path.err.exp b/tests/functional/lang/eval-fail-nonexist-path.err.exp new file mode 100644 index 000000000..c8fe39d12 --- /dev/null +++ b/tests/functional/lang/eval-fail-nonexist-path.err.exp @@ -0,0 +1 @@ +error: operation 'addToStoreFromDump' is not supported by store 'dummy' diff --git a/tests/functional/lang/eval-fail-nonexist-path.nix b/tests/functional/lang/eval-fail-nonexist-path.nix new file mode 100644 index 000000000..f2f08107b --- /dev/null +++ b/tests/functional/lang/eval-fail-nonexist-path.nix @@ -0,0 +1,4 @@ +# This must fail to evaluate, since ./fnord doesn't exist. If it did +# exist, it would produce "/nix/store/<hash>-fnord/xyzzy" (with an +# appropriate context). +"${./fnord}/xyzzy" diff --git a/tests/functional/lang/eval-fail-path-slash.err.exp b/tests/functional/lang/eval-fail-path-slash.err.exp new file mode 100644 index 000000000..f0011c97f --- /dev/null +++ b/tests/functional/lang/eval-fail-path-slash.err.exp @@ -0,0 +1,8 @@ +error: path has a trailing slash + + at /pwd/lang/eval-fail-path-slash.nix:6:12: + + 5| # and https://nixos.org/nix-dev/2016-June/020829.html + 6| /nix/store/ + | ^ + 7| diff --git a/tests/functional/lang/eval-fail-path-slash.nix b/tests/functional/lang/eval-fail-path-slash.nix new file mode 100644 index 000000000..8c2e104c7 --- /dev/null +++ b/tests/functional/lang/eval-fail-path-slash.nix @@ -0,0 +1,6 @@ +# Trailing slashes in paths are not allowed. +# This restriction could be lifted sometime, +# for example if we make '/' a path concatenation operator. +# See https://github.com/NixOS/nix/issues/1138 +# and https://nixos.org/nix-dev/2016-June/020829.html +/nix/store/ diff --git a/tests/functional/lang/eval-fail-recursion.err.exp b/tests/functional/lang/eval-fail-recursion.err.exp new file mode 100644 index 000000000..af64133cb --- /dev/null +++ b/tests/functional/lang/eval-fail-recursion.err.exp @@ -0,0 +1,16 @@ +error: + … in the right operand of the update (//) operator + + at /pwd/lang/eval-fail-recursion.nix:1:12: + + 1| let a = {} // a; in a.foo + | ^ + 2| + + error: infinite recursion encountered + + at /pwd/lang/eval-fail-recursion.nix:1:15: + + 1| let a = {} // a; in a.foo + | ^ + 2| diff --git a/tests/functional/lang/eval-fail-recursion.nix b/tests/functional/lang/eval-fail-recursion.nix new file mode 100644 index 000000000..075b5ed06 --- /dev/null +++ b/tests/functional/lang/eval-fail-recursion.nix @@ -0,0 +1 @@ +let a = {} // a; in a.foo diff --git a/tests/functional/lang/eval-fail-remove.err.exp b/tests/functional/lang/eval-fail-remove.err.exp new file mode 100644 index 000000000..e82cdac98 --- /dev/null +++ b/tests/functional/lang/eval-fail-remove.err.exp @@ -0,0 +1,19 @@ +error: + … while evaluating the attribute 'body' + + at /pwd/lang/eval-fail-remove.nix:4:3: + + 3| + 4| body = (removeAttrs attrs ["x"]).x; + | ^ + 5| } + + error: attribute 'x' missing + + at /pwd/lang/eval-fail-remove.nix:4:10: + + 3| + 4| body = (removeAttrs attrs ["x"]).x; + | ^ + 5| } + Did you mean y? diff --git a/tests/functional/lang/eval-fail-remove.nix b/tests/functional/lang/eval-fail-remove.nix new file mode 100644 index 000000000..539e0eb0a --- /dev/null +++ b/tests/functional/lang/eval-fail-remove.nix @@ -0,0 +1,5 @@ +let { + attrs = {x = 123; y = 456;}; + + body = (removeAttrs attrs ["x"]).x; +}
\ No newline at end of file diff --git a/tests/functional/lang/eval-fail-scope-5.err.exp b/tests/functional/lang/eval-fail-scope-5.err.exp new file mode 100644 index 000000000..22b6166f8 --- /dev/null +++ b/tests/functional/lang/eval-fail-scope-5.err.exp @@ -0,0 +1,36 @@ +error: + … while evaluating the attribute 'body' + + at /pwd/lang/eval-fail-scope-5.nix:8:3: + + 7| + 8| body = f {}; + | ^ + 9| + + … from call site + + at /pwd/lang/eval-fail-scope-5.nix:8:10: + + 7| + 8| body = f {}; + | ^ + 9| + + … while calling 'f' + + at /pwd/lang/eval-fail-scope-5.nix:6:7: + + 5| + 6| f = {x ? y, y ? x}: x + y; + | ^ + 7| + + error: infinite recursion encountered + + at /pwd/lang/eval-fail-scope-5.nix:6:12: + + 5| + 6| f = {x ? y, y ? x}: x + y; + | ^ + 7| diff --git a/tests/functional/lang/eval-fail-scope-5.nix b/tests/functional/lang/eval-fail-scope-5.nix new file mode 100644 index 000000000..f89a65a99 --- /dev/null +++ b/tests/functional/lang/eval-fail-scope-5.nix @@ -0,0 +1,10 @@ +let { + + x = "a"; + y = "b"; + + f = {x ? y, y ? x}: x + y; + + body = f {}; + +} diff --git a/tests/functional/lang/eval-fail-seq.err.exp b/tests/functional/lang/eval-fail-seq.err.exp new file mode 100644 index 000000000..33a7e9491 --- /dev/null +++ b/tests/functional/lang/eval-fail-seq.err.exp @@ -0,0 +1,18 @@ +error: + … while calling the 'seq' builtin + + at /pwd/lang/eval-fail-seq.nix:1:1: + + 1| builtins.seq (abort "foo") 2 + | ^ + 2| + + … while calling the 'abort' builtin + + at /pwd/lang/eval-fail-seq.nix:1:15: + + 1| builtins.seq (abort "foo") 2 + | ^ + 2| + + error: evaluation aborted with the following error message: 'foo' diff --git a/tests/functional/lang/eval-fail-seq.nix b/tests/functional/lang/eval-fail-seq.nix new file mode 100644 index 000000000..cddbbfd32 --- /dev/null +++ b/tests/functional/lang/eval-fail-seq.nix @@ -0,0 +1 @@ +builtins.seq (abort "foo") 2 diff --git a/tests/functional/lang/eval-fail-set-override.err.exp b/tests/functional/lang/eval-fail-set-override.err.exp new file mode 100644 index 000000000..beb29d678 --- /dev/null +++ b/tests/functional/lang/eval-fail-set-override.err.exp @@ -0,0 +1,6 @@ +error: + … while evaluating the `__overrides` attribute + + at «none»:0: (source not available) + + error: value is an integer while a set was expected diff --git a/tests/functional/lang/eval-fail-set-override.nix b/tests/functional/lang/eval-fail-set-override.nix new file mode 100644 index 000000000..03551c186 --- /dev/null +++ b/tests/functional/lang/eval-fail-set-override.nix @@ -0,0 +1 @@ +rec { __overrides = 1; } diff --git a/tests/functional/lang/eval-fail-set.err.exp b/tests/functional/lang/eval-fail-set.err.exp new file mode 100644 index 000000000..0d0140508 --- /dev/null +++ b/tests/functional/lang/eval-fail-set.err.exp @@ -0,0 +1,7 @@ +error: undefined variable 'x' + + at /pwd/lang/eval-fail-set.nix:1:3: + + 1| 8.x + | ^ + 2| diff --git a/tests/functional/lang/eval-fail-set.nix b/tests/functional/lang/eval-fail-set.nix new file mode 100644 index 000000000..c6b7980b6 --- /dev/null +++ b/tests/functional/lang/eval-fail-set.nix @@ -0,0 +1 @@ +8.x diff --git a/tests/functional/lang/eval-fail-substring.err.exp b/tests/functional/lang/eval-fail-substring.err.exp new file mode 100644 index 000000000..dc26a00bd --- /dev/null +++ b/tests/functional/lang/eval-fail-substring.err.exp @@ -0,0 +1,12 @@ +error: + … while calling the 'substring' builtin + + at /pwd/lang/eval-fail-substring.nix:1:1: + + 1| builtins.substring (builtins.sub 0 1) 1 "x" + | ^ + 2| + + error: negative start position in 'substring' + + at «none»:0: (source not available) diff --git a/tests/functional/lang/eval-fail-substring.nix b/tests/functional/lang/eval-fail-substring.nix new file mode 100644 index 000000000..f37c2bc0a --- /dev/null +++ b/tests/functional/lang/eval-fail-substring.nix @@ -0,0 +1 @@ +builtins.substring (builtins.sub 0 1) 1 "x" diff --git a/tests/functional/lang/eval-fail-to-path.err.exp b/tests/functional/lang/eval-fail-to-path.err.exp new file mode 100644 index 000000000..43ed2bdfc --- /dev/null +++ b/tests/functional/lang/eval-fail-to-path.err.exp @@ -0,0 +1,14 @@ +error: + … while calling the 'toPath' builtin + + at /pwd/lang/eval-fail-to-path.nix:1:1: + + 1| builtins.toPath "foo/bar" + | ^ + 2| + + … while evaluating the first argument passed to builtins.toPath + + at «none»:0: (source not available) + + error: string 'foo/bar' doesn't represent an absolute path diff --git a/tests/functional/lang/eval-fail-to-path.nix b/tests/functional/lang/eval-fail-to-path.nix new file mode 100644 index 000000000..5e322bc31 --- /dev/null +++ b/tests/functional/lang/eval-fail-to-path.nix @@ -0,0 +1 @@ +builtins.toPath "foo/bar" diff --git a/tests/functional/lang/eval-fail-toJSON.err.exp b/tests/functional/lang/eval-fail-toJSON.err.exp new file mode 100644 index 000000000..4e618c203 --- /dev/null +++ b/tests/functional/lang/eval-fail-toJSON.err.exp @@ -0,0 +1,57 @@ +error: + … while calling the 'toJSON' builtin + + at /pwd/lang/eval-fail-toJSON.nix:1:1: + + 1| builtins.toJSON { + | ^ + 2| a.b = [ + + … while evaluating attribute 'a' + + at /pwd/lang/eval-fail-toJSON.nix:2:3: + + 1| builtins.toJSON { + 2| a.b = [ + | ^ + 3| true + + … while evaluating attribute 'b' + + at /pwd/lang/eval-fail-toJSON.nix:2:3: + + 1| builtins.toJSON { + 2| a.b = [ + | ^ + 3| true + + … while evaluating list element at index 3 + + … while evaluating attribute 'c' + + at /pwd/lang/eval-fail-toJSON.nix:7:7: + + 6| { + 7| c.d = throw "hah no"; + | ^ + 8| } + + … while evaluating attribute 'd' + + at /pwd/lang/eval-fail-toJSON.nix:7:7: + + 6| { + 7| c.d = throw "hah no"; + | ^ + 8| } + + … while calling the 'throw' builtin + + at /pwd/lang/eval-fail-toJSON.nix:7:13: + + 6| { + 7| c.d = throw "hah no"; + | ^ + 8| } + + error: hah no diff --git a/tests/functional/lang/eval-fail-toJSON.nix b/tests/functional/lang/eval-fail-toJSON.nix new file mode 100644 index 000000000..8112e1c1f --- /dev/null +++ b/tests/functional/lang/eval-fail-toJSON.nix @@ -0,0 +1,10 @@ +builtins.toJSON { + a.b = [ + true + false + "it's a bird" + { + c.d = throw "hah no"; + } + ]; +} diff --git a/tests/functional/lang/eval-fail-undeclared-arg.err.exp b/tests/functional/lang/eval-fail-undeclared-arg.err.exp new file mode 100644 index 000000000..30db743c7 --- /dev/null +++ b/tests/functional/lang/eval-fail-undeclared-arg.err.exp @@ -0,0 +1,17 @@ +error: + … from call site + + at /pwd/lang/eval-fail-undeclared-arg.nix:1:1: + + 1| ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";} + | ^ + 2| + + error: function 'anonymous lambda' called with unexpected argument 'y' + + at /pwd/lang/eval-fail-undeclared-arg.nix:1:2: + + 1| ({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";} + | ^ + 2| + Did you mean one of x or z? diff --git a/tests/functional/lang/eval-fail-undeclared-arg.nix b/tests/functional/lang/eval-fail-undeclared-arg.nix new file mode 100644 index 000000000..cafdf1636 --- /dev/null +++ b/tests/functional/lang/eval-fail-undeclared-arg.nix @@ -0,0 +1 @@ +({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";} diff --git a/tests/functional/lang/eval-okay-any-all.exp b/tests/functional/lang/eval-okay-any-all.exp new file mode 100644 index 000000000..eb273f45b --- /dev/null +++ b/tests/functional/lang/eval-okay-any-all.exp @@ -0,0 +1 @@ +[ false false true true true true false true ] diff --git a/tests/functional/lang/eval-okay-any-all.nix b/tests/functional/lang/eval-okay-any-all.nix new file mode 100644 index 000000000..a3f26ea2a --- /dev/null +++ b/tests/functional/lang/eval-okay-any-all.nix @@ -0,0 +1,11 @@ +with builtins; + +[ (any (x: x == 1) []) + (any (x: x == 1) [2 3 4]) + (any (x: x == 1) [1 2 3 4]) + (any (x: x == 1) [4 3 2 1]) + (all (x: x == 1) []) + (all (x: x == 1) [1]) + (all (x: x == 1) [1 2 3]) + (all (x: x == 1) [1 1 1]) +] diff --git a/tests/functional/lang/eval-okay-arithmetic.exp b/tests/functional/lang/eval-okay-arithmetic.exp new file mode 100644 index 000000000..5c54d10b7 --- /dev/null +++ b/tests/functional/lang/eval-okay-arithmetic.exp @@ -0,0 +1 @@ +2216 diff --git a/tests/functional/lang/eval-okay-arithmetic.nix b/tests/functional/lang/eval-okay-arithmetic.nix new file mode 100644 index 000000000..7e9e6a0b6 --- /dev/null +++ b/tests/functional/lang/eval-okay-arithmetic.nix @@ -0,0 +1,59 @@ +with import ./lib.nix; + +let { + + /* Supposedly tail recursive version: + + range_ = accum: first: last: + if first == last then ([first] ++ accum) + else range_ ([first] ++ accum) (builtins.add first 1) last; + + range = range_ []; + */ + + x = 12; + + err = abort "urgh"; + + body = sum + [ (sum (range 1 50)) + (123 + 456) + (0 + -10 + -(-11) + -x) + (10 - 7 - -2) + (10 - (6 - -1)) + (10 - 1 + 2) + (3 * 4 * 5) + (56088 / 123 / 2) + (3 + 4 * const 5 0 - 6 / id 2) + + (builtins.bitAnd 12 10) # 0b1100 & 0b1010 = 8 + (builtins.bitOr 12 10) # 0b1100 | 0b1010 = 14 + (builtins.bitXor 12 10) # 0b1100 ^ 0b1010 = 6 + + (if 3 < 7 then 1 else err) + (if 7 < 3 then err else 1) + (if 3 < 3 then err else 1) + + (if 3 <= 7 then 1 else err) + (if 7 <= 3 then err else 1) + (if 3 <= 3 then 1 else err) + + (if 3 > 7 then err else 1) + (if 7 > 3 then 1 else err) + (if 3 > 3 then err else 1) + + (if 3 >= 7 then err else 1) + (if 7 >= 3 then 1 else err) + (if 3 >= 3 then 1 else err) + + (if 2 > 1 == 1 < 2 then 1 else err) + (if 1 + 2 * 3 >= 7 then 1 else err) + (if 1 + 2 * 3 < 7 then err else 1) + + # Not integer, but so what. + (if "aa" < "ab" then 1 else err) + (if "aa" < "aa" then err else 1) + (if "foo" < "foobar" then 1 else err) + ]; + +} diff --git a/tests/functional/lang/eval-okay-attrnames.exp b/tests/functional/lang/eval-okay-attrnames.exp new file mode 100644 index 000000000..b4aa387e0 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrnames.exp @@ -0,0 +1 @@ +"newxfoonewxy" diff --git a/tests/functional/lang/eval-okay-attrnames.nix b/tests/functional/lang/eval-okay-attrnames.nix new file mode 100644 index 000000000..e5b26e9f2 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrnames.nix @@ -0,0 +1,11 @@ +with import ./lib.nix; + +let + + attrs = {y = "y"; x = "x"; foo = "foo";} // rec {x = "newx"; bar = x;}; + + names = builtins.attrNames attrs; + + values = map (name: builtins.getAttr name attrs) names; + +in assert values == builtins.attrValues attrs; concat values diff --git a/tests/functional/lang/eval-okay-attrs.exp b/tests/functional/lang/eval-okay-attrs.exp new file mode 100644 index 000000000..45b0f829e --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs.exp @@ -0,0 +1 @@ +987 diff --git a/tests/functional/lang/eval-okay-attrs.nix b/tests/functional/lang/eval-okay-attrs.nix new file mode 100644 index 000000000..810b31a5d --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs.nix @@ -0,0 +1,5 @@ +let { + as = { x = 123; y = 456; } // { z = 789; } // { z = 987; }; + + body = if as ? a then as.a else assert as ? z; as.z; +} diff --git a/tests/functional/lang/eval-okay-attrs2.exp b/tests/functional/lang/eval-okay-attrs2.exp new file mode 100644 index 000000000..45b0f829e --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs2.exp @@ -0,0 +1 @@ +987 diff --git a/tests/functional/lang/eval-okay-attrs2.nix b/tests/functional/lang/eval-okay-attrs2.nix new file mode 100644 index 000000000..9e06b83ac --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs2.nix @@ -0,0 +1,10 @@ +let { + as = { x = 123; y = 456; } // { z = 789; } // { z = 987; }; + + A = "a"; + Z = "z"; + + body = if builtins.hasAttr A as + then builtins.getAttr A as + else assert builtins.hasAttr Z as; builtins.getAttr Z as; +} diff --git a/tests/functional/lang/eval-okay-attrs3.exp b/tests/functional/lang/eval-okay-attrs3.exp new file mode 100644 index 000000000..19de4fdf7 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs3.exp @@ -0,0 +1 @@ +"foo 22 80 itchyxac" diff --git a/tests/functional/lang/eval-okay-attrs3.nix b/tests/functional/lang/eval-okay-attrs3.nix new file mode 100644 index 000000000..f29de11fe --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs3.nix @@ -0,0 +1,22 @@ +let + + config = + { + services.sshd.enable = true; + services.sshd.port = 22; + services.httpd.port = 80; + hostName = "itchy"; + a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z = "x"; + foo = { + a = "a"; + b.c = "c"; + }; + }; + +in + if config.services.sshd.enable + then "foo ${toString config.services.sshd.port} ${toString config.services.httpd.port} ${config.hostName}" + + "${config.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z}" + + "${config.foo.a}" + + "${config.foo.b.c}" + else "bar" diff --git a/tests/functional/lang/eval-okay-attrs4.exp b/tests/functional/lang/eval-okay-attrs4.exp new file mode 100644 index 000000000..185173144 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs4.exp @@ -0,0 +1 @@ +[ true false true false false true false false ] diff --git a/tests/functional/lang/eval-okay-attrs4.nix b/tests/functional/lang/eval-okay-attrs4.nix new file mode 100644 index 000000000..43ec81210 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs4.nix @@ -0,0 +1,7 @@ +let + + as = { x.y.z = 123; a.b.c = 456; }; + + bs = null; + +in [ (as ? x) (as ? y) (as ? x.y.z) (as ? x.y.z.a) (as ? x.y.a) (as ? a.b.c) (bs ? x) (bs ? x.y.z) ] diff --git a/tests/functional/lang/eval-okay-attrs5.exp b/tests/functional/lang/eval-okay-attrs5.exp new file mode 100644 index 000000000..ce0430d78 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs5.exp @@ -0,0 +1 @@ +[ 123 "foo" 456 456 "foo" "xyzzy" "xyzzy" true ] diff --git a/tests/functional/lang/eval-okay-attrs5.nix b/tests/functional/lang/eval-okay-attrs5.nix new file mode 100644 index 000000000..a4584cd3b --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs5.nix @@ -0,0 +1,21 @@ +with import ./lib.nix; + +let + + as = { x.y.z = 123; a.b.c = 456; }; + + bs = { f-o-o.bar = "foo"; }; + + or = x: y: x || y; + +in + [ as.x.y.z + as.foo or "foo" + as.x.y.bla or as.a.b.c + as.a.b.c or as.x.y.z + as.x.y.bla or bs.f-o-o.bar or "xyzzy" + as.x.y.bla or bs.bar.foo or "xyzzy" + (123).bla or null.foo or "xyzzy" + # Backwards compatibility test. + (fold or [] [true false false]) + ] diff --git a/tests/functional/lang/eval-okay-attrs6.exp b/tests/functional/lang/eval-okay-attrs6.exp new file mode 100644 index 000000000..b46938032 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs6.exp @@ -0,0 +1 @@ +{ __overrides = { bar = "qux"; }; bar = "qux"; foo = "bar"; } diff --git a/tests/functional/lang/eval-okay-attrs6.nix b/tests/functional/lang/eval-okay-attrs6.nix new file mode 100644 index 000000000..2e5c85483 --- /dev/null +++ b/tests/functional/lang/eval-okay-attrs6.nix @@ -0,0 +1,4 @@ +rec { + "${"foo"}" = "bar"; + __overrides = { bar = "qux"; }; +} diff --git a/tests/functional/lang/eval-okay-autoargs.exp b/tests/functional/lang/eval-okay-autoargs.exp new file mode 100644 index 000000000..7a8391786 --- /dev/null +++ b/tests/functional/lang/eval-okay-autoargs.exp @@ -0,0 +1 @@ +"xyzzy!xyzzy!foobar" diff --git a/tests/functional/lang/eval-okay-autoargs.flags b/tests/functional/lang/eval-okay-autoargs.flags new file mode 100644 index 000000000..ae3762254 --- /dev/null +++ b/tests/functional/lang/eval-okay-autoargs.flags @@ -0,0 +1 @@ +--arg lib import(lang/lib.nix) --argstr xyzzy xyzzy! -A result diff --git a/tests/functional/lang/eval-okay-autoargs.nix b/tests/functional/lang/eval-okay-autoargs.nix new file mode 100644 index 000000000..815f51b1d --- /dev/null +++ b/tests/functional/lang/eval-okay-autoargs.nix @@ -0,0 +1,15 @@ +let + + foobar = "foobar"; + +in + +{ xyzzy2 ? xyzzy # mutually recursive args +, xyzzy ? "blaat" # will be overridden by --argstr +, fb ? foobar +, lib # will be set by --arg +}: + +{ + result = lib.concat [xyzzy xyzzy2 fb]; +} diff --git a/tests/functional/lang/eval-okay-backslash-newline-1.exp b/tests/functional/lang/eval-okay-backslash-newline-1.exp new file mode 100644 index 000000000..3e754364c --- /dev/null +++ b/tests/functional/lang/eval-okay-backslash-newline-1.exp @@ -0,0 +1 @@ +"a\nb" diff --git a/tests/functional/lang/eval-okay-backslash-newline-1.nix b/tests/functional/lang/eval-okay-backslash-newline-1.nix new file mode 100644 index 000000000..7fef3dddd --- /dev/null +++ b/tests/functional/lang/eval-okay-backslash-newline-1.nix @@ -0,0 +1,2 @@ +"a\ +b" diff --git a/tests/functional/lang/eval-okay-backslash-newline-2.exp b/tests/functional/lang/eval-okay-backslash-newline-2.exp new file mode 100644 index 000000000..3e754364c --- /dev/null +++ b/tests/functional/lang/eval-okay-backslash-newline-2.exp @@ -0,0 +1 @@ +"a\nb" diff --git a/tests/functional/lang/eval-okay-backslash-newline-2.nix b/tests/functional/lang/eval-okay-backslash-newline-2.nix new file mode 100644 index 000000000..35ddf495c --- /dev/null +++ b/tests/functional/lang/eval-okay-backslash-newline-2.nix @@ -0,0 +1,2 @@ +''a''\ +b'' diff --git a/tests/functional/lang/eval-okay-builtins-add.exp b/tests/functional/lang/eval-okay-builtins-add.exp new file mode 100644 index 000000000..0350b518a --- /dev/null +++ b/tests/functional/lang/eval-okay-builtins-add.exp @@ -0,0 +1 @@ +[ 5 4 "int" "tt" "float" 4 ] diff --git a/tests/functional/lang/eval-okay-builtins-add.nix b/tests/functional/lang/eval-okay-builtins-add.nix new file mode 100644 index 000000000..c84181622 --- /dev/null +++ b/tests/functional/lang/eval-okay-builtins-add.nix @@ -0,0 +1,8 @@ +[ +(builtins.add 2 3) +(builtins.add 2 2) +(builtins.typeOf (builtins.add 2 2)) +("t" + "t") +(builtins.typeOf (builtins.add 2.0 2)) +(builtins.add 2.0 2) +] diff --git a/tests/functional/lang/eval-okay-builtins.exp b/tests/functional/lang/eval-okay-builtins.exp new file mode 100644 index 000000000..0661686d6 --- /dev/null +++ b/tests/functional/lang/eval-okay-builtins.exp @@ -0,0 +1 @@ +/foo diff --git a/tests/functional/lang/eval-okay-builtins.nix b/tests/functional/lang/eval-okay-builtins.nix new file mode 100644 index 000000000..e9d65e88a --- /dev/null +++ b/tests/functional/lang/eval-okay-builtins.nix @@ -0,0 +1,12 @@ +assert builtins ? currentSystem; +assert !builtins ? __currentSystem; + +let { + + x = if builtins ? dirOf then builtins.dirOf /foo/bar else ""; + + y = if builtins ? fnord then builtins.fnord "foo" else ""; + + body = x + y; + +} diff --git a/tests/functional/lang/eval-okay-callable-attrs.exp b/tests/functional/lang/eval-okay-callable-attrs.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-callable-attrs.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-callable-attrs.nix b/tests/functional/lang/eval-okay-callable-attrs.nix new file mode 100644 index 000000000..310a030df --- /dev/null +++ b/tests/functional/lang/eval-okay-callable-attrs.nix @@ -0,0 +1 @@ +({ __functor = self: x: self.foo && x; foo = false; } // { foo = true; }) true diff --git a/tests/functional/lang/eval-okay-catattrs.exp b/tests/functional/lang/eval-okay-catattrs.exp new file mode 100644 index 000000000..b4a1e66d6 --- /dev/null +++ b/tests/functional/lang/eval-okay-catattrs.exp @@ -0,0 +1 @@ +[ 1 2 ] diff --git a/tests/functional/lang/eval-okay-catattrs.nix b/tests/functional/lang/eval-okay-catattrs.nix new file mode 100644 index 000000000..2c3dc10da --- /dev/null +++ b/tests/functional/lang/eval-okay-catattrs.nix @@ -0,0 +1 @@ +builtins.catAttrs "a" [ { a = 1; } { b = 0; } { a = 2; } ] diff --git a/tests/functional/lang/eval-okay-closure.exp b/tests/functional/lang/eval-okay-closure.exp new file mode 100644 index 000000000..e7dbf9781 --- /dev/null +++ b/tests/functional/lang/eval-okay-closure.exp @@ -0,0 +1 @@ +[ { foo = true; key = -13; } { foo = true; key = -12; } { foo = true; key = -11; } { foo = true; key = -9; } { foo = true; key = -8; } { foo = true; key = -7; } { foo = true; key = -5; } { foo = true; key = -4; } { foo = true; key = -3; } { key = -1; } { foo = true; key = 0; } { foo = true; key = 1; } { foo = true; key = 2; } { foo = true; key = 4; } { foo = true; key = 5; } { foo = true; key = 6; } { key = 8; } { foo = true; key = 9; } { foo = true; key = 10; } { foo = true; key = 13; } { foo = true; key = 14; } { foo = true; key = 15; } { key = 17; } { foo = true; key = 18; } { foo = true; key = 19; } { foo = true; key = 22; } { foo = true; key = 23; } { key = 26; } { foo = true; key = 27; } { foo = true; key = 28; } { foo = true; key = 31; } { foo = true; key = 32; } { key = 35; } { foo = true; key = 36; } { foo = true; key = 40; } { foo = true; key = 41; } { key = 44; } { foo = true; key = 45; } { foo = true; key = 49; } { key = 53; } { foo = true; key = 54; } { foo = true; key = 58; } { key = 62; } { foo = true; key = 67; } { key = 71; } { key = 80; } ] diff --git a/tests/functional/lang/eval-okay-closure.exp.xml b/tests/functional/lang/eval-okay-closure.exp.xml new file mode 100644 index 000000000..dffc03a99 --- /dev/null +++ b/tests/functional/lang/eval-okay-closure.exp.xml @@ -0,0 +1,343 @@ +<?xml version='1.0' encoding='utf-8'?> +<expr> + <list> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-13" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-12" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-11" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-9" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-8" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-7" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-5" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-4" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="-3" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="-1" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="0" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="1" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="2" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="4" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="5" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="6" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="8" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="9" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="10" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="13" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="14" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="15" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="17" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="18" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="19" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="22" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="23" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="26" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="27" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="28" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="31" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="32" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="35" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="36" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="40" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="41" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="44" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="45" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="49" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="53" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="54" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="58" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="62" /> + </attr> + </attrs> + <attrs> + <attr name="foo"> + <bool value="true" /> + </attr> + <attr name="key"> + <int value="67" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="71" /> + </attr> + </attrs> + <attrs> + <attr name="key"> + <int value="80" /> + </attr> + </attrs> + </list> +</expr> diff --git a/tests/functional/lang/eval-okay-closure.nix b/tests/functional/lang/eval-okay-closure.nix new file mode 100644 index 000000000..cccd4dc35 --- /dev/null +++ b/tests/functional/lang/eval-okay-closure.nix @@ -0,0 +1,13 @@ +let + + closure = builtins.genericClosure { + startSet = [{key = 80;}]; + operator = {key, foo ? false}: + if builtins.lessThan key 0 + then [] + else [{key = builtins.sub key 9;} {key = builtins.sub key 13; foo = true;}]; + }; + + sort = (import ./lib.nix).sortBy (a: b: builtins.lessThan a.key b.key); + +in sort closure diff --git a/tests/functional/lang/eval-okay-comments.exp b/tests/functional/lang/eval-okay-comments.exp new file mode 100644 index 000000000..7182dc2f9 --- /dev/null +++ b/tests/functional/lang/eval-okay-comments.exp @@ -0,0 +1 @@ +"abcdefghijklmnopqrstuvwxyz" diff --git a/tests/functional/lang/eval-okay-comments.nix b/tests/functional/lang/eval-okay-comments.nix new file mode 100644 index 000000000..cb2cce218 --- /dev/null +++ b/tests/functional/lang/eval-okay-comments.nix @@ -0,0 +1,59 @@ +# A simple comment +"a"+ # And another +## A double comment +"b"+ ## And another +# Nested # comments # +"c"+ # and # some # other # +# An empty line, following here: + +"d"+ # and a comment not starting the line ! + +"e"+ +/* multiline comments */ +"f" + +/* multiline + comments, + on + multiple + lines +*/ +"g" + +# Small, tricky comments +/**/ "h"+ /*/*/ "i"+ /***/ "j"+ /* /*/ "k"+ /*/* /*/ "l"+ +# Comments with an even number of ending '*' used to fail: +"m"+ +/* */ /* **/ /* ***/ /* ****/ "n"+ +/* */ /** */ /*** */ /**** */ "o"+ +/** **/ /*** ***/ /**** ****/ "p"+ +/* * ** *** **** ***** */ "q"+ +# Random comments +/* ***** ////// * / * / /* */ "r"+ +# Mixed comments +/* # */ +"s"+ +# /* # +"t"+ +# /* # */ +"u"+ +# /*********/ +"v"+ +## */* +"w"+ +/* + * Multiline, decorated comments + * # This ain't a nest'd comm'nt + */ +"x"+ +''${/** with **/"y" + # real + /* comments + inside ! # */ + + # (and empty lines) + +}''+ /* And a multiline comment, + on the same line, + after some spaces +*/ # followed by a one-line comment +"z" +/* EOF */ diff --git a/tests/functional/lang/eval-okay-concat.exp b/tests/functional/lang/eval-okay-concat.exp new file mode 100644 index 000000000..bb4bbd577 --- /dev/null +++ b/tests/functional/lang/eval-okay-concat.exp @@ -0,0 +1 @@ +[ 1 2 3 4 5 6 7 8 9 ] diff --git a/tests/functional/lang/eval-okay-concat.nix b/tests/functional/lang/eval-okay-concat.nix new file mode 100644 index 000000000..d158a9bf0 --- /dev/null +++ b/tests/functional/lang/eval-okay-concat.nix @@ -0,0 +1 @@ +[1 2 3] ++ [4 5 6] ++ [7 8 9] diff --git a/tests/functional/lang/eval-okay-concatmap.exp b/tests/functional/lang/eval-okay-concatmap.exp new file mode 100644 index 000000000..3b8be7739 --- /dev/null +++ b/tests/functional/lang/eval-okay-concatmap.exp @@ -0,0 +1 @@ +[ [ 1 3 5 7 9 ] [ "a" "z" "b" "z" ] ] diff --git a/tests/functional/lang/eval-okay-concatmap.nix b/tests/functional/lang/eval-okay-concatmap.nix new file mode 100644 index 000000000..97da5d37a --- /dev/null +++ b/tests/functional/lang/eval-okay-concatmap.nix @@ -0,0 +1,5 @@ +with import ./lib.nix; + +[ (builtins.concatMap (x: if x / 2 * 2 == x then [] else [ x ]) (range 0 10)) + (builtins.concatMap (x: [x] ++ ["z"]) ["a" "b"]) +] diff --git a/tests/functional/lang/eval-okay-concatstringssep.exp b/tests/functional/lang/eval-okay-concatstringssep.exp new file mode 100644 index 000000000..93987647f --- /dev/null +++ b/tests/functional/lang/eval-okay-concatstringssep.exp @@ -0,0 +1 @@ +[ "" "foobarxyzzy" "foo, bar, xyzzy" "foo" "" ] diff --git a/tests/functional/lang/eval-okay-concatstringssep.nix b/tests/functional/lang/eval-okay-concatstringssep.nix new file mode 100644 index 000000000..adc4c41bd --- /dev/null +++ b/tests/functional/lang/eval-okay-concatstringssep.nix @@ -0,0 +1,8 @@ +with builtins; + +[ (concatStringsSep "" []) + (concatStringsSep "" ["foo" "bar" "xyzzy"]) + (concatStringsSep ", " ["foo" "bar" "xyzzy"]) + (concatStringsSep ", " ["foo"]) + (concatStringsSep ", " []) +] diff --git a/tests/functional/lang/eval-okay-context-introspection.exp b/tests/functional/lang/eval-okay-context-introspection.exp new file mode 100644 index 000000000..03b400cc8 --- /dev/null +++ b/tests/functional/lang/eval-okay-context-introspection.exp @@ -0,0 +1 @@ +[ true true true true true true ] diff --git a/tests/functional/lang/eval-okay-context-introspection.nix b/tests/functional/lang/eval-okay-context-introspection.nix new file mode 100644 index 000000000..50a78d946 --- /dev/null +++ b/tests/functional/lang/eval-okay-context-introspection.nix @@ -0,0 +1,41 @@ +let + drv = derivation { + name = "fail"; + builder = "/bin/false"; + system = "x86_64-linux"; + outputs = [ "out" "foo" ]; + }; + + path = "${./eval-okay-context-introspection.nix}"; + + desired-context = { + "${builtins.unsafeDiscardStringContext path}" = { + path = true; + }; + "${builtins.unsafeDiscardStringContext drv.drvPath}" = { + outputs = [ "foo" "out" ]; + allOutputs = true; + }; + }; + + combo-path = "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}"; + legit-context = builtins.getContext combo-path; + + reconstructed-path = builtins.appendContext + (builtins.unsafeDiscardStringContext combo-path) + desired-context; + + # Eta rule for strings with context. + etaRule = str: + str == builtins.appendContext + (builtins.unsafeDiscardStringContext str) + (builtins.getContext str); + +in [ + (legit-context == desired-context) + (reconstructed-path == combo-path) + (etaRule "foo") + (etaRule drv.drvPath) + (etaRule drv.foo.outPath) + (etaRule (builtins.unsafeDiscardOutputDependency drv.drvPath)) +] diff --git a/tests/functional/lang/eval-okay-context.exp b/tests/functional/lang/eval-okay-context.exp new file mode 100644 index 000000000..2f535bdbc --- /dev/null +++ b/tests/functional/lang/eval-okay-context.exp @@ -0,0 +1 @@ +"foo eval-okay-context.nix bar" diff --git a/tests/functional/lang/eval-okay-context.nix b/tests/functional/lang/eval-okay-context.nix new file mode 100644 index 000000000..7b9531cfe --- /dev/null +++ b/tests/functional/lang/eval-okay-context.nix @@ -0,0 +1,6 @@ +let s = "foo ${builtins.substring 33 100 (baseNameOf "${./eval-okay-context.nix}")} bar"; +in + if s != "foo eval-okay-context.nix bar" + then abort "context not discarded" + else builtins.unsafeDiscardStringContext s + diff --git a/tests/functional/lang/eval-okay-curpos.exp b/tests/functional/lang/eval-okay-curpos.exp new file mode 100644 index 000000000..65fd65b4d --- /dev/null +++ b/tests/functional/lang/eval-okay-curpos.exp @@ -0,0 +1 @@ +[ 3 7 4 9 ] diff --git a/tests/functional/lang/eval-okay-curpos.nix b/tests/functional/lang/eval-okay-curpos.nix new file mode 100644 index 000000000..b79553df0 --- /dev/null +++ b/tests/functional/lang/eval-okay-curpos.nix @@ -0,0 +1,5 @@ +# Bla +let + x = __curPos; + y = __curPos; +in [ x.line x.column y.line y.column ] diff --git a/tests/functional/lang/eval-okay-deepseq.exp b/tests/functional/lang/eval-okay-deepseq.exp new file mode 100644 index 000000000..8d38505c1 --- /dev/null +++ b/tests/functional/lang/eval-okay-deepseq.exp @@ -0,0 +1 @@ +456 diff --git a/tests/functional/lang/eval-okay-deepseq.nix b/tests/functional/lang/eval-okay-deepseq.nix new file mode 100644 index 000000000..53aa4b1dc --- /dev/null +++ b/tests/functional/lang/eval-okay-deepseq.nix @@ -0,0 +1 @@ +builtins.deepSeq (let as = { x = 123; y = as; }; in as) 456 diff --git a/tests/functional/lang/eval-okay-delayed-with-inherit.exp b/tests/functional/lang/eval-okay-delayed-with-inherit.exp new file mode 100644 index 000000000..eaacb55c1 --- /dev/null +++ b/tests/functional/lang/eval-okay-delayed-with-inherit.exp @@ -0,0 +1 @@ +"b-overridden" diff --git a/tests/functional/lang/eval-okay-delayed-with-inherit.nix b/tests/functional/lang/eval-okay-delayed-with-inherit.nix new file mode 100644 index 000000000..84b388c27 --- /dev/null +++ b/tests/functional/lang/eval-okay-delayed-with-inherit.nix @@ -0,0 +1,24 @@ +let + pkgs_ = with pkgs; { + a = derivation { + name = "a"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "touch $out" ]; + inherit b; + }; + + inherit b; + }; + + packageOverrides = p: { + b = derivation { + name = "b-overridden"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "touch $out" ]; + }; + }; + + pkgs = pkgs_ // (packageOverrides pkgs_); +in pkgs.a.b.name diff --git a/tests/functional/lang/eval-okay-delayed-with.exp b/tests/functional/lang/eval-okay-delayed-with.exp new file mode 100644 index 000000000..8e7c61ab8 --- /dev/null +++ b/tests/functional/lang/eval-okay-delayed-with.exp @@ -0,0 +1 @@ +"b-overridden b-overridden a" diff --git a/tests/functional/lang/eval-okay-delayed-with.nix b/tests/functional/lang/eval-okay-delayed-with.nix new file mode 100644 index 000000000..3fb023e1c --- /dev/null +++ b/tests/functional/lang/eval-okay-delayed-with.nix @@ -0,0 +1,29 @@ +let + + pkgs_ = with pkgs; { + a = derivation { + name = "a"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "touch $out" ]; + inherit b; + }; + + b = derivation { + name = "b"; + system = builtins.currentSystem; + builder = "/bin/sh"; + args = [ "-c" "touch $out" ]; + inherit a; + }; + + c = b; + }; + + packageOverrides = pkgs: with pkgs; { + b = derivation (b.drvAttrs // { name = "${b.name}-overridden"; }); + }; + + pkgs = pkgs_ // (packageOverrides pkgs_); + +in "${pkgs.a.b.name} ${pkgs.c.name} ${pkgs.b.a.name}" diff --git a/tests/functional/lang/eval-okay-dynamic-attrs-2.exp b/tests/functional/lang/eval-okay-dynamic-attrs-2.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-dynamic-attrs-2.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-dynamic-attrs-2.nix b/tests/functional/lang/eval-okay-dynamic-attrs-2.nix new file mode 100644 index 000000000..6d57bf854 --- /dev/null +++ b/tests/functional/lang/eval-okay-dynamic-attrs-2.nix @@ -0,0 +1 @@ +{ a."${"b"}" = true; a."${"c"}" = false; }.a.b diff --git a/tests/functional/lang/eval-okay-dynamic-attrs-bare.exp b/tests/functional/lang/eval-okay-dynamic-attrs-bare.exp new file mode 100644 index 000000000..df8750afc --- /dev/null +++ b/tests/functional/lang/eval-okay-dynamic-attrs-bare.exp @@ -0,0 +1 @@ +{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; } diff --git a/tests/functional/lang/eval-okay-dynamic-attrs-bare.nix b/tests/functional/lang/eval-okay-dynamic-attrs-bare.nix new file mode 100644 index 000000000..0dbe15e63 --- /dev/null +++ b/tests/functional/lang/eval-okay-dynamic-attrs-bare.nix @@ -0,0 +1,17 @@ +let + aString = "a"; + + bString = "b"; +in { + hasAttrs = { a.b = null; } ? ${aString}.b; + + selectAttrs = { a.b = true; }.a.${bString}; + + selectOrAttrs = { }.${aString} or true; + + binds = { ${aString}."${bString}c" = true; }.a.bc; + + recBinds = rec { ${bString} = a; a = true; }.b; + + multiAttrs = { ${aString} = true; ${bString} = false; }.a; +} diff --git a/tests/functional/lang/eval-okay-dynamic-attrs.exp b/tests/functional/lang/eval-okay-dynamic-attrs.exp new file mode 100644 index 000000000..df8750afc --- /dev/null +++ b/tests/functional/lang/eval-okay-dynamic-attrs.exp @@ -0,0 +1 @@ +{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; } diff --git a/tests/functional/lang/eval-okay-dynamic-attrs.nix b/tests/functional/lang/eval-okay-dynamic-attrs.nix new file mode 100644 index 000000000..ee02ac7e6 --- /dev/null +++ b/tests/functional/lang/eval-okay-dynamic-attrs.nix @@ -0,0 +1,17 @@ +let + aString = "a"; + + bString = "b"; +in { + hasAttrs = { a.b = null; } ? "${aString}".b; + + selectAttrs = { a.b = true; }.a."${bString}"; + + selectOrAttrs = { }."${aString}" or true; + + binds = { "${aString}"."${bString}c" = true; }.a.bc; + + recBinds = rec { "${bString}" = a; a = true; }.b; + + multiAttrs = { "${aString}" = true; "${bString}" = false; }.a; +} diff --git a/tests/functional/lang/eval-okay-elem.exp b/tests/functional/lang/eval-okay-elem.exp new file mode 100644 index 000000000..3cf6c0e96 --- /dev/null +++ b/tests/functional/lang/eval-okay-elem.exp @@ -0,0 +1 @@ +[ true false 30 ] diff --git a/tests/functional/lang/eval-okay-elem.nix b/tests/functional/lang/eval-okay-elem.nix new file mode 100644 index 000000000..71ea7a4ed --- /dev/null +++ b/tests/functional/lang/eval-okay-elem.nix @@ -0,0 +1,6 @@ +with import ./lib.nix; + +let xs = range 10 40; in + +[ (builtins.elem 23 xs) (builtins.elem 42 xs) (builtins.elemAt xs 20) ] + diff --git a/tests/functional/lang/eval-okay-empty-args.exp b/tests/functional/lang/eval-okay-empty-args.exp new file mode 100644 index 000000000..cb5537d5d --- /dev/null +++ b/tests/functional/lang/eval-okay-empty-args.exp @@ -0,0 +1 @@ +"ab" diff --git a/tests/functional/lang/eval-okay-empty-args.nix b/tests/functional/lang/eval-okay-empty-args.nix new file mode 100644 index 000000000..78c133afd --- /dev/null +++ b/tests/functional/lang/eval-okay-empty-args.nix @@ -0,0 +1 @@ +({}: {x,y,}: "${x}${y}") {} {x = "a"; y = "b";} diff --git a/tests/functional/lang/eval-okay-eq-derivations.exp b/tests/functional/lang/eval-okay-eq-derivations.exp new file mode 100644 index 000000000..ec04aab6a --- /dev/null +++ b/tests/functional/lang/eval-okay-eq-derivations.exp @@ -0,0 +1 @@ +[ true true true false ] diff --git a/tests/functional/lang/eval-okay-eq-derivations.nix b/tests/functional/lang/eval-okay-eq-derivations.nix new file mode 100644 index 000000000..d526cb4a2 --- /dev/null +++ b/tests/functional/lang/eval-okay-eq-derivations.nix @@ -0,0 +1,10 @@ +let + + drvA1 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; }; + drvA2 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; }; + drvA3 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; } // { dummy = 1; }; + + drvC1 = derivation { name = "c"; builder = "/foo"; system = "i686-linux"; }; + drvC2 = derivation { name = "c"; builder = "/bar"; system = "i686-linux"; }; + +in [ (drvA1 == drvA1) (drvA1 == drvA2) (drvA1 == drvA3) (drvC1 == drvC2) ] diff --git a/tests/functional/lang/eval-okay-eq.exp b/tests/functional/lang/eval-okay-eq.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-eq.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-eq.nix b/tests/functional/lang/eval-okay-eq.nix new file mode 100644 index 000000000..73d200b38 --- /dev/null +++ b/tests/functional/lang/eval-okay-eq.nix @@ -0,0 +1,3 @@ +["foobar" (rec {x = 1; y = x;})] +== +[("foo" + "bar") ({x = 1; y = 1;})] diff --git a/tests/functional/lang/eval-okay-filter.exp b/tests/functional/lang/eval-okay-filter.exp new file mode 100644 index 000000000..355d51c27 --- /dev/null +++ b/tests/functional/lang/eval-okay-filter.exp @@ -0,0 +1 @@ +[ 0 2 4 6 8 10 100 102 104 106 108 110 ] diff --git a/tests/functional/lang/eval-okay-filter.nix b/tests/functional/lang/eval-okay-filter.nix new file mode 100644 index 000000000..85109b0d0 --- /dev/null +++ b/tests/functional/lang/eval-okay-filter.nix @@ -0,0 +1,5 @@ +with import ./lib.nix; + +builtins.filter + (x: x / 2 * 2 == x) + (builtins.concatLists [ (range 0 10) (range 100 110) ]) diff --git a/tests/functional/lang/eval-okay-flake-ref-to-string.exp b/tests/functional/lang/eval-okay-flake-ref-to-string.exp new file mode 100644 index 000000000..110f8442d --- /dev/null +++ b/tests/functional/lang/eval-okay-flake-ref-to-string.exp @@ -0,0 +1 @@ +"github:NixOS/nixpkgs/23.05?dir=lib" diff --git a/tests/functional/lang/eval-okay-flake-ref-to-string.nix b/tests/functional/lang/eval-okay-flake-ref-to-string.nix new file mode 100644 index 000000000..dbb4e5b2a --- /dev/null +++ b/tests/functional/lang/eval-okay-flake-ref-to-string.nix @@ -0,0 +1,7 @@ +builtins.flakeRefToString { + type = "github"; + owner = "NixOS"; + repo = "nixpkgs"; + ref = "23.05"; + dir = "lib"; +} diff --git a/tests/functional/lang/eval-okay-flatten.exp b/tests/functional/lang/eval-okay-flatten.exp new file mode 100644 index 000000000..b979b2b8b --- /dev/null +++ b/tests/functional/lang/eval-okay-flatten.exp @@ -0,0 +1 @@ +"1234567" diff --git a/tests/functional/lang/eval-okay-flatten.nix b/tests/functional/lang/eval-okay-flatten.nix new file mode 100644 index 000000000..fe911e968 --- /dev/null +++ b/tests/functional/lang/eval-okay-flatten.nix @@ -0,0 +1,8 @@ +with import ./lib.nix; + +let { + + l = ["1" "2" ["3" ["4"] ["5" "6"]] "7"]; + + body = concat (flatten l); +} diff --git a/tests/functional/lang/eval-okay-float.exp b/tests/functional/lang/eval-okay-float.exp new file mode 100644 index 000000000..3c50a8adc --- /dev/null +++ b/tests/functional/lang/eval-okay-float.exp @@ -0,0 +1 @@ +[ 3.4 3.5 2.5 1.5 ] diff --git a/tests/functional/lang/eval-okay-float.nix b/tests/functional/lang/eval-okay-float.nix new file mode 100644 index 000000000..b2702c7b1 --- /dev/null +++ b/tests/functional/lang/eval-okay-float.nix @@ -0,0 +1,6 @@ +[ + (1.1 + 2.3) + (builtins.add (0.5 + 0.5) (2.0 + 0.5)) + ((0.5 + 0.5) * (2.0 + 0.5)) + ((1.5 + 1.5) / (0.5 * 4.0)) +] diff --git a/tests/functional/lang/eval-okay-floor-ceil.exp b/tests/functional/lang/eval-okay-floor-ceil.exp new file mode 100644 index 000000000..81f80420b --- /dev/null +++ b/tests/functional/lang/eval-okay-floor-ceil.exp @@ -0,0 +1 @@ +"23;24;23;23" diff --git a/tests/functional/lang/eval-okay-floor-ceil.nix b/tests/functional/lang/eval-okay-floor-ceil.nix new file mode 100644 index 000000000..d76a0d86e --- /dev/null +++ b/tests/functional/lang/eval-okay-floor-ceil.nix @@ -0,0 +1,9 @@ +with import ./lib.nix; + +let + n1 = builtins.floor 23.5; + n2 = builtins.ceil 23.5; + n3 = builtins.floor 23; + n4 = builtins.ceil 23; +in + builtins.concatStringsSep ";" (map toString [ n1 n2 n3 n4 ]) diff --git a/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp new file mode 100644 index 000000000..d81cc0710 --- /dev/null +++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp @@ -0,0 +1 @@ +42 diff --git a/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix new file mode 100644 index 000000000..c666e07f3 --- /dev/null +++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix @@ -0,0 +1,9 @@ +# Tests that the rhs argument of op is not forced unconditionally +let + lst = builtins.foldl' + (acc: x: acc ++ [ x ]) + [ ] + [ 42 (throw "this shouldn't be evaluated") ]; +in + +builtins.head lst diff --git a/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp new file mode 100644 index 000000000..d81cc0710 --- /dev/null +++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp @@ -0,0 +1 @@ +42 diff --git a/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix new file mode 100644 index 000000000..abcd5366a --- /dev/null +++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix @@ -0,0 +1,6 @@ +# Checks that the nul value for the accumulator is not forced unconditionally. +# Some languages provide a foldl' that is strict in this argument, but Nix does not. +builtins.foldl' + (_: x: x) + (throw "This is never forced") + [ "but the results of applying op are" 42 ] diff --git a/tests/functional/lang/eval-okay-foldlStrict.exp b/tests/functional/lang/eval-okay-foldlStrict.exp new file mode 100644 index 000000000..837e12b40 --- /dev/null +++ b/tests/functional/lang/eval-okay-foldlStrict.exp @@ -0,0 +1 @@ +500500 diff --git a/tests/functional/lang/eval-okay-foldlStrict.nix b/tests/functional/lang/eval-okay-foldlStrict.nix new file mode 100644 index 000000000..3b87188d2 --- /dev/null +++ b/tests/functional/lang/eval-okay-foldlStrict.nix @@ -0,0 +1,3 @@ +with import ./lib.nix; + +builtins.foldl' (x: y: x + y) 0 (range 1 1000) diff --git a/tests/functional/lang/eval-okay-fromTOML-timestamps.exp b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp new file mode 100644 index 000000000..08b3c69a6 --- /dev/null +++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp @@ -0,0 +1 @@ +{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt2 = { _type = "timestamp"; value = "00:32:00.999999"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; } diff --git a/tests/functional/lang/eval-okay-fromTOML-timestamps.flags b/tests/functional/lang/eval-okay-fromTOML-timestamps.flags new file mode 100644 index 000000000..9ed39dc6b --- /dev/null +++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.flags @@ -0,0 +1 @@ +--extra-experimental-features parse-toml-timestamps diff --git a/tests/functional/lang/eval-okay-fromTOML-timestamps.nix b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix new file mode 100644 index 000000000..74cff9470 --- /dev/null +++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix @@ -0,0 +1,130 @@ +builtins.fromTOML '' + key = "value" + bare_key = "value" + bare-key = "value" + 1234 = "value" + + "127.0.0.1" = "value" + "character encoding" = "value" + "ʎǝʞ" = "value" + 'key2' = "value" + 'quoted "value"' = "value" + + name = "Orange" + + physical.color = "orange" + physical.shape = "round" + site."google.com" = true + + # This is legal according to the spec, but cpptoml doesn't handle it. + #a.b.c = 1 + #a.d = 2 + + str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." + + int1 = +99 + int2 = 42 + int3 = 0 + int4 = -17 + int5 = 1_000 + int6 = 5_349_221 + int7 = 1_2_3_4_5 + + hex1 = 0xDEADBEEF + hex2 = 0xdeadbeef + hex3 = 0xdead_beef + + oct1 = 0o01234567 + oct2 = 0o755 + + bin1 = 0b11010110 + + flt1 = +1.0 + flt2 = 3.1415 + flt3 = -0.01 + flt4 = 5e+22 + flt5 = 1e6 + flt6 = -2E-2 + flt7 = 6.626e-34 + flt8 = 9_224_617.445_991_228_313 + + bool1 = true + bool2 = false + + odt1 = 1979-05-27T07:32:00Z + odt2 = 1979-05-27T00:32:00-07:00 + odt3 = 1979-05-27T00:32:00.999999-07:00 + odt4 = 1979-05-27 07:32:00Z + ldt1 = 1979-05-27T07:32:00 + ldt2 = 1979-05-27T00:32:00.999999 + ld1 = 1979-05-27 + lt1 = 07:32:00 + lt2 = 00:32:00.999999 + + arr1 = [ 1, 2, 3 ] + arr2 = [ "red", "yellow", "green" ] + arr3 = [ [ 1, 2 ], [3, 4, 5] ] + arr4 = [ "all", 'strings', """are the same""", ''''type''''] + arr5 = [ [ 1, 2 ], ["a", "b", "c"] ] + + arr7 = [ + 1, 2, 3 + ] + + arr8 = [ + 1, + 2, # this is ok + ] + + [table-1] + key1 = "some string" + key2 = 123 + + + [table-2] + key1 = "another string" + key2 = 456 + + [dog."tater.man"] + type.name = "pug" + + [a.b.c] + [ d.e.f ] + [ g . h . i ] + [ j . "ʞ" . 'l' ] + [x.y.z.w] + + name = { first = "Tom", last = "Preston-Werner" } + point = { x = 1, y = 2 } + animal = { type.name = "pug" } + + [[products]] + name = "Hammer" + sku = 738594937 + + [[products]] + + [[products]] + name = "Nail" + sku = 284758393 + color = "gray" + + [[fruit]] + name = "apple" + + [fruit.physical] + color = "red" + shape = "round" + + [[fruit.variety]] + name = "red delicious" + + [[fruit.variety]] + name = "granny smith" + + [[fruit]] + name = "banana" + + [[fruit.variety]] + name = "plantain" +'' diff --git a/tests/functional/lang/eval-okay-fromTOML.exp b/tests/functional/lang/eval-okay-fromTOML.exp new file mode 100644 index 000000000..d0dd3af2c --- /dev/null +++ b/tests/functional/lang/eval-okay-fromTOML.exp @@ -0,0 +1 @@ +[ { clients = { data = [ [ "gamma" "delta" ] [ 1 2 ] ]; hosts = [ "alpha" "omega" ]; }; database = { connection_max = 5000; enabled = true; ports = [ 8001 8001 8002 ]; server = "192.168.1.1"; }; owner = { name = "Tom Preston-Werner"; }; servers = { alpha = { dc = "eqdc10"; ip = "10.0.0.1"; }; beta = { dc = "eqdc10"; ip = "10.0.0.2"; }; }; title = "TOML Example"; } { "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; name = "Orange"; oct1 = 342391; oct2 = 493; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; } { metadata = { "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"; "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"; "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"; "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"; }; package = [ { dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" ]; name = "aho-corasick"; source = "registry+https://github.com/rust-lang/crates.io-index"; version = "0.6.4"; } { name = "ansi_term"; source = "registry+https://github.com/rust-lang/crates.io-index"; version = "0.9.0"; } { dependencies = [ "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" ]; name = "atty"; source = "registry+https://github.com/rust-lang/crates.io-index"; version = "0.2.10"; } ]; } { a = [ [ { b = true; } ] ]; c = [ [ { d = true; } ] ]; e = [ [ 123 ] ]; } ] diff --git a/tests/functional/lang/eval-okay-fromTOML.nix b/tests/functional/lang/eval-okay-fromTOML.nix new file mode 100644 index 000000000..963932689 --- /dev/null +++ b/tests/functional/lang/eval-okay-fromTOML.nix @@ -0,0 +1,208 @@ +[ + + (builtins.fromTOML '' + # This is a TOML document. + + title = "TOML Example" + + [owner] + name = "Tom Preston-Werner" + #dob = 1979-05-27T07:32:00-08:00 # First class dates + + [database] + server = "192.168.1.1" + ports = [ 8001, 8001, 8002 ] + connection_max = 5000 + enabled = true + + [servers] + + # Indentation (tabs and/or spaces) is allowed but not required + [servers.alpha] + ip = "10.0.0.1" + dc = "eqdc10" + + [servers.beta] + ip = "10.0.0.2" + dc = "eqdc10" + + [clients] + data = [ ["gamma", "delta"], [1, 2] ] + + # Line breaks are OK when inside arrays + hosts = [ + "alpha", + "omega" + ] + '') + + (builtins.fromTOML '' + key = "value" + bare_key = "value" + bare-key = "value" + 1234 = "value" + + "127.0.0.1" = "value" + "character encoding" = "value" + "ʎǝʞ" = "value" + 'key2' = "value" + 'quoted "value"' = "value" + + name = "Orange" + + physical.color = "orange" + physical.shape = "round" + site."google.com" = true + + # This is legal according to the spec, but cpptoml doesn't handle it. + #a.b.c = 1 + #a.d = 2 + + str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF." + + int1 = +99 + int2 = 42 + int3 = 0 + int4 = -17 + int5 = 1_000 + int6 = 5_349_221 + int7 = 1_2_3_4_5 + + hex1 = 0xDEADBEEF + hex2 = 0xdeadbeef + hex3 = 0xdead_beef + + oct1 = 0o01234567 + oct2 = 0o755 + + bin1 = 0b11010110 + + flt1 = +1.0 + flt2 = 3.1415 + flt3 = -0.01 + flt4 = 5e+22 + flt5 = 1e6 + flt6 = -2E-2 + flt7 = 6.626e-34 + flt8 = 9_224_617.445_991_228_313 + + bool1 = true + bool2 = false + + # FIXME: not supported because Nix doesn't have a date/time type. + #odt1 = 1979-05-27T07:32:00Z + #odt2 = 1979-05-27T00:32:00-07:00 + #odt3 = 1979-05-27T00:32:00.999999-07:00 + #odt4 = 1979-05-27 07:32:00Z + #ldt1 = 1979-05-27T07:32:00 + #ldt2 = 1979-05-27T00:32:00.999999 + #ld1 = 1979-05-27 + #lt1 = 07:32:00 + #lt2 = 00:32:00.999999 + + arr1 = [ 1, 2, 3 ] + arr2 = [ "red", "yellow", "green" ] + arr3 = [ [ 1, 2 ], [3, 4, 5] ] + arr4 = [ "all", 'strings', """are the same""", ''''type''''] + arr5 = [ [ 1, 2 ], ["a", "b", "c"] ] + + arr7 = [ + 1, 2, 3 + ] + + arr8 = [ + 1, + 2, # this is ok + ] + + [table-1] + key1 = "some string" + key2 = 123 + + + [table-2] + key1 = "another string" + key2 = 456 + + [dog."tater.man"] + type.name = "pug" + + [a.b.c] + [ d.e.f ] + [ g . h . i ] + [ j . "ʞ" . 'l' ] + [x.y.z.w] + + name = { first = "Tom", last = "Preston-Werner" } + point = { x = 1, y = 2 } + animal = { type.name = "pug" } + + [[products]] + name = "Hammer" + sku = 738594937 + + [[products]] + + [[products]] + name = "Nail" + sku = 284758393 + color = "gray" + + [[fruit]] + name = "apple" + + [fruit.physical] + color = "red" + shape = "round" + + [[fruit.variety]] + name = "red delicious" + + [[fruit.variety]] + name = "granny smith" + + [[fruit]] + name = "banana" + + [[fruit.variety]] + name = "plantain" + '') + + (builtins.fromTOML '' + [[package]] + name = "aho-corasick" + version = "0.6.4" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [[package]] + name = "ansi_term" + version = "0.9.0" + source = "registry+https://github.com/rust-lang/crates.io-index" + + [[package]] + name = "atty" + version = "0.2.10" + source = "registry+https://github.com/rust-lang/crates.io-index" + dependencies = [ + "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)", + "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + ] + + [metadata] + "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4" + "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" + "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6" + "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef" + '') + + (builtins.fromTOML '' + a = [[{ b = true }]] + c = [ [ { d = true } ] ] + e = [[123]] + '') + +] diff --git a/tests/functional/lang/eval-okay-fromjson-escapes.exp b/tests/functional/lang/eval-okay-fromjson-escapes.exp new file mode 100644 index 000000000..add5505a8 --- /dev/null +++ b/tests/functional/lang/eval-okay-fromjson-escapes.exp @@ -0,0 +1 @@ +"quote \" reverse solidus \\ solidus / backspace formfeed newline \n carriage return \r horizontal tab \t 1 char unicode encoded backspace 1 char unicode encoded e with accent é 2 char unicode encoded s with caron š 3 char unicode encoded rightwards arrow →" diff --git a/tests/functional/lang/eval-okay-fromjson-escapes.nix b/tests/functional/lang/eval-okay-fromjson-escapes.nix new file mode 100644 index 000000000..f00713507 --- /dev/null +++ b/tests/functional/lang/eval-okay-fromjson-escapes.nix @@ -0,0 +1,3 @@ +# This string contains all supported escapes in a JSON string, per json.org +# \b and \f are not supported by Nix +builtins.fromJSON ''"quote \" reverse solidus \\ solidus \/ backspace \b formfeed \f newline \n carriage return \r horizontal tab \t 1 char unicode encoded backspace \u0008 1 char unicode encoded e with accent \u00e9 2 char unicode encoded s with caron \u0161 3 char unicode encoded rightwards arrow \u2192"'' diff --git a/tests/functional/lang/eval-okay-fromjson.exp b/tests/functional/lang/eval-okay-fromjson.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-fromjson.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-fromjson.nix b/tests/functional/lang/eval-okay-fromjson.nix new file mode 100644 index 000000000..4c526b9ae --- /dev/null +++ b/tests/functional/lang/eval-okay-fromjson.nix @@ -0,0 +1,41 @@ +builtins.fromJSON + '' + { + "Video": { + "Title": "The Penguin Chronicles", + "Width": 1920, + "Height": 1080, + "EmbeddedData": [3.14159, 23493,null, true ,false, -10], + "Thumb": { + "Url": "http://www.example.com/video/5678931", + "Width": 200, + "Height": 250 + }, + "Animated" : false, + "IDs": [116, 943, 234, 38793, true ,false,null, -100], + "Escapes": "\"\\\/\t\n\r\t", + "Subtitle" : false, + "Latitude": 37.7668, + "Longitude": -122.3959 + } + } + '' +== + { Video = + { Title = "The Penguin Chronicles"; + Width = 1920; + Height = 1080; + EmbeddedData = [ 3.14159 23493 null true false (0-10) ]; + Thumb = + { Url = "http://www.example.com/video/5678931"; + Width = 200; + Height = 250; + }; + Animated = false; + IDs = [ 116 943 234 38793 true false null (0-100) ]; + Escapes = "\"\\\/\t\n\r\t"; # supported in JSON but not Nix: \b\f + Subtitle = false; + Latitude = 37.7668; + Longitude = -122.3959; + }; + } diff --git a/tests/functional/lang/eval-okay-functionargs.exp b/tests/functional/lang/eval-okay-functionargs.exp new file mode 100644 index 000000000..c1c9f8ffa --- /dev/null +++ b/tests/functional/lang/eval-okay-functionargs.exp @@ -0,0 +1 @@ +[ "stdenv" "fetchurl" "aterm-stdenv" "aterm-stdenv2" "libX11" "libXv" "mplayer-stdenv2.libXv-libX11" "mplayer-stdenv2.libXv-libX11_2" "nix-stdenv-aterm-stdenv" "nix-stdenv2-aterm2-stdenv2" ] diff --git a/tests/functional/lang/eval-okay-functionargs.exp.xml b/tests/functional/lang/eval-okay-functionargs.exp.xml new file mode 100644 index 000000000..651f54c36 --- /dev/null +++ b/tests/functional/lang/eval-okay-functionargs.exp.xml @@ -0,0 +1,15 @@ +<?xml version='1.0' encoding='utf-8'?> +<expr> + <list> + <string value="stdenv" /> + <string value="fetchurl" /> + <string value="aterm-stdenv" /> + <string value="aterm-stdenv2" /> + <string value="libX11" /> + <string value="libXv" /> + <string value="mplayer-stdenv2.libXv-libX11" /> + <string value="mplayer-stdenv2.libXv-libX11_2" /> + <string value="nix-stdenv-aterm-stdenv" /> + <string value="nix-stdenv2-aterm2-stdenv2" /> + </list> +</expr> diff --git a/tests/functional/lang/eval-okay-functionargs.nix b/tests/functional/lang/eval-okay-functionargs.nix new file mode 100644 index 000000000..68dca62ee --- /dev/null +++ b/tests/functional/lang/eval-okay-functionargs.nix @@ -0,0 +1,80 @@ +let + + stdenvFun = { }: { name = "stdenv"; }; + stdenv2Fun = { }: { name = "stdenv2"; }; + fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; }; + atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; }; + aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; }; + nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; }; + + mplayerFun = + { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null }: + assert stdenv.name == "stdenv2"; + assert enableX11 -> xorg.libXv.name == "libXv"; + assert enableFoo -> foo != null; + { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; }; + + makeOverridable = f: origArgs: f origArgs // + { override = newArgs: + makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs)); + }; + + callPackage_ = pkgs: f: args: + makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args); + + allPackages = + { overrides ? (pkgs: pkgsPrev: { }) }: + let + callPackage = callPackage_ pkgs; + pkgs = pkgsStd // (overrides pkgs pkgsStd); + pkgsStd = { + inherit pkgs; + stdenv = callPackage stdenvFun { }; + stdenv2 = callPackage stdenv2Fun { }; + fetchurl = callPackage fetchurlFun { }; + aterm = callPackage atermFun { }; + xorg = callPackage xorgFun { }; + mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; }; + nix = callPackage nixFun { }; + }; + in pkgs; + + libX11Fun = { stdenv, fetchurl }: { name = "libX11"; }; + libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; }; + libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; }; + + xorgFun = + { pkgs }: + let callPackage = callPackage_ (pkgs // pkgs.xorg); in + { + libX11 = callPackage libX11Fun { }; + libXv = callPackage libXvFun { }; + }; + +in + +let + + pkgs = allPackages { }; + + pkgs2 = allPackages { + overrides = pkgs: pkgsPrev: { + stdenv = pkgs.stdenv2; + nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; }; + xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; }; + }; + }; + +in + + [ pkgs.stdenv.name + pkgs.fetchurl.name + pkgs.aterm.name + pkgs2.aterm.name + pkgs.xorg.libX11.name + pkgs.xorg.libXv.name + pkgs.mplayer.name + pkgs2.mplayer.name + pkgs.nix.name + pkgs2.nix.name + ] diff --git a/tests/functional/lang/eval-okay-getattrpos-functionargs.exp b/tests/functional/lang/eval-okay-getattrpos-functionargs.exp new file mode 100644 index 000000000..7f9ac40e8 --- /dev/null +++ b/tests/functional/lang/eval-okay-getattrpos-functionargs.exp @@ -0,0 +1 @@ +{ column = 11; file = "eval-okay-getattrpos-functionargs.nix"; line = 2; } diff --git a/tests/functional/lang/eval-okay-getattrpos-functionargs.nix b/tests/functional/lang/eval-okay-getattrpos-functionargs.nix new file mode 100644 index 000000000..11d6bb0e3 --- /dev/null +++ b/tests/functional/lang/eval-okay-getattrpos-functionargs.nix @@ -0,0 +1,4 @@ +let + fun = { foo }: {}; + pos = builtins.unsafeGetAttrPos "foo" (builtins.functionArgs fun); +in { inherit (pos) column line; file = baseNameOf pos.file; } diff --git a/tests/functional/lang/eval-okay-getattrpos-undefined.exp b/tests/functional/lang/eval-okay-getattrpos-undefined.exp new file mode 100644 index 000000000..19765bd50 --- /dev/null +++ b/tests/functional/lang/eval-okay-getattrpos-undefined.exp @@ -0,0 +1 @@ +null diff --git a/tests/functional/lang/eval-okay-getattrpos-undefined.nix b/tests/functional/lang/eval-okay-getattrpos-undefined.nix new file mode 100644 index 000000000..14dd38f77 --- /dev/null +++ b/tests/functional/lang/eval-okay-getattrpos-undefined.nix @@ -0,0 +1 @@ +builtins.unsafeGetAttrPos "abort" builtins diff --git a/tests/functional/lang/eval-okay-getattrpos.exp b/tests/functional/lang/eval-okay-getattrpos.exp new file mode 100644 index 000000000..469249bbc --- /dev/null +++ b/tests/functional/lang/eval-okay-getattrpos.exp @@ -0,0 +1 @@ +{ column = 5; file = "eval-okay-getattrpos.nix"; line = 3; } diff --git a/tests/functional/lang/eval-okay-getattrpos.nix b/tests/functional/lang/eval-okay-getattrpos.nix new file mode 100644 index 000000000..ca6b07961 --- /dev/null +++ b/tests/functional/lang/eval-okay-getattrpos.nix @@ -0,0 +1,6 @@ +let + as = { + foo = "bar"; + }; + pos = builtins.unsafeGetAttrPos "foo" as; +in { inherit (pos) column line; file = baseNameOf pos.file; } diff --git a/tests/functional/lang/eval-okay-getenv.exp b/tests/functional/lang/eval-okay-getenv.exp new file mode 100644 index 000000000..14e24d419 --- /dev/null +++ b/tests/functional/lang/eval-okay-getenv.exp @@ -0,0 +1 @@ +"foobar" diff --git a/tests/functional/lang/eval-okay-getenv.nix b/tests/functional/lang/eval-okay-getenv.nix new file mode 100644 index 000000000..4cfec5f55 --- /dev/null +++ b/tests/functional/lang/eval-okay-getenv.nix @@ -0,0 +1 @@ +builtins.getEnv "TEST_VAR" + (if builtins.getEnv "NO_SUCH_VAR" == "" then "bar" else "bla") diff --git a/tests/functional/lang/eval-okay-groupBy.exp b/tests/functional/lang/eval-okay-groupBy.exp new file mode 100644 index 000000000..bfca5652a --- /dev/null +++ b/tests/functional/lang/eval-okay-groupBy.exp @@ -0,0 +1 @@ +{ "1" = [ 9 ]; "2" = [ 8 ]; "3" = [ 13 29 ]; "4" = [ 3 4 10 11 17 18 ]; "5" = [ 0 23 26 28 ]; "6" = [ 1 12 21 27 30 ]; "7" = [ 7 22 ]; "8" = [ 14 ]; "9" = [ 19 ]; b = [ 16 25 ]; c = [ 24 ]; d = [ 2 ]; e = [ 5 6 15 31 ]; f = [ 20 ]; } diff --git a/tests/functional/lang/eval-okay-groupBy.nix b/tests/functional/lang/eval-okay-groupBy.nix new file mode 100644 index 000000000..862d89dbd --- /dev/null +++ b/tests/functional/lang/eval-okay-groupBy.nix @@ -0,0 +1,5 @@ +with import ./lib.nix; + +builtins.groupBy (n: + builtins.substring 0 1 (builtins.hashString "sha256" (toString n)) +) (range 0 31) diff --git a/tests/functional/lang/eval-okay-hash.exp b/tests/functional/lang/eval-okay-hash.exp new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/functional/lang/eval-okay-hash.exp diff --git a/tests/functional/lang/eval-okay-hashfile.exp b/tests/functional/lang/eval-okay-hashfile.exp new file mode 100644 index 000000000..ff1e8293e --- /dev/null +++ b/tests/functional/lang/eval-okay-hashfile.exp @@ -0,0 +1 @@ +[ "d3b07384d113edec49eaa6238ad5ff00" "0f343b0931126a20f133d67c2b018a3b" "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15" "60cacbf3d72e1e7834203da608037b1bf83b40e8" "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c" "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" "0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6" "8efb4f73c5655351c444eb109230c556d39e2c7624e9c11abc9e3fb4b9b9254218cc5085b454a9698d085cfa92198491f07a723be4574adc70617b73eb0b6461" ] diff --git a/tests/functional/lang/eval-okay-hashfile.nix b/tests/functional/lang/eval-okay-hashfile.nix new file mode 100644 index 000000000..aff5a1856 --- /dev/null +++ b/tests/functional/lang/eval-okay-hashfile.nix @@ -0,0 +1,4 @@ +let + paths = [ ./data ./binary-data ]; +in + builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"]) diff --git a/tests/functional/lang/eval-okay-hashstring.exp b/tests/functional/lang/eval-okay-hashstring.exp new file mode 100644 index 000000000..d720a082d --- /dev/null +++ b/tests/functional/lang/eval-okay-hashstring.exp @@ -0,0 +1 @@ +[ "d41d8cd98f00b204e9800998ecf8427e" "6c69ee7f211c640419d5366cc076ae46" "bb3438fbabd460ea6dbd27d153e2233b" "da39a3ee5e6b4b0d3255bfef95601890afd80709" "cd54e8568c1b37cf1e5badb0779bcbf382212189" "6d12e10b1d331dad210e47fd25d4f260802b7e77" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "900a4469df00ccbfd0c145c6d1e4b7953dd0afafadd7534e3a4019e8d38fc663" "ad0387b3bd8652f730ca46d25f9c170af0fd589f42e7f23f5a9e6412d97d7e56" "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" "9d0886f8c6b389398a16257bc79780fab9831c7fc11c8ab07fa732cb7b348feade382f92617c9c5305fefba0af02ab5fd39a587d330997ff5bd0db19f7666653" "21644b72aa259e5a588cd3afbafb1d4310f4889680f6c83b9d531596a5a284f34dbebff409d23bcc86aee6bad10c891606f075c6f4755cb536da27db5693f3a7" ] diff --git a/tests/functional/lang/eval-okay-hashstring.nix b/tests/functional/lang/eval-okay-hashstring.nix new file mode 100644 index 000000000..b0f62b245 --- /dev/null +++ b/tests/functional/lang/eval-okay-hashstring.nix @@ -0,0 +1,4 @@ +let + strings = [ "" "text 1" "text 2" ]; +in + builtins.concatLists (map (hash: map (builtins.hashString hash) strings) ["md5" "sha1" "sha256" "sha512"]) diff --git a/tests/functional/lang/eval-okay-if.exp b/tests/functional/lang/eval-okay-if.exp new file mode 100644 index 000000000..00750edc0 --- /dev/null +++ b/tests/functional/lang/eval-okay-if.exp @@ -0,0 +1 @@ +3 diff --git a/tests/functional/lang/eval-okay-if.nix b/tests/functional/lang/eval-okay-if.nix new file mode 100644 index 000000000..23e4c74d5 --- /dev/null +++ b/tests/functional/lang/eval-okay-if.nix @@ -0,0 +1 @@ +if "foo" != "f" + "oo" then 1 else if false then 2 else 3 diff --git a/tests/functional/lang/eval-okay-import.exp b/tests/functional/lang/eval-okay-import.exp new file mode 100644 index 000000000..c508125b5 --- /dev/null +++ b/tests/functional/lang/eval-okay-import.exp @@ -0,0 +1 @@ +[ 1 2 3 4 5 6 7 8 9 10 ] diff --git a/tests/functional/lang/eval-okay-import.nix b/tests/functional/lang/eval-okay-import.nix new file mode 100644 index 000000000..0b18d9413 --- /dev/null +++ b/tests/functional/lang/eval-okay-import.nix @@ -0,0 +1,11 @@ +let + + overrides = { + import = fn: scopedImport overrides fn; + + scopedImport = attrs: fn: scopedImport (overrides // attrs) fn; + + builtins = builtins // overrides; + } // import ./lib.nix; + +in scopedImport overrides ./imported.nix diff --git a/tests/functional/lang/eval-okay-ind-string.exp b/tests/functional/lang/eval-okay-ind-string.exp new file mode 100644 index 000000000..7862331fa --- /dev/null +++ b/tests/functional/lang/eval-okay-ind-string.exp @@ -0,0 +1 @@ +"This is an indented multi-line string\nliteral. An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed. Thus,\nin this case four spaces will be\nstripped from each line, even though\n THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n followed by a newline, it's stripped, but\n that's not the case here. Two spaces are\n stripped because of the \" \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', \${.\n Tabs are not interpreted as whitespace (since we can't guess\n what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored. But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n Similarly you can force an indentation level,\n in this case to 2 spaces. This works because the anti-quote\n is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n rm -f /var/run/opengl-driver\n ln -sf 123 /var/run/opengl-driver\n\n rm -f /var/log/slim.log\n \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: \${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\ncut -d $'\\t' -f 1\nending dollar $$\n" diff --git a/tests/functional/lang/eval-okay-ind-string.nix b/tests/functional/lang/eval-okay-ind-string.nix new file mode 100644 index 000000000..95d59b508 --- /dev/null +++ b/tests/functional/lang/eval-okay-ind-string.nix @@ -0,0 +1,128 @@ +let + + s1 = '' + This is an indented multi-line string + literal. An amount of whitespace at + the start of each line matching the minimum + indentation of all lines in the string + literal together will be removed. Thus, + in this case four spaces will be + stripped from each line, even though + THIS LINE is indented six spaces. + + Also, empty lines don't count in the + determination of the indentation level (the + previous empty line has indentation 0, but + it doesn't matter). + ''; + + s2 = '' If the string starts with whitespace + followed by a newline, it's stripped, but + that's not the case here. Two spaces are + stripped because of the " " at the start. + ''; + + s3 = '' + This line is indented + a bit further. + ''; # indentation of last line doesn't count if it's empty + + s4 = '' + Anti-quotations, like ${if true then "so" else "not so"}, are + also allowed. + ''; + + s5 = '' + The \ is not special here. + ' can be followed by any character except another ', e.g. 'x'. + Likewise for $, e.g. $$ or $varName. + But ' followed by ' is special, as is $ followed by {. + If you want them, use anti-quotations: ${"''"}, ${"\${"}. + ''; + + s6 = '' + Tabs are not interpreted as whitespace (since we can't guess + what tab settings are intended), so don't use them. + This line starts with a space and a tab, so only one + space will be stripped from each line. + ''; + + s7 = '' + Also note that if the last line (just before the closing ' ') + consists only of whitespace, it's ignored. But here there is + some non-whitespace stuff, so the line isn't removed. ''; + + s8 = '' ${""} + This shows a hacky way to preserve an empty line after the start. + But there's no reason to do so: you could just repeat the empty + line. + ''; + + s9 = '' + ${""} Similarly you can force an indentation level, + in this case to 2 spaces. This works because the anti-quote + is significant (not whitespace). + ''; + + s10 = '' + ''; + + s11 = ''''; + + s12 = '' ''; + + s13 = '' + start on network-interfaces + + start script + + rm -f /var/run/opengl-driver + ${if true + then "ln -sf 123 /var/run/opengl-driver" + else if true + then "ln -sf 456 /var/run/opengl-driver" + else "" + } + + rm -f /var/log/slim.log + + end script + + env SLIM_CFGFILE=${"abc"} + env SLIM_THEMESDIR=${"def"} + env FONTCONFIG_FILE=/etc/fonts/fonts.conf # !!! cleanup + env XKB_BINDIR=${"foo"}/bin # Needed for the Xkb extension. + env LD_LIBRARY_PATH=${"libX11"}/lib:${"libXext"}/lib:/usr/lib/ # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz) + + ${if true + then "env XORG_DRI_DRIVER_PATH=${"nvidiaDrivers"}/X11R6/lib/modules/drivers/" + else if true + then "env XORG_DRI_DRIVER_PATH=${"mesa"}/lib/modules/dri" + else "" + } + + exec ${"slim"}/bin/slim + ''; + + s14 = '' + Escaping of ' followed by ': ''' + Escaping of $ followed by {: ''${ + And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t. + ''; + + # Regression test: string interpolation in '${x}' should work, but didn't. + s15 = let x = "bla"; in '' + foo + '${x}' + bar + ''; + + # Regression test: accept $'. + s16 = '' + cut -d $'\t' -f 1 + ''; + + # Accept dollars at end of strings + s17 = ''ending dollar $'' + ''$'' + "\n"; + +in s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17 diff --git a/tests/functional/lang/eval-okay-intersectAttrs.exp b/tests/functional/lang/eval-okay-intersectAttrs.exp new file mode 100644 index 000000000..50445bc0e --- /dev/null +++ b/tests/functional/lang/eval-okay-intersectAttrs.exp @@ -0,0 +1 @@ +[ { } { a = 1; } { a = 1; } { a = "a"; } { m = 1; } { m = "m"; } { n = 1; } { n = "n"; } { n = 1; p = 2; } { n = "n"; p = "p"; } { n = 1; p = 2; } { n = "n"; p = "p"; } { a = "a"; b = "b"; c = "c"; d = "d"; e = "e"; f = "f"; g = "g"; h = "h"; i = "i"; j = "j"; k = "k"; l = "l"; m = "m"; n = "n"; o = "o"; p = "p"; q = "q"; r = "r"; s = "s"; t = "t"; u = "u"; v = "v"; w = "w"; x = "x"; y = "y"; z = "z"; } true ] diff --git a/tests/functional/lang/eval-okay-intersectAttrs.nix b/tests/functional/lang/eval-okay-intersectAttrs.nix new file mode 100644 index 000000000..39d49938c --- /dev/null +++ b/tests/functional/lang/eval-okay-intersectAttrs.nix @@ -0,0 +1,50 @@ +let + alphabet = + { a = "a"; + b = "b"; + c = "c"; + d = "d"; + e = "e"; + f = "f"; + g = "g"; + h = "h"; + i = "i"; + j = "j"; + k = "k"; + l = "l"; + m = "m"; + n = "n"; + o = "o"; + p = "p"; + q = "q"; + r = "r"; + s = "s"; + t = "t"; + u = "u"; + v = "v"; + w = "w"; + x = "x"; + y = "y"; + z = "z"; + }; + foo = { + inherit (alphabet) f o b a r z q u x; + aa = throw "aa"; + }; + alphabetFail = builtins.mapAttrs throw alphabet; +in +[ (builtins.intersectAttrs { a = abort "l1"; } { b = abort "r1"; }) + (builtins.intersectAttrs { a = abort "l2"; } { a = 1; }) + (builtins.intersectAttrs alphabetFail { a = 1; }) + (builtins.intersectAttrs { a = abort "laa"; } alphabet) + (builtins.intersectAttrs alphabetFail { m = 1; }) + (builtins.intersectAttrs { m = abort "lam"; } alphabet) + (builtins.intersectAttrs alphabetFail { n = 1; }) + (builtins.intersectAttrs { n = abort "lan"; } alphabet) + (builtins.intersectAttrs alphabetFail { n = 1; p = 2; }) + (builtins.intersectAttrs { n = abort "lan2"; p = abort "lap"; } alphabet) + (builtins.intersectAttrs alphabetFail { n = 1; p = 2; }) + (builtins.intersectAttrs { n = abort "lan2"; p = abort "lap"; } alphabet) + (builtins.intersectAttrs alphabetFail alphabet) + (builtins.intersectAttrs alphabet foo == builtins.intersectAttrs foo alphabet) +] diff --git a/tests/functional/lang/eval-okay-let.exp b/tests/functional/lang/eval-okay-let.exp new file mode 100644 index 000000000..14e24d419 --- /dev/null +++ b/tests/functional/lang/eval-okay-let.exp @@ -0,0 +1 @@ +"foobar" diff --git a/tests/functional/lang/eval-okay-let.nix b/tests/functional/lang/eval-okay-let.nix new file mode 100644 index 000000000..fe118c528 --- /dev/null +++ b/tests/functional/lang/eval-okay-let.nix @@ -0,0 +1,5 @@ +let { + x = "foo"; + y = "bar"; + body = x + y; +} diff --git a/tests/functional/lang/eval-okay-list.exp b/tests/functional/lang/eval-okay-list.exp new file mode 100644 index 000000000..f784f26d8 --- /dev/null +++ b/tests/functional/lang/eval-okay-list.exp @@ -0,0 +1 @@ +"foobarblatest" diff --git a/tests/functional/lang/eval-okay-list.nix b/tests/functional/lang/eval-okay-list.nix new file mode 100644 index 000000000..d433bcf90 --- /dev/null +++ b/tests/functional/lang/eval-okay-list.nix @@ -0,0 +1,7 @@ +with import ./lib.nix; + +let { + + body = concat ["foo" "bar" "bla" "test"]; + +}
\ No newline at end of file diff --git a/tests/functional/lang/eval-okay-listtoattrs.exp b/tests/functional/lang/eval-okay-listtoattrs.exp new file mode 100644 index 000000000..74abef7bc --- /dev/null +++ b/tests/functional/lang/eval-okay-listtoattrs.exp @@ -0,0 +1 @@ +"AAbar" diff --git a/tests/functional/lang/eval-okay-listtoattrs.nix b/tests/functional/lang/eval-okay-listtoattrs.nix new file mode 100644 index 000000000..4186e029b --- /dev/null +++ b/tests/functional/lang/eval-okay-listtoattrs.nix @@ -0,0 +1,11 @@ +# this test shows how to use listToAttrs and that evaluation is still lazy (throw isn't called) +with import ./lib.nix; + +let + asi = name: value : { inherit name value; }; + list = [ ( asi "a" "A" ) ( asi "b" "B" ) ]; + a = builtins.listToAttrs list; + b = builtins.listToAttrs ( list ++ list ); + r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ]; + x = builtins.listToAttrs [ (asi "foo" "bar") (asi "foo" "bla") ]; +in concat (map (x: x.a) r.result) + x.foo diff --git a/tests/functional/lang/eval-okay-logic.exp b/tests/functional/lang/eval-okay-logic.exp new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/tests/functional/lang/eval-okay-logic.exp @@ -0,0 +1 @@ +1 diff --git a/tests/functional/lang/eval-okay-logic.nix b/tests/functional/lang/eval-okay-logic.nix new file mode 100644 index 000000000..fbb127944 --- /dev/null +++ b/tests/functional/lang/eval-okay-logic.nix @@ -0,0 +1 @@ +assert !false && (true || false) -> true; 1 diff --git a/tests/functional/lang/eval-okay-map.exp b/tests/functional/lang/eval-okay-map.exp new file mode 100644 index 000000000..dbb64f717 --- /dev/null +++ b/tests/functional/lang/eval-okay-map.exp @@ -0,0 +1 @@ +"foobarblabarxyzzybar" diff --git a/tests/functional/lang/eval-okay-map.nix b/tests/functional/lang/eval-okay-map.nix new file mode 100644 index 000000000..a76c1d811 --- /dev/null +++ b/tests/functional/lang/eval-okay-map.nix @@ -0,0 +1,3 @@ +with import ./lib.nix; + +concat (map (x: x + "bar") [ "foo" "bla" "xyzzy" ])
\ No newline at end of file diff --git a/tests/functional/lang/eval-okay-mapattrs.exp b/tests/functional/lang/eval-okay-mapattrs.exp new file mode 100644 index 000000000..3f113f17b --- /dev/null +++ b/tests/functional/lang/eval-okay-mapattrs.exp @@ -0,0 +1 @@ +{ x = "x-foo"; y = "y-bar"; } diff --git a/tests/functional/lang/eval-okay-mapattrs.nix b/tests/functional/lang/eval-okay-mapattrs.nix new file mode 100644 index 000000000..f075b6275 --- /dev/null +++ b/tests/functional/lang/eval-okay-mapattrs.nix @@ -0,0 +1,3 @@ +with import ./lib.nix; + +builtins.mapAttrs (name: value: name + "-" + value) { x = "foo"; y = "bar"; } diff --git a/tests/functional/lang/eval-okay-merge-dynamic-attrs.exp b/tests/functional/lang/eval-okay-merge-dynamic-attrs.exp new file mode 100644 index 000000000..157d677ce --- /dev/null +++ b/tests/functional/lang/eval-okay-merge-dynamic-attrs.exp @@ -0,0 +1 @@ +{ set1 = { a = 1; b = 2; }; set2 = { a = 1; b = 2; }; set3 = { a = 1; b = 2; }; set4 = { a = 1; b = 2; }; } diff --git a/tests/functional/lang/eval-okay-merge-dynamic-attrs.nix b/tests/functional/lang/eval-okay-merge-dynamic-attrs.nix new file mode 100644 index 000000000..f459a554f --- /dev/null +++ b/tests/functional/lang/eval-okay-merge-dynamic-attrs.nix @@ -0,0 +1,13 @@ +{ + set1 = { a = 1; }; + set1 = { "${"b" + ""}" = 2; }; + + set2 = { "${"b" + ""}" = 2; }; + set2 = { a = 1; }; + + set3.a = 1; + set3."${"b" + ""}" = 2; + + set4."${"b" + ""}" = 2; + set4.a = 1; +} diff --git a/tests/functional/lang/eval-okay-nested-with.exp b/tests/functional/lang/eval-okay-nested-with.exp new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/tests/functional/lang/eval-okay-nested-with.exp @@ -0,0 +1 @@ +2 diff --git a/tests/functional/lang/eval-okay-nested-with.nix b/tests/functional/lang/eval-okay-nested-with.nix new file mode 100644 index 000000000..ba9d79aa7 --- /dev/null +++ b/tests/functional/lang/eval-okay-nested-with.nix @@ -0,0 +1,3 @@ +with { x = 1; }; +with { x = 2; }; +x diff --git a/tests/functional/lang/eval-okay-new-let.exp b/tests/functional/lang/eval-okay-new-let.exp new file mode 100644 index 000000000..f98b38807 --- /dev/null +++ b/tests/functional/lang/eval-okay-new-let.exp @@ -0,0 +1 @@ +"xyzzyfoobar" diff --git a/tests/functional/lang/eval-okay-new-let.nix b/tests/functional/lang/eval-okay-new-let.nix new file mode 100644 index 000000000..738123141 --- /dev/null +++ b/tests/functional/lang/eval-okay-new-let.nix @@ -0,0 +1,14 @@ +let + + f = z: + + let + x = "foo"; + y = "bar"; + body = 1; # compat test + in + z + x + y; + + arg = "xyzzy"; + +in f arg diff --git a/tests/functional/lang/eval-okay-null-dynamic-attrs.exp b/tests/functional/lang/eval-okay-null-dynamic-attrs.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-null-dynamic-attrs.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-null-dynamic-attrs.nix b/tests/functional/lang/eval-okay-null-dynamic-attrs.nix new file mode 100644 index 000000000..b060c0bc9 --- /dev/null +++ b/tests/functional/lang/eval-okay-null-dynamic-attrs.nix @@ -0,0 +1 @@ +{ ${null} = true; } == {} diff --git a/tests/functional/lang/eval-okay-overrides.exp b/tests/functional/lang/eval-okay-overrides.exp new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/tests/functional/lang/eval-okay-overrides.exp @@ -0,0 +1 @@ +2 diff --git a/tests/functional/lang/eval-okay-overrides.nix b/tests/functional/lang/eval-okay-overrides.nix new file mode 100644 index 000000000..719bdc9c0 --- /dev/null +++ b/tests/functional/lang/eval-okay-overrides.nix @@ -0,0 +1,9 @@ +let + + overrides = { a = 2; b = 3; }; + +in (rec { + __overrides = overrides; + x = a; + a = 1; +}).x diff --git a/tests/functional/lang/eval-okay-parse-flake-ref.exp b/tests/functional/lang/eval-okay-parse-flake-ref.exp new file mode 100644 index 000000000..fc17ba085 --- /dev/null +++ b/tests/functional/lang/eval-okay-parse-flake-ref.exp @@ -0,0 +1 @@ +{ dir = "lib"; owner = "NixOS"; ref = "23.05"; repo = "nixpkgs"; type = "github"; } diff --git a/tests/functional/lang/eval-okay-parse-flake-ref.nix b/tests/functional/lang/eval-okay-parse-flake-ref.nix new file mode 100644 index 000000000..db4ed2742 --- /dev/null +++ b/tests/functional/lang/eval-okay-parse-flake-ref.nix @@ -0,0 +1 @@ + builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib" diff --git a/tests/functional/lang/eval-okay-partition.exp b/tests/functional/lang/eval-okay-partition.exp new file mode 100644 index 000000000..cd8b8b020 --- /dev/null +++ b/tests/functional/lang/eval-okay-partition.exp @@ -0,0 +1 @@ +{ right = [ 0 2 4 6 8 10 100 102 104 106 108 110 ]; wrong = [ 1 3 5 7 9 101 103 105 107 109 ]; } diff --git a/tests/functional/lang/eval-okay-partition.nix b/tests/functional/lang/eval-okay-partition.nix new file mode 100644 index 000000000..846d2ce49 --- /dev/null +++ b/tests/functional/lang/eval-okay-partition.nix @@ -0,0 +1,5 @@ +with import ./lib.nix; + +builtins.partition + (x: x / 2 * 2 == x) + (builtins.concatLists [ (range 0 10) (range 100 110) ]) diff --git a/tests/functional/lang/eval-okay-path-string-interpolation.exp b/tests/functional/lang/eval-okay-path-string-interpolation.exp new file mode 100644 index 000000000..5b8ea0243 --- /dev/null +++ b/tests/functional/lang/eval-okay-path-string-interpolation.exp @@ -0,0 +1 @@ +{ absolute = /foo; expr = /pwd/lang/foo/bar; home = /fake-home/foo; notfirst = /pwd/lang/bar/foo; simple = /pwd/lang/foo; slashes = /foo/bar; surrounded = /pwd/lang/a-foo-b; } diff --git a/tests/functional/lang/eval-okay-path-string-interpolation.nix b/tests/functional/lang/eval-okay-path-string-interpolation.nix new file mode 100644 index 000000000..497d7c1c7 --- /dev/null +++ b/tests/functional/lang/eval-okay-path-string-interpolation.nix @@ -0,0 +1,12 @@ +let + foo = "foo"; +in +{ + simple = ./${foo}; + surrounded = ./a-${foo}-b; + absolute = /${foo}; + expr = ./${foo + "/bar"}; + home = ~/${foo}; + notfirst = ./bar/${foo}; + slashes = /${foo}/${"bar"}; +} diff --git a/tests/functional/lang/eval-okay-path.exp b/tests/functional/lang/eval-okay-path.exp new file mode 100644 index 000000000..3ce7f8283 --- /dev/null +++ b/tests/functional/lang/eval-okay-path.exp @@ -0,0 +1 @@ +"/nix/store/ya937r4ydw0l6kayq8jkyqaips9c75jm-output" diff --git a/tests/functional/lang/eval-okay-path.nix b/tests/functional/lang/eval-okay-path.nix new file mode 100644 index 000000000..e67168cf3 --- /dev/null +++ b/tests/functional/lang/eval-okay-path.nix @@ -0,0 +1,7 @@ +builtins.path + { path = ./.; + filter = path: _: baseNameOf path == "data"; + recursive = true; + sha256 = "1yhm3gwvg5a41yylymgblsclk95fs6jy72w0wv925mmidlhcq4sw"; + name = "output"; + } diff --git a/tests/functional/lang/eval-okay-pathexists.exp b/tests/functional/lang/eval-okay-pathexists.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-pathexists.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-pathexists.nix b/tests/functional/lang/eval-okay-pathexists.nix new file mode 100644 index 000000000..c5e7a62de --- /dev/null +++ b/tests/functional/lang/eval-okay-pathexists.nix @@ -0,0 +1,29 @@ +builtins.pathExists (./lib.nix) +&& builtins.pathExists (builtins.toPath ./lib.nix) +&& builtins.pathExists (builtins.toString ./lib.nix) +&& !builtins.pathExists (builtins.toString ./lib.nix + "/") +&& !builtins.pathExists (builtins.toString ./lib.nix + "/.") +# FIXME +# && !builtins.pathExists (builtins.toString ./lib.nix + "/..") +# && !builtins.pathExists (builtins.toString ./lib.nix + "/a/..") +# && !builtins.pathExists (builtins.toString ./lib.nix + "/../lib.nix") +&& !builtins.pathExists (builtins.toString ./lib.nix + "/./") +&& !builtins.pathExists (builtins.toString ./lib.nix + "/./.") +&& builtins.pathExists (builtins.toString ./.. + "/lang/lib.nix") +&& !builtins.pathExists (builtins.toString ./.. + "lang/lib.nix") +&& builtins.pathExists (builtins.toString ./. + "/../lang/lib.nix") +&& builtins.pathExists (builtins.toString ./. + "/../lang/./lib.nix") +&& builtins.pathExists (builtins.toString ./.) +&& builtins.pathExists (builtins.toString ./. + "/") +&& builtins.pathExists (builtins.toString ./. + "/../lang") +&& builtins.pathExists (builtins.toString ./. + "/../lang/") +&& builtins.pathExists (builtins.toString ./. + "/../lang/.") +&& builtins.pathExists (builtins.toString ./. + "/../lang/./") +&& builtins.pathExists (builtins.toString ./. + "/../lang//./") +&& builtins.pathExists (builtins.toString ./. + "/../lang/..") +&& builtins.pathExists (builtins.toString ./. + "/../lang/../") +&& builtins.pathExists (builtins.toString ./. + "/../lang/..//") +&& builtins.pathExists (builtins.toPath (builtins.toString ./lib.nix)) +&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix)) +&& builtins.pathExists ./lib.nix +&& !builtins.pathExists ./bla.nix diff --git a/tests/functional/lang/eval-okay-patterns.exp b/tests/functional/lang/eval-okay-patterns.exp new file mode 100644 index 000000000..a4304010f --- /dev/null +++ b/tests/functional/lang/eval-okay-patterns.exp @@ -0,0 +1 @@ +"abcxyzDDDDEFijk" diff --git a/tests/functional/lang/eval-okay-patterns.nix b/tests/functional/lang/eval-okay-patterns.nix new file mode 100644 index 000000000..96fd25a01 --- /dev/null +++ b/tests/functional/lang/eval-okay-patterns.nix @@ -0,0 +1,16 @@ +let + + f = args@{x, y, z}: x + args.y + z; + + g = {x, y, z}@args: f args; + + h = {x ? "d", y ? x, z ? args.x}@args: x + y + z; + + j = {x, y, z, ...}: x + y + z; + +in + f {x = "a"; y = "b"; z = "c";} + + g {x = "x"; y = "y"; z = "z";} + + h {x = "D";} + + h {x = "D"; y = "E"; z = "F";} + + j {x = "i"; y = "j"; z = "k"; bla = "bla"; foo = "bar";} diff --git a/tests/functional/lang/eval-okay-print.err.exp b/tests/functional/lang/eval-okay-print.err.exp new file mode 100644 index 000000000..3fc99be3e --- /dev/null +++ b/tests/functional/lang/eval-okay-print.err.exp @@ -0,0 +1 @@ +trace: [ <CODE> ] diff --git a/tests/functional/lang/eval-okay-print.exp b/tests/functional/lang/eval-okay-print.exp new file mode 100644 index 000000000..0d960fb70 --- /dev/null +++ b/tests/functional/lang/eval-okay-print.exp @@ -0,0 +1 @@ +[ null <PRIMOP> <PRIMOP-APP> <LAMBDA> [ [ «repeated» ] ] ] diff --git a/tests/functional/lang/eval-okay-print.nix b/tests/functional/lang/eval-okay-print.nix new file mode 100644 index 000000000..d36ba4da3 --- /dev/null +++ b/tests/functional/lang/eval-okay-print.nix @@ -0,0 +1 @@ +with builtins; trace [(1+1)] [ null toString (deepSeq "x") (a: a) (let x=[x]; in x) ] diff --git a/tests/functional/lang/eval-okay-readDir.exp b/tests/functional/lang/eval-okay-readDir.exp new file mode 100644 index 000000000..6413f6d4f --- /dev/null +++ b/tests/functional/lang/eval-okay-readDir.exp @@ -0,0 +1 @@ +{ bar = "regular"; foo = "directory"; ldir = "symlink"; linked = "symlink"; } diff --git a/tests/functional/lang/eval-okay-readDir.nix b/tests/functional/lang/eval-okay-readDir.nix new file mode 100644 index 000000000..a7ec9292a --- /dev/null +++ b/tests/functional/lang/eval-okay-readDir.nix @@ -0,0 +1 @@ +builtins.readDir ./readDir diff --git a/tests/functional/lang/eval-okay-readFileType.exp b/tests/functional/lang/eval-okay-readFileType.exp new file mode 100644 index 000000000..6413f6d4f --- /dev/null +++ b/tests/functional/lang/eval-okay-readFileType.exp @@ -0,0 +1 @@ +{ bar = "regular"; foo = "directory"; ldir = "symlink"; linked = "symlink"; } diff --git a/tests/functional/lang/eval-okay-readFileType.nix b/tests/functional/lang/eval-okay-readFileType.nix new file mode 100644 index 000000000..174fb6c3a --- /dev/null +++ b/tests/functional/lang/eval-okay-readFileType.nix @@ -0,0 +1,6 @@ +{ + bar = builtins.readFileType ./readDir/bar; + foo = builtins.readFileType ./readDir/foo; + linked = builtins.readFileType ./readDir/linked; + ldir = builtins.readFileType ./readDir/ldir; +} diff --git a/tests/functional/lang/eval-okay-readfile.exp b/tests/functional/lang/eval-okay-readfile.exp new file mode 100644 index 000000000..a2c87d0c4 --- /dev/null +++ b/tests/functional/lang/eval-okay-readfile.exp @@ -0,0 +1 @@ +"builtins.readFile ./eval-okay-readfile.nix\n" diff --git a/tests/functional/lang/eval-okay-readfile.nix b/tests/functional/lang/eval-okay-readfile.nix new file mode 100644 index 000000000..82f7cb174 --- /dev/null +++ b/tests/functional/lang/eval-okay-readfile.nix @@ -0,0 +1 @@ +builtins.readFile ./eval-okay-readfile.nix diff --git a/tests/functional/lang/eval-okay-redefine-builtin.exp b/tests/functional/lang/eval-okay-redefine-builtin.exp new file mode 100644 index 000000000..c508d5366 --- /dev/null +++ b/tests/functional/lang/eval-okay-redefine-builtin.exp @@ -0,0 +1 @@ +false diff --git a/tests/functional/lang/eval-okay-redefine-builtin.nix b/tests/functional/lang/eval-okay-redefine-builtin.nix new file mode 100644 index 000000000..df9fc3f37 --- /dev/null +++ b/tests/functional/lang/eval-okay-redefine-builtin.nix @@ -0,0 +1,3 @@ +let + throw = abort "Error!"; +in (builtins.tryEval <foobaz>).success diff --git a/tests/functional/lang/eval-okay-regex-match.exp b/tests/functional/lang/eval-okay-regex-match.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-regex-match.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-regex-match.nix b/tests/functional/lang/eval-okay-regex-match.nix new file mode 100644 index 000000000..273e25907 --- /dev/null +++ b/tests/functional/lang/eval-okay-regex-match.nix @@ -0,0 +1,29 @@ +with builtins; + +let + + matches = pat: s: match pat s != null; + + splitFN = match "((.*)/)?([^/]*)\\.(nix|cc)"; + +in + +assert matches "foobar" "foobar"; +assert matches "fo*" "f"; +assert !matches "fo+" "f"; +assert matches "fo*" "fo"; +assert matches "fo*" "foo"; +assert matches "fo+" "foo"; +assert matches "fo{1,2}" "foo"; +assert !matches "fo{1,2}" "fooo"; +assert !matches "fo*" "foobar"; +assert matches "[[:space:]]+([^[:space:]]+)[[:space:]]+" " foo "; +assert !matches "[[:space:]]+([[:upper:]]+)[[:space:]]+" " foo "; + +assert match "(.*)\\.nix" "foobar.nix" == [ "foobar" ]; +assert match "[[:space:]]+([[:upper:]]+)[[:space:]]+" " FOO " == [ "FOO" ]; + +assert splitFN "/path/to/foobar.nix" == [ "/path/to/" "/path/to" "foobar" "nix" ]; +assert splitFN "foobar.cc" == [ null null "foobar" "cc" ]; + +true diff --git a/tests/functional/lang/eval-okay-regex-split.exp b/tests/functional/lang/eval-okay-regex-split.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-regex-split.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-regex-split.nix b/tests/functional/lang/eval-okay-regex-split.nix new file mode 100644 index 000000000..0073e0577 --- /dev/null +++ b/tests/functional/lang/eval-okay-regex-split.nix @@ -0,0 +1,48 @@ +with builtins; + +# Non capturing regex returns empty lists +assert split "foobar" "foobar" == ["" [] ""]; +assert split "fo*" "f" == ["" [] ""]; +assert split "fo+" "f" == ["f"]; +assert split "fo*" "fo" == ["" [] ""]; +assert split "fo*" "foo" == ["" [] ""]; +assert split "fo+" "foo" == ["" [] ""]; +assert split "fo{1,2}" "foo" == ["" [] ""]; +assert split "fo{1,2}" "fooo" == ["" [] "o"]; +assert split "fo*" "foobar" == ["" [] "bar"]; + +# Capturing regex returns a list of sub-matches +assert split "(fo*)" "f" == ["" ["f"] ""]; +assert split "(fo+)" "f" == ["f"]; +assert split "(fo*)" "fo" == ["" ["fo"] ""]; +assert split "(f)(o*)" "f" == ["" ["f" ""] ""]; +assert split "(f)(o*)" "foo" == ["" ["f" "oo"] ""]; +assert split "(fo+)" "foo" == ["" ["foo"] ""]; +assert split "(fo{1,2})" "foo" == ["" ["foo"] ""]; +assert split "(fo{1,2})" "fooo" == ["" ["foo"] "o"]; +assert split "(fo*)" "foobar" == ["" ["foo"] "bar"]; + +# Matches are greedy. +assert split "(o+)" "oooofoooo" == ["" ["oooo"] "f" ["oooo"] ""]; + +# Matches multiple times. +assert split "(b)" "foobarbaz" == ["foo" ["b"] "ar" ["b"] "az"]; + +# Split large strings containing newlines. null are inserted when a +# pattern within the current did not match anything. +assert split "[[:space:]]+|([',.!?])" '' + Nix Rocks! + That's why I use it. +'' == [ + "Nix" [ null ] "Rocks" ["!"] "" [ null ] + "That" ["'"] "s" [ null ] "why" [ null ] "I" [ null ] "use" [ null ] "it" ["."] "" [ null ] + "" +]; + +# Documentation examples +assert split "(a)b" "abc" == [ "" [ "a" ] "c" ]; +assert split "([ac])" "abc" == [ "" [ "a" ] "b" [ "c" ] "" ]; +assert split "(a)|(c)" "abc" == [ "" [ "a" null ] "b" [ null "c" ] "" ]; +assert split "([[:upper:]]+)" " FOO " == [ " " [ "FOO" ] " " ]; + +true diff --git a/tests/functional/lang/eval-okay-regression-20220122.exp b/tests/functional/lang/eval-okay-regression-20220122.exp new file mode 100644 index 000000000..00750edc0 --- /dev/null +++ b/tests/functional/lang/eval-okay-regression-20220122.exp @@ -0,0 +1 @@ +3 diff --git a/tests/functional/lang/eval-okay-regression-20220122.nix b/tests/functional/lang/eval-okay-regression-20220122.nix new file mode 100644 index 000000000..694e9a13b --- /dev/null +++ b/tests/functional/lang/eval-okay-regression-20220122.nix @@ -0,0 +1 @@ +((_: _) 1) + ((__: __) 2) diff --git a/tests/functional/lang/eval-okay-regression-20220125.exp b/tests/functional/lang/eval-okay-regression-20220125.exp new file mode 100644 index 000000000..00750edc0 --- /dev/null +++ b/tests/functional/lang/eval-okay-regression-20220125.exp @@ -0,0 +1 @@ +3 diff --git a/tests/functional/lang/eval-okay-regression-20220125.nix b/tests/functional/lang/eval-okay-regression-20220125.nix new file mode 100644 index 000000000..485502373 --- /dev/null +++ b/tests/functional/lang/eval-okay-regression-20220125.nix @@ -0,0 +1,2 @@ +((__curPosFoo: __curPosFoo) 1) + ((__curPosBar: __curPosBar) 2) + diff --git a/tests/functional/lang/eval-okay-remove.exp b/tests/functional/lang/eval-okay-remove.exp new file mode 100644 index 000000000..8d38505c1 --- /dev/null +++ b/tests/functional/lang/eval-okay-remove.exp @@ -0,0 +1 @@ +456 diff --git a/tests/functional/lang/eval-okay-remove.nix b/tests/functional/lang/eval-okay-remove.nix new file mode 100644 index 000000000..4ad5ba897 --- /dev/null +++ b/tests/functional/lang/eval-okay-remove.nix @@ -0,0 +1,5 @@ +let { + attrs = {x = 123; y = 456;}; + + body = (removeAttrs attrs ["x"]).y; +}
\ No newline at end of file diff --git a/tests/functional/lang/eval-okay-replacestrings.exp b/tests/functional/lang/eval-okay-replacestrings.exp new file mode 100644 index 000000000..eac67c5fe --- /dev/null +++ b/tests/functional/lang/eval-okay-replacestrings.exp @@ -0,0 +1 @@ +[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" "fubar" ] diff --git a/tests/functional/lang/eval-okay-replacestrings.nix b/tests/functional/lang/eval-okay-replacestrings.nix new file mode 100644 index 000000000..a803e6519 --- /dev/null +++ b/tests/functional/lang/eval-okay-replacestrings.nix @@ -0,0 +1,12 @@ +with builtins; + +[ (replaceStrings ["o"] ["a"] "foobar") + (replaceStrings ["o"] [""] "foobar") + (replaceStrings ["oo"] ["u"] "foobar") + (replaceStrings ["oo" "a"] ["a" "oo"] "foobar") + (replaceStrings ["oo" "oo"] ["u" "i"] "foobar") + (replaceStrings [""] ["X"] "abc") + (replaceStrings [""] ["X"] "") + (replaceStrings ["-"] ["_"] "a-b") + (replaceStrings ["oo" "XX"] ["u" (throw "unreachable")] "foobar") +] diff --git a/tests/functional/lang/eval-okay-scope-1.exp b/tests/functional/lang/eval-okay-scope-1.exp new file mode 100644 index 000000000..00750edc0 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-1.exp @@ -0,0 +1 @@ +3 diff --git a/tests/functional/lang/eval-okay-scope-1.nix b/tests/functional/lang/eval-okay-scope-1.nix new file mode 100644 index 000000000..fa38a7174 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-1.nix @@ -0,0 +1,6 @@ +(({x}: x: + + { x = 1; + y = x; + } +) {x = 2;} 3).y diff --git a/tests/functional/lang/eval-okay-scope-2.exp b/tests/functional/lang/eval-okay-scope-2.exp new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-2.exp @@ -0,0 +1 @@ +1 diff --git a/tests/functional/lang/eval-okay-scope-2.nix b/tests/functional/lang/eval-okay-scope-2.nix new file mode 100644 index 000000000..eb8b02bc4 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-2.nix @@ -0,0 +1,6 @@ +((x: {x}: + rec { + x = 1; + y = x; + } +) 2 {x = 3;}).y diff --git a/tests/functional/lang/eval-okay-scope-3.exp b/tests/functional/lang/eval-okay-scope-3.exp new file mode 100644 index 000000000..b8626c4cf --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-3.exp @@ -0,0 +1 @@ +4 diff --git a/tests/functional/lang/eval-okay-scope-3.nix b/tests/functional/lang/eval-okay-scope-3.nix new file mode 100644 index 000000000..10d6bc04d --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-3.nix @@ -0,0 +1,6 @@ +((x: as: {x}: + rec { + inherit (as) x; + y = x; + } +) 2 {x = 4;} {x = 3;}).y diff --git a/tests/functional/lang/eval-okay-scope-4.exp b/tests/functional/lang/eval-okay-scope-4.exp new file mode 100644 index 000000000..00ff03a46 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-4.exp @@ -0,0 +1 @@ +"ccdd" diff --git a/tests/functional/lang/eval-okay-scope-4.nix b/tests/functional/lang/eval-okay-scope-4.nix new file mode 100644 index 000000000..dc8243bc8 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-4.nix @@ -0,0 +1,10 @@ +let { + + x = "a"; + y = "b"; + + f = {x ? y, y ? x}: x + y; + + body = f {x = "c";} + f {y = "d";}; + +} diff --git a/tests/functional/lang/eval-okay-scope-6.exp b/tests/functional/lang/eval-okay-scope-6.exp new file mode 100644 index 000000000..00ff03a46 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-6.exp @@ -0,0 +1 @@ +"ccdd" diff --git a/tests/functional/lang/eval-okay-scope-6.nix b/tests/functional/lang/eval-okay-scope-6.nix new file mode 100644 index 000000000..0995d4e7e --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-6.nix @@ -0,0 +1,7 @@ +let { + + f = {x ? y, y ? x}: x + y; + + body = f {x = "c";} + f {y = "d";}; + +} diff --git a/tests/functional/lang/eval-okay-scope-7.exp b/tests/functional/lang/eval-okay-scope-7.exp new file mode 100644 index 000000000..d00491fd7 --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-7.exp @@ -0,0 +1 @@ +1 diff --git a/tests/functional/lang/eval-okay-scope-7.nix b/tests/functional/lang/eval-okay-scope-7.nix new file mode 100644 index 000000000..4da02968f --- /dev/null +++ b/tests/functional/lang/eval-okay-scope-7.nix @@ -0,0 +1,6 @@ +rec { + inherit (x) y; + x = { + y = 1; + }; +}.y diff --git a/tests/functional/lang/eval-okay-search-path.exp b/tests/functional/lang/eval-okay-search-path.exp new file mode 100644 index 000000000..4519bc406 --- /dev/null +++ b/tests/functional/lang/eval-okay-search-path.exp @@ -0,0 +1 @@ +"abccX" diff --git a/tests/functional/lang/eval-okay-search-path.flags b/tests/functional/lang/eval-okay-search-path.flags new file mode 100644 index 000000000..dfad1c611 --- /dev/null +++ b/tests/functional/lang/eval-okay-search-path.flags @@ -0,0 +1 @@ +-I lang/dir1 -I lang/dir2 -I dir5=lang/dir3 diff --git a/tests/functional/lang/eval-okay-search-path.nix b/tests/functional/lang/eval-okay-search-path.nix new file mode 100644 index 000000000..6fe33decc --- /dev/null +++ b/tests/functional/lang/eval-okay-search-path.nix @@ -0,0 +1,10 @@ +with import ./lib.nix; +with builtins; + +assert isFunction (import <nix/fetchurl.nix>); + +assert length __nixPath == 5; +assert length (filter (x: baseNameOf x.path == "dir4") __nixPath) == 1; + +import <a.nix> + import <b.nix> + import <c.nix> + import <dir5/c.nix> + + (let __nixPath = [ { path = ./dir2; } { path = ./dir1; } ]; in import <a.nix>) diff --git a/tests/functional/lang/eval-okay-seq.exp b/tests/functional/lang/eval-okay-seq.exp new file mode 100644 index 000000000..0cfbf0888 --- /dev/null +++ b/tests/functional/lang/eval-okay-seq.exp @@ -0,0 +1 @@ +2 diff --git a/tests/functional/lang/eval-okay-seq.nix b/tests/functional/lang/eval-okay-seq.nix new file mode 100644 index 000000000..0a9a21c03 --- /dev/null +++ b/tests/functional/lang/eval-okay-seq.nix @@ -0,0 +1 @@ +builtins.seq 1 2 diff --git a/tests/functional/lang/eval-okay-sort.exp b/tests/functional/lang/eval-okay-sort.exp new file mode 100644 index 000000000..899119e20 --- /dev/null +++ b/tests/functional/lang/eval-okay-sort.exp @@ -0,0 +1 @@ +[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] [ [ ] [ ] [ 1 ] [ 1 4 ] [ 1 5 ] [ 1 6 ] [ 2 ] [ 2 3 ] [ 3 ] [ 3 ] ] ] diff --git a/tests/functional/lang/eval-okay-sort.nix b/tests/functional/lang/eval-okay-sort.nix new file mode 100644 index 000000000..50aa78e40 --- /dev/null +++ b/tests/functional/lang/eval-okay-sort.nix @@ -0,0 +1,20 @@ +with builtins; + +[ (sort lessThan [ 483 249 526 147 42 77 ]) + (sort (x: y: y < x) [ 483 249 526 147 42 77 ]) + (sort lessThan [ "foo" "bar" "xyzzy" "fnord" ]) + (sort (x: y: x.key < y.key) + [ { key = 1; value = "foo"; } { key = 2; value = "bar"; } { key = 1; value = "fnord"; } ]) + (sort lessThan [ + [ 1 6 ] + [ ] + [ 2 3 ] + [ 3 ] + [ 1 5 ] + [ 2 ] + [ 1 ] + [ ] + [ 1 4 ] + [ 3 ] + ]) +] diff --git a/tests/functional/lang/eval-okay-splitversion.exp b/tests/functional/lang/eval-okay-splitversion.exp new file mode 100644 index 000000000..153ceb818 --- /dev/null +++ b/tests/functional/lang/eval-okay-splitversion.exp @@ -0,0 +1 @@ +[ "1" "2" "3" ] diff --git a/tests/functional/lang/eval-okay-splitversion.nix b/tests/functional/lang/eval-okay-splitversion.nix new file mode 100644 index 000000000..9e5c99d2e --- /dev/null +++ b/tests/functional/lang/eval-okay-splitversion.nix @@ -0,0 +1 @@ +builtins.splitVersion "1.2.3" diff --git a/tests/functional/lang/eval-okay-string.exp b/tests/functional/lang/eval-okay-string.exp new file mode 100644 index 000000000..63f650f73 --- /dev/null +++ b/tests/functional/lang/eval-okay-string.exp @@ -0,0 +1 @@ +"foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar$\"$\"$" diff --git a/tests/functional/lang/eval-okay-string.nix b/tests/functional/lang/eval-okay-string.nix new file mode 100644 index 000000000..47cc989ad --- /dev/null +++ b/tests/functional/lang/eval-okay-string.nix @@ -0,0 +1,12 @@ +"foo" + "bar" + + toString (/a/b + /c/d) + + toString (/foo/bar + "/../xyzzy/." + "/foo.txt") + + ("/../foo" + toString /x/y) + + "escape: \"quote\" \n \\" + + "end +of +line" + + "foo${if true then "b${"a" + "r"}" else "xyzzy"}blaat" + + "foo$bar" + + "$\"$\"" + + "$" diff --git a/tests/functional/lang/eval-okay-strings-as-attrs-names.exp b/tests/functional/lang/eval-okay-strings-as-attrs-names.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-strings-as-attrs-names.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-strings-as-attrs-names.nix b/tests/functional/lang/eval-okay-strings-as-attrs-names.nix new file mode 100644 index 000000000..5e40928db --- /dev/null +++ b/tests/functional/lang/eval-okay-strings-as-attrs-names.nix @@ -0,0 +1,20 @@ +let + + attr = { + "key 1" = "test"; + "key 2" = "caseok"; + }; + + t1 = builtins.getAttr "key 1" attr; + t2 = attr."key 2"; + t3 = attr ? "key 1"; + t4 = builtins.attrNames { inherit (attr) "key 1"; }; + + # This is permitted, but there is currently no way to reference this + # variable. + "foo bar" = 1; + +in t1 == "test" + && t2 == "caseok" + && t3 == true + && t4 == ["key 1"] diff --git a/tests/functional/lang/eval-okay-substring.exp b/tests/functional/lang/eval-okay-substring.exp new file mode 100644 index 000000000..6aace04b0 --- /dev/null +++ b/tests/functional/lang/eval-okay-substring.exp @@ -0,0 +1 @@ +"ooxfoobarybarzobaabbc" diff --git a/tests/functional/lang/eval-okay-substring.nix b/tests/functional/lang/eval-okay-substring.nix new file mode 100644 index 000000000..424af00d9 --- /dev/null +++ b/tests/functional/lang/eval-okay-substring.nix @@ -0,0 +1,21 @@ +with builtins; + +let + + s = "foobar"; + +in + +substring 1 2 s ++ "x" ++ substring 0 (stringLength s) s ++ "y" ++ substring 3 100 s ++ "z" ++ substring 2 (sub (stringLength s) 3) s ++ "a" ++ substring 3 0 s ++ "b" ++ substring 3 1 s ++ "c" ++ substring 5 10 "perl" diff --git a/tests/functional/lang/eval-okay-tail-call-1.exp-disabled b/tests/functional/lang/eval-okay-tail-call-1.exp-disabled new file mode 100644 index 000000000..f7393e847 --- /dev/null +++ b/tests/functional/lang/eval-okay-tail-call-1.exp-disabled @@ -0,0 +1 @@ +100000 diff --git a/tests/functional/lang/eval-okay-tail-call-1.nix b/tests/functional/lang/eval-okay-tail-call-1.nix new file mode 100644 index 000000000..a3962ce3f --- /dev/null +++ b/tests/functional/lang/eval-okay-tail-call-1.nix @@ -0,0 +1,3 @@ +let + f = n: if n == 100000 then n else f (n + 1); +in f 0 diff --git a/tests/functional/lang/eval-okay-tojson.exp b/tests/functional/lang/eval-okay-tojson.exp new file mode 100644 index 000000000..e92aae323 --- /dev/null +++ b/tests/functional/lang/eval-okay-tojson.exp @@ -0,0 +1 @@ +"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3,\"j\":1.44,\"k\":\"foo\"}" diff --git a/tests/functional/lang/eval-okay-tojson.nix b/tests/functional/lang/eval-okay-tojson.nix new file mode 100644 index 000000000..ce67943be --- /dev/null +++ b/tests/functional/lang/eval-okay-tojson.nix @@ -0,0 +1,13 @@ +builtins.toJSON + { a = 123; + b = -456; + c = "foo"; + d = "foo\n\"bar\""; + e = true; + f = false; + g = [ 1 2 3 ]; + h = [ "a" [ "b" { "foo\nbar" = {}; } ] ]; + i = 1 + 2; + j = 1.44; + k = { __toString = self: self.a; a = "foo"; }; + } diff --git a/tests/functional/lang/eval-okay-toxml.exp b/tests/functional/lang/eval-okay-toxml.exp new file mode 100644 index 000000000..828220890 --- /dev/null +++ b/tests/functional/lang/eval-okay-toxml.exp @@ -0,0 +1 @@ +"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n <attrs>\n <attr name=\"a\">\n <string value=\"s\" />\n </attr>\n </attrs>\n</expr>\n" diff --git a/tests/functional/lang/eval-okay-toxml.nix b/tests/functional/lang/eval-okay-toxml.nix new file mode 100644 index 000000000..068c97a6c --- /dev/null +++ b/tests/functional/lang/eval-okay-toxml.nix @@ -0,0 +1,3 @@ +# Make sure the expected XML output is produced; in particular, make sure it +# doesn't contain source location information. +builtins.toXML { a = "s"; } diff --git a/tests/functional/lang/eval-okay-toxml2.exp b/tests/functional/lang/eval-okay-toxml2.exp new file mode 100644 index 000000000..634a841eb --- /dev/null +++ b/tests/functional/lang/eval-okay-toxml2.exp @@ -0,0 +1 @@ +"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n <list>\n <string value=\"ab\" />\n <int value=\"10\" />\n <attrs>\n <attr name=\"x\">\n <string value=\"x\" />\n </attr>\n <attr name=\"y\">\n <string value=\"x\" />\n </attr>\n </attrs>\n </list>\n</expr>\n" diff --git a/tests/functional/lang/eval-okay-toxml2.nix b/tests/functional/lang/eval-okay-toxml2.nix new file mode 100644 index 000000000..ff1791b30 --- /dev/null +++ b/tests/functional/lang/eval-okay-toxml2.nix @@ -0,0 +1 @@ +builtins.toXML [("a" + "b") 10 (rec {x = "x"; y = x;})] diff --git a/tests/functional/lang/eval-okay-tryeval.exp b/tests/functional/lang/eval-okay-tryeval.exp new file mode 100644 index 000000000..2b2e6fa71 --- /dev/null +++ b/tests/functional/lang/eval-okay-tryeval.exp @@ -0,0 +1 @@ +{ x = { success = true; value = "x"; }; y = { success = false; value = false; }; z = { success = false; value = false; }; } diff --git a/tests/functional/lang/eval-okay-tryeval.nix b/tests/functional/lang/eval-okay-tryeval.nix new file mode 100644 index 000000000..629bc440a --- /dev/null +++ b/tests/functional/lang/eval-okay-tryeval.nix @@ -0,0 +1,5 @@ +{ + x = builtins.tryEval "x"; + y = builtins.tryEval (assert false; "y"); + z = builtins.tryEval (throw "bla"); +} diff --git a/tests/functional/lang/eval-okay-types.exp b/tests/functional/lang/eval-okay-types.exp new file mode 100644 index 000000000..92a153299 --- /dev/null +++ b/tests/functional/lang/eval-okay-types.exp @@ -0,0 +1 @@ +[ true false true false true false true false true true true true true true true true true true true false true true true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ] diff --git a/tests/functional/lang/eval-okay-types.nix b/tests/functional/lang/eval-okay-types.nix new file mode 100644 index 000000000..9b58be5d1 --- /dev/null +++ b/tests/functional/lang/eval-okay-types.nix @@ -0,0 +1,37 @@ +with builtins; + +[ (isNull null) + (isNull (x: x)) + (isFunction (x: x)) + (isFunction "fnord") + (isString ("foo" + "bar")) + (isString [ "x" ]) + (isInt (1 + 2)) + (isInt { x = 123; }) + (isInt (1 / 2)) + (isInt (1 + 1)) + (isInt (1 / 2)) + (isInt (1 * 2)) + (isInt (1 - 2)) + (isFloat (1.2)) + (isFloat (1 + 1.0)) + (isFloat (1 / 2.0)) + (isFloat (1 * 2.0)) + (isFloat (1 - 2.0)) + (isBool (true && false)) + (isBool null) + (isPath /nix/store) + (isPath ./.) + (isAttrs { x = 123; }) + (isAttrs null) + (typeOf (3 * 4)) + (typeOf true) + (typeOf "xyzzy") + (typeOf null) + (typeOf { x = 456; }) + (typeOf [ 1 2 3 ]) + (typeOf (x: x)) + (typeOf ((x: y: x) 1)) + (typeOf map) + (typeOf (map (x: x))) +] diff --git a/tests/functional/lang/eval-okay-versions.exp b/tests/functional/lang/eval-okay-versions.exp new file mode 100644 index 000000000..27ba77dda --- /dev/null +++ b/tests/functional/lang/eval-okay-versions.exp @@ -0,0 +1 @@ +true diff --git a/tests/functional/lang/eval-okay-versions.nix b/tests/functional/lang/eval-okay-versions.nix new file mode 100644 index 000000000..e9111f5f4 --- /dev/null +++ b/tests/functional/lang/eval-okay-versions.nix @@ -0,0 +1,43 @@ +let + + name1 = "hello-1.0.2"; + name2 = "hello"; + name3 = "915resolution-0.5.2"; + name4 = "xf86-video-i810-1.7.4"; + name5 = "name-that-ends-with-dash--1.0"; + + eq = 0; + lt = builtins.sub 0 1; + gt = 1; + + versionTest = v1: v2: expected: + let d1 = builtins.compareVersions v1 v2; + d2 = builtins.compareVersions v2 v1; + in d1 == builtins.sub 0 d2 && d1 == expected; + + tests = [ + ((builtins.parseDrvName name1).name == "hello") + ((builtins.parseDrvName name1).version == "1.0.2") + ((builtins.parseDrvName name2).name == "hello") + ((builtins.parseDrvName name2).version == "") + ((builtins.parseDrvName name3).name == "915resolution") + ((builtins.parseDrvName name3).version == "0.5.2") + ((builtins.parseDrvName name4).name == "xf86-video-i810") + ((builtins.parseDrvName name4).version == "1.7.4") + ((builtins.parseDrvName name5).name == "name-that-ends-with-dash") + ((builtins.parseDrvName name5).version == "-1.0") + (versionTest "1.0" "2.3" lt) + (versionTest "2.1" "2.3" lt) + (versionTest "2.3" "2.3" eq) + (versionTest "2.5" "2.3" gt) + (versionTest "3.1" "2.3" gt) + (versionTest "2.3.1" "2.3" gt) + (versionTest "2.3.1" "2.3a" gt) + (versionTest "2.3pre1" "2.3" lt) + (versionTest "2.3pre3" "2.3pre12" lt) + (versionTest "2.3a" "2.3c" lt) + (versionTest "2.3pre1" "2.3c" lt) + (versionTest "2.3pre1" "2.3q" lt) + ]; + +in (import ./lib.nix).and tests diff --git a/tests/functional/lang/eval-okay-with.exp b/tests/functional/lang/eval-okay-with.exp new file mode 100644 index 000000000..378c8dc80 --- /dev/null +++ b/tests/functional/lang/eval-okay-with.exp @@ -0,0 +1 @@ +"xyzzybarxyzzybar" diff --git a/tests/functional/lang/eval-okay-with.nix b/tests/functional/lang/eval-okay-with.nix new file mode 100644 index 000000000..033e8d3ab --- /dev/null +++ b/tests/functional/lang/eval-okay-with.nix @@ -0,0 +1,19 @@ +let { + + a = "xyzzy"; + + as = { + a = "foo"; + b = "bar"; + }; + + bs = { + a = "bar"; + }; + + x = with as; a + b; + + y = with as; with bs; a + b; + + body = x + y; +} diff --git a/tests/functional/lang/eval-okay-xml.exp.xml b/tests/functional/lang/eval-okay-xml.exp.xml new file mode 100644 index 000000000..20099326c --- /dev/null +++ b/tests/functional/lang/eval-okay-xml.exp.xml @@ -0,0 +1,52 @@ +<?xml version='1.0' encoding='utf-8'?> +<expr> + <attrs> + <attr name="a"> + <string value="foo" /> + </attr> + <attr name="at"> + <function> + <attrspat name="args"> + <attr name="x" /> + <attr name="y" /> + <attr name="z" /> + </attrspat> + </function> + </attr> + <attr name="b"> + <string value="bar" /> + </attr> + <attr name="c"> + <string value="foobar" /> + </attr> + <attr name="ellipsis"> + <function> + <attrspat ellipsis="1"> + <attr name="x" /> + <attr name="y" /> + <attr name="z" /> + </attrspat> + </function> + </attr> + <attr name="f"> + <function> + <attrspat> + <attr name="x" /> + <attr name="y" /> + <attr name="z" /> + </attrspat> + </function> + </attr> + <attr name="id"> + <function> + <varpat name="x" /> + </function> + </attr> + <attr name="x"> + <int value="123" /> + </attr> + <attr name="y"> + <float value="567.89" /> + </attr> + </attrs> +</expr> diff --git a/tests/functional/lang/eval-okay-xml.nix b/tests/functional/lang/eval-okay-xml.nix new file mode 100644 index 000000000..9ee9f8a0b --- /dev/null +++ b/tests/functional/lang/eval-okay-xml.nix @@ -0,0 +1,21 @@ +rec { + + x = 123; + + y = 567.890; + + a = "foo"; + + b = "bar"; + + c = "foo" + "bar"; + + f = {z, x, y}: if y then x else z; + + id = x: x; + + at = args@{x, y, z}: x; + + ellipsis = {x, y, z, ...}: x; + +} diff --git a/tests/functional/lang/eval-okay-zipAttrsWith.exp b/tests/functional/lang/eval-okay-zipAttrsWith.exp new file mode 100644 index 000000000..9c0b15d22 --- /dev/null +++ b/tests/functional/lang/eval-okay-zipAttrsWith.exp @@ -0,0 +1 @@ +{ "0" = { n = "0"; v = [ 5 23 29 ]; }; "1" = { n = "1"; v = [ 7 30 ]; }; "2" = { n = "2"; v = [ 18 ]; }; "4" = { n = "4"; v = [ 10 ]; }; "5" = { n = "5"; v = [ 15 25 26 31 ]; }; "6" = { n = "6"; v = [ 3 14 ]; }; "7" = { n = "7"; v = [ 12 ]; }; "8" = { n = "8"; v = [ 2 6 8 9 ]; }; "9" = { n = "9"; v = [ 0 16 ]; }; a = { n = "a"; v = [ 17 21 22 27 ]; }; c = { n = "c"; v = [ 11 24 ]; }; d = { n = "d"; v = [ 4 13 28 ]; }; e = { n = "e"; v = [ 20 ]; }; f = { n = "f"; v = [ 1 19 ]; }; } diff --git a/tests/functional/lang/eval-okay-zipAttrsWith.nix b/tests/functional/lang/eval-okay-zipAttrsWith.nix new file mode 100644 index 000000000..877d4e5fa --- /dev/null +++ b/tests/functional/lang/eval-okay-zipAttrsWith.nix @@ -0,0 +1,9 @@ +with import ./lib.nix; + +let + str = builtins.hashString "sha256" "test"; +in +builtins.zipAttrsWith + (n: v: { inherit n v; }) + (map (n: { ${builtins.substring n 1 str} = n; }) + (range 0 31)) diff --git a/tests/functional/lang/framework.sh b/tests/functional/lang/framework.sh new file mode 100644 index 000000000..516bff8ad --- /dev/null +++ b/tests/functional/lang/framework.sh @@ -0,0 +1,33 @@ +# Golden test support +# +# Test that the output of the given test matches what is expected. If +# `_NIX_TEST_ACCEPT` is non-empty also update the expected output so +# that next time the test succeeds. +function diffAndAcceptInner() { + local -r testName=$1 + local -r got="$2" + local -r expected="$3" + + # Absence of expected file indicates empty output expected. + if test -e "$expected"; then + local -r expectedOrEmpty="$expected" + else + local -r expectedOrEmpty=lang/empty.exp + fi + + # Diff so we get a nice message + if ! diff --unified "$got" "$expectedOrEmpty"; then + echo "FAIL: evaluation result of $testName not as expected" + badDiff=1 + fi + + # Update expected if `_NIX_TEST_ACCEPT` is non-empty. + if test -n "${_NIX_TEST_ACCEPT-}"; then + cp "$got" "$expected" + # Delete empty expected files to avoid bloating the repo with + # empty files. + if ! test -s "$expected"; then + rm "$expected" + fi + fi +} diff --git a/tests/functional/lang/imported.nix b/tests/functional/lang/imported.nix new file mode 100644 index 000000000..fb39ee4ef --- /dev/null +++ b/tests/functional/lang/imported.nix @@ -0,0 +1,3 @@ +# The function ‘range’ comes from lib.nix and was added to the lexical +# scope by scopedImport. +range 1 5 ++ import ./imported2.nix diff --git a/tests/functional/lang/imported2.nix b/tests/functional/lang/imported2.nix new file mode 100644 index 000000000..6d0a2992b --- /dev/null +++ b/tests/functional/lang/imported2.nix @@ -0,0 +1 @@ +range 6 10 diff --git a/tests/functional/lang/lib.nix b/tests/functional/lang/lib.nix new file mode 100644 index 000000000..028a53831 --- /dev/null +++ b/tests/functional/lang/lib.nix @@ -0,0 +1,61 @@ +with builtins; + +rec { + + fold = op: nul: list: + if list == [] + then nul + else op (head list) (fold op nul (tail list)); + + concat = + fold (x: y: x + y) ""; + + and = fold (x: y: x && y) true; + + flatten = x: + if isList x + then fold (x: y: (flatten x) ++ y) [] x + else [x]; + + sum = foldl' (x: y: add x y) 0; + + hasSuffix = ext: fileName: + let lenFileName = stringLength fileName; + lenExt = stringLength ext; + in !(lessThan lenFileName lenExt) && + substring (sub lenFileName lenExt) lenFileName fileName == ext; + + # Split a list at the given position. + splitAt = pos: list: + if pos == 0 then {first = []; second = list;} else + if list == [] then {first = []; second = [];} else + let res = splitAt (sub pos 1) (tail list); + in {first = [(head list)] ++ res.first; second = res.second;}; + + # Stable merge sort. + sortBy = comp: list: + if lessThan 1 (length list) + then + let + split = splitAt (div (length list) 2) list; + first = sortBy comp split.first; + second = sortBy comp split.second; + in mergeLists comp first second + else list; + + mergeLists = comp: list1: list2: + if list1 == [] then list2 else + if list2 == [] then list1 else + if comp (head list2) (head list1) then [(head list2)] ++ mergeLists comp list1 (tail list2) else + [(head list1)] ++ mergeLists comp (tail list1) list2; + + id = x: x; + + const = x: y: x; + + range = first: last: + if first > last + then [] + else genList (n: first + n) (last - first + 1); + +} diff --git a/tests/functional/lang/parse-fail-dup-attrs-1.err.exp b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp new file mode 100644 index 000000000..4fe6b7a1f --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp @@ -0,0 +1,7 @@ +error: attribute 'x' already defined at «stdin»:1:3 + + at «stdin»:3:3: + + 2| y = 456; + 3| x = 789; + | ^ diff --git a/tests/functional/lang/parse-fail-dup-attrs-1.nix b/tests/functional/lang/parse-fail-dup-attrs-1.nix new file mode 100644 index 000000000..2c02317d2 --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-1.nix @@ -0,0 +1,4 @@ +{ x = 123; + y = 456; + x = 789; +} diff --git a/tests/functional/lang/parse-fail-dup-attrs-2.err.exp b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp new file mode 100644 index 000000000..3aba2891f --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp @@ -0,0 +1,7 @@ +error: attribute 'x' already defined at «stdin»:9:5 + + at «stdin»:10:17: + + 9| x = 789; + 10| inherit (as) x; + | ^ diff --git a/tests/functional/lang/parse-fail-dup-attrs-2.nix b/tests/functional/lang/parse-fail-dup-attrs-2.nix new file mode 100644 index 000000000..864d9865e --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-2.nix @@ -0,0 +1,13 @@ +let { + + as = { + x = 123; + y = 456; + }; + + bs = { + x = 789; + inherit (as) x; + }; + +} diff --git a/tests/functional/lang/parse-fail-dup-attrs-3.err.exp b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp new file mode 100644 index 000000000..3aba2891f --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp @@ -0,0 +1,7 @@ +error: attribute 'x' already defined at «stdin»:9:5 + + at «stdin»:10:17: + + 9| x = 789; + 10| inherit (as) x; + | ^ diff --git a/tests/functional/lang/parse-fail-dup-attrs-3.nix b/tests/functional/lang/parse-fail-dup-attrs-3.nix new file mode 100644 index 000000000..114d19779 --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-3.nix @@ -0,0 +1,13 @@ +let { + + as = { + x = 123; + y = 456; + }; + + bs = rec { + x = 789; + inherit (as) x; + }; + +} diff --git a/tests/functional/lang/parse-fail-dup-attrs-4.err.exp b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp new file mode 100644 index 000000000..ff68446a1 --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp @@ -0,0 +1,7 @@ +error: attribute 'services.ssh.port' already defined at «stdin»:2:3 + + at «stdin»:3:3: + + 2| services.ssh.port = 22; + 3| services.ssh.port = 23; + | ^ diff --git a/tests/functional/lang/parse-fail-dup-attrs-4.nix b/tests/functional/lang/parse-fail-dup-attrs-4.nix new file mode 100644 index 000000000..77417432b --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-4.nix @@ -0,0 +1,4 @@ +{ + services.ssh.port = 22; + services.ssh.port = 23; +} diff --git a/tests/functional/lang/parse-fail-dup-attrs-6.err.exp b/tests/functional/lang/parse-fail-dup-attrs-6.err.exp new file mode 100644 index 000000000..74823fc25 --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-6.err.exp @@ -0,0 +1 @@ +error: attribute ‘services.ssh’ at (string):3:3 already defined at (string):2:3 diff --git a/tests/functional/lang/parse-fail-dup-attrs-7.err.exp b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp new file mode 100644 index 000000000..512a499ca --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp @@ -0,0 +1,7 @@ +error: attribute 'x' already defined at «stdin»:6:12 + + at «stdin»:7:12: + + 6| inherit x; + 7| inherit x; + | ^ diff --git a/tests/functional/lang/parse-fail-dup-attrs-7.nix b/tests/functional/lang/parse-fail-dup-attrs-7.nix new file mode 100644 index 000000000..bbc3eb08c --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-attrs-7.nix @@ -0,0 +1,9 @@ +rec { + + x = 1; + + as = { + inherit x; + inherit x; + }; +}
\ No newline at end of file diff --git a/tests/functional/lang/parse-fail-dup-formals.err.exp b/tests/functional/lang/parse-fail-dup-formals.err.exp new file mode 100644 index 000000000..1d566fb33 --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-formals.err.exp @@ -0,0 +1,6 @@ +error: duplicate formal function argument 'x' + + at «stdin»:1:8: + + 1| {x, y, x}: x + | ^ diff --git a/tests/functional/lang/parse-fail-dup-formals.nix b/tests/functional/lang/parse-fail-dup-formals.nix new file mode 100644 index 000000000..a0edd91a9 --- /dev/null +++ b/tests/functional/lang/parse-fail-dup-formals.nix @@ -0,0 +1 @@ +{x, y, x}: x
\ No newline at end of file diff --git a/tests/functional/lang/parse-fail-eof-in-string.err.exp b/tests/functional/lang/parse-fail-eof-in-string.err.exp new file mode 100644 index 000000000..f9fa72312 --- /dev/null +++ b/tests/functional/lang/parse-fail-eof-in-string.err.exp @@ -0,0 +1,7 @@ +error: syntax error, unexpected end of file, expecting '"' + + at «stdin»:3:5: + + 2| # Note that this file must not end with a newline. + 3| a 1"$ + | ^ diff --git a/tests/functional/lang/parse-fail-eof-in-string.nix b/tests/functional/lang/parse-fail-eof-in-string.nix new file mode 100644 index 000000000..19775d2ec --- /dev/null +++ b/tests/functional/lang/parse-fail-eof-in-string.nix @@ -0,0 +1,3 @@ +# https://github.com/NixOS/nix/issues/6562 +# Note that this file must not end with a newline. +a 1"$
\ No newline at end of file diff --git a/tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp b/tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp new file mode 100644 index 000000000..32f776795 --- /dev/null +++ b/tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp @@ -0,0 +1,8 @@ +error: attribute 'z' already defined at «stdin»:3:16 + + at «stdin»:2:3: + + 1| { + 2| x.z = 3; + | ^ + 3| x = { y = 3; z = 3; }; diff --git a/tests/functional/lang/parse-fail-mixed-nested-attrs1.nix b/tests/functional/lang/parse-fail-mixed-nested-attrs1.nix new file mode 100644 index 000000000..11e40e66f --- /dev/null +++ b/tests/functional/lang/parse-fail-mixed-nested-attrs1.nix @@ -0,0 +1,4 @@ +{ + x.z = 3; + x = { y = 3; z = 3; }; +} diff --git a/tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp b/tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp new file mode 100644 index 000000000..0437cd50c --- /dev/null +++ b/tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp @@ -0,0 +1,8 @@ +error: attribute 'y' already defined at «stdin»:3:9 + + at «stdin»:2:3: + + 1| { + 2| x.y.y = 3; + | ^ + 3| x = { y.y= 3; z = 3; }; diff --git a/tests/functional/lang/parse-fail-mixed-nested-attrs2.nix b/tests/functional/lang/parse-fail-mixed-nested-attrs2.nix new file mode 100644 index 000000000..17da82e5f --- /dev/null +++ b/tests/functional/lang/parse-fail-mixed-nested-attrs2.nix @@ -0,0 +1,4 @@ +{ + x.y.y = 3; + x = { y.y= 3; z = 3; }; +} diff --git a/tests/functional/lang/parse-fail-patterns-1.err.exp b/tests/functional/lang/parse-fail-patterns-1.err.exp new file mode 100644 index 000000000..634a04aaa --- /dev/null +++ b/tests/functional/lang/parse-fail-patterns-1.err.exp @@ -0,0 +1,7 @@ +error: duplicate formal function argument 'args' + + at «stdin»:1:1: + + 1| args@{args, x, y, z}: x + | ^ + 2| diff --git a/tests/functional/lang/parse-fail-patterns-1.nix b/tests/functional/lang/parse-fail-patterns-1.nix new file mode 100644 index 000000000..7b4061641 --- /dev/null +++ b/tests/functional/lang/parse-fail-patterns-1.nix @@ -0,0 +1 @@ +args@{args, x, y, z}: x diff --git a/tests/functional/lang/parse-fail-regression-20060610.err.exp b/tests/functional/lang/parse-fail-regression-20060610.err.exp new file mode 100644 index 000000000..167d01e85 --- /dev/null +++ b/tests/functional/lang/parse-fail-regression-20060610.err.exp @@ -0,0 +1,8 @@ +error: undefined variable 'gcc' + + at «stdin»:8:12: + + 7| + 8| body = ({ + | ^ + 9| inherit gcc; diff --git a/tests/functional/lang/parse-fail-regression-20060610.nix b/tests/functional/lang/parse-fail-regression-20060610.nix new file mode 100644 index 000000000..b1934f7e1 --- /dev/null +++ b/tests/functional/lang/parse-fail-regression-20060610.nix @@ -0,0 +1,11 @@ +let { + x = + {gcc}: + { + inherit gcc; + }; + + body = ({ + inherit gcc; + }).gcc; +} diff --git a/tests/functional/lang/parse-fail-undef-var-2.err.exp b/tests/functional/lang/parse-fail-undef-var-2.err.exp new file mode 100644 index 000000000..77c96bbd2 --- /dev/null +++ b/tests/functional/lang/parse-fail-undef-var-2.err.exp @@ -0,0 +1,7 @@ +error: syntax error, unexpected ':', expecting '}' + + at «stdin»:3:13: + + 2| + 3| f = {x, y : + | ^ diff --git a/tests/functional/lang/parse-fail-undef-var-2.nix b/tests/functional/lang/parse-fail-undef-var-2.nix new file mode 100644 index 000000000..c10a52b1e --- /dev/null +++ b/tests/functional/lang/parse-fail-undef-var-2.nix @@ -0,0 +1,7 @@ +let { + + f = {x, y : ["baz" "bar" z "bat"]}: x + y; + + body = f {x = "foo"; y = "bar";}; + +} diff --git a/tests/functional/lang/parse-fail-undef-var.err.exp b/tests/functional/lang/parse-fail-undef-var.err.exp new file mode 100644 index 000000000..48e88747f --- /dev/null +++ b/tests/functional/lang/parse-fail-undef-var.err.exp @@ -0,0 +1,7 @@ +error: undefined variable 'y' + + at «stdin»:1:4: + + 1| x: y + | ^ + 2| diff --git a/tests/functional/lang/parse-fail-undef-var.nix b/tests/functional/lang/parse-fail-undef-var.nix new file mode 100644 index 000000000..7b6300811 --- /dev/null +++ b/tests/functional/lang/parse-fail-undef-var.nix @@ -0,0 +1 @@ +x: y diff --git a/tests/functional/lang/parse-fail-utf8.err.exp b/tests/functional/lang/parse-fail-utf8.err.exp new file mode 100644 index 000000000..6087479a3 --- /dev/null +++ b/tests/functional/lang/parse-fail-utf8.err.exp @@ -0,0 +1,6 @@ +error: syntax error, unexpected invalid token, expecting end of file + + at «stdin»:1:5: + + 1| 123 + | ^ diff --git a/tests/functional/lang/parse-fail-utf8.nix b/tests/functional/lang/parse-fail-utf8.nix new file mode 100644 index 000000000..34948d48a --- /dev/null +++ b/tests/functional/lang/parse-fail-utf8.nix @@ -0,0 +1 @@ +123 é 4 diff --git a/tests/functional/lang/parse-okay-1.exp b/tests/functional/lang/parse-okay-1.exp new file mode 100644 index 000000000..d5ab5f18a --- /dev/null +++ b/tests/functional/lang/parse-okay-1.exp @@ -0,0 +1 @@ +({ x, y, z }: ((x + y) + z)) diff --git a/tests/functional/lang/parse-okay-1.nix b/tests/functional/lang/parse-okay-1.nix new file mode 100644 index 000000000..23a58ed10 --- /dev/null +++ b/tests/functional/lang/parse-okay-1.nix @@ -0,0 +1 @@ +{x, y, z}: x + y + z diff --git a/tests/functional/lang/parse-okay-crlf.exp b/tests/functional/lang/parse-okay-crlf.exp new file mode 100644 index 000000000..4213609fc --- /dev/null +++ b/tests/functional/lang/parse-okay-crlf.exp @@ -0,0 +1 @@ +rec { foo = "multi\nline\n string\n test\r"; x = y; y = 123; z = 456; } diff --git a/tests/functional/lang/parse-okay-crlf.nix b/tests/functional/lang/parse-okay-crlf.nix new file mode 100644 index 000000000..21518d4c6 --- /dev/null +++ b/tests/functional/lang/parse-okay-crlf.nix @@ -0,0 +1,17 @@ +rec { + + /* Dit is
+ een test. */ + + x = + # Dit is een test.
y; + + y = 123;
+ + # CR or CR/LF (but not explicit \r's) in strings should be + # translated to LF. + foo = "multi
line
+ string + test\r"; + + z = 456;
} diff --git a/tests/functional/lang/parse-okay-dup-attrs-5.exp b/tests/functional/lang/parse-okay-dup-attrs-5.exp new file mode 100644 index 000000000..88b0b036f --- /dev/null +++ b/tests/functional/lang/parse-okay-dup-attrs-5.exp @@ -0,0 +1 @@ +{ services = { ssh = { enable = true; port = 23; }; }; } diff --git a/tests/functional/lang/parse-okay-dup-attrs-5.nix b/tests/functional/lang/parse-okay-dup-attrs-5.nix new file mode 100644 index 000000000..f4b9efd0c --- /dev/null +++ b/tests/functional/lang/parse-okay-dup-attrs-5.nix @@ -0,0 +1,4 @@ +{ + services.ssh = { enable = true; }; + services.ssh.port = 23; +} diff --git a/tests/functional/lang/parse-okay-dup-attrs-6.exp b/tests/functional/lang/parse-okay-dup-attrs-6.exp new file mode 100644 index 000000000..88b0b036f --- /dev/null +++ b/tests/functional/lang/parse-okay-dup-attrs-6.exp @@ -0,0 +1 @@ +{ services = { ssh = { enable = true; port = 23; }; }; } diff --git a/tests/functional/lang/parse-okay-dup-attrs-6.nix b/tests/functional/lang/parse-okay-dup-attrs-6.nix new file mode 100644 index 000000000..ae6d7a769 --- /dev/null +++ b/tests/functional/lang/parse-okay-dup-attrs-6.nix @@ -0,0 +1,4 @@ +{ + services.ssh.port = 23; + services.ssh = { enable = true; }; +} diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp new file mode 100644 index 000000000..89c66f760 --- /dev/null +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp @@ -0,0 +1 @@ +{ x = { q = 3; y = 3; z = 3; }; } diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix new file mode 100644 index 000000000..fd1001c8c --- /dev/null +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix @@ -0,0 +1,4 @@ +{ + x = { y = 3; z = 3; }; + x.q = 3; +} diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp new file mode 100644 index 000000000..89c66f760 --- /dev/null +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp @@ -0,0 +1 @@ +{ x = { q = 3; y = 3; z = 3; }; } diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix new file mode 100644 index 000000000..ad066b680 --- /dev/null +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix @@ -0,0 +1,4 @@ +{ + x.q = 3; + x = { y = 3; z = 3; }; +} diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp new file mode 100644 index 000000000..b89a59734 --- /dev/null +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp @@ -0,0 +1 @@ +{ services = { httpd = { enable = true; }; ssh = { enable = true; port = 123; }; }; } diff --git a/tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix new file mode 100644 index 000000000..45a33e480 --- /dev/null +++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix @@ -0,0 +1,7 @@ +{ + services.ssh.enable = true; + services.ssh = { port = 123; }; + services = { + httpd.enable = true; + }; +} diff --git a/tests/functional/lang/parse-okay-regression-20041027.exp b/tests/functional/lang/parse-okay-regression-20041027.exp new file mode 100644 index 000000000..9df7219e4 --- /dev/null +++ b/tests/functional/lang/parse-okay-regression-20041027.exp @@ -0,0 +1 @@ +({ fetchurl, stdenv }: ((stdenv).mkDerivation { name = "libXi-6.0.1"; src = (fetchurl { md5 = "7e935a42428d63a387b3c048be0f2756"; url = "http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2"; }); })) diff --git a/tests/functional/lang/parse-okay-regression-20041027.nix b/tests/functional/lang/parse-okay-regression-20041027.nix new file mode 100644 index 000000000..ae2e256ee --- /dev/null +++ b/tests/functional/lang/parse-okay-regression-20041027.nix @@ -0,0 +1,11 @@ +{stdenv, fetchurl /* pkgconfig, libX11 */ }: + +stdenv.mkDerivation { + name = "libXi-6.0.1"; + src = fetchurl { + url = http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2; + md5 = "7e935a42428d63a387b3c048be0f2756"; + }; +/* buildInputs = [pkgconfig]; + propagatedBuildInputs = [libX11]; */ +} diff --git a/tests/functional/lang/parse-okay-regression-751.exp b/tests/functional/lang/parse-okay-regression-751.exp new file mode 100644 index 000000000..e2ed886fe --- /dev/null +++ b/tests/functional/lang/parse-okay-regression-751.exp @@ -0,0 +1 @@ +(let const = (a: "const"); in ((const { x = "q"; }))) diff --git a/tests/functional/lang/parse-okay-regression-751.nix b/tests/functional/lang/parse-okay-regression-751.nix new file mode 100644 index 000000000..05c78b301 --- /dev/null +++ b/tests/functional/lang/parse-okay-regression-751.nix @@ -0,0 +1,2 @@ +let const = a: "const"; in +''${ const { x = "q"; }}'' diff --git a/tests/functional/lang/parse-okay-subversion.exp b/tests/functional/lang/parse-okay-subversion.exp new file mode 100644 index 000000000..4168ee8bf --- /dev/null +++ b/tests/functional/lang/parse-okay-subversion.exp @@ -0,0 +1 @@ +({ fetchurl, localServer ? false, httpServer ? false, sslSupport ? false, pythonBindings ? false, javaSwigBindings ? false, javahlBindings ? false, stdenv, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null }: assert (expat != null); assert (localServer -> (db4 != null)); assert (httpServer -> ((httpd != null) && ((httpd).expat == expat))); assert (sslSupport -> ((openssl != null) && (httpServer -> ((httpd).openssl == openssl)))); assert (pythonBindings -> ((swig != null) && (swig).pythonSupport)); assert (javaSwigBindings -> ((swig != null) && (swig).javaSupport)); assert (javahlBindings -> (j2sdk != null)); ((stdenv).mkDerivation { builder = /foo/bar; db4 = (if localServer then db4 else null); inherit expat ; inherit httpServer ; httpd = (if httpServer then httpd else null); j2sdk = (if javaSwigBindings then (swig).j2sdk else (if javahlBindings then j2sdk else null)); inherit javaSwigBindings ; inherit javahlBindings ; inherit localServer ; name = "subversion-1.1.1"; openssl = (if sslSupport then openssl else null); patches = (if javahlBindings then [ (/javahl.patch) ] else [ ]); python = (if pythonBindings then (swig).python else null); inherit pythonBindings ; src = (fetchurl { md5 = "a180c3fe91680389c210c99def54d9e0"; url = "http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2"; }); inherit sslSupport ; swig = (if (pythonBindings || javaSwigBindings) then swig else null); })) diff --git a/tests/functional/lang/parse-okay-subversion.nix b/tests/functional/lang/parse-okay-subversion.nix new file mode 100644 index 000000000..356272815 --- /dev/null +++ b/tests/functional/lang/parse-okay-subversion.nix @@ -0,0 +1,43 @@ +{ localServer ? false +, httpServer ? false +, sslSupport ? false +, pythonBindings ? false +, javaSwigBindings ? false +, javahlBindings ? false +, stdenv, fetchurl +, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null +}: + +assert expat != null; +assert localServer -> db4 != null; +assert httpServer -> httpd != null && httpd.expat == expat; +assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); +assert pythonBindings -> swig != null && swig.pythonSupport; +assert javaSwigBindings -> swig != null && swig.javaSupport; +assert javahlBindings -> j2sdk != null; + +stdenv.mkDerivation { + name = "subversion-1.1.1"; + + builder = /foo/bar; + src = fetchurl { + url = http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2; + md5 = "a180c3fe91680389c210c99def54d9e0"; + }; + + # This is a hopefully temporary fix for the problem that + # libsvnjavahl.so isn't linked against libstdc++, which causes + # loading the library into the JVM to fail. + patches = if javahlBindings then [/javahl.patch] else []; + + openssl = if sslSupport then openssl else null; + httpd = if httpServer then httpd else null; + db4 = if localServer then db4 else null; + swig = if pythonBindings || javaSwigBindings then swig else null; + python = if pythonBindings then swig.python else null; + j2sdk = if javaSwigBindings then swig.j2sdk else + if javahlBindings then j2sdk else null; + + inherit expat localServer httpServer sslSupport + pythonBindings javaSwigBindings javahlBindings; +} diff --git a/tests/functional/lang/parse-okay-url.exp b/tests/functional/lang/parse-okay-url.exp new file mode 100644 index 000000000..e5f0829b0 --- /dev/null +++ b/tests/functional/lang/parse-okay-url.exp @@ -0,0 +1 @@ +[ ("x:x") ("https://svn.cs.uu.nl:12443/repos/trace/trunk") ("http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2") ("http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz") ("http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz") ("https://ftp5.gwdg.de/pub/linux/archlinux/extra/os/x86_64/unzip-6.0-14-x86_64.pkg.tar.zst") ("ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz") ] diff --git a/tests/functional/lang/parse-okay-url.nix b/tests/functional/lang/parse-okay-url.nix new file mode 100644 index 000000000..08de27d0a --- /dev/null +++ b/tests/functional/lang/parse-okay-url.nix @@ -0,0 +1,8 @@ +[ x:x + https://svn.cs.uu.nl:12443/repos/trace/trunk + http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2 + http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz + http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz + https://ftp5.gwdg.de/pub/linux/archlinux/extra/os/x86_64/unzip-6.0-14-x86_64.pkg.tar.zst + ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz +] diff --git a/tests/functional/lang/readDir/bar b/tests/functional/lang/readDir/bar new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/functional/lang/readDir/bar diff --git a/tests/functional/lang/readDir/foo/git-hates-directories b/tests/functional/lang/readDir/foo/git-hates-directories new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tests/functional/lang/readDir/foo/git-hates-directories diff --git a/tests/functional/lang/readDir/ldir b/tests/functional/lang/readDir/ldir new file mode 120000 index 000000000..191028156 --- /dev/null +++ b/tests/functional/lang/readDir/ldir @@ -0,0 +1 @@ +foo
\ No newline at end of file diff --git a/tests/functional/lang/readDir/linked b/tests/functional/lang/readDir/linked new file mode 120000 index 000000000..c503f86a0 --- /dev/null +++ b/tests/functional/lang/readDir/linked @@ -0,0 +1 @@ +foo/git-hates-directories
\ No newline at end of file diff --git a/tests/functional/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh new file mode 100644 index 000000000..71b716b84 --- /dev/null +++ b/tests/functional/legacy-ssh-store.sh @@ -0,0 +1,4 @@ +source common.sh + +# Check that store ping trusted doesn't yet work with ssh:// +nix --store ssh://localhost?remote-store=$TEST_ROOT/other-store store ping --json | jq -e 'has("trusted") | not' diff --git a/tests/functional/linux-sandbox-cert-test.nix b/tests/functional/linux-sandbox-cert-test.nix new file mode 100644 index 000000000..2fc083ea9 --- /dev/null +++ b/tests/functional/linux-sandbox-cert-test.nix @@ -0,0 +1,30 @@ +{ mode }: + +with import ./config.nix; + +mkDerivation ( + { + name = "ssl-export"; + buildCommand = '' + # Add some indirection, otherwise grepping into the debug output finds the string. + report () { echo CERT_$1_IN_SANDBOX; } + + if [ -f /etc/ssl/certs/ca-certificates.crt ]; then + content=$(</etc/ssl/certs/ca-certificates.crt) + if [ "$content" == CERT_CONTENT ]; then + report present + fi + else + report missing + fi + + # Always fail, because we do not want to bother with fixed-output + # derivations being cached, and do not want to compute the right hash. + false; + ''; + } // { + fixed-output = { outputHash = "sha256:0000000000000000000000000000000000000000000000000000000000000000"; }; + normal = { }; + }.${mode} +) + diff --git a/tests/functional/linux-sandbox.sh b/tests/functional/linux-sandbox.sh new file mode 100644 index 000000000..ff7d257bd --- /dev/null +++ b/tests/functional/linux-sandbox.sh @@ -0,0 +1,75 @@ +source common.sh + +needLocalStore "the sandbox only runs on the builder side, so it makes no sense to test it with the daemon" + +clearStore + +requireSandboxSupport + +# Note: we need to bind-mount $SHELL into the chroot. Currently we +# only support the case where $SHELL is in the Nix store, because +# otherwise things get complicated (e.g. if it's in /bin, do we need +# /lib as well?). +if [[ ! $SHELL =~ /nix/store ]]; then skipTest "Shell is not from Nix store"; fi +# An alias to automatically bind-mount the $SHELL on nix-build invocations +nix-sandbox-build () { nix-build --no-out-link --sandbox-paths /nix/store "$@"; } + +chmod -R u+w $TEST_ROOT/store0 || true +rm -rf $TEST_ROOT/store0 + +export NIX_STORE_DIR=/my/store +export NIX_REMOTE=$TEST_ROOT/store0 + +outPath=$(nix-sandbox-build dependencies.nix) + +[[ $outPath =~ /my/store/.*-dependencies ]] + +nix path-info -r $outPath | grep input-2 + +nix store ls -R -l $outPath | grep foobar + +nix store cat $outPath/foobar | grep FOOBAR + +# Test --check without hash rewriting. +nix-sandbox-build dependencies.nix --check + +# Test that sandboxed builds with --check and -K can move .check directory to store +nix-sandbox-build check.nix -A nondeterministic + +# `100 + 4` means non-determinstic, see doc/manual/src/command-ref/status-build-failure.md +expectStderr 104 nix-sandbox-build check.nix -A nondeterministic --check -K > $TEST_ROOT/log +grepQuietInverse 'error: renaming' $TEST_ROOT/log +grepQuiet 'may not be deterministic' $TEST_ROOT/log + +# Test that sandboxed builds cannot write to /etc easily +# `100` means build failure without extra info, see doc/manual/src/command-ref/status-build-failure.md +expectStderr 100 nix-sandbox-build -E 'with import ./config.nix; mkDerivation { name = "etc-write"; buildCommand = "echo > /etc/test"; }' | + grepQuiet "/etc/test: Permission denied" + + +## Test mounting of SSL certificates into the sandbox +testCert () { + expectation=$1 # "missing" | "present" + mode=$2 # "normal" | "fixed-output" + certFile=$3 # a string that can be the path to a cert file + # `100` means build failure without extra info, see doc/manual/src/command-ref/status-build-failure.md + [ "$mode" == fixed-output ] && ret=1 || ret=100 + expectStderr $ret nix-sandbox-build linux-sandbox-cert-test.nix --argstr mode "$mode" --option ssl-cert-file "$certFile" | + grepQuiet "CERT_${expectation}_IN_SANDBOX" +} + +nocert=$TEST_ROOT/no-cert-file.pem +cert=$TEST_ROOT/some-cert-file.pem +echo -n "CERT_CONTENT" > $cert + +# No cert in sandbox when not a fixed-output derivation +testCert missing normal "$cert" + +# No cert in sandbox when ssl-cert-file is empty +testCert missing fixed-output "" + +# No cert in sandbox when ssl-cert-file is a nonexistent file +testCert missing fixed-output "$nocert" + +# Cert in sandbox when ssl-cert-file is set to an existing file +testCert present fixed-output "$cert" diff --git a/tests/functional/local-store.sh b/tests/functional/local-store.sh new file mode 100644 index 000000000..89502f864 --- /dev/null +++ b/tests/functional/local-store.sh @@ -0,0 +1,22 @@ +source common.sh + +cd $TEST_ROOT + +echo example > example.txt +mkdir -p ./x + +NIX_STORE_DIR=$TEST_ROOT/x + +CORRECT_PATH=$(nix-store --store ./x --add example.txt) + +PATH1=$(nix path-info --store ./x $CORRECT_PATH) +[ $CORRECT_PATH == $PATH1 ] + +PATH2=$(nix path-info --store "$PWD/x" $CORRECT_PATH) +[ $CORRECT_PATH == $PATH2 ] + +PATH3=$(nix path-info --store "local?root=$PWD/x" $CORRECT_PATH) +[ $CORRECT_PATH == $PATH3 ] + +# Ensure store ping trusted works with local store +nix --store ./x store ping --json | jq -e '.trusted' diff --git a/tests/functional/local.mk b/tests/functional/local.mk new file mode 100644 index 000000000..d60f1e08e --- /dev/null +++ b/tests/functional/local.mk @@ -0,0 +1,146 @@ +nix_tests = \ + test-infra.sh \ + init.sh \ + flakes/flakes.sh \ + flakes/run.sh \ + flakes/mercurial.sh \ + flakes/circular.sh \ + flakes/init.sh \ + flakes/inputs.sh \ + flakes/follow-paths.sh \ + flakes/bundle.sh \ + flakes/check.sh \ + flakes/unlocked-override.sh \ + flakes/absolute-paths.sh \ + flakes/build-paths.sh \ + flakes/flake-in-submodule.sh \ + gc.sh \ + nix-collect-garbage-d.sh \ + remote-store.sh \ + legacy-ssh-store.sh \ + lang.sh \ + lang-test-infra.sh \ + experimental-features.sh \ + fetchMercurial.sh \ + gc-auto.sh \ + user-envs.sh \ + user-envs-migration.sh \ + binary-cache.sh \ + multiple-outputs.sh \ + nix-build.sh \ + gc-concurrent.sh \ + repair.sh \ + fixed.sh \ + export-graph.sh \ + timeout.sh \ + fetchGitRefs.sh \ + gc-runtime.sh \ + tarball.sh \ + fetchGit.sh \ + fetchurl.sh \ + fetchPath.sh \ + fetchTree-file.sh \ + simple.sh \ + referrers.sh \ + optimise-store.sh \ + substitute-with-invalid-ca.sh \ + signing.sh \ + hash.sh \ + gc-non-blocking.sh \ + check.sh \ + nix-shell.sh \ + check-refs.sh \ + build-remote-input-addressed.sh \ + secure-drv-outputs.sh \ + restricted.sh \ + fetchGitSubmodules.sh \ + flakes/search-root.sh \ + readfile-context.sh \ + nix-channel.sh \ + recursive.sh \ + dependencies.sh \ + check-reqs.sh \ + build-remote-content-addressed-fixed.sh \ + build-remote-content-addressed-floating.sh \ + build-remote-trustless-should-pass-0.sh \ + build-remote-trustless-should-pass-1.sh \ + build-remote-trustless-should-pass-2.sh \ + build-remote-trustless-should-pass-3.sh \ + build-remote-trustless-should-fail-0.sh \ + nar-access.sh \ + pure-eval.sh \ + eval.sh \ + repl.sh \ + binary-cache-build-remote.sh \ + search.sh \ + logging.sh \ + export.sh \ + config.sh \ + add.sh \ + local-store.sh \ + filter-source.sh \ + misc.sh \ + dump-db.sh \ + linux-sandbox.sh \ + supplementary-groups.sh \ + build-dry.sh \ + structured-attrs.sh \ + shell.sh \ + brotli.sh \ + zstd.sh \ + compression-levels.sh \ + nix-copy-ssh.sh \ + nix-copy-ssh-ng.sh \ + post-hook.sh \ + function-trace.sh \ + flakes/config.sh \ + fmt.sh \ + eval-store.sh \ + why-depends.sh \ + derivation-json.sh \ + import-derivation.sh \ + nix_path.sh \ + case-hack.sh \ + placeholders.sh \ + ssh-relay.sh \ + build.sh \ + build-delete.sh \ + output-normalization.sh \ + selfref-gc.sh \ + db-migration.sh \ + bash-profile.sh \ + pass-as-file.sh \ + nix-profile.sh \ + suggestions.sh \ + store-ping.sh \ + fetchClosure.sh \ + completions.sh \ + flakes/show.sh \ + impure-derivations.sh \ + path-from-hash-part.sh \ + test-libstoreconsumer.sh \ + toString-path.sh \ + read-only-store.sh \ + nested-sandboxing.sh + +ifeq ($(HAVE_LIBCPUID), 1) + nix_tests += compute-levels.sh +endif + +ifeq ($(BUILD_SHARED_LIBS), 1) + nix_tests += plugins.sh +endif + +$(d)/test-libstoreconsumer.sh.test $(d)/test-libstoreconsumer.sh.test-debug: \ + $(d)/test-libstoreconsumer/test-libstoreconsumer +$(d)/plugins.sh.test $(d)/plugins.sh.test-debug: \ + $(d)/plugins/libplugintest.$(SO_EXT) + +install-tests += $(foreach x, $(nix_tests), $(d)/$(x)) + +test-clean-files := \ + $(d)/common/vars-and-functions.sh \ + $(d)/config.nix + +clean-files += $(test-clean-files) +test-deps += $(test-clean-files) diff --git a/tests/functional/logging.sh b/tests/functional/logging.sh new file mode 100644 index 000000000..1481b9b36 --- /dev/null +++ b/tests/functional/logging.sh @@ -0,0 +1,26 @@ +source common.sh + +clearStore + +path=$(nix-build dependencies.nix --no-out-link) + +# Test nix-store -l. +[ "$(nix-store -l $path)" = FOO ] + +# Test compressed logs. +clearStore +rm -rf $NIX_LOG_DIR +(! nix-store -l $path) +nix-build dependencies.nix --no-out-link --compress-build-log +[ "$(nix-store -l $path)" = FOO ] + +# test whether empty logs work fine with `nix log`. +builder="$(mktemp)" +echo -e "#!/bin/sh\nmkdir \$out" > "$builder" +outp="$(nix-build -E \ + 'with import ./config.nix; mkDerivation { name = "fnord"; builder = '"$builder"'; }' \ + --out-link "$(mktemp -d)/result")" + +test -d "$outp" + +nix log "$outp" diff --git a/tests/functional/misc.sh b/tests/functional/misc.sh new file mode 100644 index 000000000..af96d20bd --- /dev/null +++ b/tests/functional/misc.sh @@ -0,0 +1,32 @@ +source common.sh + +# Tests miscellaneous commands. + +# Do all commands have help? +#nix-env --help | grepQuiet install +#nix-store --help | grepQuiet realise +#nix-instantiate --help | grepQuiet eval +#nix-hash --help | grepQuiet base32 + +# Can we ask for the version number? +nix-env --version | grep "$version" + +# Usage errors. +expect 1 nix-env --foo 2>&1 | grep "no operation" +expect 1 nix-env -q --foo 2>&1 | grep "unknown flag" + +# Eval Errors. +eval_arg_res=$(nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 || true) +echo $eval_arg_res | grep "at «string»:1:15:" +echo $eval_arg_res | grep "infinite recursion encountered" + +eval_stdin_res=$(echo 'let a = {} // a; in a.foo' | nix-instantiate --eval -E - 2>&1 || true) +echo $eval_stdin_res | grep "at «stdin»:1:15:" +echo $eval_stdin_res | grep "infinite recursion encountered" + +# Attribute path errors +expectStderr 1 nix-instantiate --eval -E '{}' -A '"x' | grepQuiet "missing closing quote in selection path" +expectStderr 1 nix-instantiate --eval -E '[]' -A 'x' | grepQuiet "should be a set" +expectStderr 1 nix-instantiate --eval -E '{}' -A '1' | grepQuiet "should be a list" +expectStderr 1 nix-instantiate --eval -E '{}' -A '.' | grepQuiet "empty attribute name" +expectStderr 1 nix-instantiate --eval -E '[]' -A '1' | grepQuiet "out of range" diff --git a/tests/functional/multiple-outputs.nix b/tests/functional/multiple-outputs.nix new file mode 100644 index 000000000..413d392e4 --- /dev/null +++ b/tests/functional/multiple-outputs.nix @@ -0,0 +1,130 @@ +with import ./config.nix; + +rec { + + # Want to ensure that "out" doesn't get a suffix on it's path. + nameCheck = mkDerivation { + name = "multiple-outputs-a"; + outputs = [ "out" "dev" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $first $second + test -z $all + echo "first" > $first/file + echo "second" > $second/file + ln -s $first $second/link + ''; + helloString = "Hello, world!"; + }; + + a = mkDerivation { + name = "multiple-outputs-a"; + outputs = [ "first" "second" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $first $second + test -z $all + echo "first" > $first/file + echo "second" > $second/file + ln -s $first $second/link + ''; + helloString = "Hello, world!"; + }; + + use-a = mkDerivation { + name = "use-a"; + inherit (a) first second; + builder = builtins.toFile "builder.sh" + '' + cat $first/file $second/file >$out + ''; + }; + + b = mkDerivation { + defaultOutput = assert a.second.helloString == "Hello, world!"; a; + firstOutput = assert a.outputName == "first"; a.first.first; + secondOutput = assert a.second.outputName == "second"; a.second.first.first.second.second.first.second; + allOutputs = a.all; + name = "multiple-outputs-b"; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + test "$firstOutput $secondOutput" = "$allOutputs" + test "$defaultOutput" = "$firstOutput" + test "$(cat $firstOutput/file)" = "first" + test "$(cat $secondOutput/file)" = "second" + echo "success" > $out/file + ''; + }; + + c = mkDerivation { + name = "multiple-outputs-c"; + drv = b.drvPath; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + ln -s $drv $out/drv + ''; + }; + + d = mkDerivation { + name = "multiple-outputs-d"; + drv = builtins.unsafeDiscardOutputDependency b.drvPath; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + echo $drv > $out/drv + ''; + }; + + cyclic = (mkDerivation { + name = "cyclic-outputs"; + outputs = [ "a" "b" "c" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $a $b $c + echo $a > $b/foo + echo $b > $c/bar + echo $c > $a/baz + ''; + }).a; + + e = mkDerivation { + name = "multiple-outputs-e"; + outputs = [ "a_a" "b" "c" ]; + meta.outputsToInstall = [ "a_a" "b" ]; + buildCommand = "mkdir $a_a $b $c"; + }; + + independent = mkDerivation { + name = "multiple-outputs-independent"; + outputs = [ "first" "second" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir $first $second + test -z $all + echo "first" > $first/file + echo "second" > $second/file + ''; + }; + + use-independent = mkDerivation { + name = "use-independent"; + inherit (a) first second; + builder = builtins.toFile "builder.sh" + '' + cat $first/file $second/file >$out + ''; + }; + + invalid-output-name-1 = mkDerivation { + name = "invalid-output-name-1"; + outputs = [ "out/"]; + }; + + invalid-output-name-2 = mkDerivation { + name = "invalid-output-name-2"; + outputs = [ "x" "foo$"]; + }; + +} diff --git a/tests/functional/multiple-outputs.sh b/tests/functional/multiple-outputs.sh new file mode 100644 index 000000000..330600d08 --- /dev/null +++ b/tests/functional/multiple-outputs.sh @@ -0,0 +1,88 @@ +source common.sh + +clearStore + +rm -f $TEST_ROOT/result* + +# Test whether the output names match our expectations +outPath=$(nix-instantiate multiple-outputs.nix --eval -A nameCheck.out.outPath) +[ "$(echo "$outPath" | sed -E 's_^".*/[^-/]*-([^/]*)"$_\1_')" = "multiple-outputs-a" ] +outPath=$(nix-instantiate multiple-outputs.nix --eval -A nameCheck.dev.outPath) +[ "$(echo "$outPath" | sed -E 's_^".*/[^-/]*-([^/]*)"$_\1_')" = "multiple-outputs-a-dev" ] + +# Test whether read-only evaluation works when referring to the +# ‘drvPath’ attribute. +echo "evaluating c..." +#drvPath=$(nix-instantiate multiple-outputs.nix -A c --readonly-mode) + +# And check whether the resulting derivation explicitly depends on all +# outputs. +drvPath=$(nix-instantiate multiple-outputs.nix -A c) +#[ "$drvPath" = "$drvPath2" ] +grepQuiet 'multiple-outputs-a.drv",\["first","second"\]' $drvPath +grepQuiet 'multiple-outputs-b.drv",\["out"\]' $drvPath + +# While we're at it, test the ‘unsafeDiscardOutputDependency’ primop. +outPath=$(nix-build multiple-outputs.nix -A d --no-out-link) +drvPath=$(cat $outPath/drv) +outPath=$(nix-store -q $drvPath) +(! [ -e "$outPath" ]) + +# Do a build of something that depends on a derivation with multiple +# outputs. +echo "building b..." +outPath=$(nix-build multiple-outputs.nix -A b --no-out-link) +echo "output path is $outPath" +[ "$(cat "$outPath"/file)" = "success" ] + +# Test nix-build on a derivation with multiple outputs. +outPath1=$(nix-build multiple-outputs.nix -A a -o $TEST_ROOT/result) +[ -e $TEST_ROOT/result-first ] +(! [ -e $TEST_ROOT/result-second ]) +nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result +[ "$(cat $TEST_ROOT/result-first/file)" = "first" ] +[ "$(cat $TEST_ROOT/result-second/file)" = "second" ] +[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ] +hash1=$(nix-store -q --hash $TEST_ROOT/result-second) + +outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a) --no-out-link) +[[ $outPath1 = $outPath2 ]] + +outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a.first) --no-out-link) +[[ $outPath1 = $outPath2 ]] + +outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a.second) --no-out-link) +[[ $(cat $outPath2/file) = second ]] + +[[ $(nix-build $(nix-instantiate multiple-outputs.nix -A a.all) --no-out-link | wc -l) -eq 2 ]] + +# Delete one of the outputs and rebuild it. This will cause a hash +# rewrite. +env -u NIX_REMOTE nix store delete $TEST_ROOT/result-second --ignore-liveness +nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result +[ "$(cat $TEST_ROOT/result-second/file)" = "second" ] +[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ] +hash2=$(nix-store -q --hash $TEST_ROOT/result-second) +[ "$hash1" = "$hash2" ] + +# Make sure that nix-build works on derivations with multiple outputs. +echo "building a.first..." +nix-build multiple-outputs.nix -A a.first --no-out-link + +# Cyclic outputs should be rejected. +echo "building cyclic..." +if nix-build multiple-outputs.nix -A cyclic --no-out-link; then + echo "Cyclic outputs incorrectly accepted!" + exit 1 +fi + +# Do a GC. This should leave an empty store. +echo "collecting garbage..." +rm $TEST_ROOT/result* +nix-store --gc --keep-derivations --keep-outputs +nix-store --gc --print-roots +rm -rf $NIX_STORE_DIR/.links +rmdir $NIX_STORE_DIR + +expect 1 nix build -f multiple-outputs.nix invalid-output-name-1 2>&1 | grep 'contains illegal character' +expect 1 nix build -f multiple-outputs.nix invalid-output-name-2 2>&1 | grep 'contains illegal character' diff --git a/tests/functional/nar-access.nix b/tests/functional/nar-access.nix new file mode 100644 index 000000000..0e2a7f721 --- /dev/null +++ b/tests/functional/nar-access.nix @@ -0,0 +1,23 @@ +with import ./config.nix; + +rec { + a = mkDerivation { + name = "nar-index-a"; + builder = builtins.toFile "builder.sh" + '' + mkdir $out + mkdir $out/foo + touch $out/foo-x + touch $out/foo/bar + touch $out/foo/baz + touch $out/qux + mkdir $out/zyx + + cat >$out/foo/data <<EOF + lasjdöaxnasd +asdom 12398 +ä"§Æẞ¢«»”alsd +EOF + ''; + }; +}
\ No newline at end of file diff --git a/tests/functional/nar-access.sh b/tests/functional/nar-access.sh new file mode 100644 index 000000000..d487d58d2 --- /dev/null +++ b/tests/functional/nar-access.sh @@ -0,0 +1,56 @@ +source common.sh + +echo "building test path" +storePath="$(nix-build nar-access.nix -A a --no-out-link)" + +cd "$TEST_ROOT" + +# Dump path to nar. +narFile="$TEST_ROOT/path.nar" +nix-store --dump $storePath > $narFile + +# Check that find and nar ls match. +( cd $storePath; find . | sort ) > files.find +nix nar ls -R -d $narFile "" | sort > files.ls-nar +diff -u files.find files.ls-nar + +# Check that file contents of data match. +nix nar cat $narFile /foo/data > data.cat-nar +diff -u data.cat-nar $storePath/foo/data + +# Check that file contents of baz match. +nix nar cat $narFile /foo/baz > baz.cat-nar +diff -u baz.cat-nar $storePath/foo/baz + +nix store cat $storePath/foo/baz > baz.cat-nar +diff -u baz.cat-nar $storePath/foo/baz + +# Test --json. +diff -u \ + <(nix nar ls --json $narFile / | jq -S) \ + <(echo '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' | jq -S) +diff -u \ + <(nix nar ls --json -R $narFile /foo | jq -S) \ + <(echo '{"type":"directory","entries":{"bar":{"type":"regular","size":0,"narOffset":368},"baz":{"type":"regular","size":0,"narOffset":552},"data":{"type":"regular","size":58,"narOffset":736}}}' | jq -S) +diff -u \ + <(nix nar ls --json -R $narFile /foo/bar | jq -S) \ + <(echo '{"type":"regular","size":0,"narOffset":368}' | jq -S) +diff -u \ + <(nix store ls --json $storePath | jq -S) \ + <(echo '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' | jq -S) +diff -u \ + <(nix store ls --json -R $storePath/foo | jq -S) \ + <(echo '{"type":"directory","entries":{"bar":{"type":"regular","size":0},"baz":{"type":"regular","size":0},"data":{"type":"regular","size":58}}}' | jq -S) +diff -u \ + <(nix store ls --json -R $storePath/foo/bar| jq -S) \ + <(echo '{"type":"regular","size":0}' | jq -S) + +# Test missing files. +expect 1 nix store ls --json -R $storePath/xyzzy 2>&1 | grep 'does not exist in NAR' +expect 1 nix store ls $storePath/xyzzy 2>&1 | grep 'does not exist' + +# Test failure to dump. +if nix-store --dump $storePath >/dev/full ; then + echo "dumping to /dev/full should fail" + exit -1 +fi diff --git a/tests/functional/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh new file mode 100644 index 000000000..61fe043c6 --- /dev/null +++ b/tests/functional/nested-sandboxing.sh @@ -0,0 +1,11 @@ +source common.sh +# This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing. +[[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported" + +requireSandboxSupport + +source ./nested-sandboxing/command.sh + +expectStderr 100 runNixBuild badStoreUrl 2 | grepQuiet '`sandbox-build-dir` must not contain' + +runNixBuild goodStoreUrl 5 diff --git a/tests/functional/nested-sandboxing/command.sh b/tests/functional/nested-sandboxing/command.sh new file mode 100644 index 000000000..69366486c --- /dev/null +++ b/tests/functional/nested-sandboxing/command.sh @@ -0,0 +1,29 @@ +export NIX_BIN_DIR=$(dirname $(type -p nix)) +# TODO Get Nix and its closure more flexibly +export EXTRA_SANDBOX="/nix/store $(dirname $NIX_BIN_DIR)" + +badStoreUrl () { + local altitude=$1 + echo $TEST_ROOT/store-$altitude +} + +goodStoreUrl () { + local altitude=$1 + echo $("badStoreUrl" "$altitude")?store=/foo-$altitude +} + +# The non-standard sandbox-build-dir helps ensure that we get the same behavior +# whether this test is being run in a derivation as part of the nix build or +# being manually run by a developer outside a derivation +runNixBuild () { + local storeFun=$1 + local altitude=$2 + nix-build \ + --no-substitute --no-out-link \ + --store "$("$storeFun" "$altitude")" \ + --extra-sandbox-paths "$EXTRA_SANDBOX" \ + ./nested-sandboxing/runner.nix \ + --arg altitude "$((altitude - 1))" \ + --argstr storeFun "$storeFun" \ + --sandbox-build-dir /build-non-standard +} diff --git a/tests/functional/nested-sandboxing/runner.nix b/tests/functional/nested-sandboxing/runner.nix new file mode 100644 index 000000000..9a5822c88 --- /dev/null +++ b/tests/functional/nested-sandboxing/runner.nix @@ -0,0 +1,24 @@ +{ altitude, storeFun }: + +with import ../config.nix; + +mkDerivation { + name = "nested-sandboxing"; + busybox = builtins.getEnv "busybox"; + EXTRA_SANDBOX = builtins.getEnv "EXTRA_SANDBOX"; + buildCommand = if altitude == 0 then '' + echo Deep enough! > $out + '' else '' + cp -r ${../common} ./common + cp ${../common.sh} ./common.sh + cp ${../config.nix} ./config.nix + cp -r ${./.} ./nested-sandboxing + + export PATH=${builtins.getEnv "NIX_BIN_DIR"}:$PATH + + source common.sh + source ./nested-sandboxing/command.sh + + runNixBuild ${storeFun} ${toString altitude} >> $out + ''; +} diff --git a/tests/functional/nix-build-examples.nix b/tests/functional/nix-build-examples.nix new file mode 100644 index 000000000..e54dbbf62 --- /dev/null +++ b/tests/functional/nix-build-examples.nix @@ -0,0 +1,33 @@ +with import ./config.nix; + +rec { + + input0 = mkDerivation { + name = "dependencies-input-0"; + buildCommand = "mkdir $out; echo foo > $out/bar"; + }; + + input1 = mkDerivation { + name = "dependencies-input-1"; + buildCommand = "mkdir $out; echo FOO > $out/foo"; + }; + + input2 = mkDerivation { + name = "dependencies-input-2"; + buildCommand = '' + mkdir $out + echo BAR > $out/bar + echo ${input0} > $out/input0 + ''; + }; + + body = mkDerivation { + name = "dependencies-top"; + builder = ./dependencies.builder0.sh + "/FOOBAR/../."; + input1 = input1 + "/."; + input2 = "${input2}/."; + input1_drv = input1; + meta.description = "Random test package"; + }; + +} diff --git a/tests/functional/nix-build.sh b/tests/functional/nix-build.sh new file mode 100644 index 000000000..44a5a14cd --- /dev/null +++ b/tests/functional/nix-build.sh @@ -0,0 +1,43 @@ +source common.sh + +clearStore + +outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result) +test "$(cat $TEST_ROOT/result/foobar)" = FOOBAR + +# The result should be retained by a GC. +echo A +target=$(readLink $TEST_ROOT/result) +echo B +echo target is $target +nix-store --gc +test -e $target/foobar + +# But now it should be gone. +rm $TEST_ROOT/result +nix-store --gc +if test -e $target/foobar; then false; fi + +outPath2=$(nix-build $(nix-instantiate dependencies.nix) --no-out-link) +[[ $outPath = $outPath2 ]] + +outPath2=$(nix-build $(nix-instantiate dependencies.nix)!out --no-out-link) +[[ $outPath = $outPath2 ]] + +outPath2=$(nix-store -r $(nix-instantiate --add-root $TEST_ROOT/indirect dependencies.nix)!out) +[[ $outPath = $outPath2 ]] + +# The order of the paths on stdout must correspond to the -A options +# https://github.com/NixOS/nix/issues/4197 + +input0="$(nix-build nix-build-examples.nix -A input0 --no-out-link)" +input1="$(nix-build nix-build-examples.nix -A input1 --no-out-link)" +input2="$(nix-build nix-build-examples.nix -A input2 --no-out-link)" +body="$(nix-build nix-build-examples.nix -A body --no-out-link)" + +outPathsA="$(echo $(nix-build nix-build-examples.nix -A input0 -A input1 -A input2 -A body --no-out-link))" +[[ "$outPathsA" = "$input0 $input1 $input2 $body" ]] + +# test a different ordering to make sure it fails, not just in 23 out of 24 permutations +outPathsB="$(echo $(nix-build nix-build-examples.nix -A body -A input1 -A input2 -A input0 --no-out-link))" +[[ "$outPathsB" = "$body $input1 $input2 $input0" ]] diff --git a/tests/functional/nix-channel.sh b/tests/functional/nix-channel.sh new file mode 100644 index 000000000..b5d935004 --- /dev/null +++ b/tests/functional/nix-channel.sh @@ -0,0 +1,66 @@ +source common.sh + +clearProfiles + +rm -f $TEST_HOME/.nix-channels $TEST_HOME/.nix-profile + +# Test add/list/remove. +nix-channel --add http://foo/bar xyzzy +nix-channel --list | grepQuiet http://foo/bar +nix-channel --remove xyzzy +[[ $(nix-channel --list-generations | wc -l) == 1 ]] + +[ -e $TEST_HOME/.nix-channels ] +[ "$(cat $TEST_HOME/.nix-channels)" = '' ] + +# Test the XDG Base Directories support + +export NIX_CONFIG="use-xdg-base-directories = true" + +nix-channel --add http://foo/bar xyzzy +nix-channel --list | grepQuiet http://foo/bar +nix-channel --remove xyzzy + +unset NIX_CONFIG + +[ -e $TEST_HOME/.local/state/nix/channels ] +[ "$(cat $TEST_HOME/.local/state/nix/channels)" = '' ] + +# Create a channel. +rm -rf $TEST_ROOT/foo +mkdir -p $TEST_ROOT/foo +nix copy --to file://$TEST_ROOT/foo?compression="bzip2" $(nix-store -r $(nix-instantiate dependencies.nix)) +rm -rf $TEST_ROOT/nixexprs +mkdir -p $TEST_ROOT/nixexprs +cp config.nix dependencies.nix dependencies.builder*.sh $TEST_ROOT/nixexprs/ +ln -s dependencies.nix $TEST_ROOT/nixexprs/default.nix +(cd $TEST_ROOT && tar cvf - nixexprs) | bzip2 > $TEST_ROOT/foo/nixexprs.tar.bz2 + +# Test the update action. +nix-channel --add file://$TEST_ROOT/foo +nix-channel --update +[[ $(nix-channel --list-generations | wc -l) == 2 ]] + +# Do a query. +nix-env -qa \* --meta --xml --out-path > $TEST_ROOT/meta.xml +grepQuiet 'meta.*description.*Random test package' $TEST_ROOT/meta.xml +grepQuiet 'item.*attrPath="foo".*name="dependencies-top"' $TEST_ROOT/meta.xml + +# Do an install. +nix-env -i dependencies-top +[ -e $TEST_HOME/.nix-profile/foobar ] + +# Test updating from a tarball +nix-channel --add file://$TEST_ROOT/foo/nixexprs.tar.bz2 bar +nix-channel --update + +# Do a query. +nix-env -qa \* --meta --xml --out-path > $TEST_ROOT/meta.xml +grepQuiet 'meta.*description.*Random test package' $TEST_ROOT/meta.xml +grepQuiet 'item.*attrPath="bar".*name="dependencies-top"' $TEST_ROOT/meta.xml +grepQuiet 'item.*attrPath="foo".*name="dependencies-top"' $TEST_ROOT/meta.xml + +# Do an install. +nix-env -i dependencies-top +[ -e $TEST_HOME/.nix-profile/foobar ] + diff --git a/tests/functional/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh new file mode 100644 index 000000000..bf30f8938 --- /dev/null +++ b/tests/functional/nix-collect-garbage-d.sh @@ -0,0 +1,40 @@ +source common.sh + +clearStore + +## Test `nix-collect-garbage -d` + +# TODO make `nix-env` doesn't work with CA derivations, and make +# `ca/nix-collect-garbage-d.sh` wrapper. + +testCollectGarbageD () { + clearProfiles + # Run two `nix-env` commands, should create two generations of + # the profile + nix-env -f ./user-envs.nix -i foo-1.0 "$@" + nix-env -f ./user-envs.nix -i foo-2.0pre1 "$@" + [[ $(nix-env --list-generations "$@" | wc -l) -eq 2 ]] + + # Clear the profile history. There should be only one generation + # left + nix-collect-garbage -d + [[ $(nix-env --list-generations "$@" | wc -l) -eq 1 ]] +} + +testCollectGarbageD + +# Run the same test, but forcing the profiles an arbitrary location. +rm ~/.nix-profile +ln -s $TEST_ROOT/blah ~/.nix-profile +testCollectGarbageD + +# Run the same test, but forcing the profiles at their legacy location under +# /nix/var/nix. +# +# Note that we *don't* use the default profile; `nix-collect-garbage` will +# need to check the legacy conditional unconditionally not just follow +# `~/.nix-profile` to pass this test. +# +# Regression test for #8294 +rm ~/.nix-profile +testCollectGarbageD --profile "$NIX_STATE_DIR/profiles/per-user/me" diff --git a/tests/functional/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh new file mode 100644 index 000000000..45e53c9c0 --- /dev/null +++ b/tests/functional/nix-copy-ssh-ng.sh @@ -0,0 +1,18 @@ +source common.sh + +clearStore +clearCache + +remoteRoot=$TEST_ROOT/store2 +chmod -R u+w "$remoteRoot" || true +rm -rf "$remoteRoot" + +outPath=$(nix-build --no-out-link dependencies.nix) + +nix store ping --store "ssh-ng://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" + +# Regression test for https://github.com/NixOS/nix/issues/6253 +nix copy --to "ssh-ng://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath --no-check-sigs & +nix copy --to "ssh-ng://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath --no-check-sigs + +[ -f $remoteRoot$outPath/foobar ] diff --git a/tests/functional/nix-copy-ssh.sh b/tests/functional/nix-copy-ssh.sh new file mode 100644 index 000000000..eb801548d --- /dev/null +++ b/tests/functional/nix-copy-ssh.sh @@ -0,0 +1,20 @@ +source common.sh + +clearStore +clearCache + +remoteRoot=$TEST_ROOT/store2 +chmod -R u+w "$remoteRoot" || true +rm -rf "$remoteRoot" + +outPath=$(nix-build --no-out-link dependencies.nix) + +nix copy --to "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath + +[ -f $remoteRoot$outPath/foobar ] + +clearStore + +nix copy --no-check-sigs --from "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath + +[ -f $outPath/foobar ] diff --git a/tests/functional/nix-daemon-untrusting.sh b/tests/functional/nix-daemon-untrusting.sh new file mode 100755 index 000000000..bcdb70989 --- /dev/null +++ b/tests/functional/nix-daemon-untrusting.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +exec nix-daemon --force-untrusted "$@" diff --git a/tests/functional/nix-profile.sh b/tests/functional/nix-profile.sh new file mode 100644 index 000000000..7c478a0cd --- /dev/null +++ b/tests/functional/nix-profile.sh @@ -0,0 +1,187 @@ +source common.sh + +clearStore +clearProfiles + +enableFeatures "ca-derivations" +restartDaemon + +# Make a flake. +flake1Dir=$TEST_ROOT/flake1 +mkdir -p $flake1Dir + +cat > $flake1Dir/flake.nix <<EOF +{ + description = "Bla bla"; + + outputs = { self }: with import ./config.nix; rec { + packages.$system.default = mkDerivation { + name = "profile-test-\${builtins.readFile ./version}"; + outputs = [ "out" "man" "dev" ]; + builder = builtins.toFile "builder.sh" + '' + mkdir -p \$out/bin + cat > \$out/bin/hello <<EOF + #! ${shell} + echo Hello \${builtins.readFile ./who} + EOF + chmod +x \$out/bin/hello + echo DONE + mkdir -p \$man/share/man + mkdir -p \$dev/include + ''; + __contentAddressed = import ./ca.nix; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + meta.outputsToInstall = [ "out" "man" ]; + }; + }; +} +EOF + +printf World > $flake1Dir/who +printf 1.0 > $flake1Dir/version +printf false > $flake1Dir/ca.nix + +cp ./config.nix $flake1Dir/ + +# Test upgrading from nix-env. +nix-env -f ./user-envs.nix -i foo-1.0 +nix profile list | grep -A2 'Index:.*0' | grep 'Store paths:.*foo-1.0' +nix profile install $flake1Dir -L +nix profile list | grep -A4 'Index:.*1' | grep 'Locked flake URL:.*narHash' +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] +[ -e $TEST_HOME/.nix-profile/share/man ] +(! [ -e $TEST_HOME/.nix-profile/include ]) +nix profile history +nix profile history | grep "packages.$system.default: ∅ -> 1.0" +nix profile diff-closures | grep 'env-manifest.nix: ε → ∅' + +# Test XDG Base Directories support + +export NIX_CONFIG="use-xdg-base-directories = true" +nix profile remove 1 +nix profile install $flake1Dir +[[ $($TEST_HOME/.local/state/nix/profile/bin/hello) = "Hello World" ]] +unset NIX_CONFIG + +# Test upgrading a package. +printf NixOS > $flake1Dir/who +printf 2.0 > $flake1Dir/version +nix profile upgrade 1 +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello NixOS" ]] +nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 2.0, 2.0-man" + +# Test 'history', 'diff-closures'. +nix profile diff-closures + +# Test rollback. +nix profile rollback +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] + +# Test uninstall. +[ -e $TEST_HOME/.nix-profile/bin/foo ] +nix profile remove 0 +(! [ -e $TEST_HOME/.nix-profile/bin/foo ]) +nix profile history | grep 'foo: 1.0 -> ∅' +nix profile diff-closures | grep 'Version 3 -> 4' + +# Test installing a non-flake package. +nix profile install --file ./simple.nix '' +[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]] +nix profile remove 1 +nix profile install $(nix-build --no-out-link ./simple.nix) +[[ $(cat $TEST_HOME/.nix-profile/hello) = "Hello World!" ]] + +# Test wipe-history. +nix profile wipe-history +[[ $(nix profile history | grep Version | wc -l) -eq 1 ]] + +# Test upgrade to CA package. +printf true > $flake1Dir/ca.nix +printf 3.0 > $flake1Dir/version +nix profile upgrade 0 +nix profile history | grep "packages.$system.default: 1.0, 1.0-man -> 3.0, 3.0-man" + +# Test new install of CA package. +nix profile remove 0 +printf 4.0 > $flake1Dir/version +printf Utrecht > $flake1Dir/who +nix profile install $flake1Dir +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]] +[[ $(nix path-info --json $(realpath $TEST_HOME/.nix-profile/bin/hello) | jq -r .[].ca) =~ fixed:r:sha256: ]] + +# Override the outputs. +nix profile remove 0 1 +nix profile install "$flake1Dir^*" +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Utrecht" ]] +[ -e $TEST_HOME/.nix-profile/share/man ] +[ -e $TEST_HOME/.nix-profile/include ] + +printf Nix > $flake1Dir/who +nix profile upgrade 0 +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello Nix" ]] +[ -e $TEST_HOME/.nix-profile/share/man ] +[ -e $TEST_HOME/.nix-profile/include ] + +nix profile remove 0 +nix profile install "$flake1Dir^man" +(! [ -e $TEST_HOME/.nix-profile/bin/hello ]) +[ -e $TEST_HOME/.nix-profile/share/man ] +(! [ -e $TEST_HOME/.nix-profile/include ]) + +# test priority +nix profile remove 0 + +# Make another flake. +flake2Dir=$TEST_ROOT/flake2 +printf World > $flake1Dir/who +cp -r $flake1Dir $flake2Dir +printf World2 > $flake2Dir/who + +nix profile install $flake1Dir +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] +expect 1 nix profile install $flake2Dir +diff -u <( + nix --offline profile install $flake2Dir 2>&1 1> /dev/null \ + | grep -vE "^warning: " \ + | grep -vE "^error \(ignored\): " \ + || true +) <(cat << EOF +error: An existing package already provides the following file: + + $(nix build --no-link --print-out-paths ${flake1Dir}"#default.out")/bin/hello + + This is the conflicting file from the new package: + + $(nix build --no-link --print-out-paths ${flake2Dir}"#default.out")/bin/hello + + To remove the existing package: + + nix profile remove path:${flake1Dir}#packages.${system}.default + + The new package can also be installed next to the existing one by assigning a different priority. + The conflicting packages have a priority of 5. + To prioritise the new package: + + nix profile install path:${flake2Dir}#packages.${system}.default --priority 4 + + To prioritise the existing package: + + nix profile install path:${flake2Dir}#packages.${system}.default --priority 6 +EOF +) +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] +nix profile install $flake2Dir --priority 100 +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] +nix profile install $flake2Dir --priority 0 +[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World2" ]] +# nix profile install $flake1Dir --priority 100 +# [[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]] + +# Ensure that conflicts are handled properly even when the installables aren't +# flake references. +# Regression test for https://github.com/NixOS/nix/issues/8284 +clearProfiles +nix profile install $(nix build $flake1Dir --no-link --print-out-paths) +expect 1 nix profile install --impure --expr "(builtins.getFlake ''$flake2Dir'').packages.$system.default" diff --git a/tests/functional/nix-shell.sh b/tests/functional/nix-shell.sh new file mode 100644 index 000000000..edaa1249b --- /dev/null +++ b/tests/functional/nix-shell.sh @@ -0,0 +1,147 @@ +source common.sh + +clearStore + +if [[ -n ${CONTENT_ADDRESSED:-} ]]; then + shellDotNix="$PWD/ca-shell.nix" +else + shellDotNix="$PWD/shell.nix" +fi + +export NIX_PATH=nixpkgs="$shellDotNix" + +# Test nix-shell -A +export IMPURE_VAR=foo +export SELECTED_IMPURE_VAR=baz + +output=$(nix-shell --pure "$shellDotNix" -A shellDrv --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') + +[ "$output" = " - foo - bar - true" ] + +# Test --keep +output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR "$shellDotNix" -A shellDrv --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"') + +[ "$output" = " - foo - bar - baz" ] + +# Test nix-shell on a .drv +[[ $(nix-shell --pure $(nix-instantiate "$shellDotNix" -A shellDrv) --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') = " - foo - bar - false" ]] + +[[ $(nix-shell --pure $(nix-instantiate "$shellDotNix" -A shellDrv) --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $TEST_inNixShell"') = " - foo - bar - false" ]] + +# Test nix-shell on a .drv symlink + +# Legacy: absolute path and .drv extension required +nix-instantiate "$shellDotNix" -A shellDrv --add-root $TEST_ROOT/shell.drv +[[ $(nix-shell --pure $TEST_ROOT/shell.drv --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]] + +# New behaviour: just needs to resolve to a derivation in the store +nix-instantiate "$shellDotNix" -A shellDrv --add-root $TEST_ROOT/shell +[[ $(nix-shell --pure $TEST_ROOT/shell --run \ + 'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]] + +# Test nix-shell -p +output=$(NIX_PATH=nixpkgs="$shellDotNix" nix-shell --pure -p foo bar --run 'echo "$(foo) $(bar)"') +[ "$output" = "foo bar" ] + +# Test nix-shell -p --arg x y +output=$(NIX_PATH=nixpkgs="$shellDotNix" nix-shell --pure -p foo --argstr fooContents baz --run 'echo "$(foo)"') +[ "$output" = "baz" ] + +# Test nix-shell shebang mode +sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/shell.shebang.sh +chmod a+rx $TEST_ROOT/shell.shebang.sh + +output=$($TEST_ROOT/shell.shebang.sh abc def) +[ "$output" = "foo bar abc def" ] + +# Test nix-shell shebang mode again with metacharacters in the filename. +# First word of filename is chosen to not match any file in the test root. +sed -e "s|@ENV_PROG@|$(type -P env)|" shell.shebang.sh > $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh +chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.sh + +output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.sh abc def) +[ "$output" = "foo bar abc def" ] + +# Test nix-shell shebang mode for ruby +# This uses a fake interpreter that returns the arguments passed +# This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected. +sed -e "s|@SHELL_PROG@|$(type -P nix-shell)|" shell.shebang.rb > $TEST_ROOT/shell.shebang.rb +chmod a+rx $TEST_ROOT/shell.shebang.rb + +output=$($TEST_ROOT/shell.shebang.rb abc ruby) +[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/shell.shebang.rb abc ruby' ] + +# Test nix-shell shebang mode for ruby again with metacharacters in the filename. +# Note: fake interpreter only space-separates args without adding escapes to its output. +sed -e "s|@SHELL_PROG@|$(type -P nix-shell)|" shell.shebang.rb > $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb +chmod a+rx $TEST_ROOT/spaced\ \\\'\"shell.shebang.rb + +output=$($TEST_ROOT/spaced\ \\\'\"shell.shebang.rb abc ruby) +[ "$output" = '-e load(ARGV.shift) -- '"$TEST_ROOT"'/spaced \'\''"shell.shebang.rb abc ruby' ] + +# Test 'nix develop'. +nix develop -f "$shellDotNix" shellDrv -c bash -c '[[ -n $stdenv ]]' + +# Ensure `nix develop -c` preserves stdin +echo foo | nix develop -f "$shellDotNix" shellDrv -c cat | grepQuiet foo + +# Ensure `nix develop -c` actually executes the command if stdout isn't a terminal +nix develop -f "$shellDotNix" shellDrv -c echo foo |& grepQuiet foo + +# Test 'nix print-dev-env'. + +nix print-dev-env -f "$shellDotNix" shellDrv > $TEST_ROOT/dev-env.sh +nix print-dev-env -f "$shellDotNix" shellDrv --json > $TEST_ROOT/dev-env.json + +# Test with raw drv + +shellDrv=$(nix-instantiate "$shellDotNix" -A shellDrv.out) + +nix develop $shellDrv -c bash -c '[[ -n $stdenv ]]' + +nix print-dev-env $shellDrv > $TEST_ROOT/dev-env2.sh +nix print-dev-env $shellDrv --json > $TEST_ROOT/dev-env2.json + +diff $TEST_ROOT/dev-env{,2}.sh +diff $TEST_ROOT/dev-env{,2}.json + +# Ensure `nix print-dev-env --json` contains variable assignments. +[[ $(jq -r .variables.arr1.value[2] $TEST_ROOT/dev-env.json) = '3 4' ]] + +# Run tests involving `source <(nix print-dev-inv)` in subshells to avoid modifying the current +# environment. + +set +u # FIXME: Make print-dev-env `set -u` compliant (issue #7951) + +# Ensure `source <(nix print-dev-env)` modifies the environment. +( + path=$PATH + source $TEST_ROOT/dev-env.sh + [[ -n $stdenv ]] + [[ ${arr1[2]} = "3 4" ]] + [[ ${arr2[1]} = $'\n' ]] + [[ ${arr2[2]} = $'x\ny' ]] + [[ $(fun) = blabla ]] + [[ $PATH = $(jq -r .variables.PATH.value $TEST_ROOT/dev-env.json):$path ]] +) + +# Ensure `source <(nix print-dev-env)` handles the case when PATH is empty. +( + path=$PATH + PATH= + source $TEST_ROOT/dev-env.sh + [[ $PATH = $(PATH=$path jq -r .variables.PATH.value $TEST_ROOT/dev-env.json) ]] +) + +# Test nix-shell with ellipsis and no `inNixShell` argument (for backwards compat with old nixpkgs) +cat >$TEST_ROOT/shell-ellipsis.nix <<EOF +{ system ? "x86_64-linux", ... }@args: +assert (!(args ? inNixShell)); +(import $shellDotNix { }).shellDrv +EOF +nix-shell $TEST_ROOT/shell-ellipsis.nix --run "true" diff --git a/tests/functional/nix_path.sh b/tests/functional/nix_path.sh new file mode 100644 index 000000000..2b222b4a1 --- /dev/null +++ b/tests/functional/nix_path.sh @@ -0,0 +1,14 @@ +# Regression for https://github.com/NixOS/nix/issues/5998 and https://github.com/NixOS/nix/issues/5980 + +source common.sh + +export NIX_PATH=non-existent=/non-existent/but-unused-anyways:by-absolute-path=$PWD:by-relative-path=. + +nix-instantiate --eval -E '<by-absolute-path/simple.nix>' --restrict-eval +nix-instantiate --eval -E '<by-relative-path/simple.nix>' --restrict-eval + +# Should ideally also test this, but there’s no pure way to do it, so just trust me that it works +# nix-instantiate --eval -E '<nixpkgs>' -I nixpkgs=channel:nixos-unstable --restrict-eval + +[[ $(nix-instantiate --find-file by-absolute-path/simple.nix) = $PWD/simple.nix ]] +[[ $(nix-instantiate --find-file by-relative-path/simple.nix) = $PWD/simple.nix ]] diff --git a/tests/functional/optimise-store.sh b/tests/functional/optimise-store.sh new file mode 100644 index 000000000..8c2d05cd5 --- /dev/null +++ b/tests/functional/optimise-store.sh @@ -0,0 +1,44 @@ +source common.sh + +clearStore + +outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) +outPath2=$(echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store) + +inode1="$(stat --format=%i $outPath1/foo)" +inode2="$(stat --format=%i $outPath2/foo)" +if [ "$inode1" != "$inode2" ]; then + echo "inodes do not match" + exit 1 +fi + +nlink="$(stat --format=%h $outPath1/foo)" +if [ "$nlink" != 3 ]; then + echo "link count incorrect" + exit 1 +fi + +outPath3=$(echo 'with import ./config.nix; mkDerivation { name = "foo3"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link) + +inode3="$(stat --format=%i $outPath3/foo)" +if [ "$inode1" = "$inode3" ]; then + echo "inodes match unexpectedly" + exit 1 +fi + +# XXX: This should work through the daemon too +NIX_REMOTE="" nix-store --optimise + +inode1="$(stat --format=%i $outPath1/foo)" +inode3="$(stat --format=%i $outPath3/foo)" +if [ "$inode1" != "$inode3" ]; then + echo "inodes do not match" + exit 1 +fi + +nix-store --gc + +if [ -n "$(ls $NIX_STORE_DIR/.links)" ]; then + echo ".links directory not empty after GC" + exit 1 +fi diff --git a/tests/functional/output-normalization.sh b/tests/functional/output-normalization.sh new file mode 100644 index 000000000..0f6df5e31 --- /dev/null +++ b/tests/functional/output-normalization.sh @@ -0,0 +1,9 @@ +source common.sh + +testNormalization () { + clearStore + outPath=$(nix-build ./simple.nix --no-out-link) + test "$(stat -c %Y $outPath)" -eq 1 +} + +testNormalization diff --git a/tests/functional/parallel.builder.sh b/tests/functional/parallel.builder.sh new file mode 100644 index 000000000..d092bc5a6 --- /dev/null +++ b/tests/functional/parallel.builder.sh @@ -0,0 +1,29 @@ +echo "DOING $text" + + +# increase counter +while ! ln -s x $shared.lock 2> /dev/null; do + sleep 1 +done +test -f $shared.cur || echo 0 > $shared.cur +test -f $shared.max || echo 0 > $shared.max +new=$(($(cat $shared.cur) + 1)) +if test $new -gt $(cat $shared.max); then + echo $new > $shared.max +fi +echo $new > $shared.cur +rm $shared.lock + + +echo -n $(cat $inputs)$text > $out + +sleep $sleepTime + + +# decrease counter +while ! ln -s x $shared.lock 2> /dev/null; do + sleep 1 +done +test -f $shared.cur || echo 0 > $shared.cur +echo $(($(cat $shared.cur) - 1)) > $shared.cur +rm $shared.lock diff --git a/tests/functional/parallel.nix b/tests/functional/parallel.nix new file mode 100644 index 000000000..23f142059 --- /dev/null +++ b/tests/functional/parallel.nix @@ -0,0 +1,19 @@ +{sleepTime ? 3}: + +with import ./config.nix; + +let + + mkDrv = text: inputs: mkDerivation { + name = "parallel"; + builder = ./parallel.builder.sh; + inherit text inputs shared sleepTime; + }; + + a = mkDrv "a" []; + b = mkDrv "b" [a]; + c = mkDrv "c" [a]; + d = mkDrv "d" [a]; + e = mkDrv "e" [b c d]; + +in e diff --git a/tests/functional/parallel.sh b/tests/functional/parallel.sh new file mode 100644 index 000000000..3b7bbe5a2 --- /dev/null +++ b/tests/functional/parallel.sh @@ -0,0 +1,56 @@ +source common.sh + + +# First, test that -jN performs builds in parallel. +echo "testing nix-build -j..." + +clearStore + +rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max + +outPath=$(nix-build -j10000 parallel.nix --no-out-link) + +echo "output path is $outPath" + +text=$(cat "$outPath") +if test "$text" != "abacade"; then exit 1; fi + +if test "$(cat $_NIX_TEST_SHARED.cur)" != 0; then fail "wrong current process count"; fi +if test "$(cat $_NIX_TEST_SHARED.max)" != 3; then fail "not enough parallelism"; fi + + +# Second, test that parallel invocations of nix-build perform builds +# in parallel, and don't block waiting on locks held by the others. +echo "testing multiple nix-build -j1..." + +clearStore + +rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max + +drvPath=$(nix-instantiate parallel.nix --argstr sleepTime 15) + +cmd="nix-store -j1 -r $drvPath" + +$cmd & +pid1=$! +echo "pid 1 is $pid1" + +$cmd & +pid2=$! +echo "pid 2 is $pid2" + +$cmd & +pid3=$! +echo "pid 3 is $pid3" + +$cmd & +pid4=$! +echo "pid 4 is $pid4" + +wait $pid1 || fail "instance 1 failed: $?" +wait $pid2 || fail "instance 2 failed: $?" +wait $pid3 || fail "instance 3 failed: $?" +wait $pid4 || fail "instance 4 failed: $?" + +if test "$(cat $_NIX_TEST_SHARED.cur)" != 0; then fail "wrong current process count"; fi +if test "$(cat $_NIX_TEST_SHARED.max)" != 3; then fail "not enough parallelism"; fi diff --git a/tests/functional/pass-as-file.sh b/tests/functional/pass-as-file.sh new file mode 100644 index 000000000..2c0bc5031 --- /dev/null +++ b/tests/functional/pass-as-file.sh @@ -0,0 +1,18 @@ +source common.sh + +clearStore + +outPath=$(nix-build --no-out-link -E " +with import ./config.nix; + +mkDerivation { + name = \"pass-as-file\"; + passAsFile = [ \"foo\" ]; + foo = [ \"xyzzy\" ]; + builder = builtins.toFile \"builder.sh\" '' + [ \"\$(basename \$fooPath)\" = .attr-1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89ic ] + [ \"\$(cat \$fooPath)\" = xyzzy ] + touch \$out + ''; +} +") diff --git a/tests/functional/path-from-hash-part.sh b/tests/functional/path-from-hash-part.sh new file mode 100644 index 000000000..bdd104434 --- /dev/null +++ b/tests/functional/path-from-hash-part.sh @@ -0,0 +1,10 @@ +source common.sh + +path=$(nix build --no-link --print-out-paths -f simple.nix) + +hash_part=$(basename $path) +hash_part=${hash_part:0:32} + +path2=$(nix store path-from-hash-part $hash_part) + +[[ $path = $path2 ]] diff --git a/tests/functional/path.nix b/tests/functional/path.nix new file mode 100644 index 000000000..883c3c41b --- /dev/null +++ b/tests/functional/path.nix @@ -0,0 +1,14 @@ +with import ./config.nix; + +mkDerivation { + name = "filter"; + builder = builtins.toFile "builder" "ln -s $input $out"; + input = + builtins.path { + path = ((builtins.getEnv "TEST_ROOT") + "/filterin"); + filter = path: type: + type != "symlink" + && baseNameOf path != "foo" + && !((import ./lang/lib.nix).hasSuffix ".bak" (baseNameOf path)); + }; +} diff --git a/tests/functional/placeholders.sh b/tests/functional/placeholders.sh new file mode 100644 index 000000000..cd1bb7bc2 --- /dev/null +++ b/tests/functional/placeholders.sh @@ -0,0 +1,20 @@ +source common.sh + +clearStore + +nix-build --no-out-link -E ' + with import ./config.nix; + + mkDerivation { + name = "placeholders"; + outputs = [ "out" "bin" "dev" ]; + buildCommand = " + echo foo1 > $out + echo foo2 > $bin + echo foo3 > $dev + [[ $(cat ${placeholder "out"}) = foo1 ]] + [[ $(cat ${placeholder "bin"}) = foo2 ]] + [[ $(cat ${placeholder "dev"}) = foo3 ]] + "; + } +' diff --git a/tests/functional/plugins.sh b/tests/functional/plugins.sh new file mode 100644 index 000000000..baf71a362 --- /dev/null +++ b/tests/functional/plugins.sh @@ -0,0 +1,9 @@ +source common.sh + +if [[ $BUILD_SHARED_LIBS != 1 ]]; then + skipTest "Plugins are not supported" +fi + +res=$(nix --option setting-set true --option plugin-files $PWD/plugins/libplugintest* eval --expr builtins.anotherNull) + +[ "$res"x = "nullx" ] diff --git a/tests/functional/plugins/local.mk b/tests/functional/plugins/local.mk new file mode 100644 index 000000000..40350aa96 --- /dev/null +++ b/tests/functional/plugins/local.mk @@ -0,0 +1,11 @@ +libraries += libplugintest + +libplugintest_DIR := $(d) + +libplugintest_SOURCES := $(d)/plugintest.cc + +libplugintest_ALLOW_UNDEFINED := 1 + +libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1 + +libplugintest_CXXFLAGS := -I src/libutil -I src/libstore -I src/libexpr -I src/libfetchers diff --git a/tests/functional/plugins/plugintest.cc b/tests/functional/plugins/plugintest.cc new file mode 100644 index 000000000..e02fd68d5 --- /dev/null +++ b/tests/functional/plugins/plugintest.cc @@ -0,0 +1,28 @@ +#include "config.hh" +#include "primops.hh" + +using namespace nix; + +struct MySettings : Config +{ + Setting<bool> settingSet{this, false, "setting-set", + "Whether the plugin-defined setting was set"}; +}; + +MySettings mySettings; + +static GlobalConfig::Register rs(&mySettings); + +static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args, Value & v) +{ + if (mySettings.settingSet) + v.mkNull(); + else + v.mkBool(false); +} + +static RegisterPrimOp rp({ + .name = "anotherNull", + .arity = 0, + .fun = prim_anotherNull, +}); diff --git a/tests/functional/post-hook.sh b/tests/functional/post-hook.sh new file mode 100644 index 000000000..752f8220c --- /dev/null +++ b/tests/functional/post-hook.sh @@ -0,0 +1,31 @@ +source common.sh + +clearStore + +rm -f $TEST_ROOT/result + +export REMOTE_STORE=file:$TEST_ROOT/remote_store +echo 'require-sigs = false' >> $NIX_CONF_DIR/nix.conf + +restartDaemon + +if isDaemonNewer "2.13"; then + pushToStore="$PWD/push-to-store.sh" +else + pushToStore="$PWD/push-to-store-old.sh" +fi + +# Build the dependencies and push them to the remote store. +nix-build -o $TEST_ROOT/result dependencies.nix --post-build-hook "$pushToStore" +# See if all outputs are passed to the post-build hook by only specifying one +# We're not able to test CA tests this way +export BUILD_HOOK_ONLY_OUT_PATHS=$([ ! $NIX_TESTS_CA_BY_DEFAULT ]) +nix-build -o $TEST_ROOT/result-mult multiple-outputs.nix -A a.first --post-build-hook "$pushToStore" + +clearStore + +# Ensure that the remote store contains both the runtime and build-time +# closure of what we've just built. +nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix +nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix input1_drv +nix copy --from "$REMOTE_STORE" --no-require-sigs -f multiple-outputs.nix a^second diff --git a/tests/functional/pure-eval.nix b/tests/functional/pure-eval.nix new file mode 100644 index 000000000..ed25b3d45 --- /dev/null +++ b/tests/functional/pure-eval.nix @@ -0,0 +1,3 @@ +{ + x = 123; +} diff --git a/tests/functional/pure-eval.sh b/tests/functional/pure-eval.sh new file mode 100644 index 000000000..5334bf28e --- /dev/null +++ b/tests/functional/pure-eval.sh @@ -0,0 +1,34 @@ +source common.sh + +clearStore + +nix eval --expr 'assert 1 + 2 == 3; true' + +[[ $(nix eval --impure --expr 'builtins.readFile ./pure-eval.sh') =~ clearStore ]] + +missingImpureErrorMsg=$(! nix eval --expr 'builtins.readFile ./pure-eval.sh' 2>&1) + +echo "$missingImpureErrorMsg" | grepQuiet -- --impure || \ + fail "The error message should mention the “--impure” flag to unblock users" + +[[ $(nix eval --expr 'builtins.pathExists ./pure-eval.sh') == false ]] || \ + fail "Calling 'pathExists' on a non-authorised path should return false" + +(! nix eval --expr builtins.currentTime) +(! nix eval --expr builtins.currentSystem) + +(! nix-instantiate --pure-eval ./simple.nix) + +[[ $(nix eval --impure --expr "(import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x") == 123 ]] +(! nix eval --expr "(import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x") +nix eval --expr "(import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; sha256 = \"$(nix hash file pure-eval.nix --type sha256)\"; })).x" + +rm -rf $TEST_ROOT/eval-out +nix eval --store dummy:// --write-to $TEST_ROOT/eval-out --expr '{ x = "foo" + "bar"; y = { z = "bla"; }; }' +[[ $(cat $TEST_ROOT/eval-out/x) = foobar ]] +[[ $(cat $TEST_ROOT/eval-out/y/z) = bla ]] + +rm -rf $TEST_ROOT/eval-out +(! nix eval --store dummy:// --write-to $TEST_ROOT/eval-out --expr '{ "." = "bla"; }') + +(! nix eval --expr '~/foo') diff --git a/tests/functional/push-to-store-old.sh b/tests/functional/push-to-store-old.sh new file mode 100755 index 000000000..4187958b2 --- /dev/null +++ b/tests/functional/push-to-store-old.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -x +set -e + +[ -n "$OUT_PATHS" ] +[ -n "$DRV_PATH" ] + +echo Pushing "$OUT_PATHS" to "$REMOTE_STORE" +if [ -n "$BUILD_HOOK_ONLY_OUT_PATHS" ]; then + printf "%s" "$OUT_PATHS" | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs +else + printf "%s" "$DRV_PATH" | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs +fi diff --git a/tests/functional/push-to-store.sh b/tests/functional/push-to-store.sh new file mode 100755 index 000000000..9e4e475e0 --- /dev/null +++ b/tests/functional/push-to-store.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -x +set -e + +[ -n "$OUT_PATHS" ] +[ -n "$DRV_PATH" ] + +echo Pushing "$OUT_PATHS" to "$REMOTE_STORE" +if [ -n "$BUILD_HOOK_ONLY_OUT_PATHS" ]; then + printf "%s" "$OUT_PATHS" | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs +else + printf "%s" "$DRV_PATH"^'*' | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs +fi diff --git a/tests/functional/read-only-store.sh b/tests/functional/read-only-store.sh new file mode 100644 index 000000000..d63920c19 --- /dev/null +++ b/tests/functional/read-only-store.sh @@ -0,0 +1,42 @@ +source common.sh + +enableFeatures "read-only-local-store" + +needLocalStore "cannot open store read-only when daemon has already opened it writeable" + +clearStore + +happy () { + # We can do a read-only query just fine with a read-only store + nix --store local?read-only=true path-info $dummyPath + + # We can "write" an already-present store-path a read-only store, because no IO is actually required + nix-store --store local?read-only=true --add dummy +} +## Testing read-only mode without forcing the underlying store to actually be read-only + +# Make sure the command fails when the store doesn't already have a database +expectStderr 1 nix-store --store local?read-only=true --add dummy | grepQuiet "database does not exist, and cannot be created in read-only mode" + +# Make sure the store actually has a current-database, with at least one store object +dummyPath=$(nix-store --add dummy) + +# Try again and make sure we fail when adding a item not already in the store +expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "attempt to write a readonly database" + +# Test a few operations that should work with the read-only store in its current state +happy + +## Testing read-only mode with an underlying store that is actually read-only + +# Ensure store is actually read-only +chmod -R -w $TEST_ROOT/store +chmod -R -w $TEST_ROOT/var + +# Make sure we fail on add operations on the read-only store +# This is only for adding files that are not *already* in the store +expectStderr 1 nix-store --add eval.nix | grepQuiet "error: opening lock file '$(readlink -e $TEST_ROOT)/var/nix/db/big-lock'" +expectStderr 1 nix-store --store local?read-only=true --add eval.nix | grepQuiet "Permission denied" + +# Test the same operations from before should again succeed +happy diff --git a/tests/functional/readfile-context.nix b/tests/functional/readfile-context.nix new file mode 100644 index 000000000..54cd1afd9 --- /dev/null +++ b/tests/functional/readfile-context.nix @@ -0,0 +1,28 @@ +with import ./config.nix; + +let + + input = import ./simple.nix; + + dependent = mkDerivation { + name = "dependent"; + buildCommand = '' + mkdir -p $out + echo -n "$input1" > "$out/file1" + echo -n "$input2" > "$out/file2" + ''; + input1 = "${input}/hello"; + input2 = "hello"; + }; + + readDependent = mkDerivation { + # Will evaluate correctly because file2 doesn't have any references, + # even though the `dependent` derivation does. + name = builtins.readFile (dependent + "/file2"); + buildCommand = '' + echo "$input" > "$out" + ''; + input = builtins.readFile (dependent + "/file1"); + }; + +in readDependent diff --git a/tests/functional/readfile-context.sh b/tests/functional/readfile-context.sh new file mode 100644 index 000000000..31e70ddb1 --- /dev/null +++ b/tests/functional/readfile-context.sh @@ -0,0 +1,16 @@ +source common.sh + +clearStore + +outPath=$(nix-build --no-out-link readfile-context.nix) + +# Set a GC root. +ln -s $outPath "$NIX_STATE_DIR"/gcroots/foo + +# Check that file exists. +[ "$(cat $(cat $outPath))" = "Hello World!" ] + +nix-collect-garbage + +# Check that file still exists. +[ "$(cat $(cat $outPath))" = "Hello World!" ] diff --git a/tests/functional/recursive.nix b/tests/functional/recursive.nix new file mode 100644 index 000000000..fa8cc04db --- /dev/null +++ b/tests/functional/recursive.nix @@ -0,0 +1,56 @@ +with import ./config.nix; + +mkDerivation rec { + name = "recursive"; + dummy = builtins.toFile "dummy" "bla bla"; + SHELL = shell; + + # Note: this is a string without context. + unreachable = builtins.getEnv "unreachable"; + + NIX_TESTS_CA_BY_DEFAULT = builtins.getEnv "NIX_TESTS_CA_BY_DEFAULT"; + + requiredSystemFeatures = [ "recursive-nix" ]; + + buildCommand = '' + mkdir $out + opts="--experimental-features nix-command ${if (NIX_TESTS_CA_BY_DEFAULT == "1") then "--extra-experimental-features ca-derivations" else ""}" + + PATH=${builtins.getEnv "NIX_BIN_DIR"}:$PATH + + # Check that we can query/build paths in our input closure. + nix $opts path-info $dummy + nix $opts build $dummy + + # Make sure we cannot query/build paths not in out input closure. + [[ -e $unreachable ]] + (! nix $opts path-info $unreachable) + (! nix $opts build $unreachable) + + # Add something to the store. + echo foobar > foobar + foobar=$(nix $opts store add-path ./foobar) + + nix $opts path-info $foobar + nix $opts build $foobar + + # Add it to our closure. + ln -s $foobar $out/foobar + + [[ $(nix $opts path-info --all | wc -l) -eq 4 ]] + + # Build a derivation. + nix $opts build -L --impure --expr ' + with import ${./config.nix}; + mkDerivation { + name = "inner1"; + buildCommand = "echo $fnord blaat > $out"; + fnord = builtins.toFile "fnord" "fnord"; + } + ' + + [[ $(nix $opts path-info --json ./result) =~ fnord ]] + + ln -s $(nix $opts path-info ./result) $out/inner1 + ''; +} diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh new file mode 100644 index 000000000..0bf00f8fa --- /dev/null +++ b/tests/functional/recursive.sh @@ -0,0 +1,19 @@ +source common.sh + +enableFeatures 'recursive-nix' +restartDaemon + +clearStore + +rm -f $TEST_ROOT/result + +export unreachable=$(nix store add-path ./recursive.sh) + +NIX_BIN_DIR=$(dirname $(type -p nix)) nix --extra-experimental-features 'nix-command recursive-nix' build -o $TEST_ROOT/result -L --impure --file ./recursive.nix + +[[ $(cat $TEST_ROOT/result/inner1) =~ blaat ]] + +# Make sure the recursively created paths are in the closure. +nix path-info -r $TEST_ROOT/result | grep foobar +nix path-info -r $TEST_ROOT/result | grep fnord +nix path-info -r $TEST_ROOT/result | grep inner1 diff --git a/tests/functional/referrers.sh b/tests/functional/referrers.sh new file mode 100644 index 000000000..81323c280 --- /dev/null +++ b/tests/functional/referrers.sh @@ -0,0 +1,38 @@ +source common.sh + +needLocalStore "uses some low-level store manipulations that aren’t available through the daemon" + +clearStore + +max=500 + +reference=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-bla +touch $reference +(echo $reference && echo && echo 0) | nix-store --register-validity + +echo "making registration..." + +set +x +for ((n = 0; n < $max; n++)); do + storePath=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-$n + echo -n > $storePath + ref2=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-$((n+1)) + if test $((n+1)) = $max; then + ref2=$reference + fi + echo $storePath; echo; echo 2; echo $reference; echo $ref2 +done > $TEST_ROOT/reg_info +set -x + +echo "registering..." + +nix-store --register-validity < $TEST_ROOT/reg_info + +echo "collecting garbage..." +ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref +nix-store --gc + +if [ -n "$(type -p sqlite3)" -a "$(sqlite3 $NIX_STATE_DIR/db/db.sqlite 'select count(*) from Refs')" -ne 0 ]; then + echo "referrers not cleaned up" + exit 1 +fi diff --git a/tests/functional/remote-store.sh b/tests/functional/remote-store.sh new file mode 100644 index 000000000..50e6f24b9 --- /dev/null +++ b/tests/functional/remote-store.sh @@ -0,0 +1,43 @@ +source common.sh + +clearStore + +# Ensure "fake ssh" remote store works just as legacy fake ssh would. +nix --store ssh-ng://localhost?remote-store=$TEST_ROOT/other-store doctor + +# Ensure that store ping trusted works with ssh-ng:// +nix --store ssh-ng://localhost?remote-store=$TEST_ROOT/other-store store ping --json | jq -e '.trusted' + +startDaemon + +if isDaemonNewer "2.15pre0"; then + # Ensure that ping works trusted with new daemon + nix store ping --json | jq -e '.trusted' +else + # And the the field is absent with the old daemon + nix store ping --json | jq -e 'has("trusted") | not' +fi + +# Test import-from-derivation through the daemon. +[[ $(nix eval --impure --raw --expr ' + with import ./config.nix; + import ( + mkDerivation { + name = "foo"; + bla = import ./dependencies.nix {}; + buildCommand = " + echo \\\"hi\\\" > $out + "; + } + ) +') = hi ]] + +storeCleared=1 NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs.sh + +nix-store --gc --max-freed 1K + +nix-store --dump-db > $TEST_ROOT/d1 +NIX_REMOTE= nix-store --dump-db > $TEST_ROOT/d2 +cmp $TEST_ROOT/d1 $TEST_ROOT/d2 + +killDaemon diff --git a/tests/functional/repair.sh b/tests/functional/repair.sh new file mode 100644 index 000000000..c8f07b1c6 --- /dev/null +++ b/tests/functional/repair.sh @@ -0,0 +1,123 @@ +source common.sh + +needLocalStore "--repair needs a local store" + +clearStore + +path=$(nix-build dependencies.nix -o $TEST_ROOT/result) +path2=$(nix-store -qR $path | grep input-2) + +nix-store --verify --check-contents -v + +hash=$(nix-hash $path2) + +# Corrupt a path and check whether nix-build --repair can fix it. +chmod u+w $path2 +touch $path2/bad + +(! nix-store --verify --check-contents -v) + +# The path can be repaired by rebuilding the derivation. +nix-store --verify --check-contents --repair + +(! [ -e $path2/bad ]) +(! [ -w $path2 ]) + +nix-store --verify-path $path2 + +# Re-corrupt and delete the deriver. Now --verify --repair should +# not work. +chmod u+w $path2 +touch $path2/bad + +nix-store --delete $(nix-store -q --referrers-closure $(nix-store -qd $path2)) + +(! nix-store --verify --check-contents --repair) + +nix-build dependencies.nix -o $TEST_ROOT/result --repair + +if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then + echo "path not repaired properly" >&2 + exit 1 +fi + +# Corrupt a path that has a substitute and check whether nix-store +# --verify can fix it. +clearCache + +nix copy --to file://$cacheDir $path + +chmod u+w $path2 +rm -rf $path2 + +nix-store --verify --check-contents --repair --substituters "file://$cacheDir" --no-require-sigs + +if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then + echo "path not repaired properly" >&2 + exit 1 +fi + +# Check --verify-path and --repair-path. +nix-store --verify-path $path2 + +chmod u+w $path2 +rm -rf $path2 + +if nix-store --verify-path $path2; then + echo "nix-store --verify-path succeeded unexpectedly" >&2 + exit 1 +fi + +nix-store --repair-path $path2 --substituters "file://$cacheDir" --no-require-sigs + +if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then + echo "path not repaired properly" >&2 + exit 1 +fi + +# Check that --repair-path also checks content of optimised symlinks (1/2) +nix-store --verify-path $path2 + +if (! nix-store --optimize); then + echo "nix-store --optimize failed to optimize the store" >&2 + exit 1 +fi +chmod u+w $path2/bar +echo 'rabrab' > $path2/bar # different length + +if nix-store --verify-path $path2; then + echo "nix-store --verify-path did not detect .links file corruption" >&2 + exit 1 +fi + +nix-store --repair-path $path2 --option auto-optimise-store true + +if [ "$(nix-hash $path2)" != "$hash" -o "BAR" != "$(< $path2/bar)" ]; then + echo "path not repaired properly" >&2 + exit 1 +fi + +# Check that --repair-path also checks content of optimised symlinks (2/2) +nix-store --verify-path $path2 + +if (! nix-store --optimize); then + echo "nix-store --optimize failed to optimize the store" >&2 + exit 1 +fi +chmod u+w $path2 +chmod u+w $path2/bar +sed -e 's/./X/g' < $path2/bar > $path2/tmp # same length, different content. +cp $path2/tmp $path2/bar +rm $path2/tmp + +if nix-store --verify-path $path2; then + echo "nix-store --verify-path did not detect .links file corruption" >&2 + exit 1 +fi + +nix-store --repair-path $path2 --substituters "file://$cacheDir" --no-require-sigs --option auto-optimise-store true + +if [ "$(nix-hash $path2)" != "$hash" -o "BAR" != "$(< $path2/bar)" ]; then + echo "path not repaired properly" >&2 + exit 1 +fi diff --git a/tests/functional/repl.sh b/tests/functional/repl.sh new file mode 100644 index 000000000..bb8b60e50 --- /dev/null +++ b/tests/functional/repl.sh @@ -0,0 +1,179 @@ +source common.sh + +testDir="$PWD" +cd "$TEST_ROOT" + +replCmds=" +simple = 1 +simple = import $testDir/simple.nix +:bl simple +:log simple +" + +replFailingCmds=" +failing = import $testDir/simple-failing.nix +:b failing +:log failing +" + +replUndefinedVariable=" +import $testDir/undefined-variable.nix +" + +testRepl () { + local nixArgs=("$@") + rm -rf repl-result-out || true # cleanup from other runs backed by a foreign nix store + local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replCmds")" + echo "$replOutput" + local outPath=$(echo "$replOutput" |& + grep -o -E "$NIX_STORE_DIR/\w*-simple") + nix path-info "${nixArgs[@]}" "$outPath" + [ "$(realpath ./repl-result-out)" == "$outPath" ] || fail "nix repl :bl doesn't make a symlink" + # run it again without checking the output to ensure the previously created symlink gets overwritten + nix repl "${nixArgs[@]}" <<< "$replCmds" || fail "nix repl does not work twice with the same inputs" + + # simple.nix prints a PATH during build + echo "$replOutput" | grepQuiet -s 'PATH=' || fail "nix repl :log doesn't output logs" + local replOutput="$(nix repl "${nixArgs[@]}" <<< "$replFailingCmds" 2>&1)" + echo "$replOutput" + echo "$replOutput" | grepQuiet -s 'This should fail' \ + || fail "nix repl :log doesn't output logs for a failed derivation" + local replOutput="$(nix repl --show-trace "${nixArgs[@]}" <<< "$replUndefinedVariable" 2>&1)" + echo "$replOutput" + echo "$replOutput" | grepQuiet -s "while evaluating the file" \ + || fail "nix repl --show-trace doesn't show the trace" + + nix repl "${nixArgs[@]}" --option pure-eval true 2>&1 <<< "builtins.currentSystem" \ + | grep "attribute 'currentSystem' missing" + nix repl "${nixArgs[@]}" 2>&1 <<< "builtins.currentSystem" \ + | grep "$(nix-instantiate --eval -E 'builtins.currentSystem')" +} + +# Simple test, try building a drv +testRepl +# Same thing (kind-of), but with a remote store. +testRepl --store "$TEST_ROOT/store?real=$NIX_STORE_DIR" + +# Remove ANSI escape sequences. They can prevent grep from finding a match. +stripColors () { + sed -E 's/\x1B\[([0-9]{1,2}(;[0-9]{1,2})?)?[m|K]//g' +} + +testReplResponseGeneral () { + local grepMode="$1"; shift + local commands="$1"; shift + local expectedResponse="$1"; shift + local response="$(nix repl "$@" <<< "$commands" | stripColors)" + echo "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \ + || fail "repl command set: + +$commands + +does not respond with: + +$expectedResponse + +but with: + +$response +" +} + +testReplResponse () { + testReplResponseGeneral --basic-regexp "$@" +} + +testReplResponseNoRegex () { + testReplResponseGeneral --fixed-strings "$@" +} + +# :a uses the newest version of a symbol +testReplResponse ' +:a { a = "1"; } +:a { a = "2"; } +"result: ${a}" +' "result: 2" + +# check dollar escaping https://github.com/NixOS/nix/issues/4909 +# note the escaped \, +# \\ +# because the second argument is a regex +testReplResponseNoRegex ' +"$" + "{hi}" +' '"\${hi}"' + +testReplResponse ' +drvPath +' '".*-simple.drv"' \ +$testDir/simple.nix + +testReplResponse ' +drvPath +' '".*-simple.drv"' \ +--file $testDir/simple.nix --experimental-features 'ca-derivations' + +testReplResponse ' +drvPath +' '".*-simple.drv"' \ +--file $testDir/simple.nix --extra-experimental-features 'repl-flake ca-derivations' + +mkdir -p flake && cat <<EOF > flake/flake.nix +{ + outputs = { self }: { + foo = 1; + bar.baz = 2; + + changingThing = "beforeChange"; + }; +} +EOF +testReplResponse ' +foo + baz +' "3" \ + ./flake ./flake\#bar --experimental-features 'flakes repl-flake' + +# Test the `:reload` mechansim with flakes: +# - Eval `./flake#changingThing` +# - Modify the flake +# - Re-eval it +# - Check that the result has changed +replResult=$( ( +echo "changingThing" +sleep 1 # Leave the repl the time to eval 'foo' +sed -i 's/beforeChange/afterChange/' flake/flake.nix +echo ":reload" +echo "changingThing" +) | nix repl ./flake --experimental-features 'flakes repl-flake') +echo "$replResult" | grepQuiet -s beforeChange +echo "$replResult" | grepQuiet -s afterChange + +# Test recursive printing and formatting +# Normal output should print attributes in lexicographical order non-recursively +testReplResponseNoRegex ' +{ a = { b = 2; }; l = [ 1 2 3 ]; s = "string"; n = 1234; x = rec { y = { z = { inherit y; }; }; }; } +' '{ a = { ... }; l = [ ... ]; n = 1234; s = "string"; x = { ... }; }' + +# Same for lists, but order is preserved +testReplResponseNoRegex ' +[ 42 1 "thingy" ({ a = 1; }) ([ 1 2 3 ]) ] +' '[ 42 1 "thingy" { ... } [ ... ] ]' + +# Same for let expressions +testReplResponseNoRegex ' +let x = { y = { a = 1; }; inherit x; }; in x +' '{ x = { ... }; y = { ... }; }' + +# The :p command should recursively print sets, but prevent infinite recursion +testReplResponseNoRegex ' +:p { a = { b = 2; }; s = "string"; n = 1234; x = rec { y = { z = { inherit y; }; }; }; } +' '{ a = { b = 2; }; n = 1234; s = "string"; x = { y = { z = { y = «repeated»; }; }; }; }' + +# Same for lists +testReplResponseNoRegex ' +:p [ 42 1 "thingy" (rec { a = 1; b = { inherit a; inherit b; }; }) ([ 1 2 3 ]) ] +' '[ 42 1 "thingy" { a = 1; b = { a = 1; b = «repeated»; }; } [ 1 2 3 ] ]' + +# Same for let expressions +testReplResponseNoRegex ' +:p let x = { y = { a = 1; }; inherit x; }; in x +' '{ x = { x = «repeated»; y = { a = 1; }; }; y = «repeated»; }' diff --git a/tests/functional/restricted.nix b/tests/functional/restricted.nix new file mode 100644 index 000000000..e0ef58402 --- /dev/null +++ b/tests/functional/restricted.nix @@ -0,0 +1 @@ +1 + 2 diff --git a/tests/functional/restricted.sh b/tests/functional/restricted.sh new file mode 100644 index 000000000..197ae7a10 --- /dev/null +++ b/tests/functional/restricted.sh @@ -0,0 +1,53 @@ +source common.sh + +clearStore + +nix-instantiate --restrict-eval --eval -E '1 + 2' +(! nix-instantiate --eval --restrict-eval ./restricted.nix) +(! nix-instantiate --eval --restrict-eval <(echo '1 + 2')) +nix-instantiate --restrict-eval ./simple.nix -I src=. +nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh + +(! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix') +nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../.. + +(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel') +nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel' -I src=../../src + +(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>') +nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=. + +p=$(nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)") +cmp $p restricted.sh + +(! nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval) + +(! nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/") + +nix eval --raw --expr "builtins.fetchurl file://$(pwd)/restricted.sh" --impure --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh" + +(! nix eval --raw --expr "builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz" --impure --restrict-eval) +(! nix eval --raw --expr "builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz" --impure --restrict-eval) +(! nix eval --raw --expr "fetchGit git://github.com/NixOS/patchelf.git" --impure --restrict-eval) + +ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix +[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]] +(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix) +(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT) +(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .) +nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I . + +[[ $(nix eval --raw --impure --restrict-eval -I . --expr 'builtins.readFile "${import ./simple.nix}/hello"') == 'Hello World!' ]] + +# Check whether we can leak symlink information through directory traversal. +traverseDir="$(pwd)/restricted-traverse-me" +ln -sfn "$(pwd)/restricted-secret" "$(pwd)/restricted-innocent" +mkdir -p "$traverseDir" +goUp="..$(echo "$traverseDir" | sed -e 's,[^/]\+,..,g')" +output="$(nix eval --raw --restrict-eval -I "$traverseDir" \ + --expr "builtins.readFile \"$traverseDir/$goUp$(pwd)/restricted-innocent\"" \ + 2>&1 || :)" +echo "$output" | grep "is forbidden" +echo "$output" | grepInverse -F restricted-secret + +expectStderr 1 nix-instantiate --restrict-eval true ./dependencies.nix | grepQuiet "forbidden in restricted mode" diff --git a/tests/functional/search.nix b/tests/functional/search.nix new file mode 100644 index 000000000..fea6e7a7a --- /dev/null +++ b/tests/functional/search.nix @@ -0,0 +1,25 @@ +with import ./config.nix; + +{ + hello = mkDerivation rec { + name = "hello-${version}"; + version = "0.1"; + buildCommand = "touch $out"; + meta.description = "Empty file"; + }; + foo = mkDerivation rec { + name = "foo-5"; + buildCommand = '' + mkdir -p $out + echo ${name} > $out/${name} + ''; + }; + bar = mkDerivation rec { + name = "bar-3"; + buildCommand = '' + echo "Does not build successfully" + exit 1 + ''; + meta.description = "broken bar"; + }; +} diff --git a/tests/functional/search.sh b/tests/functional/search.sh new file mode 100644 index 000000000..8742f8736 --- /dev/null +++ b/tests/functional/search.sh @@ -0,0 +1,46 @@ +source common.sh + +clearStore +clearCache + +(( $(nix search -f search.nix '' hello | wc -l) > 0 )) + +# Check descriptions are searched +(( $(nix search -f search.nix '' broken | wc -l) > 0 )) + +# Check search that matches nothing +(( $(nix search -f search.nix '' nosuchpackageexists | wc -l) == 0 )) + +# Search for multiple arguments +(( $(nix search -f search.nix '' hello empty | wc -l) == 2 )) + +# Multiple arguments will not exist +(( $(nix search -f search.nix '' hello broken | wc -l) == 0 )) + +## Search expressions + +# Check that empty search string matches all +nix search -f search.nix '' |grepQuiet foo +nix search -f search.nix '' |grepQuiet bar +nix search -f search.nix '' |grepQuiet hello + +## Tests for multiple regex/match highlighting + +e=$'\x1b' # grep doesn't support \e, \033 or even \x1b +# Multiple overlapping regexes +(( $(nix search -f search.nix '' 'oo' 'foo' 'oo' | grep -c "$e\[32;1mfoo$e\\[0;1m") == 1 )) +(( $(nix search -f search.nix '' 'broken b' 'en bar' | grep -c "$e\[32;1mbroken bar$e\\[0m") == 1 )) + +# Multiple matches +# Searching for 'o' should yield the 'o' in 'broken bar', the 'oo' in foo and 'o' in hello +(( $(nix search -f search.nix '' 'o' | grep -Eoc "$e\[32;1mo{1,2}$e\[(0|0;1)m") == 3 )) +# Searching for 'b' should yield the 'b' in bar and the two 'b's in 'broken bar' +# NOTE: This does not work with `grep -c` because it counts the two 'b's in 'broken bar' as one matched line +(( $(nix search -f search.nix '' 'b' | grep -Eo "$e\[32;1mb$e\[(0|0;1)m" | wc -l) == 3 )) + +## Tests for --exclude +(( $(nix search -f search.nix -e hello | grep -c hello) == 0 )) + +(( $(nix search -f search.nix foo --exclude 'foo|bar' | grep -Ec 'foo|bar') == 0 )) +(( $(nix search -f search.nix foo -e foo --exclude bar | grep -Ec 'foo|bar') == 0 )) +[[ $(nix search -f search.nix -e bar --json | jq -c 'keys') == '["foo","hello"]' ]] diff --git a/tests/functional/secure-drv-outputs.nix b/tests/functional/secure-drv-outputs.nix new file mode 100644 index 000000000..b4ac8ff53 --- /dev/null +++ b/tests/functional/secure-drv-outputs.nix @@ -0,0 +1,23 @@ +with import ./config.nix; + +{ + + good = mkDerivation { + name = "good"; + builder = builtins.toFile "builder" + '' + mkdir $out + echo > $out/good + ''; + }; + + bad = mkDerivation { + name = "good"; + builder = builtins.toFile "builder" + '' + mkdir $out + echo > $out/bad + ''; + }; + +} diff --git a/tests/functional/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh new file mode 100644 index 000000000..50a9c4428 --- /dev/null +++ b/tests/functional/secure-drv-outputs.sh @@ -0,0 +1,36 @@ +# Test that users cannot register specially-crafted derivations that +# produce output paths belonging to other derivations. This could be +# used to inject malware into the store. + +source common.sh + +clearStore + +startDaemon + +# Determine the output path of the "good" derivation. +goodOut=$(nix-store -q $(nix-instantiate ./secure-drv-outputs.nix -A good)) + +# Instantiate the "bad" derivation. +badDrv=$(nix-instantiate ./secure-drv-outputs.nix -A bad) +badOut=$(nix-store -q $badDrv) + +# Rewrite the bad derivation to produce the output path of the good +# derivation. +rm -f $TEST_ROOT/bad.drv +sed -e "s|$badOut|$goodOut|g" < $badDrv > $TEST_ROOT/bad.drv + +# Add the manipulated derivation to the store and build it. This +# should fail. +if badDrv2=$(nix-store --add $TEST_ROOT/bad.drv); then + nix-store -r "$badDrv2" +fi + +# Now build the good derivation. +goodOut2=$(nix-build ./secure-drv-outputs.nix -A good --no-out-link) +test "$goodOut" = "$goodOut2" + +if ! test -e "$goodOut"/good; then + echo "Bad derivation stole the output path of the good derivation!" + exit 1 +fi diff --git a/tests/functional/selfref-gc.sh b/tests/functional/selfref-gc.sh new file mode 100644 index 000000000..3f1f50eea --- /dev/null +++ b/tests/functional/selfref-gc.sh @@ -0,0 +1,30 @@ +source common.sh + +requireDaemonNewerThan "2.6.0pre20211215" + +clearStore + +nix-build --no-out-link -E ' + with import ./config.nix; + + let d1 = mkDerivation { + name = "selfref-gc"; + outputs = [ "out" ]; + buildCommand = " + echo SELF_REF: $out > $out + "; + }; in + + # the only change from d1 is d1 as an (unused) build input + # to get identical store path in CA. + mkDerivation { + name = "selfref-gc"; + outputs = [ "out" ]; + buildCommand = " + echo UNUSED: ${d1} + echo SELF_REF: $out > $out + "; + } +' + +nix-collect-garbage diff --git a/tests/functional/shell-hello.nix b/tests/functional/shell-hello.nix new file mode 100644 index 000000000..3fdd3501d --- /dev/null +++ b/tests/functional/shell-hello.nix @@ -0,0 +1,26 @@ +with import ./config.nix; + +{ + hello = mkDerivation { + name = "hello"; + outputs = [ "out" "dev" ]; + meta.outputsToInstall = [ "out" ]; + buildCommand = + '' + mkdir -p $out/bin $dev/bin + + cat > $out/bin/hello <<EOF + #! ${shell} + who=\$1 + echo "Hello \''${who:-World} from $out/bin/hello" + EOF + chmod +x $out/bin/hello + + cat > $dev/bin/hello2 <<EOF + #! ${shell} + echo "Hello2" + EOF + chmod +x $dev/bin/hello2 + ''; + }; +} diff --git a/tests/functional/shell.nix b/tests/functional/shell.nix new file mode 100644 index 000000000..92d94fbc2 --- /dev/null +++ b/tests/functional/shell.nix @@ -0,0 +1,90 @@ +{ inNixShell ? false, contentAddressed ? false, fooContents ? "foo" }: + +let cfg = import ./config.nix; in +with cfg; + +let + mkDerivation = + if contentAddressed then + args: cfg.mkDerivation ({ + __contentAddressed = true; + outputHashMode = "recursive"; + outputHashAlgo = "sha256"; + } // args) + else cfg.mkDerivation; +in + +let pkgs = rec { + setupSh = builtins.toFile "setup" '' + export VAR_FROM_STDENV_SETUP=foo + for pkg in $buildInputs; do + export PATH=$PATH:$pkg/bin + done + + # mimic behavior of stdenv for `$out` etc. for structured attrs. + if [ -n "''${NIX_ATTRS_SH_FILE}" ]; then + for o in "''${!outputs[@]}"; do + eval "''${o}=''${outputs[$o]}" + export "''${o}" + done + fi + + declare -a arr1=(1 2 "3 4" 5) + declare -a arr2=(x $'\n' $'x\ny') + fun() { + echo blabla + } + ''; + + stdenv = mkDerivation { + name = "stdenv"; + buildCommand = '' + mkdir -p $out + ln -s ${setupSh} $out/setup + ''; + }; + + shellDrv = mkDerivation { + name = "shellDrv"; + builder = "/does/not/exist"; + VAR_FROM_NIX = "bar"; + ASCII_PERCENT = "%"; + ASCII_AT = "@"; + TEST_inNixShell = if inNixShell then "true" else "false"; + inherit stdenv; + outputs = ["dev" "out"]; + }; + + # Used by nix-shell -p + runCommand = name: args: buildCommand: mkDerivation (args // { + inherit name buildCommand stdenv; + }); + + foo = runCommand "foo" {} '' + mkdir -p $out/bin + echo 'echo ${fooContents}' > $out/bin/foo + chmod a+rx $out/bin/foo + ln -s ${shell} $out/bin/bash + ''; + + bar = runCommand "bar" {} '' + mkdir -p $out/bin + echo 'echo bar' > $out/bin/bar + chmod a+rx $out/bin/bar + ''; + + bash = shell; + bashInteractive = runCommand "bash" {} '' + mkdir -p $out/bin + ln -s ${shell} $out/bin/bash + ''; + + # ruby "interpreter" that outputs "$@" + ruby = runCommand "ruby" {} '' + mkdir -p $out/bin + echo 'printf %s "$*"' > $out/bin/ruby + chmod a+rx $out/bin/ruby + ''; + + inherit pkgs; +}; in pkgs diff --git a/tests/functional/shell.sh b/tests/functional/shell.sh new file mode 100644 index 000000000..d2f7cf14e --- /dev/null +++ b/tests/functional/shell.sh @@ -0,0 +1,32 @@ +source common.sh + +clearStore +clearCache + +nix shell -f shell-hello.nix hello -c hello | grep 'Hello World' +nix shell -f shell-hello.nix hello -c hello NixOS | grep 'Hello NixOS' + +# Test output selection. +nix shell -f shell-hello.nix hello^dev -c hello2 | grep 'Hello2' +nix shell -f shell-hello.nix 'hello^*' -c hello2 | grep 'Hello2' + +requireSandboxSupport + +chmod -R u+w $TEST_ROOT/store0 || true +rm -rf $TEST_ROOT/store0 + +clearStore + +path=$(nix eval --raw -f shell-hello.nix hello) + +# Note: we need the sandbox paths to ensure that the shell is +# visible in the sandbox. +nix shell --sandbox-build-dir /build-tmp \ + --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' \ + --store $TEST_ROOT/store0 -f shell-hello.nix hello -c hello | grep 'Hello World' + +path2=$(nix shell --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' --store $TEST_ROOT/store0 -f shell-hello.nix hello -c $SHELL -c 'type -p hello') + +[[ $path/bin/hello = $path2 ]] + +[[ -e $TEST_ROOT/store0/nix/store/$(basename $path)/bin/hello ]] diff --git a/tests/functional/shell.shebang.rb b/tests/functional/shell.shebang.rb new file mode 100644 index 000000000..ea67eb09c --- /dev/null +++ b/tests/functional/shell.shebang.rb @@ -0,0 +1,7 @@ +#! @SHELL_PROG@ +#! ruby +#! nix-shell -I nixpkgs=shell.nix --no-substitute +#! nix-shell --pure -p ruby -i ruby + +# Contents doesn't matter. +abort("This shouldn't be executed.") diff --git a/tests/functional/shell.shebang.sh b/tests/functional/shell.shebang.sh new file mode 100755 index 000000000..f7132043d --- /dev/null +++ b/tests/functional/shell.shebang.sh @@ -0,0 +1,4 @@ +#! @ENV_PROG@ nix-shell +#! nix-shell -I nixpkgs=shell.nix --no-substitute +#! nix-shell --pure -i bash -p foo bar +echo "$(foo) $(bar) $@" diff --git a/tests/functional/signing.sh b/tests/functional/signing.sh new file mode 100644 index 000000000..942b51630 --- /dev/null +++ b/tests/functional/signing.sh @@ -0,0 +1,109 @@ +source common.sh + +clearStore +clearCache + +nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1 +pk1=$(cat $TEST_ROOT/pk1) +nix-store --generate-binary-cache-key cache2.example.org $TEST_ROOT/sk2 $TEST_ROOT/pk2 +pk2=$(cat $TEST_ROOT/pk2) + +# Build a path. +outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROOT/sk1 $TEST_ROOT/sk2") + +# Verify that the path got signed. +info=$(nix path-info --json $outPath) +[[ $info =~ '"ultimate":true' ]] +[[ $info =~ 'cache1.example.org' ]] +[[ $info =~ 'cache2.example.org' ]] + +# Test "nix store verify". +nix store verify -r $outPath + +expect 2 nix store verify -r $outPath --sigs-needed 1 + +nix store verify -r $outPath --sigs-needed 1 --trusted-public-keys $pk1 + +expect 2 nix store verify -r $outPath --sigs-needed 2 --trusted-public-keys $pk1 + +nix store verify -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" + +nix store verify --all --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" + +# Build something unsigned. +outPath2=$(nix-build simple.nix --no-out-link) + +nix store verify -r $outPath + +# Verify that the path did not get signed but does have the ultimate bit. +info=$(nix path-info --json $outPath2) +[[ $info =~ '"ultimate":true' ]] +(! [[ $info =~ 'signatures' ]]) + +# Test "nix store verify". +nix store verify -r $outPath2 + +expect 2 nix store verify -r $outPath2 --sigs-needed 1 + +expect 2 nix store verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1 + +# Test "nix store sign". +nix store sign --key-file $TEST_ROOT/sk1 $outPath2 + +nix store verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1 + +# Build something content-addressed. +outPathCA=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build ./fixed.nix -A good.0 --no-out-link) + +[[ $(nix path-info --json $outPathCA) =~ '"ca":"fixed:md5:' ]] + +# Content-addressed paths don't need signatures, so they verify +# regardless of --sigs-needed. +nix store verify $outPathCA +nix store verify $outPathCA --sigs-needed 1000 + +# Check that signing a content-addressed path doesn't overflow validSigs +nix store sign --key-file $TEST_ROOT/sk1 $outPathCA +nix store verify -r $outPathCA --sigs-needed 1000 --trusted-public-keys $pk1 + +# Copy to a binary cache. +nix copy --to file://$cacheDir $outPath2 + +# Verify that signatures got copied. +info=$(nix path-info --store file://$cacheDir --json $outPath2) +(! [[ $info =~ '"ultimate":true' ]]) +[[ $info =~ 'cache1.example.org' ]] +(! [[ $info =~ 'cache2.example.org' ]]) + +# Verify that adding a signature to a path in a binary cache works. +nix store sign --store file://$cacheDir --key-file $TEST_ROOT/sk2 $outPath2 +info=$(nix path-info --store file://$cacheDir --json $outPath2) +[[ $info =~ 'cache1.example.org' ]] +[[ $info =~ 'cache2.example.org' ]] + +# Copying to a diverted store should fail due to a lack of signatures by trusted keys. +chmod -R u+w $TEST_ROOT/store0 || true +rm -rf $TEST_ROOT/store0 + +# Fails or very flaky only on GHA + macOS: +# expectStderr 1 nix copy --to $TEST_ROOT/store0 $outPath | grepQuiet -E 'cannot add path .* because it lacks a signature by a trusted key' +# but this works: +(! nix copy --to $TEST_ROOT/store0 $outPath) + +# But succeed if we supply the public keys. +nix copy --to $TEST_ROOT/store0 $outPath --trusted-public-keys $pk1 + +expect 2 nix store verify --store $TEST_ROOT/store0 -r $outPath + +nix store verify --store $TEST_ROOT/store0 -r $outPath --trusted-public-keys $pk1 +nix store verify --store $TEST_ROOT/store0 -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2" + +# It should also succeed if we disable signature checking. +(! nix copy --to $TEST_ROOT/store0 $outPath2) +nix copy --to $TEST_ROOT/store0?require-sigs=false $outPath2 + +# But signatures should still get copied. +nix store verify --store $TEST_ROOT/store0 -r $outPath2 --trusted-public-keys $pk1 + +# Content-addressed stuff can be copied without signatures. +nix copy --to $TEST_ROOT/store0 $outPathCA diff --git a/tests/functional/simple-failing.nix b/tests/functional/simple-failing.nix new file mode 100644 index 000000000..d176c9c51 --- /dev/null +++ b/tests/functional/simple-failing.nix @@ -0,0 +1,12 @@ +with import ./config.nix; + +mkDerivation { + name = "simple-failing"; + builder = builtins.toFile "builder.sh" + '' + echo "This should fail" + exit 1 + ''; + PATH = ""; + goodPath = path; +} diff --git a/tests/functional/simple.builder.sh b/tests/functional/simple.builder.sh new file mode 100644 index 000000000..569e8ca88 --- /dev/null +++ b/tests/functional/simple.builder.sh @@ -0,0 +1,11 @@ +echo "PATH=$PATH" + +# Verify that the PATH is empty. +if mkdir foo 2> /dev/null; then exit 1; fi + +# Set a PATH (!!! impure). +export PATH=$goodPath + +mkdir $out + +echo "Hello World!" > $out/hello
\ No newline at end of file diff --git a/tests/functional/simple.nix b/tests/functional/simple.nix new file mode 100644 index 000000000..4223c0f23 --- /dev/null +++ b/tests/functional/simple.nix @@ -0,0 +1,8 @@ +with import ./config.nix; + +mkDerivation { + name = "simple"; + builder = ./simple.builder.sh; + PATH = ""; + goodPath = path; +} diff --git a/tests/functional/simple.sh b/tests/functional/simple.sh new file mode 100644 index 000000000..50d44f93f --- /dev/null +++ b/tests/functional/simple.sh @@ -0,0 +1,33 @@ +source common.sh + +drvPath=$(nix-instantiate simple.nix) + +test "$(nix-store -q --binding system "$drvPath")" = "$system" + +echo "derivation is $drvPath" + +outPath=$(nix-store -rvv "$drvPath") + +echo "output path is $outPath" + +(! [ -w $outPath ]) + +text=$(cat "$outPath"/hello) +if test "$text" != "Hello World!"; then exit 1; fi + +# Directed delete: $outPath is not reachable from a root, so it should +# be deleteable. +nix-store --delete $outPath +(! [ -e $outPath/hello ]) + +outPath="$(NIX_REMOTE=local?store=/foo\&real=$TEST_ROOT/real-store nix-instantiate --readonly-mode hash-check.nix)" +if test "$outPath" != "/foo/lfy1s6ca46rm5r6w4gg9hc0axiakjcnm-dependencies.drv"; then + echo "hashDerivationModulo appears broken, got $outPath" + exit 1 +fi + +outPath="$(NIX_REMOTE=local?store=/foo\&real=$TEST_ROOT/real-store nix-instantiate --readonly-mode big-derivation-attr.nix)" +if test "$outPath" != "/foo/xxiwa5zlaajv6xdjynf9yym9g319d6mn-big-derivation-attr.drv"; then + echo "big-derivation-attr.nix hash appears broken, got $outPath. Memory corruption in large drv attr?" + exit 1 +fi diff --git a/tests/functional/ssh-relay.sh b/tests/functional/ssh-relay.sh new file mode 100644 index 000000000..053b2f00d --- /dev/null +++ b/tests/functional/ssh-relay.sh @@ -0,0 +1,16 @@ +source common.sh + +echo foo > $TEST_ROOT/hello.sh + +ssh_localhost=ssh://localhost +remote_store=?remote-store=$ssh_localhost + +store=$ssh_localhost + +store+=$remote_store +store+=$remote_store +store+=$remote_store + +out=$(nix store add-path --store "$store" $TEST_ROOT/hello.sh) + +[ foo = $(< $out) ] diff --git a/tests/functional/store-ping.sh b/tests/functional/store-ping.sh new file mode 100644 index 000000000..9846c7d3d --- /dev/null +++ b/tests/functional/store-ping.sh @@ -0,0 +1,17 @@ +source common.sh + +STORE_INFO=$(nix store ping 2>&1) +STORE_INFO_JSON=$(nix store ping --json) + +echo "$STORE_INFO" | grep "Store URL: ${NIX_REMOTE}" + +if [[ -v NIX_DAEMON_PACKAGE ]] && isDaemonNewer "2.7.0pre20220126"; then + DAEMON_VERSION=$($NIX_DAEMON_PACKAGE/bin/nix-daemon --version | cut -d' ' -f3) + echo "$STORE_INFO" | grep "Version: $DAEMON_VERSION" + [[ "$(echo "$STORE_INFO_JSON" | jq -r ".version")" == "$DAEMON_VERSION" ]] +fi + +expect 127 NIX_REMOTE=unix:$PWD/store nix store ping || \ + fail "nix store ping on a non-existent store should fail" + +[[ "$(echo "$STORE_INFO_JSON" | jq -r ".url")" == "${NIX_REMOTE:-local}" ]] diff --git a/tests/functional/structured-attrs-shell.nix b/tests/functional/structured-attrs-shell.nix new file mode 100644 index 000000000..57c1e6bd2 --- /dev/null +++ b/tests/functional/structured-attrs-shell.nix @@ -0,0 +1,21 @@ +with import ./config.nix; +let + dep = mkDerivation { + name = "dep"; + buildCommand = '' + mkdir $out; echo bla > $out/bla + ''; + }; + inherit (import ./shell.nix { inNixShell = true; }) stdenv; +in +mkDerivation { + name = "structured2"; + __structuredAttrs = true; + inherit stdenv; + outputs = [ "out" "dev" ]; + my.list = [ "a" "b" "c" ]; + exportReferencesGraph.refs = [ dep ]; + buildCommand = '' + touch ''${outputs[out]}; touch ''${outputs[dev]} + ''; +} diff --git a/tests/functional/structured-attrs.nix b/tests/functional/structured-attrs.nix new file mode 100644 index 000000000..e93139a44 --- /dev/null +++ b/tests/functional/structured-attrs.nix @@ -0,0 +1,70 @@ +with import ./config.nix; + +let + + dep = mkDerivation { + name = "dep"; + buildCommand = '' + mkdir $out; echo bla > $out/bla + ''; + }; + +in + +mkDerivation { + name = "structured"; + + __structuredAttrs = true; + + outputs = [ "out" "dev" ]; + + buildCommand = '' + set -x + + [[ $int = 123456789 ]] + [[ -z $float ]] + [[ -n $boolTrue ]] + [[ -z $boolFalse ]] + [[ -n ''${hardening[format]} ]] + [[ -z ''${hardening[fortify]} ]] + [[ ''${#buildInputs[@]} = 7 ]] + [[ ''${buildInputs[2]} = c ]] + [[ -v nothing ]] + [[ -z $nothing ]] + + mkdir ''${outputs[out]} ''${outputs[dev]} + echo bar > $dest + echo foo > $dest2 + + json=$(cat $NIX_ATTRS_JSON_FILE) + [[ $json =~ '"narHash":"sha256:1r7yc43zqnzl5b0als5vnyp649gk17i37s7mj00xr8kc47rjcybk"' ]] + [[ $json =~ '"narSize":288' ]] + [[ $json =~ '"closureSize":288' ]] + [[ $json =~ '"references":[]' ]] + ''; + + buildInputs = [ "a" "b" "c" 123 "'" "\"" null ]; + + hardening.format = true; + hardening.fortify = false; + + outer.inner = [ 1 2 3 ]; + + int = 123456789; + + float = 123.456; + + boolTrue = true; + boolFalse = false; + + nothing = null; + + dest = "${placeholder "out"}/foo"; + dest2 = "${placeholder "dev"}/foo"; + + "foo bar" = "BAD"; + "1foobar" = "BAD"; + "foo$" = "BAD"; + + exportReferencesGraph.refs = [ dep ]; +} diff --git a/tests/functional/structured-attrs.sh b/tests/functional/structured-attrs.sh new file mode 100644 index 000000000..378dbc735 --- /dev/null +++ b/tests/functional/structured-attrs.sh @@ -0,0 +1,23 @@ +source common.sh + +# 27ce722638 required some incompatible changes to the nix file, so skip this +# tests for the older versions +requireDaemonNewerThan "2.4pre20210712" + +clearStore + +rm -f $TEST_ROOT/result + +nix-build structured-attrs.nix -A all -o $TEST_ROOT/result + +[[ $(cat $TEST_ROOT/result/foo) = bar ]] +[[ $(cat $TEST_ROOT/result-dev/foo) = foo ]] + +export NIX_BUILD_SHELL=$SHELL +env NIX_PATH=nixpkgs=shell.nix nix-shell structured-attrs-shell.nix \ + --run 'test -e .attrs.json; test "3" = "$(jq ".my.list|length" < $NIX_ATTRS_JSON_FILE)"' + +# `nix develop` is a slightly special way of dealing with environment vars, it parses +# these from a shell-file exported from a derivation. This is to test especially `outputs` +# (which is an associative array in thsi case) being fine. +nix develop -f structured-attrs-shell.nix -c bash -c 'test -n "$out"' diff --git a/tests/functional/substitute-with-invalid-ca.sh b/tests/functional/substitute-with-invalid-ca.sh new file mode 100644 index 000000000..4d0b01e0f --- /dev/null +++ b/tests/functional/substitute-with-invalid-ca.sh @@ -0,0 +1,38 @@ +source common.sh + +BINARY_CACHE=file://$cacheDir + +getHash() { + basename "$1" | cut -d '-' -f 1 +} +getRemoteNarInfo () { + echo "$cacheDir/$(getHash "$1").narinfo" +} + +cat <<EOF > $TEST_HOME/good.txt +I’m a good path +EOF + +cat <<EOF > $TEST_HOME/bad.txt +I’m a bad path +EOF + +good=$(nix-store --add $TEST_HOME/good.txt) +bad=$(nix-store --add $TEST_HOME/bad.txt) +nix copy --to "$BINARY_CACHE" "$good" +nix copy --to "$BINARY_CACHE" "$bad" +nix-collect-garbage >/dev/null 2>&1 + +# Falsifying the narinfo file for '$good' +goodPathNarInfo=$(getRemoteNarInfo "$good") +badPathNarInfo=$(getRemoteNarInfo "$bad") +for fieldName in URL FileHash FileSize NarHash NarSize; do + sed -i "/^$fieldName/d" "$goodPathNarInfo" + grep -E "^$fieldName" "$badPathNarInfo" >> "$goodPathNarInfo" +done + +# Copying back '$good' from the binary cache. This should fail as it is +# corrupted +if nix copy --from "$BINARY_CACHE" "$good"; then + fail "Importing a path with a wrong CA field should fail" +fi diff --git a/tests/functional/suggestions.sh b/tests/functional/suggestions.sh new file mode 100644 index 000000000..f18fefef9 --- /dev/null +++ b/tests/functional/suggestions.sh @@ -0,0 +1,44 @@ +source common.sh + +clearStore + +cd "$TEST_HOME" + +cat <<EOF > flake.nix +{ + outputs = a: { + packages.$system = { + foo = 1; + fo1 = 1; + fo2 = 1; + fooo = 1; + foooo = 1; + fooooo = 1; + fooooo1 = 1; + fooooo2 = 1; + fooooo3 = 1; + fooooo4 = 1; + fooooo5 = 1; + fooooo6 = 1; + }; + }; +} +EOF + +# Probable typo in the requested attribute path. Suggest some close possibilities +NIX_BUILD_STDERR_WITH_SUGGESTIONS=$(! nix build .\#fob 2>&1 1>/dev/null) +[[ "$NIX_BUILD_STDERR_WITH_SUGGESTIONS" =~ "Did you mean one of fo1, fo2, foo or fooo?" ]] || \ + fail "The nix build stderr should suggest the three closest possiblities" + +# None of the possible attributes is close to `bar`, so shouldn’t suggest anything +NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION=$(! nix build .\#bar 2>&1 1>/dev/null) +[[ ! "$NIX_BUILD_STDERR_WITH_NO_CLOSE_SUGGESTION" =~ "Did you mean" ]] || \ + fail "The nix build stderr shouldn’t suggest anything if there’s nothing relevant to suggest" + +NIX_EVAL_STDERR_WITH_SUGGESTIONS=$(! nix build --impure --expr '(builtins.getFlake (builtins.toPath ./.)).packages.'$system'.fob' 2>&1 1>/dev/null) +[[ "$NIX_EVAL_STDERR_WITH_SUGGESTIONS" =~ "Did you mean one of fo1, fo2, foo or fooo?" ]] || \ + fail "The evaluator should suggest the three closest possiblities" + +NIX_EVAL_STDERR_WITH_SUGGESTIONS=$(! nix build --impure --expr '({ foo }: foo) { foo = 1; fob = 2; }' 2>&1 1>/dev/null) +[[ "$NIX_EVAL_STDERR_WITH_SUGGESTIONS" =~ "Did you mean foo?" ]] || \ + fail "The evaluator should suggest the three closest possiblities" diff --git a/tests/functional/supplementary-groups.sh b/tests/functional/supplementary-groups.sh new file mode 100644 index 000000000..d18fb2414 --- /dev/null +++ b/tests/functional/supplementary-groups.sh @@ -0,0 +1,37 @@ +source common.sh + +requireSandboxSupport +[[ $busybox =~ busybox ]] || skipTest "no busybox" +if ! command -p -v unshare; then skipTest "Need unshare"; fi +needLocalStore "The test uses --store always so we would just be bypassing the daemon" + +unshare --mount --map-root-user bash <<EOF + source common.sh + + # Avoid store dir being inside sandbox build-dir + unset NIX_STORE_DIR + unset NIX_STATE_DIR + + setLocalStore () { + export NIX_REMOTE=\$TEST_ROOT/\$1 + mkdir -p \$NIX_REMOTE + } + + cmd=(nix-build ./hermetic.nix --arg busybox "$busybox" --arg seed 1 --no-out-link) + + # Fails with default setting + # TODO better error + setLocalStore store1 + expectStderr 1 "\${cmd[@]}" | grepQuiet "unable to start build process" + + # Fails with `require-drop-supplementary-groups` + # TODO better error + setLocalStore store2 + NIX_CONFIG='require-drop-supplementary-groups = true' \ + expectStderr 1 "\${cmd[@]}" | grepQuiet "unable to start build process" + + # Works without `require-drop-supplementary-groups` + setLocalStore store3 + NIX_CONFIG='require-drop-supplementary-groups = false' \ + "\${cmd[@]}" +EOF diff --git a/tests/functional/tarball.sh b/tests/functional/tarball.sh new file mode 100644 index 000000000..6e621a28c --- /dev/null +++ b/tests/functional/tarball.sh @@ -0,0 +1,64 @@ +source common.sh + +clearStore + +rm -rf $TEST_HOME + +tarroot=$TEST_ROOT/tarball +rm -rf $tarroot +mkdir -p $tarroot +cp dependencies.nix $tarroot/default.nix +cp config.nix dependencies.builder*.sh $tarroot/ +touch -d '@1000000000' $tarroot $tarroot/* + +hash=$(nix hash path $tarroot) + +test_tarball() { + local ext="$1" + local compressor="$2" + + tarball=$TEST_ROOT/tarball.tar$ext + (cd $TEST_ROOT && tar cf - tarball) | $compressor > $tarball + + nix-env -f file://$tarball -qa --out-path | grepQuiet dependencies + + nix-build -o $TEST_ROOT/result file://$tarball + + nix-build -o $TEST_ROOT/result '<foo>' -I foo=file://$tarball + + nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)" + # Do not re-fetch paths already present + nix-build -o $TEST_ROOT/result -E "import (fetchTarball { url = file:///does-not-exist/must-remain-unused/$tarball; sha256 = \"$hash\"; })" + + nix-build -o $TEST_ROOT/result -E "import (fetchTree file://$tarball)" + nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; })" + nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })" + # Do not re-fetch paths already present + nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file:///does-not-exist/must-remain-unused/$tarball; narHash = \"$hash\"; })" + expectStderr 102 nix-build -o $TEST_ROOT/result -E "import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"sha256-xdKv2pq/IiwLSnBBJXW8hNowI4MrdZfW+SYqDQs7Tzc=\"; })" | grep 'NAR hash mismatch in input' + + [[ $(nix eval --impure --expr "(fetchTree file://$tarball).lastModified") = 1000000000 ]] + + nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })) ? submodules)" >&2 + nix-instantiate --strict --eval -E "!((import (fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; })) ? submodules)" 2>&1 | grep 'true' + + nix-instantiate --eval -E '1 + 2' -I fnord=file://no-such-tarball.tar$ext + nix-instantiate --eval -E 'with <fnord/xyzzy>; 1 + 2' -I fnord=file://no-such-tarball$ext + (! nix-instantiate --eval -E '<fnord/xyzzy> 1' -I fnord=file://no-such-tarball$ext) + + nix-instantiate --eval -E '<fnord/config.nix>' -I fnord=file://no-such-tarball$ext -I fnord=. + + # Ensure that the `name` attribute isn’t accepted as that would mess + # with the content-addressing + (! nix-instantiate --eval -E "fetchTree { type = \"tarball\"; url = file://$tarball; narHash = \"$hash\"; name = \"foo\"; }") + +} + +test_tarball '' cat +test_tarball .xz xz +test_tarball .gz gzip + +rm -rf $TEST_ROOT/tmp +mkdir -p $TEST_ROOT/tmp +(! TMPDIR=$TEST_ROOT/tmp XDG_RUNTIME_DIR=$TEST_ROOT/tmp nix-env -f file://$(pwd)/bad.tar.xz -qa --out-path) +(! [ -e $TEST_ROOT/tmp/bad ]) diff --git a/tests/functional/test-infra.sh b/tests/functional/test-infra.sh new file mode 100644 index 000000000..54ae120e7 --- /dev/null +++ b/tests/functional/test-infra.sh @@ -0,0 +1,85 @@ +# Test the functions for testing themselves! +# Also test some assumptions on how bash works that they rely on. +source common.sh + +# `true` should exit with 0 +expect 0 true + +# `false` should exit with 1 +expect 1 false + +# `expect` will fail when we get it wrong +expect 1 expect 0 false + +noisyTrue () { + echo YAY! >&2 + true +} + +noisyFalse () { + echo NAY! >&2 + false +} + +# These should redirect standard error to standard output +expectStderr 0 noisyTrue | grepQuiet YAY +expectStderr 1 noisyFalse | grepQuiet NAY + +# `set -o pipefile` is enabled + +pipefailure () { + # shellcheck disable=SC2216 + true | false | true +} +expect 1 pipefailure +unset pipefailure + +pipefailure () { + # shellcheck disable=SC2216 + false | true | true +} +expect 1 pipefailure +unset pipefailure + +commandSubstitutionPipeFailure () { + # shellcheck disable=SC2216 + res=$(set -eu -o pipefail; false | true | echo 0) +} +expect 1 commandSubstitutionPipeFailure + +# `set -u` is enabled + +# note (...), making function use subshell, as unbound variable errors +# in the outer shell are *rightly* not recoverable. +useUnbound () ( + set -eu + # shellcheck disable=SC2154 + echo "$thisVariableIsNotBound" +) +expect 1 useUnbound + +# ! alone unfortunately negates `set -e`, but it works in functions: +# shellcheck disable=SC2251 +! true +funBang () { + ! true +} +expect 1 funBang +unset funBang + +# `grep -v -q` is not what we want for exit codes, but `grepInverse` is +# Avoid `grep -v -q`. The following line proves the point, and if it fails, +# we'll know that `grep` had a breaking change or `-v -q` may not be portable. +{ echo foo; echo bar; } | grep -v -q foo +{ echo foo; echo bar; } | expect 1 grepInverse foo + +# `grepQuiet` is quiet +res=$(set -eu -o pipefail; echo foo | grepQuiet foo | wc -c) +(( res == 0 )) +unset res + +# `greqQietInverse` is both +{ echo foo; echo bar; } | expect 1 grepQuietInverse foo +res=$(set -eu -o pipefail; echo foo | expect 1 grepQuietInverse foo | wc -c) +(( res == 0 )) +unset res diff --git a/tests/functional/test-libstoreconsumer.sh b/tests/functional/test-libstoreconsumer.sh new file mode 100644 index 000000000..8a77cf5a1 --- /dev/null +++ b/tests/functional/test-libstoreconsumer.sh @@ -0,0 +1,6 @@ +source common.sh + +drv="$(nix-instantiate simple.nix)" +cat "$drv" +out="$(./test-libstoreconsumer/test-libstoreconsumer "$drv")" +cat "$out/hello" | grep -F "Hello World!" diff --git a/tests/functional/test-libstoreconsumer/README.md b/tests/functional/test-libstoreconsumer/README.md new file mode 100644 index 000000000..ded69850f --- /dev/null +++ b/tests/functional/test-libstoreconsumer/README.md @@ -0,0 +1,6 @@ + +A very simple C++ consumer of the libstore library. + + - Keep it simple. Library consumers expect something simple. + - No build hook, or any other reinvocations. + - No more global state than necessary. diff --git a/tests/functional/test-libstoreconsumer/local.mk b/tests/functional/test-libstoreconsumer/local.mk new file mode 100644 index 000000000..edc140723 --- /dev/null +++ b/tests/functional/test-libstoreconsumer/local.mk @@ -0,0 +1,15 @@ +programs += test-libstoreconsumer + +test-libstoreconsumer_DIR := $(d) + +# do not install +test-libstoreconsumer_INSTALL_DIR := + +test-libstoreconsumer_SOURCES := \ + $(wildcard $(d)/*.cc) \ + +test-libstoreconsumer_CXXFLAGS += -I src/libutil -I src/libstore + +test-libstoreconsumer_LIBS = libstore libutil + +test-libstoreconsumer_LDFLAGS = -pthread $(SODIUM_LIBS) $(EDITLINE_LIBS) $(BOOST_LDFLAGS) $(LOWDOWN_LIBS) diff --git a/tests/functional/test-libstoreconsumer/main.cc b/tests/functional/test-libstoreconsumer/main.cc new file mode 100644 index 000000000..c61489af6 --- /dev/null +++ b/tests/functional/test-libstoreconsumer/main.cc @@ -0,0 +1,45 @@ +#include "globals.hh" +#include "store-api.hh" +#include "build-result.hh" +#include <iostream> + +using namespace nix; + +int main (int argc, char **argv) +{ + try { + if (argc != 2) { + std::cerr << "Usage: " << argv[0] << " store/path/to/something.drv\n"; + return 1; + } + + std::string drvPath = argv[1]; + + initLibStore(); + + auto store = nix::openStore(); + + // build the derivation + + std::vector<DerivedPath> paths { + DerivedPath::Built { + .drvPath = makeConstantStorePathRef(store->parseStorePath(drvPath)), + .outputs = OutputsSpec::Names{"out"} + } + }; + + const auto results = store->buildPathsWithResults(paths, bmNormal, store); + + for (const auto & result : results) { + for (const auto & [outputName, realisation] : result.builtOutputs) { + std::cout << store->printStorePath(realisation.outPath) << "\n"; + } + } + + return 0; + + } catch (const std::exception & e) { + std::cerr << "Error: " << e.what() << "\n"; + return 1; + } +} diff --git a/tests/functional/timeout.nix b/tests/functional/timeout.nix new file mode 100644 index 000000000..d0e949e31 --- /dev/null +++ b/tests/functional/timeout.nix @@ -0,0 +1,31 @@ +with import ./config.nix; + +{ + + infiniteLoop = mkDerivation { + name = "timeout"; + buildCommand = '' + touch $out + echo "'timeout' builder entering an infinite loop" + while true ; do echo -n .; done + ''; + }; + + silent = mkDerivation { + name = "silent"; + buildCommand = '' + touch $out + sleep 60 + ''; + }; + + closeLog = mkDerivation { + name = "silent"; + buildCommand = '' + touch $out + exec > /dev/null 2>&1 + sleep 1000000000 + ''; + }; + +} diff --git a/tests/functional/timeout.sh b/tests/functional/timeout.sh new file mode 100644 index 000000000..b179b79a2 --- /dev/null +++ b/tests/functional/timeout.sh @@ -0,0 +1,39 @@ +# Test the `--timeout' option. + +source common.sh + +# XXX: This shouldn’t be, but #4813 cause this test to fail +needLocalStore "see #4813" + +messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) && status=0 || status=$? + +if [ $status -ne 101 ]; then + echo "error: 'nix-store' exited with '$status'; should have exited 101" + exit 1 +fi + +if echo "$messages" | grepQuietInvert "timed out"; then + echo "error: build may have failed for reasons other than timeout; output:" + echo "$messages" >&2 + exit 1 +fi + +if nix-build -Q timeout.nix -A infiniteLoop --max-build-log-size 100; then + echo "build should have failed" + exit 1 +fi + +if nix-build timeout.nix -A silent --max-silent-time 2; then + echo "build should have failed" + exit 1 +fi + +if nix-build timeout.nix -A closeLog; then + echo "build should have failed" + exit 1 +fi + +if nix build -f timeout.nix silent --max-silent-time 2; then + echo "build should have failed" + exit 1 +fi diff --git a/tests/functional/toString-path.sh b/tests/functional/toString-path.sh new file mode 100644 index 000000000..07eb87465 --- /dev/null +++ b/tests/functional/toString-path.sh @@ -0,0 +1,8 @@ +source common.sh + +mkdir -p $TEST_ROOT/foo +echo bla > $TEST_ROOT/foo/bar + +[[ $(nix eval --raw --impure --expr "builtins.readFile (builtins.toString (builtins.fetchTree { type = \"path\"; path = \"$TEST_ROOT/foo\"; } + \"/bar\"))") = bla ]] + +[[ $(nix eval --json --impure --expr "builtins.readDir (builtins.toString (builtins.fetchTree { type = \"path\"; path = \"$TEST_ROOT/foo\"; }))") = '{"bar":"regular"}' ]] diff --git a/tests/functional/undefined-variable.nix b/tests/functional/undefined-variable.nix new file mode 100644 index 000000000..579985497 --- /dev/null +++ b/tests/functional/undefined-variable.nix @@ -0,0 +1 @@ +let f = builtins.toFile "test-file.nix" "asd"; in import f diff --git a/tests/functional/user-envs-migration.sh b/tests/functional/user-envs-migration.sh new file mode 100644 index 000000000..187372b16 --- /dev/null +++ b/tests/functional/user-envs-migration.sh @@ -0,0 +1,35 @@ +# Test that the migration of user environments +# (https://github.com/NixOS/nix/pull/5226) does preserve everything + +source common.sh + +if isDaemonNewer "2.4pre20211005"; then + skipTest "Daemon is too new" +fi + + +killDaemon +unset NIX_REMOTE + +clearStore +clearProfiles +rm -rf ~/.nix-profile + +# Fill the environment using the older Nix +PATH_WITH_NEW_NIX="$PATH" +export PATH="$NIX_DAEMON_PACKAGE/bin:$PATH" + +nix-env -f user-envs.nix -i foo-1.0 +nix-env -f user-envs.nix -i bar-0.1 + +# Migrate to the new profile dir, and ensure that everything’s there +export PATH="$PATH_WITH_NEW_NIX" +nix-env -q # Trigger the migration +( [[ -L ~/.nix-profile ]] && \ + [[ $(readlink ~/.nix-profile) == ~/.local/share/nix/profiles/profile ]] ) || \ + fail "The nix profile should point to the new location" + +(nix-env -q | grep foo && nix-env -q | grep bar && \ + [[ -e ~/.nix-profile/bin/foo ]] && \ + [[ $(nix-env --list-generations | wc -l) == 2 ]]) || + fail "The nix profile should have the same content as before the migration" diff --git a/tests/functional/user-envs.builder.sh b/tests/functional/user-envs.builder.sh new file mode 100644 index 000000000..5fafa797f --- /dev/null +++ b/tests/functional/user-envs.builder.sh @@ -0,0 +1,5 @@ +mkdir $out +mkdir $out/bin +echo "#! $shell" > $out/bin/$progName +echo "echo $name" >> $out/bin/$progName +chmod +x $out/bin/$progName diff --git a/tests/functional/user-envs.nix b/tests/functional/user-envs.nix new file mode 100644 index 000000000..46f8b51dd --- /dev/null +++ b/tests/functional/user-envs.nix @@ -0,0 +1,34 @@ +# Some dummy arguments... +{ foo ? "foo" +}: + +with import ./config.nix; + +assert foo == "foo"; + +let + + platforms = let x = "foobar"; in [ x x ]; + + makeDrv = name: progName: (mkDerivation { + name = assert progName != "fail"; name; + inherit progName system; + builder = ./user-envs.builder.sh; + } // { + meta = { + description = "A silly test package with some \${escaped anti-quotation} in it"; + inherit platforms; + }; + }); + +in + + [ + (makeDrv "foo-1.0" "foo") + (makeDrv "foo-2.0pre1" "foo") + (makeDrv "bar-0.1" "bar") + (makeDrv "foo-2.0" "foo") + (makeDrv "bar-0.1.1" "bar") + (makeDrv "foo-0.1" "foo" // { meta.priority = 10; }) + (makeDrv "fail-0.1" "fail") + ] diff --git a/tests/functional/user-envs.sh b/tests/functional/user-envs.sh new file mode 100644 index 000000000..d1260ba04 --- /dev/null +++ b/tests/functional/user-envs.sh @@ -0,0 +1,190 @@ +source common.sh + +if [ -z "${storeCleared-}" ]; then + clearStore +fi + +clearProfiles + +# Query installed: should be empty. +test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0 + +nix-env --switch-profile $profiles/test + +# Query available: should contain several. +test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6 +outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0) +drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0) +[ -n "$outPath10" -a -n "$drvPath10" ] + +# Query with json +nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [ + .outputName == "out", + .outputs.out == null +] | all' +nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [ + .outputName == "out", + (.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1")) +] | all' + +# Query descriptions. +nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly +rm -rf $HOME/.nix-defexpr +ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr +nix-env -qa '*' --description | grepQuiet silly + +# Query the system. +nix-env -qa '*' --system | grepQuiet $system + +# Install "foo-1.0". +nix-env -i foo-1.0 + +# Query installed: should contain foo-1.0 now (which should be +# executable). +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-1.0 +test "$($profiles/test/bin/foo)" = "foo-1.0" + +# Test nix-env -qc to compare installed against available packages, and vice versa. +nix-env -qc '*' | grepQuiet '< 2.0' +nix-env -qac '*' | grepQuiet '> 1.0' + +# Test the -b flag to filter out source-only packages. +[ "$(nix-env -qab | wc -l)" -eq 1 ] + +# Test the -s flag to get package status. +nix-env -qas | grepQuiet 'IP- foo-1.0' +nix-env -qas | grepQuiet -- '--- bar-0.1' + +# Disable foo. +nix-env --set-flag active false foo +(! [ -e "$profiles/test/bin/foo" ]) + +# Enable foo. +nix-env --set-flag active true foo +[ -e "$profiles/test/bin/foo" ] + +# Store the path of foo-1.0. +outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0) +echo "foo-1.0 = $outPath10" +[ "$outPath10" = "$outPath10_" ] + +# Install "foo-2.0pre1": should remove foo-1.0. +nix-env -i foo-2.0pre1 + +# Query installed: should contain foo-2.0pre1 now. +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0pre1 +test "$($profiles/test/bin/foo)" = "foo-2.0pre1" + +# Upgrade "foo": should install foo-2.0. +NIX_PATH=nixpkgs=./user-envs.nix:${NIX_PATH-} nix-env -f '<nixpkgs>' -u foo + +# Query installed: should contain foo-2.0 now. +test "$(nix-env -q '*' | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0 +test "$($profiles/test/bin/foo)" = "foo-2.0" + +# Store the path of foo-2.0. +outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0) +test -n "$outPath20" + +# Install bar-0.1, uninstall foo. +nix-env -i bar-0.1 +nix-env -e foo + +# Query installed: should only contain bar-0.1 now. +if nix-env -q '*' | grepQuiet foo; then false; fi +nix-env -q '*' | grepQuiet bar + +# Rollback: should bring "foo" back. +oldGen="$(nix-store -q --resolve $profiles/test)" +nix-env --rollback +[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ] +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet bar + +# Rollback again: should remove "bar". +nix-env --rollback +nix-env -q '*' | grepQuiet foo-2.0 +if nix-env -q '*' | grepQuiet bar; then false; fi + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 7 + +# Doing the same operation twice results in the same generation, which triggers +# "lazy" behaviour and does not create a new symlink. + +nix-env -i foo +nix-env -i foo + +# Count generations. +nix-env --list-generations +test "$(nix-env --list-generations | wc -l)" -eq 8 + +# Switch to a specified generation. +nix-env --switch-generation 7 +[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ] + +# Install foo-1.0, now using its store path. +nix-env -i "$outPath10" +nix-env -q '*' | grepQuiet foo-1.0 +nix-store -qR $profiles/test | grep "$outPath10" +nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)" +[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ] + +# Uninstall foo-1.0, using a symlink to its store path. +ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink +nix-env -e $TEST_ROOT/symlink +if nix-env -q '*' | grepQuiet foo; then false; fi +nix-store -qR $profiles/test | grepInverse "$outPath10" + +# Install foo-1.0, now using a symlink to its store path. +nix-env -i $TEST_ROOT/symlink +nix-env -q '*' | grepQuiet foo + +# Delete all old generations. +nix-env --delete-generations old + +# Run the garbage collector. This should get rid of foo-2.0 but not +# foo-1.0. +nix-collect-garbage +test -e "$outPath10" +(! [ -e "$outPath20" ]) + +# Uninstall everything +nix-env -e '*' +test "$(nix-env -q '*' | wc -l)" -eq 0 + +# Installing "foo" should only install the newest foo. +nix-env -i foo +test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1 +nix-env -q '*' | grepQuiet foo-2.0 + +# On the other hand, this should install both (and should fail due to +# a collision). +nix-env -e '*' +(! nix-env -i foo-1.0 foo-2.0) + +# Installing "*" should install one foo and one bar. +nix-env -e '*' +nix-env -i '*' +test "$(nix-env -q '*' | wc -l)" -eq 2 +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet bar-0.1.1 + +# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it +# should be possible to install both without a collision. Also test +# ‘--set-flag priority’ to manually override the declared priorities. +nix-env -e '*' +nix-env -i foo-0.1 foo-1.0 +[ "$($profiles/test/bin/foo)" = "foo-1.0" ] +nix-env --set-flag priority 1 foo-0.1 +[ "$($profiles/test/bin/foo)" = "foo-0.1" ] + +# Test nix-env --set. +nix-env --set $outPath10 +[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] +nix-env --set $drvPath10 +[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ] diff --git a/tests/functional/why-depends.sh b/tests/functional/why-depends.sh new file mode 100644 index 000000000..9680bf80e --- /dev/null +++ b/tests/functional/why-depends.sh @@ -0,0 +1,29 @@ +source common.sh + +clearStore + +cp ./dependencies.nix ./dependencies.builder0.sh ./config.nix $TEST_HOME + +cd $TEST_HOME + +nix why-depends --derivation --file ./dependencies.nix input2_drv input1_drv +nix why-depends --file ./dependencies.nix input2_drv input1_drv + +nix-build ./dependencies.nix -A input0_drv -o dep +nix-build ./dependencies.nix -o toplevel + +FAST_WHY_DEPENDS_OUTPUT=$(nix why-depends ./toplevel ./dep) +PRECISE_WHY_DEPENDS_OUTPUT=$(nix why-depends ./toplevel ./dep --precise) + +# Both outputs should show that `input-2` is in the dependency chain +echo "$FAST_WHY_DEPENDS_OUTPUT" | grepQuiet input-2 +echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grepQuiet input-2 + +# But only the “precise” one should refer to `reference-to-input-2` +echo "$FAST_WHY_DEPENDS_OUTPUT" | grepQuietInverse reference-to-input-2 +echo "$PRECISE_WHY_DEPENDS_OUTPUT" | grepQuiet reference-to-input-2 + +<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '2p' | grepQuiet "└───reference-to-input-2 -> " +<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '3p' | grep " →" | grepQuiet "dependencies-input-2" +<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '4p' | grepQuiet " └───input0: …" # in input-2, file input0 +<<<"$PRECISE_WHY_DEPENDS_OUTPUT" sed -n '5p' | grep " →" | grepQuiet "dependencies-input-0" # is dependencies-input-0 referenced diff --git a/tests/functional/zstd.sh b/tests/functional/zstd.sh new file mode 100644 index 000000000..ba7c20501 --- /dev/null +++ b/tests/functional/zstd.sh @@ -0,0 +1,28 @@ +source common.sh + +clearStore +clearCache + +cacheURI="file://$cacheDir?compression=zstd" + +outPath=$(nix-build dependencies.nix --no-out-link) + +nix copy --to $cacheURI $outPath + +HASH=$(nix hash path $outPath) + +clearStore +clearCacheCache + +nix copy --from $cacheURI $outPath --no-check-sigs + +if ls $cacheDir/nar/*.zst &> /dev/null; then + echo "files do exist" +else + echo "nars do not exist" + exit 1 +fi + +HASH2=$(nix hash path $outPath) + +[[ $HASH = $HASH2 ]] |