aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/build-remote-trustless-after.sh2
-rw-r--r--tests/build-remote-trustless-should-fail-0.sh29
-rw-r--r--tests/build-remote-trustless-should-pass-0.sh9
-rw-r--r--tests/build-remote-trustless-should-pass-1.sh9
-rw-r--r--tests/build-remote-trustless-should-pass-2.sh13
-rw-r--r--tests/build-remote-trustless-should-pass-3.sh14
-rw-r--r--tests/build-remote-trustless.sh14
-rw-r--r--tests/build-remote.sh1
-rw-r--r--tests/build.sh28
-rw-r--r--tests/ca/build-cache.sh51
-rw-r--r--tests/ca/build.sh14
-rw-r--r--tests/ca/content-addressed.nix18
-rw-r--r--tests/ca/derivation-json.sh29
-rw-r--r--tests/ca/local.mk28
-rw-r--r--tests/check-refs.nix2
-rw-r--r--tests/check-refs.sh6
-rw-r--r--tests/check.sh3
-rw-r--r--tests/common.sh2
-rw-r--r--tests/common/vars-and-functions.sh.in6
-rw-r--r--tests/dependencies.nix13
-rw-r--r--tests/dependencies.sh20
-rw-r--r--tests/derivation-json.sh12
-rw-r--r--tests/describe-stores.sh8
-rw-r--r--tests/dyn-drv/build-built-drv.sh23
-rw-r--r--tests/dyn-drv/common.sh8
l---------tests/dyn-drv/config.nix.in1
-rw-r--r--tests/dyn-drv/dep-built-drv.sh11
-rw-r--r--tests/dyn-drv/eval-outputOf.sh80
-rw-r--r--tests/dyn-drv/local.mk15
-rw-r--r--tests/dyn-drv/old-daemon-error-hack.nix20
-rw-r--r--tests/dyn-drv/old-daemon-error-hack.sh11
-rw-r--r--tests/dyn-drv/recursive-mod-json.nix33
-rw-r--r--tests/dyn-drv/recursive-mod-json.sh27
-rw-r--r--tests/dyn-drv/text-hashed-output.nix33
-rw-r--r--tests/dyn-drv/text-hashed-output.sh26
-rw-r--r--tests/eval.sh12
-rw-r--r--tests/experimental-features.sh86
-rw-r--r--tests/export-graph.nix4
-rw-r--r--tests/fetchClosure.sh81
-rw-r--r--tests/fetchGit.sh6
-rw-r--r--tests/fetchTree-file.sh14
-rw-r--r--tests/flakes/build-paths.sh32
-rw-r--r--tests/flakes/check.sh16
-rw-r--r--tests/flakes/flakes.sh18
-rw-r--r--tests/flakes/follow-paths.sh86
-rw-r--r--tests/flakes/show.sh21
-rw-r--r--tests/gc.sh17
-rw-r--r--tests/hermetic.nix56
-rw-r--r--tests/impure-derivations.sh13
-rw-r--r--tests/installer/default.nix26
-rw-r--r--tests/lang-test-infra.sh86
-rwxr-xr-x[-rw-r--r--]tests/lang.sh131
-rw-r--r--tests/lang/empty.exp0
-rw-r--r--tests/lang/eval-fail-abort.err.exp10
-rw-r--r--tests/lang/eval-fail-antiquoted-path.err.exp1
-rw-r--r--tests/lang/eval-fail-assert.err.exp36
-rw-r--r--tests/lang/eval-fail-bad-antiquote-1.err.exp10
-rw-r--r--tests/lang/eval-fail-bad-antiquote-2.err.exp1
-rw-r--r--tests/lang/eval-fail-bad-antiquote-3.err.exp10
-rw-r--r--tests/lang/eval-fail-bad-string-interpolation-1.err.exp10
-rw-r--r--tests/lang/eval-fail-bad-string-interpolation-1.nix (renamed from tests/lang/eval-fail-bad-antiquote-1.nix)0
-rw-r--r--tests/lang/eval-fail-bad-string-interpolation-2.err.exp1
-rw-r--r--tests/lang/eval-fail-bad-string-interpolation-2.nix (renamed from tests/lang/eval-fail-bad-antiquote-2.nix)0
-rw-r--r--tests/lang/eval-fail-bad-string-interpolation-3.err.exp10
-rw-r--r--tests/lang/eval-fail-bad-string-interpolation-3.nix (renamed from tests/lang/eval-fail-bad-antiquote-3.nix)0
-rw-r--r--tests/lang/eval-fail-blackhole.err.exp18
-rw-r--r--tests/lang/eval-fail-deepseq.err.exp26
-rw-r--r--tests/lang/eval-fail-dup-dynamic-attrs.err.exp8
-rw-r--r--tests/lang/eval-fail-dup-dynamic-attrs.nix4
-rw-r--r--tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp38
-rw-r--r--tests/lang/eval-fail-fromTOML-timestamps.err.exp12
-rw-r--r--tests/lang/eval-fail-fromTOML-timestamps.nix130
-rw-r--r--tests/lang/eval-fail-hashfile-missing.err.exp19
-rw-r--r--tests/lang/eval-fail-list.err.exp10
-rw-r--r--tests/lang/eval-fail-list.nix1
-rw-r--r--tests/lang/eval-fail-missing-arg.err.exp16
-rw-r--r--tests/lang/eval-fail-nonexist-path.err.exp1
-rw-r--r--tests/lang/eval-fail-path-slash.err.exp8
-rw-r--r--tests/lang/eval-fail-recursion.err.exp16
-rw-r--r--tests/lang/eval-fail-recursion.nix1
-rw-r--r--tests/lang/eval-fail-remove.err.exp19
-rw-r--r--tests/lang/eval-fail-scope-5.err.exp36
-rw-r--r--tests/lang/eval-fail-seq.err.exp18
-rw-r--r--tests/lang/eval-fail-set-override.err.exp6
-rw-r--r--tests/lang/eval-fail-set-override.nix1
-rw-r--r--tests/lang/eval-fail-set.err.exp7
-rw-r--r--tests/lang/eval-fail-set.nix1
-rw-r--r--tests/lang/eval-fail-substring.err.exp12
-rw-r--r--tests/lang/eval-fail-to-path.err.exp14
-rw-r--r--tests/lang/eval-fail-toJSON.err.exp57
-rw-r--r--tests/lang/eval-fail-toJSON.nix10
-rw-r--r--tests/lang/eval-fail-undeclared-arg.err.exp17
-rw-r--r--tests/lang/eval-okay-flake-ref-to-string.exp1
-rw-r--r--tests/lang/eval-okay-flake-ref-to-string.nix7
-rw-r--r--tests/lang/eval-okay-fromTOML-timestamps.exp1
-rw-r--r--tests/lang/eval-okay-fromTOML-timestamps.flags1
-rw-r--r--tests/lang/eval-okay-fromTOML-timestamps.nix130
-rw-r--r--tests/lang/eval-okay-fromjson.nix14
-rw-r--r--tests/lang/eval-okay-merge-dynamic-attrs.exp1
-rw-r--r--tests/lang/eval-okay-merge-dynamic-attrs.nix13
-rw-r--r--tests/lang/eval-okay-overrides.nix2
-rw-r--r--tests/lang/eval-okay-parse-flake-ref.exp1
-rw-r--r--tests/lang/eval-okay-parse-flake-ref.nix1
-rw-r--r--tests/lang/eval-okay-path-string-interpolation.exp (renamed from tests/lang/eval-okay-path-antiquotation.exp)0
-rw-r--r--tests/lang/eval-okay-path-string-interpolation.nix (renamed from tests/lang/eval-okay-path-antiquotation.nix)0
-rw-r--r--tests/lang/eval-okay-pathexists.nix5
-rw-r--r--tests/lang/eval-okay-print.err.exp1
-rw-r--r--tests/lang/eval-okay-print.exp1
-rw-r--r--tests/lang/eval-okay-print.nix1
-rw-r--r--tests/lang/eval-okay-replacestrings.exp2
-rw-r--r--tests/lang/eval-okay-replacestrings.nix1
-rw-r--r--tests/lang/eval-okay-search-path.flags2
-rw-r--r--tests/lang/framework.sh33
-rw-r--r--tests/lang/parse-fail-dup-attrs-1.err.exp7
-rw-r--r--tests/lang/parse-fail-dup-attrs-2.err.exp7
-rw-r--r--tests/lang/parse-fail-dup-attrs-3.err.exp7
-rw-r--r--tests/lang/parse-fail-dup-attrs-4.err.exp7
-rw-r--r--tests/lang/parse-fail-dup-attrs-6.err.exp1
-rw-r--r--tests/lang/parse-fail-dup-attrs-7.err.exp7
-rw-r--r--tests/lang/parse-fail-dup-formals.err.exp6
-rw-r--r--tests/lang/parse-fail-eof-in-string.err.exp7
-rw-r--r--tests/lang/parse-fail-mixed-nested-attrs1.err.exp8
-rw-r--r--tests/lang/parse-fail-mixed-nested-attrs2.err.exp8
-rw-r--r--tests/lang/parse-fail-patterns-1.err.exp7
-rw-r--r--tests/lang/parse-fail-regression-20060610.err.exp8
-rw-r--r--tests/lang/parse-fail-undef-var-2.err.exp7
-rw-r--r--tests/lang/parse-fail-undef-var.err.exp7
-rw-r--r--tests/lang/parse-fail-utf8.err.exp6
-rw-r--r--tests/lang/parse-fail-utf8.nix (renamed from tests/lang/parse-fail-uft8.nix)0
-rw-r--r--tests/lang/parse-okay-1.exp1
-rw-r--r--tests/lang/parse-okay-crlf.exp1
-rw-r--r--tests/lang/parse-okay-dup-attrs-5.exp1
-rw-r--r--tests/lang/parse-okay-dup-attrs-6.exp1
-rw-r--r--tests/lang/parse-okay-mixed-nested-attrs-1.exp1
-rw-r--r--tests/lang/parse-okay-mixed-nested-attrs-2.exp1
-rw-r--r--tests/lang/parse-okay-mixed-nested-attrs-3.exp1
-rw-r--r--tests/lang/parse-okay-regression-20041027.exp1
-rw-r--r--tests/lang/parse-okay-regression-751.exp1
-rw-r--r--tests/lang/parse-okay-subversion.exp1
-rw-r--r--tests/lang/parse-okay-url.exp1
-rw-r--r--tests/legacy-ssh-store.sh4
-rw-r--r--tests/linux-sandbox-cert-test.nix30
-rw-r--r--tests/linux-sandbox.sh45
-rw-r--r--tests/local-store.sh3
-rw-r--r--tests/local.mk47
-rw-r--r--tests/misc.sh6
-rw-r--r--tests/nested-sandboxing.sh11
-rw-r--r--tests/nested-sandboxing/command.sh29
-rw-r--r--tests/nested-sandboxing/runner.nix24
-rw-r--r--tests/nix-channel.sh2
-rw-r--r--tests/nix-collect-garbage-d.sh40
-rw-r--r--tests/nix-copy-ssh-ng.sh18
-rwxr-xr-xtests/nix-daemon-untrusting.sh3
-rw-r--r--tests/nix-profile.sh17
-rw-r--r--tests/nix-shell.sh12
-rw-r--r--tests/nixos/authorization.nix15
-rw-r--r--tests/nixos/nix-copy.nix104
-rw-r--r--tests/nixos/tarball-flakes.nix84
-rw-r--r--tests/plugins/local.mk2
-rw-r--r--tests/plugins/plugintest.cc6
-rw-r--r--tests/post-hook.sh5
-rwxr-xr-xtests/push-to-store-old.sh6
-rwxr-xr-xtests/push-to-store.sh6
-rw-r--r--tests/read-only-store.sh42
-rw-r--r--tests/recursive.nix56
-rw-r--r--tests/recursive.sh63
-rw-r--r--tests/remote-store.sh13
-rw-r--r--tests/repl.sh62
-rw-r--r--tests/restricted.sh2
-rw-r--r--tests/signing.sh4
-rw-r--r--tests/supplementary-groups.sh37
-rw-r--r--tests/tarball.sh3
-rw-r--r--tests/test-libstoreconsumer.sh6
-rw-r--r--tests/test-libstoreconsumer/README.md6
-rw-r--r--tests/test-libstoreconsumer/local.mk15
-rw-r--r--tests/test-libstoreconsumer/main.cc45
-rw-r--r--tests/why-depends.sh5
177 files changed, 2928 insertions, 198 deletions
diff --git a/tests/build-remote-trustless-after.sh b/tests/build-remote-trustless-after.sh
new file mode 100644
index 000000000..19f59e6ae
--- /dev/null
+++ b/tests/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/build-remote-trustless-should-fail-0.sh b/tests/build-remote-trustless-should-fail-0.sh
new file mode 100644
index 000000000..fad1def59
--- /dev/null
+++ b/tests/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/build-remote-trustless-should-pass-0.sh b/tests/build-remote-trustless-should-pass-0.sh
new file mode 100644
index 000000000..2a7ebd8c6
--- /dev/null
+++ b/tests/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/build-remote-trustless-should-pass-1.sh b/tests/build-remote-trustless-should-pass-1.sh
new file mode 100644
index 000000000..516bdf092
--- /dev/null
+++ b/tests/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/build-remote-trustless-should-pass-2.sh b/tests/build-remote-trustless-should-pass-2.sh
new file mode 100644
index 000000000..b769a88f0
--- /dev/null
+++ b/tests/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/build-remote-trustless-should-pass-3.sh b/tests/build-remote-trustless-should-pass-3.sh
new file mode 100644
index 000000000..40f81da5a
--- /dev/null
+++ b/tests/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/build-remote-trustless.sh b/tests/build-remote-trustless.sh
new file mode 100644
index 000000000..9df44e0c5
--- /dev/null
+++ b/tests/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/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/build-remote.sh b/tests/build-remote.sh
index 78e12b477..d2a2132c1 100644
--- a/tests/build-remote.sh
+++ b/tests/build-remote.sh
@@ -1,6 +1,7 @@
requireSandboxSupport
[[ $busybox =~ busybox ]] || skipTest "no busybox"
+# Avoid store dir being inside sandbox build-dir
unset NIX_STORE_DIR
unset NIX_STATE_DIR
diff --git a/tests/build.sh b/tests/build.sh
index b579fc374..7fbdb0f07 100644
--- a/tests/build.sh
+++ b/tests/build.sh
@@ -57,6 +57,30 @@ nix build -f multiple-outputs.nix --json 'e^*' --no-link | jq --exit-status '
(.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)
@@ -105,3 +129,7 @@ nix build --impure -f multiple-outputs.nix --json e --no-link | jq --exit-status
(.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/ca/build-cache.sh b/tests/ca/build-cache.sh
new file mode 100644
index 000000000..6a4080fec
--- /dev/null
+++ b/tests/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/ca/build.sh b/tests/ca/build.sh
index 98e1c5125..e1a8a7625 100644
--- a/tests/ca/build.sh
+++ b/tests/ca/build.sh
@@ -2,8 +2,8 @@
source common.sh
-drv=$(nix-instantiate ./content-addressed.nix -A rootCA --arg seed 1)
-nix show-derivation "$drv" --arg seed 1
+drv=$(nix-instantiate ./content-addressed.nix -A rootCA --arg seed 1)^out
+nix derivation show "$drv" --arg seed 1
buildAttr () {
local derivationPath=$1
@@ -14,14 +14,6 @@ buildAttr () {
nix-build "${args[@]}"
}
-testRemoteCache () {
- clearCache
- local outPath=$(buildAttr dependentNonCA 1)
- nix copy --to file://$cacheDir $outPath
- clearStore
- buildAttr dependentNonCA 1 --option substituters file://$cacheDir --no-require-sigs |& grepQuietInverse "building dependent-non-ca"
-}
-
testDeterministicCA () {
[[ $(buildAttr rootCA 1) = $(buildAttr rootCA 2) ]]
}
@@ -66,8 +58,6 @@ testNormalization () {
test "$(stat -c %Y $outPath)" -eq 1
}
-# Disabled until we have it properly working
-# testRemoteCache
clearStore
testNormalization
testDeterministicCA
diff --git a/tests/ca/content-addressed.nix b/tests/ca/content-addressed.nix
index 81bc4bf5c..2559c562f 100644
--- a/tests/ca/content-addressed.nix
+++ b/tests/ca/content-addressed.nix
@@ -61,6 +61,24 @@ rec {
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";
diff --git a/tests/ca/derivation-json.sh b/tests/ca/derivation-json.sh
new file mode 100644
index 000000000..c1480fd17
--- /dev/null
+++ b/tests/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/ca/local.mk b/tests/ca/local.mk
new file mode 100644
index 000000000..0852e592e
--- /dev/null
+++ b/tests/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/ca/config.nix
diff --git a/tests/check-refs.nix b/tests/check-refs.nix
index 99d69a226..89690e456 100644
--- a/tests/check-refs.nix
+++ b/tests/check-refs.nix
@@ -2,7 +2,7 @@ with import ./config.nix;
rec {
- dep = import ./dependencies.nix;
+ dep = import ./dependencies.nix {};
makeTest = nr: args: mkDerivation ({
name = "check-refs-" + toString nr;
diff --git a/tests/check-refs.sh b/tests/check-refs.sh
index 2778e491d..3b587d1e5 100644
--- a/tests/check-refs.sh
+++ b/tests/check-refs.sh
@@ -42,8 +42,10 @@ nix-build -o $RESULT check-refs.nix -A test7
nix-build -o $RESULT check-refs.nix -A test10
if isDaemonNewer 2.12pre20230103; then
- enableFeatures discard-references
- restartDaemon
+ if ! isDaemonNewer 2.16.0; then
+ enableFeatures discard-references
+ restartDaemon
+ fi
# test11 should succeed.
test11=$(nix-build -o $RESULT check-refs.nix -A test11)
diff --git a/tests/check.sh b/tests/check.sh
index 645b90222..e13abf747 100644
--- a/tests/check.sh
+++ b/tests/check.sh
@@ -18,6 +18,9 @@ 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
diff --git a/tests/common.sh b/tests/common.sh
index 8941671d6..7b0922c9f 100644
--- a/tests/common.sh
+++ b/tests/common.sh
@@ -4,7 +4,7 @@ if [[ -z "${COMMON_SH_SOURCED-}" ]]; then
COMMON_SH_SOURCED=1
-source "$(readlink -f "$(dirname "${BASH_SOURCE[0]}")")/common/vars-and-functions.sh"
+source "$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")/common/vars-and-functions.sh"
if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then
startDaemon
fi
diff --git a/tests/common/vars-and-functions.sh.in b/tests/common/vars-and-functions.sh.in
index a9e6c802f..8f9ec4b1a 100644
--- a/tests/common/vars-and-functions.sh.in
+++ b/tests/common/vars-and-functions.sh.in
@@ -4,7 +4,7 @@ if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then
COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1
-export PS4='+(${BASH_SOURCE[0]}:$LINENO) '
+export PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) '
export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default}
export NIX_STORE_DIR
@@ -195,7 +195,7 @@ expect() {
shift
"$@" && res=0 || res="$?"
if [[ $res -ne $expected ]]; then
- echo "Expected '$expected' but got '$res' while running '${*@Q}'" >&2
+ echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2
return 1
fi
return 0
@@ -209,7 +209,7 @@ expectStderr() {
shift
"$@" 2>&1 && res=0 || res="$?"
if [[ $res -ne $expected ]]; then
- echo "Expected '$expected' but got '$res' while running '${*@Q}'" >&2
+ echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2
return 1
fi
return 0
diff --git a/tests/dependencies.nix b/tests/dependencies.nix
index 45aca1793..be1a7ae9a 100644
--- a/tests/dependencies.nix
+++ b/tests/dependencies.nix
@@ -1,3 +1,4 @@
+{ hashInvalidator ? "" }:
with import ./config.nix;
let {
@@ -21,6 +22,17 @@ let {
'';
};
+ 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/../.";
@@ -29,6 +41,7 @@ let {
input1_drv = input1;
input2_drv = input2;
input0_drv = input0;
+ fod_input_drv = fod_input;
meta.description = "Random test package";
};
diff --git a/tests/dependencies.sh b/tests/dependencies.sh
index f9da0c6bc..b93dacac0 100644
--- a/tests/dependencies.sh
+++ b/tests/dependencies.sh
@@ -15,6 +15,9 @@ if test -n "$dot"; then
$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.
@@ -50,3 +53,20 @@ 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/derivation-json.sh b/tests/derivation-json.sh
new file mode 100644
index 000000000..b6be5d977
--- /dev/null
+++ b/tests/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/describe-stores.sh b/tests/describe-stores.sh
deleted file mode 100644
index 3fea61483..000000000
--- a/tests/describe-stores.sh
+++ /dev/null
@@ -1,8 +0,0 @@
-source common.sh
-
-# Query an arbitrary value in `nix describe-stores --json`'s output just to
-# check that it has the right structure
-[[ $(nix --experimental-features 'nix-command flakes' describe-stores --json | jq '.["SSH Store"]["compress"]["defaultValue"]') == false ]]
-
-# Ensure that the output of `nix describe-stores` isn't empty
-[[ -n $(nix --experimental-features 'nix-command flakes' describe-stores) ]]
diff --git a/tests/dyn-drv/build-built-drv.sh b/tests/dyn-drv/build-built-drv.sh
new file mode 100644
index 000000000..94f3550bd
--- /dev/null
+++ b/tests/dyn-drv/build-built-drv.sh
@@ -0,0 +1,23 @@
+#!/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)
+
+out2=$(nix build "${drvDep}^out^out" --no-link)
+
+test $out1 == $out2
diff --git a/tests/dyn-drv/common.sh b/tests/dyn-drv/common.sh
new file mode 100644
index 000000000..c786f6925
--- /dev/null
+++ b/tests/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/dyn-drv/config.nix.in b/tests/dyn-drv/config.nix.in
new file mode 120000
index 000000000..af24ddb30
--- /dev/null
+++ b/tests/dyn-drv/config.nix.in
@@ -0,0 +1 @@
+../config.nix.in \ No newline at end of file
diff --git a/tests/dyn-drv/dep-built-drv.sh b/tests/dyn-drv/dep-built-drv.sh
new file mode 100644
index 000000000..c3daf2c59
--- /dev/null
+++ b/tests/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
+
+out2=$(nix-build ./text-hashed-output.nix -A wrapper --no-out-link)
+
+diff -r $out1 $out2
diff --git a/tests/dyn-drv/eval-outputOf.sh b/tests/dyn-drv/eval-outputOf.sh
new file mode 100644
index 000000000..9467feb8d
--- /dev/null
+++ b/tests/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/dyn-drv/local.mk b/tests/dyn-drv/local.mk
new file mode 100644
index 000000000..6b435499b
--- /dev/null
+++ b/tests/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/dyn-drv/config.nix
diff --git a/tests/dyn-drv/old-daemon-error-hack.nix b/tests/dyn-drv/old-daemon-error-hack.nix
new file mode 100644
index 000000000..c9d4a62d4
--- /dev/null
+++ b/tests/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/dyn-drv/old-daemon-error-hack.sh b/tests/dyn-drv/old-daemon-error-hack.sh
new file mode 100644
index 000000000..43b049973
--- /dev/null
+++ b/tests/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/dyn-drv/recursive-mod-json.nix b/tests/dyn-drv/recursive-mod-json.nix
new file mode 100644
index 000000000..c6a24ca4f
--- /dev/null
+++ b/tests/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/dyn-drv/recursive-mod-json.sh b/tests/dyn-drv/recursive-mod-json.sh
new file mode 100644
index 000000000..0698b81bd
--- /dev/null
+++ b/tests/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/dyn-drv/text-hashed-output.nix b/tests/dyn-drv/text-hashed-output.nix
new file mode 100644
index 000000000..99203b518
--- /dev/null
+++ b/tests/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/dyn-drv/text-hashed-output.sh b/tests/dyn-drv/text-hashed-output.sh
new file mode 100644
index 000000000..f3e5aa93b
--- /dev/null
+++ b/tests/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/eval.sh b/tests/eval.sh
index ffae08a6a..b81bb1e2c 100644
--- a/tests/eval.sh
+++ b/tests/eval.sh
@@ -16,9 +16,10 @@ 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 -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 ]]
@@ -26,10 +27,17 @@ nix eval --expr 'assert 1 + 2 == 3; true'
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 "./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/experimental-features.sh b/tests/experimental-features.sh
new file mode 100644
index 000000000..607bf0a8e
--- /dev/null
+++ b/tests/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/export-graph.nix b/tests/export-graph.nix
index fdac9583d..64fe36bd1 100644
--- a/tests/export-graph.nix
+++ b/tests/export-graph.nix
@@ -17,13 +17,13 @@ rec {
foo."bar.runtimeGraph" = mkDerivation {
name = "dependencies";
builder = builtins.toFile "build-graph-builder" "${printRefs}";
- exportReferencesGraph = ["refs" (import ./dependencies.nix)];
+ exportReferencesGraph = ["refs" (import ./dependencies.nix {})];
};
foo."bar.buildGraph" = mkDerivation {
name = "dependencies";
builder = builtins.toFile "build-graph-builder" "${printRefs}";
- exportReferencesGraph = ["refs" (import ./dependencies.nix).drvPath];
+ exportReferencesGraph = ["refs" (import ./dependencies.nix {}).drvPath];
};
}
diff --git a/tests/fetchClosure.sh b/tests/fetchClosure.sh
index a207f647c..a02d1ce7a 100644
--- a/tests/fetchClosure.sh
+++ b/tests/fetchClosure.sh
@@ -5,6 +5,12 @@ 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(.) | .[]')
@@ -27,20 +33,43 @@ clearStore
[ ! -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
- # In impure mode, we can use non-CA paths.
- [[ $(nix eval --raw --no-require-sigs --impure --expr "
+ # 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 {
@@ -53,6 +82,10 @@ expectStderr 1 nix eval -v --json --expr "
# 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\";
@@ -60,6 +93,8 @@ nix copy --to file://$cacheDir $caPath
}
") = $caPath ]]
+[ -e $caPath ]
+
# Check that URL query parameters aren't allowed.
clearStore
narCache=$TEST_ROOT/nar-cache
@@ -71,3 +106,45 @@ rm -rf $narCache
}
")
(! [ -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/fetchGit.sh b/tests/fetchGit.sh
index e2ccb0e97..418b4f63f 100644
--- a/tests/fetchGit.sh
+++ b/tests/fetchGit.sh
@@ -105,6 +105,8 @@ path2=$(nix eval --impure --raw --expr "(builtins.fetchGit $repo).outPath")
[[ $(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")
@@ -119,6 +121,10 @@ 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" ]]
diff --git a/tests/fetchTree-file.sh b/tests/fetchTree-file.sh
index fe569cfb8..6395c133d 100644
--- a/tests/fetchTree-file.sh
+++ b/tests/fetchTree-file.sh
@@ -27,6 +27,7 @@ test_file_flake_input () {
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)"
@@ -50,6 +51,9 @@ test_file_flake_input () {
url = "file+file://$PWD/test_input.tar.gz";
flake = false;
};
+ inputs.flake_no_ext = {
+ url = "file://$PWD/test_input_no_ext";
+ };
outputs = { ... }: {};
}
EOF
@@ -58,7 +62,7 @@ EOF
nix eval --file - <<EOF
with (builtins.fromJSON (builtins.readFile ./flake.lock));
- # Url inputs whose extension doesn’t match a known archive format should
+ # 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);
@@ -75,8 +79,16 @@ EOF
# 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
diff --git a/tests/flakes/build-paths.sh b/tests/flakes/build-paths.sh
index b399a066e..ff012e1b3 100644
--- a/tests/flakes/build-paths.sh
+++ b/tests/flakes/build-paths.sh
@@ -41,10 +41,27 @@ cat > $flake1Dir/flake.nix <<EOF
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
@@ -63,4 +80,17 @@ nix build --json --out-link $TEST_ROOT/result $flake1Dir#a6
nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a8
diff common.sh $TEST_ROOT/result
-(! nix build --impure --json --out-link $TEST_ROOT/result $flake1Dir#a9)
+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/flakes/check.sh b/tests/flakes/check.sh
index 865ca61b4..0433e5335 100644
--- a/tests/flakes/check.sh
+++ b/tests/flakes/check.sh
@@ -27,6 +27,18 @@ EOF
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;
@@ -72,6 +84,8 @@ cat > $flakeDir/flake.nix <<EOF
}
EOF
-checkRes=$(nix flake check --keep-going $flakeDir 2>&1 && fail "nix flake check should have failed" || true)
+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/flakes/flakes.sh b/tests/flakes/flakes.sh
index 5c922d7c5..128f759ea 100644
--- a/tests/flakes/flakes.sh
+++ b/tests/flakes/flakes.sh
@@ -95,9 +95,16 @@ json=$(nix flake metadata flake1 --json | jq .)
[[ $(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.
@@ -491,3 +498,14 @@ nix store delete $(nix store add-path $badFlakeDir)
[[ $(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/flakes/follow-paths.sh b/tests/flakes/follow-paths.sh
index fe9b51c65..dc97027ac 100644
--- a/tests/flakes/follow-paths.sh
+++ b/tests/flakes/follow-paths.sh
@@ -146,5 +146,87 @@ 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'"
+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/flakes/show.sh b/tests/flakes/show.sh
index dd13264b9..a3d300552 100644
--- a/tests/flakes/show.sh
+++ b/tests/flakes/show.sh
@@ -64,3 +64,24 @@ 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/gc.sh b/tests/gc.sh
index 98d6cb032..ad09a8b39 100644
--- a/tests/gc.sh
+++ b/tests/gc.sh
@@ -50,20 +50,3 @@ if test -e $outPath/foobar; then false; fi
# Check that the store is empty.
rmdir $NIX_STORE_DIR/.links
rmdir $NIX_STORE_DIR
-
-## Test `nix-collect-garbage -d`
-# `nix-env` doesn't work with CA derivations, so let's ignore that bit if we're
-# using them
-if [[ -z "${NIX_TESTS_CA_BY_DEFAULT:-}" ]]; then
- 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 ]]
-fi
diff --git a/tests/hermetic.nix b/tests/hermetic.nix
new file mode 100644
index 000000000..4c9d7a51f
--- /dev/null
+++ b/tests/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/impure-derivations.sh b/tests/impure-derivations.sh
index 7595fdd35..39d053a04 100644
--- a/tests/impure-derivations.sh
+++ b/tests/impure-derivations.sh
@@ -10,6 +10,15 @@ 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)
@@ -37,8 +46,8 @@ path4=$(nix build -L --no-link --json --file ./impure-derivations.nix impureOnIm
(! 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 show-derivation $drvPath | jq ".[\"$drvPath\"].outputs.out.impure") = true ]]
-[[ $(nix show-derivation $drvPath | jq ".[\"$drvPath\"].outputs.stuff.impure") = true ]]
+[[ $(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)
diff --git a/tests/installer/default.nix b/tests/installer/default.nix
index 31d83699d..49cfd2bcc 100644
--- a/tests/installer/default.nix
+++ b/tests/installer/default.nix
@@ -17,7 +17,7 @@ let
script = ''
tar -xf ./nix.tar.xz
mv ./nix-* nix
- ./nix/install --no-daemon
+ ./nix/install --no-daemon --no-channel-add
'';
};
@@ -30,6 +30,14 @@ let
};
};
+ mockChannel = pkgs:
+ pkgs.runCommandNoCC "mock-channel" {} ''
+ mkdir nixexprs
+ mkdir -p $out/channel
+ echo -n 'someContent' > nixexprs/someFile
+ tar cvf - nixexprs | bzip2 > $out/channel/nixexprs.tar.bz2
+ '';
+
disableSELinux = "sudo setenforce 0";
images = {
@@ -189,6 +197,11 @@ let
echo "Running installer..."
$ssh "set -eux; $installScript"
+ echo "Copying the mock channel"
+ # `scp -r` doesn't seem to work properly on some rhel instances, so let's
+ # use a plain tarpipe instead
+ tar -C ${mockChannel pkgs} -c channel | ssh -p 20022 $ssh_opts vagrant@localhost tar x -f-
+
echo "Testing Nix installation..."
$ssh <<EOF
set -ex
@@ -204,6 +217,17 @@ let
out=\$(nix-build --no-substitute -E 'derivation { name = "foo"; system = "x86_64-linux"; builder = "/bin/sh"; args = ["-c" "echo foobar > \$out"]; }')
[[ \$(cat \$out) = foobar ]]
+
+ if pgrep nix-daemon; then
+ MAYBESUDO="sudo"
+ else
+ MAYBESUDO=""
+ fi
+
+
+ $MAYBESUDO \$(which nix-channel) --add file://\$HOME/channel myChannel
+ $MAYBESUDO \$(which nix-channel) --update
+ [[ \$(nix-instantiate --eval --expr 'builtins.readFile <myChannel/someFile>') = '"someContent"' ]]
EOF
echo "Done!"
diff --git a/tests/lang-test-infra.sh b/tests/lang-test-infra.sh
new file mode 100644
index 000000000..30da8977b
--- /dev/null
+++ b/tests/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/lang.sh b/tests/lang.sh
index cdb4174eb..75dbbc38e 100644..100755
--- a/tests/lang.sh
+++ b/tests/lang.sh
@@ -1,75 +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
-fail=0
+badDiff=0
+badExitCode=0
for i in lang/parse-fail-*.nix; do
echo "parsing $i (should fail)";
- i=$(basename $i .nix)
- if ! expect 1 nix-instantiate --parse - < lang/$i.nix; then
+ 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"
- fail=1
+ 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 > lang/$i.out; then
+ 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"
- fail=1
+ badExitCode=1
fi
done
for i in lang/eval-fail-*.nix; do
echo "evaluating $i (should fail)";
- i=$(basename $i .nix)
- if ! expect 1 nix-instantiate --eval lang/$i.nix; then
+ 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"
- fail=1
+ badExitCode=1
fi
done
for i in lang/eval-okay-*.nix; do
echo "evaluating $i (should succeed)";
- i=$(basename $i .nix)
+ i=$(basename "$i" .nix)
- if test -e lang/$i.exp; then
- flags=
- if test -e lang/$i.flags; then
- flags=$(cat 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 > lang/$i.out; then
+ 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"
- fail=1
- elif ! diff <(< lang/$i.out sed -e "s|$(pwd)|/pwd|g") lang/$i.exp; then
- echo "FAIL: evaluation result of $i not as expected"
- fail=1
+ 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
- fi
- 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
+ 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"
- fail=1
- elif ! cmp -s lang/$i.out.xml lang/$i.exp.xml; then
- echo "FAIL: XML evaluation result of $i not as expected"
- fail=1
+ badExitCode=1
fi
fi
done
-exit $fail
+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/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/lang/empty.exp b/tests/lang/empty.exp
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/tests/lang/empty.exp
diff --git a/tests/lang/eval-fail-abort.err.exp b/tests/lang/eval-fail-abort.err.exp
new file mode 100644
index 000000000..345232d3f
--- /dev/null
+++ b/tests/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/lang/eval-fail-antiquoted-path.err.exp b/tests/lang/eval-fail-antiquoted-path.err.exp
new file mode 100644
index 000000000..425deba42
--- /dev/null
+++ b/tests/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/lang/eval-fail-assert.err.exp b/tests/lang/eval-fail-assert.err.exp
new file mode 100644
index 000000000..aeecd8167
--- /dev/null
+++ b/tests/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/lang/eval-fail-bad-antiquote-1.err.exp b/tests/lang/eval-fail-bad-antiquote-1.err.exp
new file mode 100644
index 000000000..cf94f53bc
--- /dev/null
+++ b/tests/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/lang/eval-fail-bad-antiquote-2.err.exp b/tests/lang/eval-fail-bad-antiquote-2.err.exp
new file mode 100644
index 000000000..c8fe39d12
--- /dev/null
+++ b/tests/lang/eval-fail-bad-antiquote-2.err.exp
@@ -0,0 +1 @@
+error: operation 'addToStoreFromDump' is not supported by store 'dummy'
diff --git a/tests/lang/eval-fail-bad-antiquote-3.err.exp b/tests/lang/eval-fail-bad-antiquote-3.err.exp
new file mode 100644
index 000000000..fbefbc826
--- /dev/null
+++ b/tests/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/lang/eval-fail-bad-string-interpolation-1.err.exp b/tests/lang/eval-fail-bad-string-interpolation-1.err.exp
new file mode 100644
index 000000000..eb73e9a52
--- /dev/null
+++ b/tests/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/lang/eval-fail-bad-antiquote-1.nix b/tests/lang/eval-fail-bad-string-interpolation-1.nix
index ffe9c983c..ffe9c983c 100644
--- a/tests/lang/eval-fail-bad-antiquote-1.nix
+++ b/tests/lang/eval-fail-bad-string-interpolation-1.nix
diff --git a/tests/lang/eval-fail-bad-string-interpolation-2.err.exp b/tests/lang/eval-fail-bad-string-interpolation-2.err.exp
new file mode 100644
index 000000000..c8fe39d12
--- /dev/null
+++ b/tests/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/lang/eval-fail-bad-antiquote-2.nix b/tests/lang/eval-fail-bad-string-interpolation-2.nix
index 3745235ce..3745235ce 100644
--- a/tests/lang/eval-fail-bad-antiquote-2.nix
+++ b/tests/lang/eval-fail-bad-string-interpolation-2.nix
diff --git a/tests/lang/eval-fail-bad-string-interpolation-3.err.exp b/tests/lang/eval-fail-bad-string-interpolation-3.err.exp
new file mode 100644
index 000000000..ac14f329b
--- /dev/null
+++ b/tests/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/lang/eval-fail-bad-antiquote-3.nix b/tests/lang/eval-fail-bad-string-interpolation-3.nix
index 65b9d4f50..65b9d4f50 100644
--- a/tests/lang/eval-fail-bad-antiquote-3.nix
+++ b/tests/lang/eval-fail-bad-string-interpolation-3.nix
diff --git a/tests/lang/eval-fail-blackhole.err.exp b/tests/lang/eval-fail-blackhole.err.exp
new file mode 100644
index 000000000..f0618d8ac
--- /dev/null
+++ b/tests/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/lang/eval-fail-deepseq.err.exp b/tests/lang/eval-fail-deepseq.err.exp
new file mode 100644
index 000000000..5e204ba73
--- /dev/null
+++ b/tests/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/lang/eval-fail-dup-dynamic-attrs.err.exp b/tests/lang/eval-fail-dup-dynamic-attrs.err.exp
new file mode 100644
index 000000000..e01f8e6d0
--- /dev/null
+++ b/tests/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/lang/eval-fail-dup-dynamic-attrs.nix b/tests/lang/eval-fail-dup-dynamic-attrs.nix
new file mode 100644
index 000000000..7ea17f6c8
--- /dev/null
+++ b/tests/lang/eval-fail-dup-dynamic-attrs.nix
@@ -0,0 +1,4 @@
+{
+ set = { "${"" + "b"}" = 1; };
+ set = { "${"b" + ""}" = 2; };
+}
diff --git a/tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp b/tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp
new file mode 100644
index 000000000..0069285fb
--- /dev/null
+++ b/tests/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/lang/eval-fail-fromTOML-timestamps.err.exp b/tests/lang/eval-fail-fromTOML-timestamps.err.exp
new file mode 100644
index 000000000..f6bd19f5a
--- /dev/null
+++ b/tests/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/lang/eval-fail-fromTOML-timestamps.nix b/tests/lang/eval-fail-fromTOML-timestamps.nix
new file mode 100644
index 000000000..74cff9470
--- /dev/null
+++ b/tests/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/lang/eval-fail-hashfile-missing.err.exp b/tests/lang/eval-fail-hashfile-missing.err.exp
new file mode 100644
index 000000000..8e77dec1e
--- /dev/null
+++ b/tests/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/lang/eval-fail-list.err.exp b/tests/lang/eval-fail-list.err.exp
new file mode 100644
index 000000000..24d682118
--- /dev/null
+++ b/tests/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/lang/eval-fail-list.nix b/tests/lang/eval-fail-list.nix
new file mode 100644
index 000000000..fa749f2f7
--- /dev/null
+++ b/tests/lang/eval-fail-list.nix
@@ -0,0 +1 @@
+8++1
diff --git a/tests/lang/eval-fail-missing-arg.err.exp b/tests/lang/eval-fail-missing-arg.err.exp
new file mode 100644
index 000000000..61fabf0d5
--- /dev/null
+++ b/tests/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/lang/eval-fail-nonexist-path.err.exp b/tests/lang/eval-fail-nonexist-path.err.exp
new file mode 100644
index 000000000..c8fe39d12
--- /dev/null
+++ b/tests/lang/eval-fail-nonexist-path.err.exp
@@ -0,0 +1 @@
+error: operation 'addToStoreFromDump' is not supported by store 'dummy'
diff --git a/tests/lang/eval-fail-path-slash.err.exp b/tests/lang/eval-fail-path-slash.err.exp
new file mode 100644
index 000000000..f0011c97f
--- /dev/null
+++ b/tests/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/lang/eval-fail-recursion.err.exp b/tests/lang/eval-fail-recursion.err.exp
new file mode 100644
index 000000000..af64133cb
--- /dev/null
+++ b/tests/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/lang/eval-fail-recursion.nix b/tests/lang/eval-fail-recursion.nix
new file mode 100644
index 000000000..075b5ed06
--- /dev/null
+++ b/tests/lang/eval-fail-recursion.nix
@@ -0,0 +1 @@
+let a = {} // a; in a.foo
diff --git a/tests/lang/eval-fail-remove.err.exp b/tests/lang/eval-fail-remove.err.exp
new file mode 100644
index 000000000..e82cdac98
--- /dev/null
+++ b/tests/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/lang/eval-fail-scope-5.err.exp b/tests/lang/eval-fail-scope-5.err.exp
new file mode 100644
index 000000000..22b6166f8
--- /dev/null
+++ b/tests/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/lang/eval-fail-seq.err.exp b/tests/lang/eval-fail-seq.err.exp
new file mode 100644
index 000000000..33a7e9491
--- /dev/null
+++ b/tests/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/lang/eval-fail-set-override.err.exp b/tests/lang/eval-fail-set-override.err.exp
new file mode 100644
index 000000000..beb29d678
--- /dev/null
+++ b/tests/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/lang/eval-fail-set-override.nix b/tests/lang/eval-fail-set-override.nix
new file mode 100644
index 000000000..03551c186
--- /dev/null
+++ b/tests/lang/eval-fail-set-override.nix
@@ -0,0 +1 @@
+rec { __overrides = 1; }
diff --git a/tests/lang/eval-fail-set.err.exp b/tests/lang/eval-fail-set.err.exp
new file mode 100644
index 000000000..0d0140508
--- /dev/null
+++ b/tests/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/lang/eval-fail-set.nix b/tests/lang/eval-fail-set.nix
new file mode 100644
index 000000000..c6b7980b6
--- /dev/null
+++ b/tests/lang/eval-fail-set.nix
@@ -0,0 +1 @@
+8.x
diff --git a/tests/lang/eval-fail-substring.err.exp b/tests/lang/eval-fail-substring.err.exp
new file mode 100644
index 000000000..dc26a00bd
--- /dev/null
+++ b/tests/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/lang/eval-fail-to-path.err.exp b/tests/lang/eval-fail-to-path.err.exp
new file mode 100644
index 000000000..43ed2bdfc
--- /dev/null
+++ b/tests/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/lang/eval-fail-toJSON.err.exp b/tests/lang/eval-fail-toJSON.err.exp
new file mode 100644
index 000000000..4e618c203
--- /dev/null
+++ b/tests/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/lang/eval-fail-toJSON.nix b/tests/lang/eval-fail-toJSON.nix
new file mode 100644
index 000000000..8112e1c1f
--- /dev/null
+++ b/tests/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/lang/eval-fail-undeclared-arg.err.exp b/tests/lang/eval-fail-undeclared-arg.err.exp
new file mode 100644
index 000000000..30db743c7
--- /dev/null
+++ b/tests/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/lang/eval-okay-flake-ref-to-string.exp b/tests/lang/eval-okay-flake-ref-to-string.exp
new file mode 100644
index 000000000..110f8442d
--- /dev/null
+++ b/tests/lang/eval-okay-flake-ref-to-string.exp
@@ -0,0 +1 @@
+"github:NixOS/nixpkgs/23.05?dir=lib"
diff --git a/tests/lang/eval-okay-flake-ref-to-string.nix b/tests/lang/eval-okay-flake-ref-to-string.nix
new file mode 100644
index 000000000..dbb4e5b2a
--- /dev/null
+++ b/tests/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/lang/eval-okay-fromTOML-timestamps.exp b/tests/lang/eval-okay-fromTOML-timestamps.exp
new file mode 100644
index 000000000..08b3c69a6
--- /dev/null
+++ b/tests/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/lang/eval-okay-fromTOML-timestamps.flags b/tests/lang/eval-okay-fromTOML-timestamps.flags
new file mode 100644
index 000000000..9ed39dc6b
--- /dev/null
+++ b/tests/lang/eval-okay-fromTOML-timestamps.flags
@@ -0,0 +1 @@
+--extra-experimental-features parse-toml-timestamps
diff --git a/tests/lang/eval-okay-fromTOML-timestamps.nix b/tests/lang/eval-okay-fromTOML-timestamps.nix
new file mode 100644
index 000000000..74cff9470
--- /dev/null
+++ b/tests/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/lang/eval-okay-fromjson.nix b/tests/lang/eval-okay-fromjson.nix
index e1c0f86cc..4c526b9ae 100644
--- a/tests/lang/eval-okay-fromjson.nix
+++ b/tests/lang/eval-okay-fromjson.nix
@@ -11,9 +11,12 @@ builtins.fromJSON
"Width": 200,
"Height": 250
},
+ "Animated" : false,
+ "IDs": [116, 943, 234, 38793, true ,false,null, -100],
+ "Escapes": "\"\\\/\t\n\r\t",
"Subtitle" : false,
- "Latitude": 46.2051,
- "Longitude": 6.0723
+ "Latitude": 37.7668,
+ "Longitude": -122.3959
}
}
''
@@ -28,8 +31,11 @@ builtins.fromJSON
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 = 46.2051;
- Longitude = 6.0723;
+ Latitude = 37.7668;
+ Longitude = -122.3959;
};
}
diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.exp b/tests/lang/eval-okay-merge-dynamic-attrs.exp
new file mode 100644
index 000000000..157d677ce
--- /dev/null
+++ b/tests/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/lang/eval-okay-merge-dynamic-attrs.nix b/tests/lang/eval-okay-merge-dynamic-attrs.nix
new file mode 100644
index 000000000..f459a554f
--- /dev/null
+++ b/tests/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/lang/eval-okay-overrides.nix b/tests/lang/eval-okay-overrides.nix
index 358742b36..719bdc9c0 100644
--- a/tests/lang/eval-okay-overrides.nix
+++ b/tests/lang/eval-okay-overrides.nix
@@ -1,6 +1,6 @@
let
- overrides = { a = 2; };
+ overrides = { a = 2; b = 3; };
in (rec {
__overrides = overrides;
diff --git a/tests/lang/eval-okay-parse-flake-ref.exp b/tests/lang/eval-okay-parse-flake-ref.exp
new file mode 100644
index 000000000..fc17ba085
--- /dev/null
+++ b/tests/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/lang/eval-okay-parse-flake-ref.nix b/tests/lang/eval-okay-parse-flake-ref.nix
new file mode 100644
index 000000000..db4ed2742
--- /dev/null
+++ b/tests/lang/eval-okay-parse-flake-ref.nix
@@ -0,0 +1 @@
+ builtins.parseFlakeRef "github:NixOS/nixpkgs/23.05?dir=lib"
diff --git a/tests/lang/eval-okay-path-antiquotation.exp b/tests/lang/eval-okay-path-string-interpolation.exp
index 5b8ea0243..5b8ea0243 100644
--- a/tests/lang/eval-okay-path-antiquotation.exp
+++ b/tests/lang/eval-okay-path-string-interpolation.exp
diff --git a/tests/lang/eval-okay-path-antiquotation.nix b/tests/lang/eval-okay-path-string-interpolation.nix
index 497d7c1c7..497d7c1c7 100644
--- a/tests/lang/eval-okay-path-antiquotation.nix
+++ b/tests/lang/eval-okay-path-string-interpolation.nix
diff --git a/tests/lang/eval-okay-pathexists.nix b/tests/lang/eval-okay-pathexists.nix
index 50c28ee0c..e1246e370 100644
--- a/tests/lang/eval-okay-pathexists.nix
+++ b/tests/lang/eval-okay-pathexists.nix
@@ -1,4 +1,7 @@
-builtins.pathExists (builtins.toPath ./lib.nix)
+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.toPath (builtins.toString ./lib.nix))
&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix))
&& builtins.pathExists ./lib.nix
diff --git a/tests/lang/eval-okay-print.err.exp b/tests/lang/eval-okay-print.err.exp
new file mode 100644
index 000000000..3fc99be3e
--- /dev/null
+++ b/tests/lang/eval-okay-print.err.exp
@@ -0,0 +1 @@
+trace: [ <CODE> ]
diff --git a/tests/lang/eval-okay-print.exp b/tests/lang/eval-okay-print.exp
new file mode 100644
index 000000000..0d960fb70
--- /dev/null
+++ b/tests/lang/eval-okay-print.exp
@@ -0,0 +1 @@
+[ null <PRIMOP> <PRIMOP-APP> <LAMBDA> [ [ «repeated» ] ] ]
diff --git a/tests/lang/eval-okay-print.nix b/tests/lang/eval-okay-print.nix
new file mode 100644
index 000000000..d36ba4da3
--- /dev/null
+++ b/tests/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/lang/eval-okay-replacestrings.exp b/tests/lang/eval-okay-replacestrings.exp
index 72e8274d8..eac67c5fe 100644
--- a/tests/lang/eval-okay-replacestrings.exp
+++ b/tests/lang/eval-okay-replacestrings.exp
@@ -1 +1 @@
-[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" ]
+[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" "fubar" ]
diff --git a/tests/lang/eval-okay-replacestrings.nix b/tests/lang/eval-okay-replacestrings.nix
index bd8031fc0..a803e6519 100644
--- a/tests/lang/eval-okay-replacestrings.nix
+++ b/tests/lang/eval-okay-replacestrings.nix
@@ -8,4 +8,5 @@ with builtins;
(replaceStrings [""] ["X"] "abc")
(replaceStrings [""] ["X"] "")
(replaceStrings ["-"] ["_"] "a-b")
+ (replaceStrings ["oo" "XX"] ["u" (throw "unreachable")] "foobar")
]
diff --git a/tests/lang/eval-okay-search-path.flags b/tests/lang/eval-okay-search-path.flags
index a28e68210..dfad1c611 100644
--- a/tests/lang/eval-okay-search-path.flags
+++ b/tests/lang/eval-okay-search-path.flags
@@ -1 +1 @@
--I lang/dir1 -I lang/dir2 -I dir5=lang/dir3 \ No newline at end of file
+-I lang/dir1 -I lang/dir2 -I dir5=lang/dir3
diff --git a/tests/lang/framework.sh b/tests/lang/framework.sh
new file mode 100644
index 000000000..516bff8ad
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-attrs-1.err.exp b/tests/lang/parse-fail-dup-attrs-1.err.exp
new file mode 100644
index 000000000..4fe6b7a1f
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-attrs-2.err.exp b/tests/lang/parse-fail-dup-attrs-2.err.exp
new file mode 100644
index 000000000..3aba2891f
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-attrs-3.err.exp b/tests/lang/parse-fail-dup-attrs-3.err.exp
new file mode 100644
index 000000000..3aba2891f
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-attrs-4.err.exp b/tests/lang/parse-fail-dup-attrs-4.err.exp
new file mode 100644
index 000000000..ff68446a1
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-attrs-6.err.exp b/tests/lang/parse-fail-dup-attrs-6.err.exp
new file mode 100644
index 000000000..74823fc25
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-attrs-7.err.exp b/tests/lang/parse-fail-dup-attrs-7.err.exp
new file mode 100644
index 000000000..512a499ca
--- /dev/null
+++ b/tests/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/lang/parse-fail-dup-formals.err.exp b/tests/lang/parse-fail-dup-formals.err.exp
new file mode 100644
index 000000000..1d566fb33
--- /dev/null
+++ b/tests/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/lang/parse-fail-eof-in-string.err.exp b/tests/lang/parse-fail-eof-in-string.err.exp
new file mode 100644
index 000000000..f9fa72312
--- /dev/null
+++ b/tests/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/lang/parse-fail-mixed-nested-attrs1.err.exp b/tests/lang/parse-fail-mixed-nested-attrs1.err.exp
new file mode 100644
index 000000000..32f776795
--- /dev/null
+++ b/tests/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/lang/parse-fail-mixed-nested-attrs2.err.exp b/tests/lang/parse-fail-mixed-nested-attrs2.err.exp
new file mode 100644
index 000000000..0437cd50c
--- /dev/null
+++ b/tests/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/lang/parse-fail-patterns-1.err.exp b/tests/lang/parse-fail-patterns-1.err.exp
new file mode 100644
index 000000000..634a04aaa
--- /dev/null
+++ b/tests/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/lang/parse-fail-regression-20060610.err.exp b/tests/lang/parse-fail-regression-20060610.err.exp
new file mode 100644
index 000000000..167d01e85
--- /dev/null
+++ b/tests/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/lang/parse-fail-undef-var-2.err.exp b/tests/lang/parse-fail-undef-var-2.err.exp
new file mode 100644
index 000000000..77c96bbd2
--- /dev/null
+++ b/tests/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/lang/parse-fail-undef-var.err.exp b/tests/lang/parse-fail-undef-var.err.exp
new file mode 100644
index 000000000..48e88747f
--- /dev/null
+++ b/tests/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/lang/parse-fail-utf8.err.exp b/tests/lang/parse-fail-utf8.err.exp
new file mode 100644
index 000000000..6087479a3
--- /dev/null
+++ b/tests/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/lang/parse-fail-uft8.nix b/tests/lang/parse-fail-utf8.nix
index 34948d48a..34948d48a 100644
--- a/tests/lang/parse-fail-uft8.nix
+++ b/tests/lang/parse-fail-utf8.nix
diff --git a/tests/lang/parse-okay-1.exp b/tests/lang/parse-okay-1.exp
new file mode 100644
index 000000000..d5ab5f18a
--- /dev/null
+++ b/tests/lang/parse-okay-1.exp
@@ -0,0 +1 @@
+({ x, y, z }: ((x + y) + z))
diff --git a/tests/lang/parse-okay-crlf.exp b/tests/lang/parse-okay-crlf.exp
new file mode 100644
index 000000000..4213609fc
--- /dev/null
+++ b/tests/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/lang/parse-okay-dup-attrs-5.exp b/tests/lang/parse-okay-dup-attrs-5.exp
new file mode 100644
index 000000000..88b0b036f
--- /dev/null
+++ b/tests/lang/parse-okay-dup-attrs-5.exp
@@ -0,0 +1 @@
+{ services = { ssh = { enable = true; port = 23; }; }; }
diff --git a/tests/lang/parse-okay-dup-attrs-6.exp b/tests/lang/parse-okay-dup-attrs-6.exp
new file mode 100644
index 000000000..88b0b036f
--- /dev/null
+++ b/tests/lang/parse-okay-dup-attrs-6.exp
@@ -0,0 +1 @@
+{ services = { ssh = { enable = true; port = 23; }; }; }
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-1.exp b/tests/lang/parse-okay-mixed-nested-attrs-1.exp
new file mode 100644
index 000000000..89c66f760
--- /dev/null
+++ b/tests/lang/parse-okay-mixed-nested-attrs-1.exp
@@ -0,0 +1 @@
+{ x = { q = 3; y = 3; z = 3; }; }
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-2.exp b/tests/lang/parse-okay-mixed-nested-attrs-2.exp
new file mode 100644
index 000000000..89c66f760
--- /dev/null
+++ b/tests/lang/parse-okay-mixed-nested-attrs-2.exp
@@ -0,0 +1 @@
+{ x = { q = 3; y = 3; z = 3; }; }
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-3.exp b/tests/lang/parse-okay-mixed-nested-attrs-3.exp
new file mode 100644
index 000000000..b89a59734
--- /dev/null
+++ b/tests/lang/parse-okay-mixed-nested-attrs-3.exp
@@ -0,0 +1 @@
+{ services = { httpd = { enable = true; }; ssh = { enable = true; port = 123; }; }; }
diff --git a/tests/lang/parse-okay-regression-20041027.exp b/tests/lang/parse-okay-regression-20041027.exp
new file mode 100644
index 000000000..9df7219e4
--- /dev/null
+++ b/tests/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/lang/parse-okay-regression-751.exp b/tests/lang/parse-okay-regression-751.exp
new file mode 100644
index 000000000..e2ed886fe
--- /dev/null
+++ b/tests/lang/parse-okay-regression-751.exp
@@ -0,0 +1 @@
+(let const = (a: "const"); in ((const { x = "q"; })))
diff --git a/tests/lang/parse-okay-subversion.exp b/tests/lang/parse-okay-subversion.exp
new file mode 100644
index 000000000..4168ee8bf
--- /dev/null
+++ b/tests/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/lang/parse-okay-url.exp b/tests/lang/parse-okay-url.exp
new file mode 100644
index 000000000..e5f0829b0
--- /dev/null
+++ b/tests/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/legacy-ssh-store.sh b/tests/legacy-ssh-store.sh
new file mode 100644
index 000000000..71b716b84
--- /dev/null
+++ b/tests/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/linux-sandbox-cert-test.nix b/tests/linux-sandbox-cert-test.nix
new file mode 100644
index 000000000..2fc083ea9
--- /dev/null
+++ b/tests/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/linux-sandbox.sh b/tests/linux-sandbox.sh
index 5a2cf7abd..ff7d257bd 100644
--- a/tests/linux-sandbox.sh
+++ b/tests/linux-sandbox.sh
@@ -11,6 +11,8 @@ requireSandboxSupport
# 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
@@ -18,7 +20,7 @@ rm -rf $TEST_ROOT/store0
export NIX_STORE_DIR=/my/store
export NIX_REMOTE=$TEST_ROOT/store0
-outPath=$(nix-build dependencies.nix --no-out-link --sandbox-paths /nix/store)
+outPath=$(nix-sandbox-build dependencies.nix)
[[ $outPath =~ /my/store/.*-dependencies ]]
@@ -29,14 +31,45 @@ nix store ls -R -l $outPath | grep foobar
nix store cat $outPath/foobar | grep FOOBAR
# Test --check without hash rewriting.
-nix-build dependencies.nix --no-out-link --check --sandbox-paths /nix/store
+nix-sandbox-build dependencies.nix --check
# Test that sandboxed builds with --check and -K can move .check directory to store
-nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link
+nix-sandbox-build check.nix -A nondeterministic
-(! nix-build check.nix -A nondeterministic --sandbox-paths /nix/store --no-out-link --check -K 2> $TEST_ROOT/log)
-if grepQuiet 'error: renaming' $TEST_ROOT/log; then false; fi
+# `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
-(! nix-build -E 'with import ./config.nix; mkDerivation { name = "etc-write"; buildCommand = "echo > /etc/test"; }' --no-out-link --sandbox-paths /nix/store)
+# `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/local-store.sh b/tests/local-store.sh
index 0247346f1..89502f864 100644
--- a/tests/local-store.sh
+++ b/tests/local-store.sh
@@ -17,3 +17,6 @@ PATH2=$(nix path-info --store "$PWD/x" $CORRECT_PATH)
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/local.mk b/tests/local.mk
index 328f27e90..4edf31303 100644
--- a/tests/local.mk
+++ b/tests/local.mk
@@ -14,18 +14,19 @@ nix_tests = \
flakes/absolute-paths.sh \
flakes/build-paths.sh \
flakes/flake-in-submodule.sh \
- ca/gc.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 \
- ca/build.sh \
- ca/new-build-cmd.sh \
nix-build.sh \
gc-concurrent.sh \
repair.sh \
@@ -43,24 +44,17 @@ nix_tests = \
referrers.sh \
optimise-store.sh \
substitute-with-invalid-ca.sh \
- ca/concurrent-builds.sh \
signing.sh \
- ca/build-with-garbage-path.sh \
hash.sh \
gc-non-blocking.sh \
check.sh \
- ca/substitute.sh \
nix-shell.sh \
- ca/signatures.sh \
- ca/nix-shell.sh \
- ca/nix-copy.sh \
check-refs.sh \
build-remote-input-addressed.sh \
secure-drv-outputs.sh \
restricted.sh \
fetchGitSubmodules.sh \
flakes/search-root.sh \
- ca/duplicate-realisation-in-closure.sh \
readfile-context.sh \
nix-channel.sh \
recursive.sh \
@@ -68,13 +62,15 @@ nix_tests = \
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 \
- ca/post-hook.sh \
repl.sh \
- ca/repl.sh \
- ca/recursive.sh \
binary-cache-build-remote.sh \
search.sh \
logging.sh \
@@ -86,6 +82,7 @@ nix_tests = \
misc.sh \
dump-db.sh \
linux-sandbox.sh \
+ supplementary-groups.sh \
build-dry.sh \
structured-attrs.sh \
shell.sh \
@@ -93,15 +90,15 @@ nix_tests = \
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 \
- ca/why-depends.sh \
+ derivation-json.sh \
import-derivation.sh \
- ca/import-derivation.sh \
nix_path.sh \
case-hack.sh \
placeholders.sh \
@@ -110,12 +107,10 @@ nix_tests = \
build.sh \
build-delete.sh \
output-normalization.sh \
- ca/nix-run.sh \
- selfref-gc.sh ca/selfref-gc.sh \
+ selfref-gc.sh \
db-migration.sh \
bash-profile.sh \
pass-as-file.sh \
- describe-stores.sh \
nix-profile.sh \
suggestions.sh \
store-ping.sh \
@@ -124,17 +119,25 @@ nix_tests = \
flakes/show.sh \
impure-derivations.sh \
path-from-hash-part.sh \
- toString-path.sh
+ test-libstoreconsumer.sh \
+ toString-path.sh \
+ read-only-store.sh \
+ nested-sandboxing.sh
ifeq ($(HAVE_LIBCPUID), 1)
nix_tests += compute-levels.sh
endif
-install-tests += $(foreach x, $(nix_tests), tests/$(x))
+install-tests += $(foreach x, $(nix_tests), $(d)/$(x))
-clean-files += $(d)/common/vars-and-functions.sh $(d)/config.nix $(d)/ca/config.nix
+clean-files += \
+ $(d)/common/vars-and-functions.sh \
+ $(d)/config.nix
-test-deps += tests/common/vars-and-functions.sh tests/config.nix tests/ca/config.nix
+test-deps += \
+ tests/common/vars-and-functions.sh \
+ tests/config.nix \
+ tests/test-libstoreconsumer/test-libstoreconsumer
ifeq ($(BUILD_SHARED_LIBS), 1)
test-deps += tests/plugins/libplugintest.$(SO_EXT)
diff --git a/tests/misc.sh b/tests/misc.sh
index 60d58310e..af96d20bd 100644
--- a/tests/misc.sh
+++ b/tests/misc.sh
@@ -24,3 +24,9 @@ eval_stdin_res=$(echo 'let a = {} // a; in a.foo' | nix-instantiate --eval -E -
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/nested-sandboxing.sh b/tests/nested-sandboxing.sh
new file mode 100644
index 000000000..d9fa788aa
--- /dev/null
+++ b/tests/nested-sandboxing.sh
@@ -0,0 +1,11 @@
+source common.sh
+# This test is run by `tests/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/nested-sandboxing/command.sh b/tests/nested-sandboxing/command.sh
new file mode 100644
index 000000000..69366486c
--- /dev/null
+++ b/tests/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/nested-sandboxing/runner.nix b/tests/nested-sandboxing/runner.nix
new file mode 100644
index 000000000..9a5822c88
--- /dev/null
+++ b/tests/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/nix-channel.sh b/tests/nix-channel.sh
index dbb3114f1..b5d935004 100644
--- a/tests/nix-channel.sh
+++ b/tests/nix-channel.sh
@@ -8,6 +8,7 @@ rm -f $TEST_HOME/.nix-channels $TEST_HOME/.nix-profile
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)" = '' ]
@@ -38,6 +39,7 @@ ln -s dependencies.nix $TEST_ROOT/nixexprs/default.nix
# 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
diff --git a/tests/nix-collect-garbage-d.sh b/tests/nix-collect-garbage-d.sh
new file mode 100644
index 000000000..bf30f8938
--- /dev/null
+++ b/tests/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/nix-copy-ssh-ng.sh b/tests/nix-copy-ssh-ng.sh
new file mode 100644
index 000000000..45e53c9c0
--- /dev/null
+++ b/tests/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/nix-daemon-untrusting.sh b/tests/nix-daemon-untrusting.sh
new file mode 100755
index 000000000..bcdb70989
--- /dev/null
+++ b/tests/nix-daemon-untrusting.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+exec nix-daemon --force-untrusted "$@"
diff --git a/tests/nix-profile.sh b/tests/nix-profile.sh
index 652e8a8f2..7c478a0cd 100644
--- a/tests/nix-profile.sh
+++ b/tests/nix-profile.sh
@@ -47,8 +47,9 @@ cp ./config.nix $flake1Dir/
# Test upgrading from nix-env.
nix-env -f ./user-envs.nix -i foo-1.0
-nix profile list | grep '0 - - .*-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 ])
@@ -144,6 +145,7 @@ 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:
@@ -156,17 +158,17 @@ error: An existing package already provides the following file:
To remove the existing package:
- nix profile remove path:${flake1Dir}
+ 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} --priority 4
+ nix profile install path:${flake2Dir}#packages.${system}.default --priority 4
To prioritise the existing package:
- nix profile install path:${flake2Dir} --priority 6
+ nix profile install path:${flake2Dir}#packages.${system}.default --priority 6
EOF
)
[[ $($TEST_HOME/.nix-profile/bin/hello) = "Hello World" ]]
@@ -176,3 +178,10 @@ 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/nix-shell.sh b/tests/nix-shell.sh
index 044b96d54..edaa1249b 100644
--- a/tests/nix-shell.sh
+++ b/tests/nix-shell.sh
@@ -98,6 +98,18 @@ nix develop -f "$shellDotNix" shellDrv -c echo foo |& grepQuiet foo
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' ]]
diff --git a/tests/nixos/authorization.nix b/tests/nixos/authorization.nix
index 7e8744dd9..fdeae06ed 100644
--- a/tests/nixos/authorization.nix
+++ b/tests/nixos/authorization.nix
@@ -75,5 +75,20 @@
su --login bob -c '(! nix-store --verify --repair 2>&1)' | tee diag 1>&2
grep -F "you are not privileged to repair paths" diag
""")
+
+ machine.succeed("""
+ set -x
+ su --login mallory -c '
+ nix-store --generate-binary-cache-key cache1.example.org sk1 pk1
+ (! nix store sign --key-file sk1 ${pathFour} 2>&1)' | tee diag 1>&2
+ grep -F "cannot open connection to remote store 'daemon'" diag
+ """)
+
+ machine.succeed("""
+ su --login bob -c '
+ nix-store --generate-binary-cache-key cache1.example.org sk1 pk1
+ nix store sign --key-file sk1 ${pathFour}
+ '
+ """)
'';
}
diff --git a/tests/nixos/nix-copy.nix b/tests/nixos/nix-copy.nix
new file mode 100644
index 000000000..ef053de03
--- /dev/null
+++ b/tests/nixos/nix-copy.nix
@@ -0,0 +1,104 @@
+# Test that ‘nix copy’ works over ssh.
+
+{ lib, config, nixpkgs, hostPkgs, ... }:
+
+let
+ pkgs = config.nodes.client.nixpkgs.pkgs;
+
+ pkgA = pkgs.cowsay;
+ pkgB = pkgs.wget;
+ pkgC = pkgs.hello;
+ pkgD = pkgs.tmux;
+
+in {
+ name = "nix-copy";
+
+ enableOCR = true;
+
+ nodes =
+ { client =
+ { config, lib, pkgs, ... }:
+ { virtualisation.writableStore = true;
+ virtualisation.additionalPaths = [ pkgA pkgD.drvPath ];
+ nix.settings.substituters = lib.mkForce [ ];
+ nix.settings.experimental-features = [ "nix-command" ];
+ services.getty.autologinUser = "root";
+ programs.ssh.extraConfig = ''
+ Host *
+ ControlMaster auto
+ ControlPath ~/.ssh/master-%h:%r@%n:%p
+ ControlPersist 15m
+ '';
+ };
+
+ server =
+ { config, pkgs, ... }:
+ { services.openssh.enable = true;
+ services.openssh.permitRootLogin = "yes";
+ users.users.root.password = "foobar";
+ virtualisation.writableStore = true;
+ virtualisation.additionalPaths = [ pkgB pkgC ];
+ };
+ };
+
+ testScript = { nodes }: ''
+ # fmt: off
+ import subprocess
+
+ # Create an SSH key on the client.
+ subprocess.run([
+ "${pkgs.openssh}/bin/ssh-keygen", "-t", "ed25519", "-f", "key", "-N", ""
+ ], capture_output=True, check=True)
+
+ start_all()
+
+ server.wait_for_unit("sshd")
+ client.wait_for_unit("network.target")
+ client.wait_for_unit("getty@tty1.service")
+ client.wait_for_text("]#")
+
+ # Copy the closure of package A from the client to the server using password authentication,
+ # and check that all prompts are visible
+ server.fail("nix-store --check-validity ${pkgA}")
+ client.send_chars("nix copy --to ssh://server ${pkgA} >&2; echo done\n")
+ client.wait_for_text("continue connecting")
+ client.send_chars("yes\n")
+ client.wait_for_text("Password:")
+ client.send_chars("foobar\n")
+ client.wait_for_text("done")
+ server.succeed("nix-store --check-validity ${pkgA}")
+
+ # Check that ControlMaster is working
+ client.send_chars("nix copy --to ssh://server ${pkgA} >&2; echo done\n")
+ client.wait_for_text("done")
+
+ client.copy_from_host("key", "/root/.ssh/id_ed25519")
+ client.succeed("chmod 600 /root/.ssh/id_ed25519")
+
+ # Install the SSH key on the server.
+ server.copy_from_host("key.pub", "/root/.ssh/authorized_keys")
+ server.succeed("systemctl restart sshd")
+ client.succeed(f"ssh -o StrictHostKeyChecking=no {server.name} 'echo hello world'")
+ client.succeed(f"ssh -O check {server.name}")
+ client.succeed(f"ssh -O exit {server.name}")
+ client.fail(f"ssh -O check {server.name}")
+
+ # Check that an explicit master will work
+ client.succeed(f"ssh -MNfS /tmp/master {server.name}")
+ client.succeed(f"ssh -S /tmp/master -O check {server.name}")
+ client.succeed("NIX_SSHOPTS='-oControlPath=/tmp/master' nix copy --to ssh://server ${pkgA} >&2")
+ client.succeed(f"ssh -S /tmp/master -O exit {server.name}")
+
+ # Copy the closure of package B from the server to the client, using ssh-ng.
+ client.fail("nix-store --check-validity ${pkgB}")
+ # Shouldn't download untrusted paths by default
+ client.fail("nix copy --from ssh-ng://server ${pkgB} >&2")
+ client.succeed("nix copy --no-check-sigs --from ssh-ng://server ${pkgB} >&2")
+ client.succeed("nix-store --check-validity ${pkgB}")
+
+ # Copy the derivation of package D's derivation from the client to the server.
+ server.fail("nix-store --check-validity ${pkgD.drvPath}")
+ client.succeed("nix copy --derivation --to ssh://server ${pkgD.drvPath} >&2")
+ server.succeed("nix-store --check-validity ${pkgD.drvPath}")
+ '';
+}
diff --git a/tests/nixos/tarball-flakes.nix b/tests/nixos/tarball-flakes.nix
new file mode 100644
index 000000000..1d43a5d04
--- /dev/null
+++ b/tests/nixos/tarball-flakes.nix
@@ -0,0 +1,84 @@
+{ lib, config, nixpkgs, ... }:
+
+let
+ pkgs = config.nodes.machine.nixpkgs.pkgs;
+
+ root = pkgs.runCommand "nixpkgs-flake" {}
+ ''
+ mkdir -p $out/stable
+
+ set -x
+ dir=nixpkgs-${nixpkgs.shortRev}
+ cp -prd ${nixpkgs} $dir
+ # Set the correct timestamp in the tarball.
+ find $dir -print0 | xargs -0 touch -t ${builtins.substring 0 12 nixpkgs.lastModifiedDate}.${builtins.substring 12 2 nixpkgs.lastModifiedDate} --
+ tar cfz $out/stable/${nixpkgs.rev}.tar.gz $dir --hard-dereference
+
+ echo 'Redirect "/latest.tar.gz" "/stable/${nixpkgs.rev}.tar.gz"' > $out/.htaccess
+
+ echo 'Header set Link "<http://localhost/stable/${nixpkgs.rev}.tar.gz?rev=${nixpkgs.rev}&revCount=1234>; rel=\"immutable\""' > $out/stable/.htaccess
+ '';
+in
+
+{
+ name = "tarball-flakes";
+
+ nodes =
+ {
+ machine =
+ { config, pkgs, ... }:
+ { networking.firewall.allowedTCPPorts = [ 80 ];
+
+ services.httpd.enable = true;
+ services.httpd.adminAddr = "foo@example.org";
+ services.httpd.extraConfig = ''
+ ErrorLog syslog:local6
+ '';
+ services.httpd.virtualHosts."localhost" =
+ { servedDirs =
+ [ { urlPath = "/";
+ dir = root;
+ }
+ ];
+ };
+
+ virtualisation.writableStore = true;
+ virtualisation.diskSize = 2048;
+ virtualisation.additionalPaths = [ pkgs.hello pkgs.fuse ];
+ virtualisation.memorySize = 4096;
+ nix.settings.substituters = lib.mkForce [ ];
+ nix.extraOptions = "experimental-features = nix-command flakes";
+ };
+ };
+
+ testScript = { nodes }: ''
+ # fmt: off
+ import json
+
+ start_all()
+
+ machine.wait_for_unit("httpd.service")
+
+ out = machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz")
+ print(out)
+ info = json.loads(out)
+
+ # Check that we got redirected to the immutable URL.
+ assert info["locked"]["url"] == "http://localhost/stable/${nixpkgs.rev}.tar.gz"
+
+ # Check that we got the rev and revCount attributes.
+ assert info["revision"] == "${nixpkgs.rev}"
+ assert info["revCount"] == 1234
+
+ # Check that fetching with rev/revCount/narHash succeeds.
+ machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?rev=" + info["revision"])
+ machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?revCount=" + str(info["revCount"]))
+ machine.succeed("nix flake metadata --json http://localhost/latest.tar.gz?narHash=" + info["locked"]["narHash"])
+
+ # Check that fetching fails if we provide incorrect attributes.
+ machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?rev=493300eb13ae6fb387fbd47bf54a85915acc31c0")
+ machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?revCount=789")
+ machine.fail("nix flake metadata --json http://localhost/latest.tar.gz?narHash=sha256-tbudgBSg+bHWHiHnlteNzN8TUvI80ygS9IULh4rklEw=")
+ '';
+
+}
diff --git a/tests/plugins/local.mk b/tests/plugins/local.mk
index 8182a6a83..40350aa96 100644
--- a/tests/plugins/local.mk
+++ b/tests/plugins/local.mk
@@ -8,4 +8,4 @@ libplugintest_ALLOW_UNDEFINED := 1
libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1
-libplugintest_CXXFLAGS := -I src/libutil -I src/libstore -I src/libexpr
+libplugintest_CXXFLAGS := -I src/libutil -I src/libstore -I src/libexpr -I src/libfetchers
diff --git a/tests/plugins/plugintest.cc b/tests/plugins/plugintest.cc
index 04b791021..e02fd68d5 100644
--- a/tests/plugins/plugintest.cc
+++ b/tests/plugins/plugintest.cc
@@ -21,4 +21,8 @@ static void prim_anotherNull (EvalState & state, const PosIdx pos, Value ** args
v.mkBool(false);
}
-static RegisterPrimOp rp("anotherNull", 0, prim_anotherNull);
+static RegisterPrimOp rp({
+ .name = "anotherNull",
+ .arity = 0,
+ .fun = prim_anotherNull,
+});
diff --git a/tests/post-hook.sh b/tests/post-hook.sh
index 0266eb15d..752f8220c 100644
--- a/tests/post-hook.sh
+++ b/tests/post-hook.sh
@@ -17,6 +17,10 @@ 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
@@ -24,3 +28,4 @@ clearStore
# 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/push-to-store-old.sh b/tests/push-to-store-old.sh
index b1495c9e2..4187958b2 100755
--- a/tests/push-to-store-old.sh
+++ b/tests/push-to-store-old.sh
@@ -7,4 +7,8 @@ set -e
[ -n "$DRV_PATH" ]
echo Pushing "$OUT_PATHS" to "$REMOTE_STORE"
-printf "%s" "$DRV_PATH" | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs
+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/push-to-store.sh b/tests/push-to-store.sh
index 0b090e1b3..9e4e475e0 100755
--- a/tests/push-to-store.sh
+++ b/tests/push-to-store.sh
@@ -7,4 +7,8 @@ set -e
[ -n "$DRV_PATH" ]
echo Pushing "$OUT_PATHS" to "$REMOTE_STORE"
-printf "%s" "$DRV_PATH"^'*' | xargs nix copy --to "$REMOTE_STORE" --no-require-sigs
+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/read-only-store.sh b/tests/read-only-store.sh
new file mode 100644
index 000000000..d63920c19
--- /dev/null
+++ b/tests/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/recursive.nix b/tests/recursive.nix
new file mode 100644
index 000000000..fa8cc04db
--- /dev/null
+++ b/tests/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/recursive.sh b/tests/recursive.sh
index 6335d44a5..0bf00f8fa 100644
--- a/tests/recursive.sh
+++ b/tests/recursive.sh
@@ -1,74 +1,15 @@
source common.sh
-sed -i 's/experimental-features .*/& recursive-nix/' "$NIX_CONF_DIR"/nix.conf
+enableFeatures 'recursive-nix'
restartDaemon
-# FIXME
-if [[ $(uname) != Linux ]]; then skipTest "Not running Linux"; fi
-
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 --expr '
- 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
- '\'\'';
- }
-'
+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 ]]
diff --git a/tests/remote-store.sh b/tests/remote-store.sh
index 1ae126794..50e6f24b9 100644
--- a/tests/remote-store.sh
+++ b/tests/remote-store.sh
@@ -5,15 +5,26 @@ 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;
+ bla = import ./dependencies.nix {};
buildCommand = "
echo \\\"hi\\\" > $out
";
diff --git a/tests/repl.sh b/tests/repl.sh
index be8adb742..bb8b60e50 100644
--- a/tests/repl.sh
+++ b/tests/repl.sh
@@ -54,11 +54,17 @@ testRepl
# Same thing (kind-of), but with a remote store.
testRepl --store "$TEST_ROOT/store?real=$NIX_STORE_DIR"
-testReplResponse () {
+# 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")"
- echo "$response" | grepQuiet -s "$expectedResponse" \
+ local response="$(nix repl "$@" <<< "$commands" | stripColors)"
+ echo "$response" | grepQuiet "$grepMode" -s "$expectedResponse" \
|| fail "repl command set:
$commands
@@ -69,7 +75,16 @@ $expectedResponse
but with:
-$response"
+$response
+"
+}
+
+testReplResponse () {
+ testReplResponseGeneral --basic-regexp "$@"
+}
+
+testReplResponseNoRegex () {
+ testReplResponseGeneral --fixed-strings "$@"
}
# :a uses the newest version of a symbol
@@ -79,6 +94,14 @@ testReplResponse '
"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"' \
@@ -123,3 +146,34 @@ 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/restricted.sh b/tests/restricted.sh
index 776893a56..17f310a4b 100644
--- a/tests/restricted.sh
+++ b/tests/restricted.sh
@@ -49,3 +49,5 @@ output="$(nix eval --raw --restrict-eval -I "$traverseDir" \
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/signing.sh b/tests/signing.sh
index 9b673c609..942b51630 100644
--- a/tests/signing.sh
+++ b/tests/signing.sh
@@ -84,6 +84,10 @@ info=$(nix path-info --store file://$cacheDir --json $outPath2)
# 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.
diff --git a/tests/supplementary-groups.sh b/tests/supplementary-groups.sh
new file mode 100644
index 000000000..d18fb2414
--- /dev/null
+++ b/tests/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/tarball.sh b/tests/tarball.sh
index 5f39658c9..6e621a28c 100644
--- a/tests/tarball.sh
+++ b/tests/tarball.sh
@@ -9,6 +9,7 @@ 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)
@@ -36,6 +37,8 @@ test_tarball() {
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'
diff --git a/tests/test-libstoreconsumer.sh b/tests/test-libstoreconsumer.sh
new file mode 100644
index 000000000..8a77cf5a1
--- /dev/null
+++ b/tests/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/test-libstoreconsumer/README.md b/tests/test-libstoreconsumer/README.md
new file mode 100644
index 000000000..ded69850f
--- /dev/null
+++ b/tests/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/test-libstoreconsumer/local.mk b/tests/test-libstoreconsumer/local.mk
new file mode 100644
index 000000000..edc140723
--- /dev/null
+++ b/tests/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/test-libstoreconsumer/main.cc b/tests/test-libstoreconsumer/main.cc
new file mode 100644
index 000000000..c61489af6
--- /dev/null
+++ b/tests/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/why-depends.sh b/tests/why-depends.sh
index b35a0d1cf..9680bf80e 100644
--- a/tests/why-depends.sh
+++ b/tests/why-depends.sh
@@ -22,3 +22,8 @@ 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