aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorShea Levy <shea@shealevy.com>2024-01-11 07:21:16 -0500
committerShea Levy <shea@shealevy.com>2024-01-11 07:21:16 -0500
commitba48ab4b954dd1c8af388d1c5a33bbd62373c6f5 (patch)
treef17f8492bed32f78c04d9d4e9ffd2b672af85943 /tests
parente7c2b35827e9f4ddbec4248c5cf1ad793a2988ad (diff)
parent4dd5171652018e29bf9e496522df3be51d615a2c (diff)
Merge branch '2.18-maintenance' into ifd-buildStore-2.18
Diffstat (limited to 'tests')
-rw-r--r--tests/dyn-drv/dep-built-drv.sh11
-rw-r--r--tests/functional/add.sh (renamed from tests/add.sh)0
-rw-r--r--tests/functional/bad.tar.xz (renamed from tests/bad.tar.xz)bin228 -> 228 bytes
-rw-r--r--tests/functional/bash-profile.sh (renamed from tests/bash-profile.sh)2
-rw-r--r--tests/functional/big-derivation-attr.nix (renamed from tests/big-derivation-attr.nix)0
-rw-r--r--tests/functional/binary-cache-build-remote.sh (renamed from tests/binary-cache-build-remote.sh)0
-rw-r--r--tests/functional/binary-cache.sh (renamed from tests/binary-cache.sh)0
-rw-r--r--tests/functional/brotli.sh (renamed from tests/brotli.sh)0
-rw-r--r--tests/functional/build-delete.sh (renamed from tests/build-delete.sh)0
-rw-r--r--tests/functional/build-dry.sh (renamed from tests/build-dry.sh)0
-rw-r--r--tests/functional/build-hook-ca-fixed.nix (renamed from tests/build-hook-ca-fixed.nix)0
-rw-r--r--tests/functional/build-hook-ca-floating.nix (renamed from tests/build-hook-ca-floating.nix)0
-rw-r--r--tests/functional/build-hook.nix (renamed from tests/build-hook.nix)0
-rw-r--r--tests/functional/build-remote-content-addressed-fixed.sh (renamed from tests/build-remote-content-addressed-fixed.sh)0
-rw-r--r--tests/functional/build-remote-content-addressed-floating.sh (renamed from tests/build-remote-content-addressed-floating.sh)0
-rw-r--r--tests/functional/build-remote-input-addressed.sh (renamed from tests/build-remote-input-addressed.sh)0
-rw-r--r--tests/functional/build-remote-trustless-after.sh (renamed from tests/build-remote-trustless-after.sh)0
-rw-r--r--tests/functional/build-remote-trustless-should-fail-0.sh (renamed from tests/build-remote-trustless-should-fail-0.sh)0
-rw-r--r--tests/functional/build-remote-trustless-should-pass-0.sh (renamed from tests/build-remote-trustless-should-pass-0.sh)0
-rw-r--r--tests/functional/build-remote-trustless-should-pass-1.sh (renamed from tests/build-remote-trustless-should-pass-1.sh)0
-rw-r--r--tests/functional/build-remote-trustless-should-pass-2.sh (renamed from tests/build-remote-trustless-should-pass-2.sh)0
-rw-r--r--tests/functional/build-remote-trustless-should-pass-3.sh (renamed from tests/build-remote-trustless-should-pass-3.sh)0
-rw-r--r--tests/functional/build-remote-trustless.sh (renamed from tests/build-remote-trustless.sh)2
-rw-r--r--tests/functional/build-remote.sh (renamed from tests/build-remote.sh)0
-rw-r--r--tests/functional/build.sh (renamed from tests/build.sh)0
-rw-r--r--tests/functional/ca-shell.nix (renamed from tests/ca-shell.nix)0
-rw-r--r--tests/functional/ca/build-cache.sh (renamed from tests/ca/build-cache.sh)0
-rw-r--r--tests/functional/ca/build-dry.sh (renamed from tests/ca/build-dry.sh)0
-rwxr-xr-xtests/functional/ca/build-with-garbage-path.sh (renamed from tests/ca/build-with-garbage-path.sh)0
-rw-r--r--tests/functional/ca/build.sh (renamed from tests/ca/build.sh)0
-rw-r--r--tests/functional/ca/common.sh (renamed from tests/ca/common.sh)0
-rwxr-xr-xtests/functional/ca/concurrent-builds.sh (renamed from tests/ca/concurrent-builds.sh)0
l---------tests/functional/ca/config.nix.in (renamed from tests/ca/config.nix.in)0
-rw-r--r--tests/functional/ca/content-addressed.nix (renamed from tests/ca/content-addressed.nix)0
-rw-r--r--tests/functional/ca/derivation-json.sh (renamed from tests/ca/derivation-json.sh)0
-rw-r--r--tests/functional/ca/duplicate-realisation-in-closure.sh (renamed from tests/ca/duplicate-realisation-in-closure.sh)0
-rw-r--r--tests/functional/ca/flake.nix (renamed from tests/ca/flake.nix)0
-rwxr-xr-xtests/functional/ca/gc.sh (renamed from tests/ca/gc.sh)0
-rw-r--r--tests/functional/ca/import-derivation.sh (renamed from tests/ca/import-derivation.sh)0
-rw-r--r--tests/functional/ca/local.mk (renamed from tests/ca/local.mk)2
-rw-r--r--tests/functional/ca/new-build-cmd.sh (renamed from tests/ca/new-build-cmd.sh)0
-rwxr-xr-xtests/functional/ca/nix-copy.sh (renamed from tests/ca/nix-copy.sh)0
-rwxr-xr-xtests/functional/ca/nix-run.sh (renamed from tests/ca/nix-run.sh)0
-rwxr-xr-xtests/functional/ca/nix-shell.sh (renamed from tests/ca/nix-shell.sh)0
-rw-r--r--tests/functional/ca/nondeterministic.nix (renamed from tests/ca/nondeterministic.nix)0
-rwxr-xr-xtests/functional/ca/post-hook.sh (renamed from tests/ca/post-hook.sh)0
-rw-r--r--tests/functional/ca/racy.nix (renamed from tests/ca/racy.nix)0
-rwxr-xr-xtests/functional/ca/recursive.sh (renamed from tests/ca/recursive.sh)0
-rw-r--r--tests/functional/ca/repl.sh (renamed from tests/ca/repl.sh)0
-rwxr-xr-xtests/functional/ca/selfref-gc.sh (renamed from tests/ca/selfref-gc.sh)0
-rw-r--r--tests/functional/ca/signatures.sh (renamed from tests/ca/signatures.sh)0
-rw-r--r--tests/functional/ca/substitute.sh (renamed from tests/ca/substitute.sh)0
-rw-r--r--tests/functional/ca/why-depends.sh (renamed from tests/ca/why-depends.sh)0
-rw-r--r--tests/functional/case-hack.sh (renamed from tests/case-hack.sh)0
-rw-r--r--tests/functional/case.nar (renamed from tests/case.nar)bin2416 -> 2416 bytes
-rw-r--r--tests/functional/check-refs.nix (renamed from tests/check-refs.nix)0
-rw-r--r--tests/functional/check-refs.sh (renamed from tests/check-refs.sh)0
-rw-r--r--tests/functional/check-reqs.nix (renamed from tests/check-reqs.nix)0
-rw-r--r--tests/functional/check-reqs.sh (renamed from tests/check-reqs.sh)0
-rw-r--r--tests/functional/check.nix (renamed from tests/check.nix)0
-rw-r--r--tests/functional/check.sh (renamed from tests/check.sh)0
-rw-r--r--tests/functional/common.sh (renamed from tests/common.sh)0
-rw-r--r--tests/functional/common/vars-and-functions.sh.in (renamed from tests/common/vars-and-functions.sh.in)2
-rw-r--r--tests/functional/completions.sh (renamed from tests/completions.sh)0
-rw-r--r--tests/functional/compression-levels.sh (renamed from tests/compression-levels.sh)0
-rw-r--r--tests/functional/compute-levels.sh (renamed from tests/compute-levels.sh)0
-rw-r--r--tests/functional/config.nix.in (renamed from tests/config.nix.in)0
-rw-r--r--tests/functional/config.sh (renamed from tests/config.sh)0
-rw-r--r--tests/functional/config/nix-with-substituters.conf (renamed from tests/config/nix-with-substituters.conf)0
-rw-r--r--tests/functional/db-migration.sh (renamed from tests/db-migration.sh)0
-rw-r--r--tests/functional/dependencies.builder0.sh (renamed from tests/dependencies.builder0.sh)0
-rw-r--r--tests/functional/dependencies.nix (renamed from tests/dependencies.nix)0
-rw-r--r--tests/functional/dependencies.sh (renamed from tests/dependencies.sh)0
-rw-r--r--tests/functional/derivation-json.sh (renamed from tests/derivation-json.sh)0
-rw-r--r--tests/functional/dummy (renamed from tests/dummy)0
-rw-r--r--tests/functional/dump-db.sh (renamed from tests/dump-db.sh)0
-rw-r--r--tests/functional/dyn-drv/build-built-drv.sh (renamed from tests/dyn-drv/build-built-drv.sh)4
-rw-r--r--tests/functional/dyn-drv/common.sh (renamed from tests/dyn-drv/common.sh)0
l---------tests/functional/dyn-drv/config.nix.in (renamed from tests/dyn-drv/config.nix.in)0
-rw-r--r--tests/functional/dyn-drv/dep-built-drv.sh11
-rw-r--r--tests/functional/dyn-drv/eval-outputOf.sh (renamed from tests/dyn-drv/eval-outputOf.sh)0
-rw-r--r--tests/functional/dyn-drv/local.mk (renamed from tests/dyn-drv/local.mk)2
-rw-r--r--tests/functional/dyn-drv/old-daemon-error-hack.nix (renamed from tests/dyn-drv/old-daemon-error-hack.nix)0
-rw-r--r--tests/functional/dyn-drv/old-daemon-error-hack.sh (renamed from tests/dyn-drv/old-daemon-error-hack.sh)0
-rw-r--r--tests/functional/dyn-drv/recursive-mod-json.nix (renamed from tests/dyn-drv/recursive-mod-json.nix)0
-rw-r--r--tests/functional/dyn-drv/recursive-mod-json.sh (renamed from tests/dyn-drv/recursive-mod-json.sh)0
-rw-r--r--tests/functional/dyn-drv/text-hashed-output.nix (renamed from tests/dyn-drv/text-hashed-output.nix)0
-rw-r--r--tests/functional/dyn-drv/text-hashed-output.sh (renamed from tests/dyn-drv/text-hashed-output.sh)0
-rw-r--r--tests/functional/eval-store.sh (renamed from tests/eval-store.sh)0
-rw-r--r--tests/functional/eval.nix (renamed from tests/eval.nix)0
-rw-r--r--tests/functional/eval.sh (renamed from tests/eval.sh)0
-rw-r--r--tests/functional/experimental-features.sh (renamed from tests/experimental-features.sh)0
-rw-r--r--tests/functional/export-graph.nix (renamed from tests/export-graph.nix)0
-rw-r--r--tests/functional/export-graph.sh (renamed from tests/export-graph.sh)0
-rw-r--r--tests/functional/export.sh (renamed from tests/export.sh)0
-rw-r--r--tests/functional/failing.nix (renamed from tests/failing.nix)0
-rw-r--r--tests/functional/fetchClosure.sh (renamed from tests/fetchClosure.sh)0
-rw-r--r--tests/functional/fetchGit.sh (renamed from tests/fetchGit.sh)0
-rw-r--r--tests/functional/fetchGitRefs.sh (renamed from tests/fetchGitRefs.sh)0
-rw-r--r--tests/functional/fetchGitSubmodules.sh (renamed from tests/fetchGitSubmodules.sh)0
-rw-r--r--tests/functional/fetchMercurial.sh (renamed from tests/fetchMercurial.sh)0
-rw-r--r--tests/functional/fetchPath.sh (renamed from tests/fetchPath.sh)0
-rw-r--r--tests/functional/fetchTree-file.sh (renamed from tests/fetchTree-file.sh)0
-rw-r--r--tests/functional/fetchurl.sh (renamed from tests/fetchurl.sh)0
-rw-r--r--tests/functional/filter-source.nix (renamed from tests/filter-source.nix)0
-rw-r--r--tests/functional/filter-source.sh (renamed from tests/filter-source.sh)0
-rw-r--r--tests/functional/fixed.builder1.sh (renamed from tests/fixed.builder1.sh)0
-rw-r--r--tests/functional/fixed.builder2.sh (renamed from tests/fixed.builder2.sh)0
-rw-r--r--tests/functional/fixed.nix (renamed from tests/fixed.nix)0
-rw-r--r--tests/functional/fixed.sh (renamed from tests/fixed.sh)0
-rw-r--r--tests/functional/flakes/absolute-paths.sh (renamed from tests/flakes/absolute-paths.sh)0
-rw-r--r--tests/functional/flakes/build-paths.sh (renamed from tests/flakes/build-paths.sh)0
-rw-r--r--tests/functional/flakes/bundle.sh (renamed from tests/flakes/bundle.sh)0
-rw-r--r--tests/functional/flakes/check.sh (renamed from tests/flakes/check.sh)0
-rw-r--r--tests/functional/flakes/circular.sh (renamed from tests/flakes/circular.sh)0
-rw-r--r--tests/functional/flakes/common.sh (renamed from tests/flakes/common.sh)0
-rw-r--r--tests/functional/flakes/config.sh (renamed from tests/flakes/config.sh)0
-rw-r--r--tests/functional/flakes/flake-in-submodule.sh (renamed from tests/flakes/flake-in-submodule.sh)0
-rw-r--r--tests/functional/flakes/flakes.sh (renamed from tests/flakes/flakes.sh)0
-rw-r--r--tests/functional/flakes/follow-paths.sh (renamed from tests/flakes/follow-paths.sh)0
-rw-r--r--tests/functional/flakes/init.sh (renamed from tests/flakes/init.sh)0
-rw-r--r--tests/functional/flakes/inputs.sh (renamed from tests/flakes/inputs.sh)0
-rw-r--r--tests/functional/flakes/mercurial.sh (renamed from tests/flakes/mercurial.sh)0
-rw-r--r--tests/functional/flakes/run.sh (renamed from tests/flakes/run.sh)0
-rw-r--r--tests/functional/flakes/search-root.sh (renamed from tests/flakes/search-root.sh)0
-rw-r--r--tests/functional/flakes/show.sh (renamed from tests/flakes/show.sh)0
-rw-r--r--tests/functional/flakes/unlocked-override.sh (renamed from tests/flakes/unlocked-override.sh)0
-rw-r--r--tests/functional/fmt.sh (renamed from tests/fmt.sh)0
-rwxr-xr-xtests/functional/fmt.simple.sh (renamed from tests/fmt.simple.sh)0
-rwxr-xr-xtests/functional/function-trace.sh (renamed from tests/function-trace.sh)0
-rw-r--r--tests/functional/gc-auto.sh (renamed from tests/gc-auto.sh)0
-rw-r--r--tests/functional/gc-concurrent.builder.sh (renamed from tests/gc-concurrent.builder.sh)0
-rw-r--r--tests/functional/gc-concurrent.nix (renamed from tests/gc-concurrent.nix)0
-rw-r--r--tests/functional/gc-concurrent.sh (renamed from tests/gc-concurrent.sh)0
-rw-r--r--tests/functional/gc-concurrent2.builder.sh (renamed from tests/gc-concurrent2.builder.sh)0
-rw-r--r--tests/functional/gc-non-blocking.sh (renamed from tests/gc-non-blocking.sh)0
-rw-r--r--tests/functional/gc-runtime.nix (renamed from tests/gc-runtime.nix)0
-rw-r--r--tests/functional/gc-runtime.sh (renamed from tests/gc-runtime.sh)0
-rw-r--r--tests/functional/gc.sh (renamed from tests/gc.sh)0
-rw-r--r--tests/functional/hash-check.nix (renamed from tests/hash-check.nix)0
-rw-r--r--tests/functional/hash.sh (renamed from tests/hash.sh)0
-rw-r--r--tests/functional/hermetic.nix (renamed from tests/hermetic.nix)0
-rw-r--r--tests/functional/ifd.nix (renamed from tests/ifd.nix)0
-rw-r--r--tests/functional/import-derivation.nix (renamed from tests/import-derivation.nix)0
-rw-r--r--tests/functional/import-derivation.sh (renamed from tests/import-derivation.sh)0
-rw-r--r--tests/functional/impure-derivations.nix (renamed from tests/impure-derivations.nix)0
-rw-r--r--tests/functional/impure-derivations.sh (renamed from tests/impure-derivations.sh)0
-rwxr-xr-xtests/functional/init.sh (renamed from tests/init.sh)0
-rwxr-xr-xtests/functional/install-darwin.sh (renamed from tests/install-darwin.sh)0
-rw-r--r--tests/functional/lang-test-infra.sh (renamed from tests/lang-test-infra.sh)0
-rwxr-xr-xtests/functional/lang.sh (renamed from tests/lang.sh)2
-rw-r--r--tests/functional/lang/binary-data (renamed from tests/lang/binary-data)bin1024 -> 1024 bytes
-rw-r--r--tests/functional/lang/data (renamed from tests/lang/data)0
-rw-r--r--tests/functional/lang/dir1/a.nix (renamed from tests/lang/dir1/a.nix)0
-rw-r--r--tests/functional/lang/dir2/a.nix (renamed from tests/lang/dir2/a.nix)0
-rw-r--r--tests/functional/lang/dir2/b.nix (renamed from tests/lang/dir2/b.nix)0
-rw-r--r--tests/functional/lang/dir3/a.nix (renamed from tests/lang/dir3/a.nix)0
-rw-r--r--tests/functional/lang/dir3/b.nix (renamed from tests/lang/dir3/b.nix)0
-rw-r--r--tests/functional/lang/dir3/c.nix (renamed from tests/lang/dir3/c.nix)0
-rw-r--r--tests/functional/lang/dir4/a.nix (renamed from tests/lang/dir4/a.nix)0
-rw-r--r--tests/functional/lang/dir4/c.nix (renamed from tests/lang/dir4/c.nix)0
-rw-r--r--tests/functional/lang/empty.exp (renamed from tests/lang/empty.exp)0
-rw-r--r--tests/functional/lang/eval-fail-abort.err.exp (renamed from tests/lang/eval-fail-abort.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-abort.nix (renamed from tests/lang/eval-fail-abort.nix)0
-rw-r--r--tests/functional/lang/eval-fail-antiquoted-path.err.exp (renamed from tests/lang/eval-fail-antiquoted-path.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-assert.err.exp (renamed from tests/lang/eval-fail-assert.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-assert.nix (renamed from tests/lang/eval-fail-assert.nix)0
-rw-r--r--tests/functional/lang/eval-fail-bad-antiquote-1.err.exp (renamed from tests/lang/eval-fail-bad-antiquote-1.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-bad-antiquote-2.err.exp (renamed from tests/lang/eval-fail-bad-antiquote-2.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-bad-antiquote-3.err.exp (renamed from tests/lang/eval-fail-bad-antiquote-3.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp (renamed from tests/lang/eval-fail-bad-string-interpolation-1.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-bad-string-interpolation-1.nix (renamed from tests/lang/eval-fail-bad-string-interpolation-1.nix)0
-rw-r--r--tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp (renamed from tests/lang/eval-fail-bad-string-interpolation-2.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-bad-string-interpolation-2.nix (renamed from tests/lang/eval-fail-bad-string-interpolation-2.nix)0
-rw-r--r--tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp (renamed from tests/lang/eval-fail-bad-string-interpolation-3.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-bad-string-interpolation-3.nix (renamed from tests/lang/eval-fail-bad-string-interpolation-3.nix)0
-rw-r--r--tests/functional/lang/eval-fail-blackhole.err.exp (renamed from tests/lang/eval-fail-blackhole.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-blackhole.nix (renamed from tests/lang/eval-fail-blackhole.nix)0
-rw-r--r--tests/functional/lang/eval-fail-deepseq.err.exp (renamed from tests/lang/eval-fail-deepseq.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-deepseq.nix (renamed from tests/lang/eval-fail-deepseq.nix)0
-rw-r--r--tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp (renamed from tests/lang/eval-fail-dup-dynamic-attrs.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-dup-dynamic-attrs.nix (renamed from tests/lang/eval-fail-dup-dynamic-attrs.nix)0
-rw-r--r--tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp (renamed from tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix (renamed from tests/lang/eval-fail-foldlStrict-strict-op-application.nix)0
-rw-r--r--tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp (renamed from tests/lang/eval-fail-fromTOML-timestamps.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-fromTOML-timestamps.nix (renamed from tests/lang/eval-fail-fromTOML-timestamps.nix)0
-rw-r--r--tests/functional/lang/eval-fail-hashfile-missing.err.exp (renamed from tests/lang/eval-fail-hashfile-missing.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-hashfile-missing.nix (renamed from tests/lang/eval-fail-hashfile-missing.nix)0
-rw-r--r--tests/functional/lang/eval-fail-list.err.exp (renamed from tests/lang/eval-fail-list.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-list.nix (renamed from tests/lang/eval-fail-list.nix)0
-rw-r--r--tests/functional/lang/eval-fail-missing-arg.err.exp (renamed from tests/lang/eval-fail-missing-arg.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-missing-arg.nix (renamed from tests/lang/eval-fail-missing-arg.nix)0
-rw-r--r--tests/functional/lang/eval-fail-nonexist-path.err.exp (renamed from tests/lang/eval-fail-nonexist-path.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-nonexist-path.nix (renamed from tests/lang/eval-fail-nonexist-path.nix)0
-rw-r--r--tests/functional/lang/eval-fail-path-slash.err.exp (renamed from tests/lang/eval-fail-path-slash.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-path-slash.nix (renamed from tests/lang/eval-fail-path-slash.nix)0
-rw-r--r--tests/functional/lang/eval-fail-recursion.err.exp (renamed from tests/lang/eval-fail-recursion.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-recursion.nix (renamed from tests/lang/eval-fail-recursion.nix)0
-rw-r--r--tests/functional/lang/eval-fail-remove.err.exp (renamed from tests/lang/eval-fail-remove.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-remove.nix (renamed from tests/lang/eval-fail-remove.nix)0
-rw-r--r--tests/functional/lang/eval-fail-scope-5.err.exp (renamed from tests/lang/eval-fail-scope-5.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-scope-5.nix (renamed from tests/lang/eval-fail-scope-5.nix)0
-rw-r--r--tests/functional/lang/eval-fail-seq.err.exp (renamed from tests/lang/eval-fail-seq.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-seq.nix (renamed from tests/lang/eval-fail-seq.nix)0
-rw-r--r--tests/functional/lang/eval-fail-set-override.err.exp (renamed from tests/lang/eval-fail-set-override.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-set-override.nix (renamed from tests/lang/eval-fail-set-override.nix)0
-rw-r--r--tests/functional/lang/eval-fail-set.err.exp (renamed from tests/lang/eval-fail-set.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-set.nix (renamed from tests/lang/eval-fail-set.nix)0
-rw-r--r--tests/functional/lang/eval-fail-substring.err.exp (renamed from tests/lang/eval-fail-substring.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-substring.nix (renamed from tests/lang/eval-fail-substring.nix)0
-rw-r--r--tests/functional/lang/eval-fail-to-path.err.exp (renamed from tests/lang/eval-fail-to-path.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-to-path.nix (renamed from tests/lang/eval-fail-to-path.nix)0
-rw-r--r--tests/functional/lang/eval-fail-toJSON.err.exp (renamed from tests/lang/eval-fail-toJSON.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-toJSON.nix (renamed from tests/lang/eval-fail-toJSON.nix)0
-rw-r--r--tests/functional/lang/eval-fail-undeclared-arg.err.exp (renamed from tests/lang/eval-fail-undeclared-arg.err.exp)0
-rw-r--r--tests/functional/lang/eval-fail-undeclared-arg.nix (renamed from tests/lang/eval-fail-undeclared-arg.nix)0
-rw-r--r--tests/functional/lang/eval-okay-any-all.exp (renamed from tests/lang/eval-okay-any-all.exp)0
-rw-r--r--tests/functional/lang/eval-okay-any-all.nix (renamed from tests/lang/eval-okay-any-all.nix)0
-rw-r--r--tests/functional/lang/eval-okay-arithmetic.exp (renamed from tests/lang/eval-okay-arithmetic.exp)0
-rw-r--r--tests/functional/lang/eval-okay-arithmetic.nix (renamed from tests/lang/eval-okay-arithmetic.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrnames.exp (renamed from tests/lang/eval-okay-attrnames.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrnames.nix (renamed from tests/lang/eval-okay-attrnames.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrs.exp (renamed from tests/lang/eval-okay-attrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrs.nix (renamed from tests/lang/eval-okay-attrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrs2.exp (renamed from tests/lang/eval-okay-attrs2.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrs2.nix (renamed from tests/lang/eval-okay-attrs2.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrs3.exp (renamed from tests/lang/eval-okay-attrs3.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrs3.nix (renamed from tests/lang/eval-okay-attrs3.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrs4.exp (renamed from tests/lang/eval-okay-attrs4.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrs4.nix (renamed from tests/lang/eval-okay-attrs4.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrs5.exp (renamed from tests/lang/eval-okay-attrs5.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrs5.nix (renamed from tests/lang/eval-okay-attrs5.nix)0
-rw-r--r--tests/functional/lang/eval-okay-attrs6.exp (renamed from tests/lang/eval-okay-attrs6.exp)0
-rw-r--r--tests/functional/lang/eval-okay-attrs6.nix (renamed from tests/lang/eval-okay-attrs6.nix)0
-rw-r--r--tests/functional/lang/eval-okay-autoargs.exp (renamed from tests/lang/eval-okay-autoargs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-autoargs.flags (renamed from tests/lang/eval-okay-autoargs.flags)0
-rw-r--r--tests/functional/lang/eval-okay-autoargs.nix (renamed from tests/lang/eval-okay-autoargs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-backslash-newline-1.exp (renamed from tests/lang/eval-okay-backslash-newline-1.exp)0
-rw-r--r--tests/functional/lang/eval-okay-backslash-newline-1.nix (renamed from tests/lang/eval-okay-backslash-newline-1.nix)0
-rw-r--r--tests/functional/lang/eval-okay-backslash-newline-2.exp (renamed from tests/lang/eval-okay-backslash-newline-2.exp)0
-rw-r--r--tests/functional/lang/eval-okay-backslash-newline-2.nix (renamed from tests/lang/eval-okay-backslash-newline-2.nix)0
-rw-r--r--tests/functional/lang/eval-okay-builtins-add.exp (renamed from tests/lang/eval-okay-builtins-add.exp)0
-rw-r--r--tests/functional/lang/eval-okay-builtins-add.nix (renamed from tests/lang/eval-okay-builtins-add.nix)0
-rw-r--r--tests/functional/lang/eval-okay-builtins.exp (renamed from tests/lang/eval-okay-builtins.exp)0
-rw-r--r--tests/functional/lang/eval-okay-builtins.nix (renamed from tests/lang/eval-okay-builtins.nix)0
-rw-r--r--tests/functional/lang/eval-okay-callable-attrs.exp (renamed from tests/lang/eval-okay-callable-attrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-callable-attrs.nix (renamed from tests/lang/eval-okay-callable-attrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-catattrs.exp (renamed from tests/lang/eval-okay-catattrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-catattrs.nix (renamed from tests/lang/eval-okay-catattrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-closure.exp (renamed from tests/lang/eval-okay-closure.exp)0
-rw-r--r--tests/functional/lang/eval-okay-closure.exp.xml (renamed from tests/lang/eval-okay-closure.exp.xml)0
-rw-r--r--tests/functional/lang/eval-okay-closure.nix (renamed from tests/lang/eval-okay-closure.nix)0
-rw-r--r--tests/functional/lang/eval-okay-comments.exp (renamed from tests/lang/eval-okay-comments.exp)0
-rw-r--r--tests/functional/lang/eval-okay-comments.nix (renamed from tests/lang/eval-okay-comments.nix)0
-rw-r--r--tests/functional/lang/eval-okay-concat.exp (renamed from tests/lang/eval-okay-concat.exp)0
-rw-r--r--tests/functional/lang/eval-okay-concat.nix (renamed from tests/lang/eval-okay-concat.nix)0
-rw-r--r--tests/functional/lang/eval-okay-concatmap.exp (renamed from tests/lang/eval-okay-concatmap.exp)0
-rw-r--r--tests/functional/lang/eval-okay-concatmap.nix (renamed from tests/lang/eval-okay-concatmap.nix)0
-rw-r--r--tests/functional/lang/eval-okay-concatstringssep.exp (renamed from tests/lang/eval-okay-concatstringssep.exp)0
-rw-r--r--tests/functional/lang/eval-okay-concatstringssep.nix (renamed from tests/lang/eval-okay-concatstringssep.nix)0
-rw-r--r--tests/functional/lang/eval-okay-context-introspection.exp (renamed from tests/lang/eval-okay-context-introspection.exp)0
-rw-r--r--tests/functional/lang/eval-okay-context-introspection.nix (renamed from tests/lang/eval-okay-context-introspection.nix)0
-rw-r--r--tests/functional/lang/eval-okay-context.exp (renamed from tests/lang/eval-okay-context.exp)0
-rw-r--r--tests/functional/lang/eval-okay-context.nix (renamed from tests/lang/eval-okay-context.nix)0
-rw-r--r--tests/functional/lang/eval-okay-curpos.exp (renamed from tests/lang/eval-okay-curpos.exp)0
-rw-r--r--tests/functional/lang/eval-okay-curpos.nix (renamed from tests/lang/eval-okay-curpos.nix)0
-rw-r--r--tests/functional/lang/eval-okay-deepseq.exp (renamed from tests/lang/eval-okay-deepseq.exp)0
-rw-r--r--tests/functional/lang/eval-okay-deepseq.nix (renamed from tests/lang/eval-okay-deepseq.nix)0
-rw-r--r--tests/functional/lang/eval-okay-delayed-with-inherit.exp (renamed from tests/lang/eval-okay-delayed-with-inherit.exp)0
-rw-r--r--tests/functional/lang/eval-okay-delayed-with-inherit.nix (renamed from tests/lang/eval-okay-delayed-with-inherit.nix)0
-rw-r--r--tests/functional/lang/eval-okay-delayed-with.exp (renamed from tests/lang/eval-okay-delayed-with.exp)0
-rw-r--r--tests/functional/lang/eval-okay-delayed-with.nix (renamed from tests/lang/eval-okay-delayed-with.nix)0
-rw-r--r--tests/functional/lang/eval-okay-dynamic-attrs-2.exp (renamed from tests/lang/eval-okay-dynamic-attrs-2.exp)0
-rw-r--r--tests/functional/lang/eval-okay-dynamic-attrs-2.nix (renamed from tests/lang/eval-okay-dynamic-attrs-2.nix)0
-rw-r--r--tests/functional/lang/eval-okay-dynamic-attrs-bare.exp (renamed from tests/lang/eval-okay-dynamic-attrs-bare.exp)0
-rw-r--r--tests/functional/lang/eval-okay-dynamic-attrs-bare.nix (renamed from tests/lang/eval-okay-dynamic-attrs-bare.nix)0
-rw-r--r--tests/functional/lang/eval-okay-dynamic-attrs.exp (renamed from tests/lang/eval-okay-dynamic-attrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-dynamic-attrs.nix (renamed from tests/lang/eval-okay-dynamic-attrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-elem.exp (renamed from tests/lang/eval-okay-elem.exp)0
-rw-r--r--tests/functional/lang/eval-okay-elem.nix (renamed from tests/lang/eval-okay-elem.nix)0
-rw-r--r--tests/functional/lang/eval-okay-empty-args.exp (renamed from tests/lang/eval-okay-empty-args.exp)0
-rw-r--r--tests/functional/lang/eval-okay-empty-args.nix (renamed from tests/lang/eval-okay-empty-args.nix)0
-rw-r--r--tests/functional/lang/eval-okay-eq-derivations.exp (renamed from tests/lang/eval-okay-eq-derivations.exp)0
-rw-r--r--tests/functional/lang/eval-okay-eq-derivations.nix (renamed from tests/lang/eval-okay-eq-derivations.nix)0
-rw-r--r--tests/functional/lang/eval-okay-eq.exp (renamed from tests/lang/eval-okay-eq.exp)0
-rw-r--r--tests/functional/lang/eval-okay-eq.nix (renamed from tests/lang/eval-okay-eq.nix)0
-rw-r--r--tests/functional/lang/eval-okay-filter.exp (renamed from tests/lang/eval-okay-filter.exp)0
-rw-r--r--tests/functional/lang/eval-okay-filter.nix (renamed from tests/lang/eval-okay-filter.nix)0
-rw-r--r--tests/functional/lang/eval-okay-flake-ref-to-string.exp (renamed from tests/lang/eval-okay-flake-ref-to-string.exp)0
-rw-r--r--tests/functional/lang/eval-okay-flake-ref-to-string.nix (renamed from tests/lang/eval-okay-flake-ref-to-string.nix)0
-rw-r--r--tests/functional/lang/eval-okay-flatten.exp (renamed from tests/lang/eval-okay-flatten.exp)0
-rw-r--r--tests/functional/lang/eval-okay-flatten.nix (renamed from tests/lang/eval-okay-flatten.nix)0
-rw-r--r--tests/functional/lang/eval-okay-float.exp (renamed from tests/lang/eval-okay-float.exp)0
-rw-r--r--tests/functional/lang/eval-okay-float.nix (renamed from tests/lang/eval-okay-float.nix)0
-rw-r--r--tests/functional/lang/eval-okay-floor-ceil.exp (renamed from tests/lang/eval-okay-floor-ceil.exp)0
-rw-r--r--tests/functional/lang/eval-okay-floor-ceil.nix (renamed from tests/lang/eval-okay-floor-ceil.nix)0
-rw-r--r--tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp (renamed from tests/lang/eval-okay-foldlStrict-lazy-elements.exp)0
-rw-r--r--tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix (renamed from tests/lang/eval-okay-foldlStrict-lazy-elements.nix)0
-rw-r--r--tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp (renamed from tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp)0
-rw-r--r--tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix (renamed from tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix)0
-rw-r--r--tests/functional/lang/eval-okay-foldlStrict.exp (renamed from tests/lang/eval-okay-foldlStrict.exp)0
-rw-r--r--tests/functional/lang/eval-okay-foldlStrict.nix (renamed from tests/lang/eval-okay-foldlStrict.nix)0
-rw-r--r--tests/functional/lang/eval-okay-fromTOML-timestamps.exp (renamed from tests/lang/eval-okay-fromTOML-timestamps.exp)0
-rw-r--r--tests/functional/lang/eval-okay-fromTOML-timestamps.flags (renamed from tests/lang/eval-okay-fromTOML-timestamps.flags)0
-rw-r--r--tests/functional/lang/eval-okay-fromTOML-timestamps.nix (renamed from tests/lang/eval-okay-fromTOML-timestamps.nix)0
-rw-r--r--tests/functional/lang/eval-okay-fromTOML.exp (renamed from tests/lang/eval-okay-fromTOML.exp)0
-rw-r--r--tests/functional/lang/eval-okay-fromTOML.nix (renamed from tests/lang/eval-okay-fromTOML.nix)0
-rw-r--r--tests/functional/lang/eval-okay-fromjson-escapes.exp (renamed from tests/lang/eval-okay-fromjson-escapes.exp)0
-rw-r--r--tests/functional/lang/eval-okay-fromjson-escapes.nix (renamed from tests/lang/eval-okay-fromjson-escapes.nix)0
-rw-r--r--tests/functional/lang/eval-okay-fromjson.exp (renamed from tests/lang/eval-okay-fromjson.exp)0
-rw-r--r--tests/functional/lang/eval-okay-fromjson.nix (renamed from tests/lang/eval-okay-fromjson.nix)0
-rw-r--r--tests/functional/lang/eval-okay-functionargs.exp (renamed from tests/lang/eval-okay-functionargs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-functionargs.exp.xml (renamed from tests/lang/eval-okay-functionargs.exp.xml)0
-rw-r--r--tests/functional/lang/eval-okay-functionargs.nix (renamed from tests/lang/eval-okay-functionargs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-getattrpos-functionargs.exp (renamed from tests/lang/eval-okay-getattrpos-functionargs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-getattrpos-functionargs.nix (renamed from tests/lang/eval-okay-getattrpos-functionargs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-getattrpos-undefined.exp (renamed from tests/lang/eval-okay-getattrpos-undefined.exp)0
-rw-r--r--tests/functional/lang/eval-okay-getattrpos-undefined.nix (renamed from tests/lang/eval-okay-getattrpos-undefined.nix)0
-rw-r--r--tests/functional/lang/eval-okay-getattrpos.exp (renamed from tests/lang/eval-okay-getattrpos.exp)0
-rw-r--r--tests/functional/lang/eval-okay-getattrpos.nix (renamed from tests/lang/eval-okay-getattrpos.nix)0
-rw-r--r--tests/functional/lang/eval-okay-getenv.exp (renamed from tests/lang/eval-okay-getenv.exp)0
-rw-r--r--tests/functional/lang/eval-okay-getenv.nix (renamed from tests/lang/eval-okay-getenv.nix)0
-rw-r--r--tests/functional/lang/eval-okay-groupBy.exp (renamed from tests/lang/eval-okay-groupBy.exp)0
-rw-r--r--tests/functional/lang/eval-okay-groupBy.nix (renamed from tests/lang/eval-okay-groupBy.nix)0
-rw-r--r--tests/functional/lang/eval-okay-hash.exp (renamed from tests/lang/eval-okay-hash.exp)0
-rw-r--r--tests/functional/lang/eval-okay-hashfile.exp (renamed from tests/lang/eval-okay-hashfile.exp)0
-rw-r--r--tests/functional/lang/eval-okay-hashfile.nix (renamed from tests/lang/eval-okay-hashfile.nix)0
-rw-r--r--tests/functional/lang/eval-okay-hashstring.exp (renamed from tests/lang/eval-okay-hashstring.exp)0
-rw-r--r--tests/functional/lang/eval-okay-hashstring.nix (renamed from tests/lang/eval-okay-hashstring.nix)0
-rw-r--r--tests/functional/lang/eval-okay-if.exp (renamed from tests/lang/eval-okay-if.exp)0
-rw-r--r--tests/functional/lang/eval-okay-if.nix (renamed from tests/lang/eval-okay-if.nix)0
-rw-r--r--tests/functional/lang/eval-okay-import.exp (renamed from tests/lang/eval-okay-import.exp)0
-rw-r--r--tests/functional/lang/eval-okay-import.nix (renamed from tests/lang/eval-okay-import.nix)0
-rw-r--r--tests/functional/lang/eval-okay-ind-string.exp (renamed from tests/lang/eval-okay-ind-string.exp)0
-rw-r--r--tests/functional/lang/eval-okay-ind-string.nix (renamed from tests/lang/eval-okay-ind-string.nix)0
-rw-r--r--tests/functional/lang/eval-okay-intersectAttrs.exp (renamed from tests/lang/eval-okay-intersectAttrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-intersectAttrs.nix (renamed from tests/lang/eval-okay-intersectAttrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-let.exp (renamed from tests/lang/eval-okay-let.exp)0
-rw-r--r--tests/functional/lang/eval-okay-let.nix (renamed from tests/lang/eval-okay-let.nix)0
-rw-r--r--tests/functional/lang/eval-okay-list.exp (renamed from tests/lang/eval-okay-list.exp)0
-rw-r--r--tests/functional/lang/eval-okay-list.nix (renamed from tests/lang/eval-okay-list.nix)0
-rw-r--r--tests/functional/lang/eval-okay-listtoattrs.exp (renamed from tests/lang/eval-okay-listtoattrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-listtoattrs.nix (renamed from tests/lang/eval-okay-listtoattrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-logic.exp (renamed from tests/lang/eval-okay-logic.exp)0
-rw-r--r--tests/functional/lang/eval-okay-logic.nix (renamed from tests/lang/eval-okay-logic.nix)0
-rw-r--r--tests/functional/lang/eval-okay-map.exp (renamed from tests/lang/eval-okay-map.exp)0
-rw-r--r--tests/functional/lang/eval-okay-map.nix (renamed from tests/lang/eval-okay-map.nix)0
-rw-r--r--tests/functional/lang/eval-okay-mapattrs.exp (renamed from tests/lang/eval-okay-mapattrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-mapattrs.nix (renamed from tests/lang/eval-okay-mapattrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-merge-dynamic-attrs.exp (renamed from tests/lang/eval-okay-merge-dynamic-attrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-merge-dynamic-attrs.nix (renamed from tests/lang/eval-okay-merge-dynamic-attrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-nested-with.exp (renamed from tests/lang/eval-okay-nested-with.exp)0
-rw-r--r--tests/functional/lang/eval-okay-nested-with.nix (renamed from tests/lang/eval-okay-nested-with.nix)0
-rw-r--r--tests/functional/lang/eval-okay-new-let.exp (renamed from tests/lang/eval-okay-new-let.exp)0
-rw-r--r--tests/functional/lang/eval-okay-new-let.nix (renamed from tests/lang/eval-okay-new-let.nix)0
-rw-r--r--tests/functional/lang/eval-okay-null-dynamic-attrs.exp (renamed from tests/lang/eval-okay-null-dynamic-attrs.exp)0
-rw-r--r--tests/functional/lang/eval-okay-null-dynamic-attrs.nix (renamed from tests/lang/eval-okay-null-dynamic-attrs.nix)0
-rw-r--r--tests/functional/lang/eval-okay-overrides.exp (renamed from tests/lang/eval-okay-overrides.exp)0
-rw-r--r--tests/functional/lang/eval-okay-overrides.nix (renamed from tests/lang/eval-okay-overrides.nix)0
-rw-r--r--tests/functional/lang/eval-okay-parse-flake-ref.exp (renamed from tests/lang/eval-okay-parse-flake-ref.exp)0
-rw-r--r--tests/functional/lang/eval-okay-parse-flake-ref.nix (renamed from tests/lang/eval-okay-parse-flake-ref.nix)0
-rw-r--r--tests/functional/lang/eval-okay-partition.exp (renamed from tests/lang/eval-okay-partition.exp)0
-rw-r--r--tests/functional/lang/eval-okay-partition.nix (renamed from tests/lang/eval-okay-partition.nix)0
-rw-r--r--tests/functional/lang/eval-okay-path-string-interpolation.exp (renamed from tests/lang/eval-okay-path-string-interpolation.exp)0
-rw-r--r--tests/functional/lang/eval-okay-path-string-interpolation.nix (renamed from tests/lang/eval-okay-path-string-interpolation.nix)0
-rw-r--r--tests/functional/lang/eval-okay-path.exp (renamed from tests/lang/eval-okay-path.exp)0
-rw-r--r--tests/functional/lang/eval-okay-path.nix (renamed from tests/lang/eval-okay-path.nix)0
-rw-r--r--tests/functional/lang/eval-okay-pathexists.exp (renamed from tests/lang/eval-okay-pathexists.exp)0
-rw-r--r--tests/functional/lang/eval-okay-pathexists.nix29
-rw-r--r--tests/functional/lang/eval-okay-patterns.exp (renamed from tests/lang/eval-okay-patterns.exp)0
-rw-r--r--tests/functional/lang/eval-okay-patterns.nix (renamed from tests/lang/eval-okay-patterns.nix)0
-rw-r--r--tests/functional/lang/eval-okay-print.err.exp (renamed from tests/lang/eval-okay-print.err.exp)0
-rw-r--r--tests/functional/lang/eval-okay-print.exp (renamed from tests/lang/eval-okay-print.exp)0
-rw-r--r--tests/functional/lang/eval-okay-print.nix (renamed from tests/lang/eval-okay-print.nix)0
-rw-r--r--tests/functional/lang/eval-okay-readDir.exp (renamed from tests/lang/eval-okay-readDir.exp)0
-rw-r--r--tests/functional/lang/eval-okay-readDir.nix (renamed from tests/lang/eval-okay-readDir.nix)0
-rw-r--r--tests/functional/lang/eval-okay-readFileType.exp (renamed from tests/lang/eval-okay-readFileType.exp)0
-rw-r--r--tests/functional/lang/eval-okay-readFileType.nix (renamed from tests/lang/eval-okay-readFileType.nix)0
-rw-r--r--tests/functional/lang/eval-okay-readfile.exp (renamed from tests/lang/eval-okay-readfile.exp)0
-rw-r--r--tests/functional/lang/eval-okay-readfile.nix (renamed from tests/lang/eval-okay-readfile.nix)0
-rw-r--r--tests/functional/lang/eval-okay-redefine-builtin.exp (renamed from tests/lang/eval-okay-redefine-builtin.exp)0
-rw-r--r--tests/functional/lang/eval-okay-redefine-builtin.nix (renamed from tests/lang/eval-okay-redefine-builtin.nix)0
-rw-r--r--tests/functional/lang/eval-okay-regex-match.exp (renamed from tests/lang/eval-okay-regex-match.exp)0
-rw-r--r--tests/functional/lang/eval-okay-regex-match.nix (renamed from tests/lang/eval-okay-regex-match.nix)0
-rw-r--r--tests/functional/lang/eval-okay-regex-split.exp (renamed from tests/lang/eval-okay-regex-split.exp)0
-rw-r--r--tests/functional/lang/eval-okay-regex-split.nix (renamed from tests/lang/eval-okay-regex-split.nix)0
-rw-r--r--tests/functional/lang/eval-okay-regression-20220122.exp (renamed from tests/lang/eval-okay-regression-20220122.exp)0
-rw-r--r--tests/functional/lang/eval-okay-regression-20220122.nix (renamed from tests/lang/eval-okay-regression-20220122.nix)0
-rw-r--r--tests/functional/lang/eval-okay-regression-20220125.exp (renamed from tests/lang/eval-okay-regression-20220125.exp)0
-rw-r--r--tests/functional/lang/eval-okay-regression-20220125.nix (renamed from tests/lang/eval-okay-regression-20220125.nix)0
-rw-r--r--tests/functional/lang/eval-okay-remove.exp (renamed from tests/lang/eval-okay-remove.exp)0
-rw-r--r--tests/functional/lang/eval-okay-remove.nix (renamed from tests/lang/eval-okay-remove.nix)0
-rw-r--r--tests/functional/lang/eval-okay-replacestrings.exp (renamed from tests/lang/eval-okay-replacestrings.exp)0
-rw-r--r--tests/functional/lang/eval-okay-replacestrings.nix (renamed from tests/lang/eval-okay-replacestrings.nix)0
-rw-r--r--tests/functional/lang/eval-okay-scope-1.exp (renamed from tests/lang/eval-okay-scope-1.exp)0
-rw-r--r--tests/functional/lang/eval-okay-scope-1.nix (renamed from tests/lang/eval-okay-scope-1.nix)0
-rw-r--r--tests/functional/lang/eval-okay-scope-2.exp (renamed from tests/lang/eval-okay-scope-2.exp)0
-rw-r--r--tests/functional/lang/eval-okay-scope-2.nix (renamed from tests/lang/eval-okay-scope-2.nix)0
-rw-r--r--tests/functional/lang/eval-okay-scope-3.exp (renamed from tests/lang/eval-okay-scope-3.exp)0
-rw-r--r--tests/functional/lang/eval-okay-scope-3.nix (renamed from tests/lang/eval-okay-scope-3.nix)0
-rw-r--r--tests/functional/lang/eval-okay-scope-4.exp (renamed from tests/lang/eval-okay-scope-4.exp)0
-rw-r--r--tests/functional/lang/eval-okay-scope-4.nix (renamed from tests/lang/eval-okay-scope-4.nix)0
-rw-r--r--tests/functional/lang/eval-okay-scope-6.exp (renamed from tests/lang/eval-okay-scope-6.exp)0
-rw-r--r--tests/functional/lang/eval-okay-scope-6.nix (renamed from tests/lang/eval-okay-scope-6.nix)0
-rw-r--r--tests/functional/lang/eval-okay-scope-7.exp (renamed from tests/lang/eval-okay-scope-7.exp)0
-rw-r--r--tests/functional/lang/eval-okay-scope-7.nix (renamed from tests/lang/eval-okay-scope-7.nix)0
-rw-r--r--tests/functional/lang/eval-okay-search-path.exp (renamed from tests/lang/eval-okay-search-path.exp)0
-rw-r--r--tests/functional/lang/eval-okay-search-path.flags (renamed from tests/lang/eval-okay-search-path.flags)0
-rw-r--r--tests/functional/lang/eval-okay-search-path.nix (renamed from tests/lang/eval-okay-search-path.nix)0
-rw-r--r--tests/functional/lang/eval-okay-seq.exp (renamed from tests/lang/eval-okay-seq.exp)0
-rw-r--r--tests/functional/lang/eval-okay-seq.nix (renamed from tests/lang/eval-okay-seq.nix)0
-rw-r--r--tests/functional/lang/eval-okay-sort.exp (renamed from tests/lang/eval-okay-sort.exp)0
-rw-r--r--tests/functional/lang/eval-okay-sort.nix (renamed from tests/lang/eval-okay-sort.nix)0
-rw-r--r--tests/functional/lang/eval-okay-splitversion.exp (renamed from tests/lang/eval-okay-splitversion.exp)0
-rw-r--r--tests/functional/lang/eval-okay-splitversion.nix (renamed from tests/lang/eval-okay-splitversion.nix)0
-rw-r--r--tests/functional/lang/eval-okay-string.exp (renamed from tests/lang/eval-okay-string.exp)0
-rw-r--r--tests/functional/lang/eval-okay-string.nix (renamed from tests/lang/eval-okay-string.nix)0
-rw-r--r--tests/functional/lang/eval-okay-strings-as-attrs-names.exp (renamed from tests/lang/eval-okay-strings-as-attrs-names.exp)0
-rw-r--r--tests/functional/lang/eval-okay-strings-as-attrs-names.nix (renamed from tests/lang/eval-okay-strings-as-attrs-names.nix)0
-rw-r--r--tests/functional/lang/eval-okay-substring.exp (renamed from tests/lang/eval-okay-substring.exp)0
-rw-r--r--tests/functional/lang/eval-okay-substring.nix (renamed from tests/lang/eval-okay-substring.nix)0
-rw-r--r--tests/functional/lang/eval-okay-tail-call-1.exp-disabled (renamed from tests/lang/eval-okay-tail-call-1.exp-disabled)0
-rw-r--r--tests/functional/lang/eval-okay-tail-call-1.nix (renamed from tests/lang/eval-okay-tail-call-1.nix)0
-rw-r--r--tests/functional/lang/eval-okay-tojson.exp (renamed from tests/lang/eval-okay-tojson.exp)0
-rw-r--r--tests/functional/lang/eval-okay-tojson.nix (renamed from tests/lang/eval-okay-tojson.nix)0
-rw-r--r--tests/functional/lang/eval-okay-toxml.exp (renamed from tests/lang/eval-okay-toxml.exp)0
-rw-r--r--tests/functional/lang/eval-okay-toxml.nix (renamed from tests/lang/eval-okay-toxml.nix)0
-rw-r--r--tests/functional/lang/eval-okay-toxml2.exp (renamed from tests/lang/eval-okay-toxml2.exp)0
-rw-r--r--tests/functional/lang/eval-okay-toxml2.nix (renamed from tests/lang/eval-okay-toxml2.nix)0
-rw-r--r--tests/functional/lang/eval-okay-tryeval.exp (renamed from tests/lang/eval-okay-tryeval.exp)0
-rw-r--r--tests/functional/lang/eval-okay-tryeval.nix (renamed from tests/lang/eval-okay-tryeval.nix)0
-rw-r--r--tests/functional/lang/eval-okay-types.exp (renamed from tests/lang/eval-okay-types.exp)0
-rw-r--r--tests/functional/lang/eval-okay-types.nix (renamed from tests/lang/eval-okay-types.nix)0
-rw-r--r--tests/functional/lang/eval-okay-versions.exp (renamed from tests/lang/eval-okay-versions.exp)0
-rw-r--r--tests/functional/lang/eval-okay-versions.nix (renamed from tests/lang/eval-okay-versions.nix)0
-rw-r--r--tests/functional/lang/eval-okay-with.exp (renamed from tests/lang/eval-okay-with.exp)0
-rw-r--r--tests/functional/lang/eval-okay-with.nix (renamed from tests/lang/eval-okay-with.nix)0
-rw-r--r--tests/functional/lang/eval-okay-xml.exp.xml (renamed from tests/lang/eval-okay-xml.exp.xml)0
-rw-r--r--tests/functional/lang/eval-okay-xml.nix (renamed from tests/lang/eval-okay-xml.nix)0
-rw-r--r--tests/functional/lang/eval-okay-zipAttrsWith.exp (renamed from tests/lang/eval-okay-zipAttrsWith.exp)0
-rw-r--r--tests/functional/lang/eval-okay-zipAttrsWith.nix (renamed from tests/lang/eval-okay-zipAttrsWith.nix)0
-rw-r--r--tests/functional/lang/framework.sh (renamed from tests/lang/framework.sh)0
-rw-r--r--tests/functional/lang/imported.nix (renamed from tests/lang/imported.nix)0
-rw-r--r--tests/functional/lang/imported2.nix (renamed from tests/lang/imported2.nix)0
-rw-r--r--tests/functional/lang/lib.nix (renamed from tests/lang/lib.nix)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-1.err.exp (renamed from tests/lang/parse-fail-dup-attrs-1.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-1.nix (renamed from tests/lang/parse-fail-dup-attrs-1.nix)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-2.err.exp (renamed from tests/lang/parse-fail-dup-attrs-2.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-2.nix (renamed from tests/lang/parse-fail-dup-attrs-2.nix)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-3.err.exp (renamed from tests/lang/parse-fail-dup-attrs-3.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-3.nix (renamed from tests/lang/parse-fail-dup-attrs-3.nix)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-4.err.exp (renamed from tests/lang/parse-fail-dup-attrs-4.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-4.nix (renamed from tests/lang/parse-fail-dup-attrs-4.nix)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-6.err.exp (renamed from tests/lang/parse-fail-dup-attrs-6.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-7.err.exp (renamed from tests/lang/parse-fail-dup-attrs-7.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-attrs-7.nix (renamed from tests/lang/parse-fail-dup-attrs-7.nix)0
-rw-r--r--tests/functional/lang/parse-fail-dup-formals.err.exp (renamed from tests/lang/parse-fail-dup-formals.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-dup-formals.nix (renamed from tests/lang/parse-fail-dup-formals.nix)0
-rw-r--r--tests/functional/lang/parse-fail-eof-in-string.err.exp (renamed from tests/lang/parse-fail-eof-in-string.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-eof-in-string.nix (renamed from tests/lang/parse-fail-eof-in-string.nix)0
-rw-r--r--tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp (renamed from tests/lang/parse-fail-mixed-nested-attrs1.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-mixed-nested-attrs1.nix (renamed from tests/lang/parse-fail-mixed-nested-attrs1.nix)0
-rw-r--r--tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp (renamed from tests/lang/parse-fail-mixed-nested-attrs2.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-mixed-nested-attrs2.nix (renamed from tests/lang/parse-fail-mixed-nested-attrs2.nix)0
-rw-r--r--tests/functional/lang/parse-fail-patterns-1.err.exp (renamed from tests/lang/parse-fail-patterns-1.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-patterns-1.nix (renamed from tests/lang/parse-fail-patterns-1.nix)0
-rw-r--r--tests/functional/lang/parse-fail-regression-20060610.err.exp (renamed from tests/lang/parse-fail-regression-20060610.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-regression-20060610.nix (renamed from tests/lang/parse-fail-regression-20060610.nix)0
-rw-r--r--tests/functional/lang/parse-fail-undef-var-2.err.exp (renamed from tests/lang/parse-fail-undef-var-2.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-undef-var-2.nix (renamed from tests/lang/parse-fail-undef-var-2.nix)0
-rw-r--r--tests/functional/lang/parse-fail-undef-var.err.exp (renamed from tests/lang/parse-fail-undef-var.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-undef-var.nix (renamed from tests/lang/parse-fail-undef-var.nix)0
-rw-r--r--tests/functional/lang/parse-fail-utf8.err.exp (renamed from tests/lang/parse-fail-utf8.err.exp)0
-rw-r--r--tests/functional/lang/parse-fail-utf8.nix (renamed from tests/lang/parse-fail-utf8.nix)0
-rw-r--r--tests/functional/lang/parse-okay-1.exp (renamed from tests/lang/parse-okay-1.exp)0
-rw-r--r--tests/functional/lang/parse-okay-1.nix (renamed from tests/lang/parse-okay-1.nix)0
-rw-r--r--tests/functional/lang/parse-okay-crlf.exp (renamed from tests/lang/parse-okay-crlf.exp)0
-rw-r--r--tests/functional/lang/parse-okay-crlf.nix (renamed from tests/lang/parse-okay-crlf.nix)0
-rw-r--r--tests/functional/lang/parse-okay-dup-attrs-5.exp (renamed from tests/lang/parse-okay-dup-attrs-5.exp)0
-rw-r--r--tests/functional/lang/parse-okay-dup-attrs-5.nix (renamed from tests/lang/parse-okay-dup-attrs-5.nix)0
-rw-r--r--tests/functional/lang/parse-okay-dup-attrs-6.exp (renamed from tests/lang/parse-okay-dup-attrs-6.exp)0
-rw-r--r--tests/functional/lang/parse-okay-dup-attrs-6.nix (renamed from tests/lang/parse-okay-dup-attrs-6.nix)0
-rw-r--r--tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp (renamed from tests/lang/parse-okay-mixed-nested-attrs-1.exp)0
-rw-r--r--tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix (renamed from tests/lang/parse-okay-mixed-nested-attrs-1.nix)0
-rw-r--r--tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp (renamed from tests/lang/parse-okay-mixed-nested-attrs-2.exp)0
-rw-r--r--tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix (renamed from tests/lang/parse-okay-mixed-nested-attrs-2.nix)0
-rw-r--r--tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp (renamed from tests/lang/parse-okay-mixed-nested-attrs-3.exp)0
-rw-r--r--tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix (renamed from tests/lang/parse-okay-mixed-nested-attrs-3.nix)0
-rw-r--r--tests/functional/lang/parse-okay-regression-20041027.exp (renamed from tests/lang/parse-okay-regression-20041027.exp)0
-rw-r--r--tests/functional/lang/parse-okay-regression-20041027.nix (renamed from tests/lang/parse-okay-regression-20041027.nix)0
-rw-r--r--tests/functional/lang/parse-okay-regression-751.exp (renamed from tests/lang/parse-okay-regression-751.exp)0
-rw-r--r--tests/functional/lang/parse-okay-regression-751.nix (renamed from tests/lang/parse-okay-regression-751.nix)0
-rw-r--r--tests/functional/lang/parse-okay-subversion.exp (renamed from tests/lang/parse-okay-subversion.exp)0
-rw-r--r--tests/functional/lang/parse-okay-subversion.nix (renamed from tests/lang/parse-okay-subversion.nix)0
-rw-r--r--tests/functional/lang/parse-okay-url.exp (renamed from tests/lang/parse-okay-url.exp)0
-rw-r--r--tests/functional/lang/parse-okay-url.nix (renamed from tests/lang/parse-okay-url.nix)0
-rw-r--r--tests/functional/lang/readDir/bar (renamed from tests/lang/readDir/bar)0
-rw-r--r--tests/functional/lang/readDir/foo/git-hates-directories (renamed from tests/lang/readDir/foo/git-hates-directories)0
l---------tests/functional/lang/readDir/ldir (renamed from tests/lang/readDir/ldir)0
l---------tests/functional/lang/readDir/linked (renamed from tests/lang/readDir/linked)0
-rw-r--r--tests/functional/legacy-ssh-store.sh (renamed from tests/legacy-ssh-store.sh)0
-rw-r--r--tests/functional/linux-sandbox-cert-test.nix (renamed from tests/linux-sandbox-cert-test.nix)0
-rw-r--r--tests/functional/linux-sandbox.sh (renamed from tests/linux-sandbox.sh)0
-rw-r--r--tests/functional/local-store.sh (renamed from tests/local-store.sh)0
-rw-r--r--tests/functional/local.mk (renamed from tests/local.mk)22
-rw-r--r--tests/functional/logging.sh (renamed from tests/logging.sh)0
-rw-r--r--tests/functional/misc.sh (renamed from tests/misc.sh)0
-rw-r--r--tests/functional/multiple-outputs.nix (renamed from tests/multiple-outputs.nix)0
-rw-r--r--tests/functional/multiple-outputs.sh (renamed from tests/multiple-outputs.sh)0
-rw-r--r--tests/functional/nar-access.nix (renamed from tests/nar-access.nix)0
-rw-r--r--tests/functional/nar-access.sh (renamed from tests/nar-access.sh)0
-rw-r--r--tests/functional/nested-sandboxing.sh (renamed from tests/nested-sandboxing.sh)2
-rw-r--r--tests/functional/nested-sandboxing/command.sh (renamed from tests/nested-sandboxing/command.sh)0
-rw-r--r--tests/functional/nested-sandboxing/runner.nix (renamed from tests/nested-sandboxing/runner.nix)0
-rw-r--r--tests/functional/nix-build-examples.nix (renamed from tests/nix-build-examples.nix)0
-rw-r--r--tests/functional/nix-build.sh (renamed from tests/nix-build.sh)0
-rw-r--r--tests/functional/nix-channel.sh (renamed from tests/nix-channel.sh)0
-rw-r--r--tests/functional/nix-collect-garbage-d.sh (renamed from tests/nix-collect-garbage-d.sh)0
-rw-r--r--tests/functional/nix-copy-ssh-ng.sh (renamed from tests/nix-copy-ssh-ng.sh)0
-rw-r--r--tests/functional/nix-copy-ssh.sh (renamed from tests/nix-copy-ssh.sh)0
-rwxr-xr-xtests/functional/nix-daemon-untrusting.sh (renamed from tests/nix-daemon-untrusting.sh)0
-rw-r--r--tests/functional/nix-profile.sh (renamed from tests/nix-profile.sh)0
-rw-r--r--tests/functional/nix-shell.sh (renamed from tests/nix-shell.sh)0
-rw-r--r--tests/functional/nix_path.sh (renamed from tests/nix_path.sh)0
-rw-r--r--tests/functional/optimise-store.sh (renamed from tests/optimise-store.sh)0
-rw-r--r--tests/functional/output-normalization.sh (renamed from tests/output-normalization.sh)0
-rw-r--r--tests/functional/parallel.builder.sh (renamed from tests/parallel.builder.sh)0
-rw-r--r--tests/functional/parallel.nix (renamed from tests/parallel.nix)0
-rw-r--r--tests/functional/parallel.sh (renamed from tests/parallel.sh)0
-rw-r--r--tests/functional/pass-as-file.sh (renamed from tests/pass-as-file.sh)0
-rw-r--r--tests/functional/path-from-hash-part.sh (renamed from tests/path-from-hash-part.sh)0
-rw-r--r--tests/functional/path.nix (renamed from tests/path.nix)0
-rw-r--r--tests/functional/placeholders.sh (renamed from tests/placeholders.sh)0
-rw-r--r--tests/functional/plugins.sh (renamed from tests/plugins.sh)0
-rw-r--r--tests/functional/plugins/local.mk (renamed from tests/plugins/local.mk)0
-rw-r--r--tests/functional/plugins/plugintest.cc (renamed from tests/plugins/plugintest.cc)0
-rw-r--r--tests/functional/post-hook.sh (renamed from tests/post-hook.sh)0
-rw-r--r--tests/functional/pure-eval.nix (renamed from tests/pure-eval.nix)0
-rw-r--r--tests/functional/pure-eval.sh (renamed from tests/pure-eval.sh)0
-rwxr-xr-xtests/functional/push-to-store-old.sh (renamed from tests/push-to-store-old.sh)0
-rwxr-xr-xtests/functional/push-to-store.sh (renamed from tests/push-to-store.sh)0
-rw-r--r--tests/functional/read-only-store.sh (renamed from tests/read-only-store.sh)0
-rw-r--r--tests/functional/readfile-context.nix (renamed from tests/readfile-context.nix)0
-rw-r--r--tests/functional/readfile-context.sh (renamed from tests/readfile-context.sh)0
-rw-r--r--tests/functional/recursive.nix (renamed from tests/recursive.nix)0
-rw-r--r--tests/functional/recursive.sh (renamed from tests/recursive.sh)0
-rw-r--r--tests/functional/referrers.sh (renamed from tests/referrers.sh)0
-rw-r--r--tests/functional/remote-store.sh (renamed from tests/remote-store.sh)0
-rw-r--r--tests/functional/repair.sh (renamed from tests/repair.sh)0
-rw-r--r--tests/functional/repl.sh (renamed from tests/repl.sh)0
-rw-r--r--tests/functional/restricted.nix (renamed from tests/restricted.nix)0
-rw-r--r--tests/functional/restricted.sh (renamed from tests/restricted.sh)6
-rw-r--r--tests/functional/search.nix (renamed from tests/search.nix)0
-rw-r--r--tests/functional/search.sh (renamed from tests/search.sh)0
-rw-r--r--tests/functional/secure-drv-outputs.nix (renamed from tests/secure-drv-outputs.nix)0
-rw-r--r--tests/functional/secure-drv-outputs.sh (renamed from tests/secure-drv-outputs.sh)0
-rw-r--r--tests/functional/selfref-gc.sh (renamed from tests/selfref-gc.sh)0
-rw-r--r--tests/functional/shell-hello.nix (renamed from tests/shell-hello.nix)0
-rw-r--r--tests/functional/shell.nix (renamed from tests/shell.nix)0
-rw-r--r--tests/functional/shell.sh (renamed from tests/shell.sh)0
-rw-r--r--tests/functional/shell.shebang.rb (renamed from tests/shell.shebang.rb)0
-rwxr-xr-xtests/functional/shell.shebang.sh (renamed from tests/shell.shebang.sh)0
-rw-r--r--tests/functional/signing.sh (renamed from tests/signing.sh)0
-rw-r--r--tests/functional/simple-failing.nix (renamed from tests/simple-failing.nix)0
-rw-r--r--tests/functional/simple.builder.sh (renamed from tests/simple.builder.sh)0
-rw-r--r--tests/functional/simple.nix (renamed from tests/simple.nix)0
-rw-r--r--tests/functional/simple.sh (renamed from tests/simple.sh)0
-rw-r--r--tests/functional/ssh-relay.sh (renamed from tests/ssh-relay.sh)0
-rw-r--r--tests/functional/store-ping.sh (renamed from tests/store-ping.sh)0
-rw-r--r--tests/functional/structured-attrs-shell.nix (renamed from tests/structured-attrs-shell.nix)0
-rw-r--r--tests/functional/structured-attrs.nix (renamed from tests/structured-attrs.nix)0
-rw-r--r--tests/functional/structured-attrs.sh (renamed from tests/structured-attrs.sh)0
-rw-r--r--tests/functional/substitute-with-invalid-ca.sh (renamed from tests/substitute-with-invalid-ca.sh)0
-rw-r--r--tests/functional/suggestions.sh (renamed from tests/suggestions.sh)0
-rw-r--r--tests/functional/supplementary-groups.sh (renamed from tests/supplementary-groups.sh)0
-rw-r--r--tests/functional/tarball.sh (renamed from tests/tarball.sh)0
-rw-r--r--tests/functional/test-infra.sh (renamed from tests/test-infra.sh)0
-rw-r--r--tests/functional/test-libstoreconsumer.sh (renamed from tests/test-libstoreconsumer.sh)0
-rw-r--r--tests/functional/test-libstoreconsumer/README.md (renamed from tests/test-libstoreconsumer/README.md)0
-rw-r--r--tests/functional/test-libstoreconsumer/local.mk (renamed from tests/test-libstoreconsumer/local.mk)0
-rw-r--r--tests/functional/test-libstoreconsumer/main.cc (renamed from tests/test-libstoreconsumer/main.cc)0
-rw-r--r--tests/functional/timeout.nix (renamed from tests/timeout.nix)0
-rw-r--r--tests/functional/timeout.sh (renamed from tests/timeout.sh)0
-rw-r--r--tests/functional/toString-path.sh (renamed from tests/toString-path.sh)0
-rw-r--r--tests/functional/undefined-variable.nix (renamed from tests/undefined-variable.nix)0
-rw-r--r--tests/functional/user-envs-migration.sh (renamed from tests/user-envs-migration.sh)0
-rw-r--r--tests/functional/user-envs.builder.sh (renamed from tests/user-envs.builder.sh)0
-rw-r--r--tests/functional/user-envs.nix (renamed from tests/user-envs.nix)0
-rw-r--r--tests/functional/user-envs.sh (renamed from tests/user-envs.sh)0
-rw-r--r--tests/functional/why-depends.sh (renamed from tests/why-depends.sh)0
-rw-r--r--tests/functional/zstd.sh (renamed from tests/zstd.sh)0
-rw-r--r--tests/lang/eval-okay-pathexists.nix8
-rw-r--r--tests/nixos/containers/containers.nix4
-rw-r--r--tests/nixos/containers/systemd-nspawn.nix2
-rw-r--r--tests/unit/libexpr-support/local.mk19
-rw-r--r--tests/unit/libexpr-support/tests/libexpr.hh140
-rw-r--r--tests/unit/libexpr-support/tests/value/context.cc30
-rw-r--r--tests/unit/libexpr-support/tests/value/context.hh31
-rw-r--r--tests/unit/libexpr/derived-path.cc68
-rw-r--r--tests/unit/libexpr/error_traces.cc1298
-rw-r--r--tests/unit/libexpr/flakeref.cc22
-rw-r--r--tests/unit/libexpr/json.cc68
-rw-r--r--tests/unit/libexpr/local.mk32
-rw-r--r--tests/unit/libexpr/primops.cc832
-rw-r--r--tests/unit/libexpr/search-path.cc90
-rw-r--r--tests/unit/libexpr/trivial.cc196
-rw-r--r--tests/unit/libexpr/value/context.cc132
-rw-r--r--tests/unit/libexpr/value/print.cc236
-rw-r--r--tests/unit/libstore-support/local.mk17
-rw-r--r--tests/unit/libstore-support/tests/derived-path.cc57
-rw-r--r--tests/unit/libstore-support/tests/derived-path.hh39
-rw-r--r--tests/unit/libstore-support/tests/libstore.hh26
-rw-r--r--tests/unit/libstore-support/tests/outputs-spec.cc24
-rw-r--r--tests/unit/libstore-support/tests/outputs-spec.hh18
-rw-r--r--tests/unit/libstore-support/tests/path.cc82
-rw-r--r--tests/unit/libstore-support/tests/path.hh32
-rw-r--r--tests/unit/libstore/derivation.cc369
-rw-r--r--tests/unit/libstore/derived-path.cc100
-rw-r--r--tests/unit/libstore/downstream-placeholder.cc41
-rw-r--r--tests/unit/libstore/local.mk27
-rw-r--r--tests/unit/libstore/machines.cc169
-rw-r--r--tests/unit/libstore/nar-info-disk-cache.cc123
-rw-r--r--tests/unit/libstore/outputs-spec.cc214
-rw-r--r--tests/unit/libstore/path.cc89
-rw-r--r--tests/unit/libstore/references.cc45
-rw-r--r--tests/unit/libstore/test-data/machines.bad_format1
-rw-r--r--tests/unit/libstore/test-data/machines.valid3
-rw-r--r--tests/unit/libutil-support/local.mk15
-rw-r--r--tests/unit/libutil-support/tests/hash.cc20
-rw-r--r--tests/unit/libutil-support/tests/hash.hh16
-rw-r--r--tests/unit/libutil/canon-path.cc162
-rw-r--r--tests/unit/libutil/chunked-vector.cc54
-rw-r--r--tests/unit/libutil/closure.cc70
-rw-r--r--tests/unit/libutil/compression.cc96
-rw-r--r--tests/unit/libutil/config.cc295
-rw-r--r--tests/unit/libutil/git.cc33
-rw-r--r--tests/unit/libutil/hash.cc77
-rw-r--r--tests/unit/libutil/hilite.cc66
-rw-r--r--tests/unit/libutil/local.mk23
-rw-r--r--tests/unit/libutil/logging.cc370
-rw-r--r--tests/unit/libutil/lru-cache.cc130
-rw-r--r--tests/unit/libutil/pool.cc127
-rw-r--r--tests/unit/libutil/references.cc46
-rw-r--r--tests/unit/libutil/suggestions.cc43
-rw-r--r--tests/unit/libutil/tests.cc659
-rw-r--r--tests/unit/libutil/url.cc338
-rw-r--r--tests/unit/libutil/xml-writer.cc105
647 files changed, 7482 insertions, 44 deletions
diff --git a/tests/dyn-drv/dep-built-drv.sh b/tests/dyn-drv/dep-built-drv.sh
deleted file mode 100644
index c3daf2c59..000000000
--- a/tests/dyn-drv/dep-built-drv.sh
+++ /dev/null
@@ -1,11 +0,0 @@
-#!/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/add.sh b/tests/functional/add.sh
index 5c3eed793..5c3eed793 100644
--- a/tests/add.sh
+++ b/tests/functional/add.sh
diff --git a/tests/bad.tar.xz b/tests/functional/bad.tar.xz
index 250a5ad1a..250a5ad1a 100644
--- a/tests/bad.tar.xz
+++ b/tests/functional/bad.tar.xz
Binary files differ
diff --git a/tests/bash-profile.sh b/tests/functional/bash-profile.sh
index e2e0d1090..3faeaaba1 100644
--- a/tests/bash-profile.sh
+++ b/tests/functional/bash-profile.sh
@@ -1,6 +1,6 @@
source common.sh
-sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh
+sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh
user=$(whoami)
rm -rf $TEST_HOME $TEST_ROOT/profile-var
diff --git a/tests/big-derivation-attr.nix b/tests/functional/big-derivation-attr.nix
index 35c1187f6..35c1187f6 100644
--- a/tests/big-derivation-attr.nix
+++ b/tests/functional/big-derivation-attr.nix
diff --git a/tests/binary-cache-build-remote.sh b/tests/functional/binary-cache-build-remote.sh
index 81cd21a4a..81cd21a4a 100644
--- a/tests/binary-cache-build-remote.sh
+++ b/tests/functional/binary-cache-build-remote.sh
diff --git a/tests/binary-cache.sh b/tests/functional/binary-cache.sh
index 7c64a115c..7c64a115c 100644
--- a/tests/binary-cache.sh
+++ b/tests/functional/binary-cache.sh
diff --git a/tests/brotli.sh b/tests/functional/brotli.sh
index dc9bbdb66..dc9bbdb66 100644
--- a/tests/brotli.sh
+++ b/tests/functional/brotli.sh
diff --git a/tests/build-delete.sh b/tests/functional/build-delete.sh
index 9c56b00e8..9c56b00e8 100644
--- a/tests/build-delete.sh
+++ b/tests/functional/build-delete.sh
diff --git a/tests/build-dry.sh b/tests/functional/build-dry.sh
index 6d1754af5..6d1754af5 100644
--- a/tests/build-dry.sh
+++ b/tests/functional/build-dry.sh
diff --git a/tests/build-hook-ca-fixed.nix b/tests/functional/build-hook-ca-fixed.nix
index 4cb9e85d1..4cb9e85d1 100644
--- a/tests/build-hook-ca-fixed.nix
+++ b/tests/functional/build-hook-ca-fixed.nix
diff --git a/tests/build-hook-ca-floating.nix b/tests/functional/build-hook-ca-floating.nix
index dfdbb82da..dfdbb82da 100644
--- a/tests/build-hook-ca-floating.nix
+++ b/tests/functional/build-hook-ca-floating.nix
diff --git a/tests/build-hook.nix b/tests/functional/build-hook.nix
index 7effd7903..7effd7903 100644
--- a/tests/build-hook.nix
+++ b/tests/functional/build-hook.nix
diff --git a/tests/build-remote-content-addressed-fixed.sh b/tests/functional/build-remote-content-addressed-fixed.sh
index ae7441591..ae7441591 100644
--- a/tests/build-remote-content-addressed-fixed.sh
+++ b/tests/functional/build-remote-content-addressed-fixed.sh
diff --git a/tests/build-remote-content-addressed-floating.sh b/tests/functional/build-remote-content-addressed-floating.sh
index e83b42b41..e83b42b41 100644
--- a/tests/build-remote-content-addressed-floating.sh
+++ b/tests/functional/build-remote-content-addressed-floating.sh
diff --git a/tests/build-remote-input-addressed.sh b/tests/functional/build-remote-input-addressed.sh
index 49d15c389..49d15c389 100644
--- a/tests/build-remote-input-addressed.sh
+++ b/tests/functional/build-remote-input-addressed.sh
diff --git a/tests/build-remote-trustless-after.sh b/tests/functional/build-remote-trustless-after.sh
index 19f59e6ae..19f59e6ae 100644
--- a/tests/build-remote-trustless-after.sh
+++ b/tests/functional/build-remote-trustless-after.sh
diff --git a/tests/build-remote-trustless-should-fail-0.sh b/tests/functional/build-remote-trustless-should-fail-0.sh
index fad1def59..fad1def59 100644
--- a/tests/build-remote-trustless-should-fail-0.sh
+++ b/tests/functional/build-remote-trustless-should-fail-0.sh
diff --git a/tests/build-remote-trustless-should-pass-0.sh b/tests/functional/build-remote-trustless-should-pass-0.sh
index 2a7ebd8c6..2a7ebd8c6 100644
--- a/tests/build-remote-trustless-should-pass-0.sh
+++ b/tests/functional/build-remote-trustless-should-pass-0.sh
diff --git a/tests/build-remote-trustless-should-pass-1.sh b/tests/functional/build-remote-trustless-should-pass-1.sh
index 516bdf092..516bdf092 100644
--- a/tests/build-remote-trustless-should-pass-1.sh
+++ b/tests/functional/build-remote-trustless-should-pass-1.sh
diff --git a/tests/build-remote-trustless-should-pass-2.sh b/tests/functional/build-remote-trustless-should-pass-2.sh
index b769a88f0..b769a88f0 100644
--- a/tests/build-remote-trustless-should-pass-2.sh
+++ b/tests/functional/build-remote-trustless-should-pass-2.sh
diff --git a/tests/build-remote-trustless-should-pass-3.sh b/tests/functional/build-remote-trustless-should-pass-3.sh
index 40f81da5a..40f81da5a 100644
--- a/tests/build-remote-trustless-should-pass-3.sh
+++ b/tests/functional/build-remote-trustless-should-pass-3.sh
diff --git a/tests/build-remote-trustless.sh b/tests/functional/build-remote-trustless.sh
index 9df44e0c5..81e5253bf 100644
--- a/tests/build-remote-trustless.sh
+++ b/tests/functional/build-remote-trustless.sh
@@ -6,7 +6,7 @@ unset NIX_STATE_DIR
remoteDir=$TEST_ROOT/remote
-# Note: ssh{-ng}://localhost bypasses ssh. See tests/build-remote.sh for
+# Note: ssh{-ng}://localhost bypasses ssh. See tests/functional/build-remote.sh for
# more details.
nix-build $file -o $TEST_ROOT/result --max-jobs 0 \
--arg busybox $busybox \
diff --git a/tests/build-remote.sh b/tests/functional/build-remote.sh
index d2a2132c1..d2a2132c1 100644
--- a/tests/build-remote.sh
+++ b/tests/functional/build-remote.sh
diff --git a/tests/build.sh b/tests/functional/build.sh
index 7fbdb0f07..7fbdb0f07 100644
--- a/tests/build.sh
+++ b/tests/functional/build.sh
diff --git a/tests/ca-shell.nix b/tests/functional/ca-shell.nix
index 36e1d1526..36e1d1526 100644
--- a/tests/ca-shell.nix
+++ b/tests/functional/ca-shell.nix
diff --git a/tests/ca/build-cache.sh b/tests/functional/ca/build-cache.sh
index 6a4080fec..6a4080fec 100644
--- a/tests/ca/build-cache.sh
+++ b/tests/functional/ca/build-cache.sh
diff --git a/tests/ca/build-dry.sh b/tests/functional/ca/build-dry.sh
index 9a72075ec..9a72075ec 100644
--- a/tests/ca/build-dry.sh
+++ b/tests/functional/ca/build-dry.sh
diff --git a/tests/ca/build-with-garbage-path.sh b/tests/functional/ca/build-with-garbage-path.sh
index 884cd2802..884cd2802 100755
--- a/tests/ca/build-with-garbage-path.sh
+++ b/tests/functional/ca/build-with-garbage-path.sh
diff --git a/tests/ca/build.sh b/tests/functional/ca/build.sh
index e1a8a7625..e1a8a7625 100644
--- a/tests/ca/build.sh
+++ b/tests/functional/ca/build.sh
diff --git a/tests/ca/common.sh b/tests/functional/ca/common.sh
index b104b5a78..b104b5a78 100644
--- a/tests/ca/common.sh
+++ b/tests/functional/ca/common.sh
diff --git a/tests/ca/concurrent-builds.sh b/tests/functional/ca/concurrent-builds.sh
index b442619e2..b442619e2 100755
--- a/tests/ca/concurrent-builds.sh
+++ b/tests/functional/ca/concurrent-builds.sh
diff --git a/tests/ca/config.nix.in b/tests/functional/ca/config.nix.in
index af24ddb30..af24ddb30 120000
--- a/tests/ca/config.nix.in
+++ b/tests/functional/ca/config.nix.in
diff --git a/tests/ca/content-addressed.nix b/tests/functional/ca/content-addressed.nix
index 2559c562f..2559c562f 100644
--- a/tests/ca/content-addressed.nix
+++ b/tests/functional/ca/content-addressed.nix
diff --git a/tests/ca/derivation-json.sh b/tests/functional/ca/derivation-json.sh
index c1480fd17..c1480fd17 100644
--- a/tests/ca/derivation-json.sh
+++ b/tests/functional/ca/derivation-json.sh
diff --git a/tests/ca/duplicate-realisation-in-closure.sh b/tests/functional/ca/duplicate-realisation-in-closure.sh
index da9cd8fb4..da9cd8fb4 100644
--- a/tests/ca/duplicate-realisation-in-closure.sh
+++ b/tests/functional/ca/duplicate-realisation-in-closure.sh
diff --git a/tests/ca/flake.nix b/tests/functional/ca/flake.nix
index 332c92a67..332c92a67 100644
--- a/tests/ca/flake.nix
+++ b/tests/functional/ca/flake.nix
diff --git a/tests/ca/gc.sh b/tests/functional/ca/gc.sh
index e9b6c5ab5..e9b6c5ab5 100755
--- a/tests/ca/gc.sh
+++ b/tests/functional/ca/gc.sh
diff --git a/tests/ca/import-derivation.sh b/tests/functional/ca/import-derivation.sh
index e98e0fbd0..e98e0fbd0 100644
--- a/tests/ca/import-derivation.sh
+++ b/tests/functional/ca/import-derivation.sh
diff --git a/tests/ca/local.mk b/tests/functional/ca/local.mk
index 0852e592e..fd87b8d1f 100644
--- a/tests/ca/local.mk
+++ b/tests/functional/ca/local.mk
@@ -25,4 +25,4 @@ clean-files += \
$(d)/config.nix
test-deps += \
- tests/ca/config.nix
+ tests/functional/ca/config.nix
diff --git a/tests/ca/new-build-cmd.sh b/tests/functional/ca/new-build-cmd.sh
index 432d4d132..432d4d132 100644
--- a/tests/ca/new-build-cmd.sh
+++ b/tests/functional/ca/new-build-cmd.sh
diff --git a/tests/ca/nix-copy.sh b/tests/functional/ca/nix-copy.sh
index 7a8307a4e..7a8307a4e 100755
--- a/tests/ca/nix-copy.sh
+++ b/tests/functional/ca/nix-copy.sh
diff --git a/tests/ca/nix-run.sh b/tests/functional/ca/nix-run.sh
index 5f46518e8..5f46518e8 100755
--- a/tests/ca/nix-run.sh
+++ b/tests/functional/ca/nix-run.sh
diff --git a/tests/ca/nix-shell.sh b/tests/functional/ca/nix-shell.sh
index 1c5a6639f..1c5a6639f 100755
--- a/tests/ca/nix-shell.sh
+++ b/tests/functional/ca/nix-shell.sh
diff --git a/tests/ca/nondeterministic.nix b/tests/functional/ca/nondeterministic.nix
index d6d099a3e..d6d099a3e 100644
--- a/tests/ca/nondeterministic.nix
+++ b/tests/functional/ca/nondeterministic.nix
diff --git a/tests/ca/post-hook.sh b/tests/functional/ca/post-hook.sh
index 705bde9d4..705bde9d4 100755
--- a/tests/ca/post-hook.sh
+++ b/tests/functional/ca/post-hook.sh
diff --git a/tests/ca/racy.nix b/tests/functional/ca/racy.nix
index 555a15484..555a15484 100644
--- a/tests/ca/racy.nix
+++ b/tests/functional/ca/racy.nix
diff --git a/tests/ca/recursive.sh b/tests/functional/ca/recursive.sh
index cd6736b24..cd6736b24 100755
--- a/tests/ca/recursive.sh
+++ b/tests/functional/ca/recursive.sh
diff --git a/tests/ca/repl.sh b/tests/functional/ca/repl.sh
index 3808c7cb2..3808c7cb2 100644
--- a/tests/ca/repl.sh
+++ b/tests/functional/ca/repl.sh
diff --git a/tests/ca/selfref-gc.sh b/tests/functional/ca/selfref-gc.sh
index 248778894..248778894 100755
--- a/tests/ca/selfref-gc.sh
+++ b/tests/functional/ca/selfref-gc.sh
diff --git a/tests/ca/signatures.sh b/tests/functional/ca/signatures.sh
index eb18a4130..eb18a4130 100644
--- a/tests/ca/signatures.sh
+++ b/tests/functional/ca/signatures.sh
diff --git a/tests/ca/substitute.sh b/tests/functional/ca/substitute.sh
index ea981adc4..ea981adc4 100644
--- a/tests/ca/substitute.sh
+++ b/tests/functional/ca/substitute.sh
diff --git a/tests/ca/why-depends.sh b/tests/functional/ca/why-depends.sh
index 0c079f63b..0c079f63b 100644
--- a/tests/ca/why-depends.sh
+++ b/tests/functional/ca/why-depends.sh
diff --git a/tests/case-hack.sh b/tests/functional/case-hack.sh
index 61bf9b94b..61bf9b94b 100644
--- a/tests/case-hack.sh
+++ b/tests/functional/case-hack.sh
diff --git a/tests/case.nar b/tests/functional/case.nar
index 22ff26db5..22ff26db5 100644
--- a/tests/case.nar
+++ b/tests/functional/case.nar
Binary files differ
diff --git a/tests/check-refs.nix b/tests/functional/check-refs.nix
index 89690e456..89690e456 100644
--- a/tests/check-refs.nix
+++ b/tests/functional/check-refs.nix
diff --git a/tests/check-refs.sh b/tests/functional/check-refs.sh
index 3b587d1e5..3b587d1e5 100644
--- a/tests/check-refs.sh
+++ b/tests/functional/check-refs.sh
diff --git a/tests/check-reqs.nix b/tests/functional/check-reqs.nix
index 41436cb48..41436cb48 100644
--- a/tests/check-reqs.nix
+++ b/tests/functional/check-reqs.nix
diff --git a/tests/check-reqs.sh b/tests/functional/check-reqs.sh
index 856c94cec..856c94cec 100644
--- a/tests/check-reqs.sh
+++ b/tests/functional/check-reqs.sh
diff --git a/tests/check.nix b/tests/functional/check.nix
index ddab8eea9..ddab8eea9 100644
--- a/tests/check.nix
+++ b/tests/functional/check.nix
diff --git a/tests/check.sh b/tests/functional/check.sh
index e13abf747..e13abf747 100644
--- a/tests/check.sh
+++ b/tests/functional/check.sh
diff --git a/tests/common.sh b/tests/functional/common.sh
index 7b0922c9f..7b0922c9f 100644
--- a/tests/common.sh
+++ b/tests/functional/common.sh
diff --git a/tests/common/vars-and-functions.sh.in b/tests/functional/common/vars-and-functions.sh.in
index 8f9ec4b1a..967d6be54 100644
--- a/tests/common/vars-and-functions.sh.in
+++ b/tests/functional/common/vars-and-functions.sh.in
@@ -6,7 +6,7 @@ COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1
export PS4='+(${BASH_SOURCE[0]-$0}:$LINENO) '
-export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default}
+export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//}
export NIX_STORE_DIR
if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
# Maybe the build directory is symlinked.
diff --git a/tests/completions.sh b/tests/functional/completions.sh
index 19dc61098..19dc61098 100644
--- a/tests/completions.sh
+++ b/tests/functional/completions.sh
diff --git a/tests/compression-levels.sh b/tests/functional/compression-levels.sh
index 85f12974a..85f12974a 100644
--- a/tests/compression-levels.sh
+++ b/tests/functional/compression-levels.sh
diff --git a/tests/compute-levels.sh b/tests/functional/compute-levels.sh
index de3da2ebd..de3da2ebd 100644
--- a/tests/compute-levels.sh
+++ b/tests/functional/compute-levels.sh
diff --git a/tests/config.nix.in b/tests/functional/config.nix.in
index 7facbdcbc..7facbdcbc 100644
--- a/tests/config.nix.in
+++ b/tests/functional/config.nix.in
diff --git a/tests/config.sh b/tests/functional/config.sh
index 723f575ed..723f575ed 100644
--- a/tests/config.sh
+++ b/tests/functional/config.sh
diff --git a/tests/config/nix-with-substituters.conf b/tests/functional/config/nix-with-substituters.conf
index 90f359a6f..90f359a6f 100644
--- a/tests/config/nix-with-substituters.conf
+++ b/tests/functional/config/nix-with-substituters.conf
diff --git a/tests/db-migration.sh b/tests/functional/db-migration.sh
index 44cd16bc0..44cd16bc0 100644
--- a/tests/db-migration.sh
+++ b/tests/functional/db-migration.sh
diff --git a/tests/dependencies.builder0.sh b/tests/functional/dependencies.builder0.sh
index 9b11576e0..9b11576e0 100644
--- a/tests/dependencies.builder0.sh
+++ b/tests/functional/dependencies.builder0.sh
diff --git a/tests/dependencies.nix b/tests/functional/dependencies.nix
index be1a7ae9a..be1a7ae9a 100644
--- a/tests/dependencies.nix
+++ b/tests/functional/dependencies.nix
diff --git a/tests/dependencies.sh b/tests/functional/dependencies.sh
index b93dacac0..b93dacac0 100644
--- a/tests/dependencies.sh
+++ b/tests/functional/dependencies.sh
diff --git a/tests/derivation-json.sh b/tests/functional/derivation-json.sh
index b6be5d977..b6be5d977 100644
--- a/tests/derivation-json.sh
+++ b/tests/functional/derivation-json.sh
diff --git a/tests/dummy b/tests/functional/dummy
index 557db03de..557db03de 100644
--- a/tests/dummy
+++ b/tests/functional/dummy
diff --git a/tests/dump-db.sh b/tests/functional/dump-db.sh
index 48647f403..48647f403 100644
--- a/tests/dump-db.sh
+++ b/tests/functional/dump-db.sh
diff --git a/tests/dyn-drv/build-built-drv.sh b/tests/functional/dyn-drv/build-built-drv.sh
index 94f3550bd..647be9457 100644
--- a/tests/dyn-drv/build-built-drv.sh
+++ b/tests/functional/dyn-drv/build-built-drv.sh
@@ -18,6 +18,4 @@ clearStore
drvDep=$(nix-instantiate ./text-hashed-output.nix -A producingDrv)
-out2=$(nix build "${drvDep}^out^out" --no-link)
-
-test $out1 == $out2
+expectStderr 1 nix build "${drvDep}^out^out" --no-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented"
diff --git a/tests/dyn-drv/common.sh b/tests/functional/dyn-drv/common.sh
index c786f6925..c786f6925 100644
--- a/tests/dyn-drv/common.sh
+++ b/tests/functional/dyn-drv/common.sh
diff --git a/tests/dyn-drv/config.nix.in b/tests/functional/dyn-drv/config.nix.in
index af24ddb30..af24ddb30 120000
--- a/tests/dyn-drv/config.nix.in
+++ b/tests/functional/dyn-drv/config.nix.in
diff --git a/tests/functional/dyn-drv/dep-built-drv.sh b/tests/functional/dyn-drv/dep-built-drv.sh
new file mode 100644
index 000000000..4f6e9b080
--- /dev/null
+++ b/tests/functional/dyn-drv/dep-built-drv.sh
@@ -0,0 +1,11 @@
+#!/usr/bin/env bash
+
+source common.sh
+
+out1=$(nix-build ./text-hashed-output.nix -A hello --no-out-link)
+
+clearStore
+
+expectStderr 1 nix-build ./text-hashed-output.nix -A wrapper --no-out-link | grepQuiet "Building dynamic derivations in one shot is not yet implemented"
+
+# diff -r $out1 $out2
diff --git a/tests/dyn-drv/eval-outputOf.sh b/tests/functional/dyn-drv/eval-outputOf.sh
index 9467feb8d..9467feb8d 100644
--- a/tests/dyn-drv/eval-outputOf.sh
+++ b/tests/functional/dyn-drv/eval-outputOf.sh
diff --git a/tests/dyn-drv/local.mk b/tests/functional/dyn-drv/local.mk
index 6b435499b..c87534944 100644
--- a/tests/dyn-drv/local.mk
+++ b/tests/functional/dyn-drv/local.mk
@@ -12,4 +12,4 @@ clean-files += \
$(d)/config.nix
test-deps += \
- tests/dyn-drv/config.nix
+ tests/functional/dyn-drv/config.nix
diff --git a/tests/dyn-drv/old-daemon-error-hack.nix b/tests/functional/dyn-drv/old-daemon-error-hack.nix
index c9d4a62d4..c9d4a62d4 100644
--- a/tests/dyn-drv/old-daemon-error-hack.nix
+++ b/tests/functional/dyn-drv/old-daemon-error-hack.nix
diff --git a/tests/dyn-drv/old-daemon-error-hack.sh b/tests/functional/dyn-drv/old-daemon-error-hack.sh
index 43b049973..43b049973 100644
--- a/tests/dyn-drv/old-daemon-error-hack.sh
+++ b/tests/functional/dyn-drv/old-daemon-error-hack.sh
diff --git a/tests/dyn-drv/recursive-mod-json.nix b/tests/functional/dyn-drv/recursive-mod-json.nix
index c6a24ca4f..c6a24ca4f 100644
--- a/tests/dyn-drv/recursive-mod-json.nix
+++ b/tests/functional/dyn-drv/recursive-mod-json.nix
diff --git a/tests/dyn-drv/recursive-mod-json.sh b/tests/functional/dyn-drv/recursive-mod-json.sh
index 0698b81bd..0698b81bd 100644
--- a/tests/dyn-drv/recursive-mod-json.sh
+++ b/tests/functional/dyn-drv/recursive-mod-json.sh
diff --git a/tests/dyn-drv/text-hashed-output.nix b/tests/functional/dyn-drv/text-hashed-output.nix
index 99203b518..99203b518 100644
--- a/tests/dyn-drv/text-hashed-output.nix
+++ b/tests/functional/dyn-drv/text-hashed-output.nix
diff --git a/tests/dyn-drv/text-hashed-output.sh b/tests/functional/dyn-drv/text-hashed-output.sh
index f3e5aa93b..f3e5aa93b 100644
--- a/tests/dyn-drv/text-hashed-output.sh
+++ b/tests/functional/dyn-drv/text-hashed-output.sh
diff --git a/tests/eval-store.sh b/tests/functional/eval-store.sh
index a34f3e82f..a34f3e82f 100644
--- a/tests/eval-store.sh
+++ b/tests/functional/eval-store.sh
diff --git a/tests/eval.nix b/tests/functional/eval.nix
index befbd17a9..befbd17a9 100644
--- a/tests/eval.nix
+++ b/tests/functional/eval.nix
diff --git a/tests/eval.sh b/tests/functional/eval.sh
index b81bb1e2c..b81bb1e2c 100644
--- a/tests/eval.sh
+++ b/tests/functional/eval.sh
diff --git a/tests/experimental-features.sh b/tests/functional/experimental-features.sh
index 607bf0a8e..607bf0a8e 100644
--- a/tests/experimental-features.sh
+++ b/tests/functional/experimental-features.sh
diff --git a/tests/export-graph.nix b/tests/functional/export-graph.nix
index 64fe36bd1..64fe36bd1 100644
--- a/tests/export-graph.nix
+++ b/tests/functional/export-graph.nix
diff --git a/tests/export-graph.sh b/tests/functional/export-graph.sh
index 1f6232a40..1f6232a40 100644
--- a/tests/export-graph.sh
+++ b/tests/functional/export-graph.sh
diff --git a/tests/export.sh b/tests/functional/export.sh
index 2238539bc..2238539bc 100644
--- a/tests/export.sh
+++ b/tests/functional/export.sh
diff --git a/tests/failing.nix b/tests/functional/failing.nix
index 2a0350d4d..2a0350d4d 100644
--- a/tests/failing.nix
+++ b/tests/functional/failing.nix
diff --git a/tests/fetchClosure.sh b/tests/functional/fetchClosure.sh
index a02d1ce7a..a02d1ce7a 100644
--- a/tests/fetchClosure.sh
+++ b/tests/functional/fetchClosure.sh
diff --git a/tests/fetchGit.sh b/tests/functional/fetchGit.sh
index 418b4f63f..418b4f63f 100644
--- a/tests/fetchGit.sh
+++ b/tests/functional/fetchGit.sh
diff --git a/tests/fetchGitRefs.sh b/tests/functional/fetchGitRefs.sh
index d643fea04..d643fea04 100644
--- a/tests/fetchGitRefs.sh
+++ b/tests/functional/fetchGitRefs.sh
diff --git a/tests/fetchGitSubmodules.sh b/tests/functional/fetchGitSubmodules.sh
index df81232e5..df81232e5 100644
--- a/tests/fetchGitSubmodules.sh
+++ b/tests/functional/fetchGitSubmodules.sh
diff --git a/tests/fetchMercurial.sh b/tests/functional/fetchMercurial.sh
index e6f8525c6..e6f8525c6 100644
--- a/tests/fetchMercurial.sh
+++ b/tests/functional/fetchMercurial.sh
diff --git a/tests/fetchPath.sh b/tests/functional/fetchPath.sh
index 29be38ce2..29be38ce2 100644
--- a/tests/fetchPath.sh
+++ b/tests/functional/fetchPath.sh
diff --git a/tests/fetchTree-file.sh b/tests/functional/fetchTree-file.sh
index 6395c133d..6395c133d 100644
--- a/tests/fetchTree-file.sh
+++ b/tests/functional/fetchTree-file.sh
diff --git a/tests/fetchurl.sh b/tests/functional/fetchurl.sh
index 8cd40c09f..8cd40c09f 100644
--- a/tests/fetchurl.sh
+++ b/tests/functional/fetchurl.sh
diff --git a/tests/filter-source.nix b/tests/functional/filter-source.nix
index 907163639..907163639 100644
--- a/tests/filter-source.nix
+++ b/tests/functional/filter-source.nix
diff --git a/tests/filter-source.sh b/tests/functional/filter-source.sh
index ba34d2eac..ba34d2eac 100644
--- a/tests/filter-source.sh
+++ b/tests/functional/filter-source.sh
diff --git a/tests/fixed.builder1.sh b/tests/functional/fixed.builder1.sh
index c41bb2b9a..c41bb2b9a 100644
--- a/tests/fixed.builder1.sh
+++ b/tests/functional/fixed.builder1.sh
diff --git a/tests/fixed.builder2.sh b/tests/functional/fixed.builder2.sh
index 31ea1579a..31ea1579a 100644
--- a/tests/fixed.builder2.sh
+++ b/tests/functional/fixed.builder2.sh
diff --git a/tests/fixed.nix b/tests/functional/fixed.nix
index babe71504..babe71504 100644
--- a/tests/fixed.nix
+++ b/tests/functional/fixed.nix
diff --git a/tests/fixed.sh b/tests/functional/fixed.sh
index f1e1ce420..f1e1ce420 100644
--- a/tests/fixed.sh
+++ b/tests/functional/fixed.sh
diff --git a/tests/flakes/absolute-paths.sh b/tests/functional/flakes/absolute-paths.sh
index e7bfba12d..e7bfba12d 100644
--- a/tests/flakes/absolute-paths.sh
+++ b/tests/functional/flakes/absolute-paths.sh
diff --git a/tests/flakes/build-paths.sh b/tests/functional/flakes/build-paths.sh
index ff012e1b3..ff012e1b3 100644
--- a/tests/flakes/build-paths.sh
+++ b/tests/functional/flakes/build-paths.sh
diff --git a/tests/flakes/bundle.sh b/tests/functional/flakes/bundle.sh
index 67bbb05ac..67bbb05ac 100644
--- a/tests/flakes/bundle.sh
+++ b/tests/functional/flakes/bundle.sh
diff --git a/tests/flakes/check.sh b/tests/functional/flakes/check.sh
index 0433e5335..0433e5335 100644
--- a/tests/flakes/check.sh
+++ b/tests/functional/flakes/check.sh
diff --git a/tests/flakes/circular.sh b/tests/functional/flakes/circular.sh
index 09cd02edf..09cd02edf 100644
--- a/tests/flakes/circular.sh
+++ b/tests/functional/flakes/circular.sh
diff --git a/tests/flakes/common.sh b/tests/functional/flakes/common.sh
index 427abcdde..427abcdde 100644
--- a/tests/flakes/common.sh
+++ b/tests/functional/flakes/common.sh
diff --git a/tests/flakes/config.sh b/tests/functional/flakes/config.sh
index d1941a6be..d1941a6be 100644
--- a/tests/flakes/config.sh
+++ b/tests/functional/flakes/config.sh
diff --git a/tests/flakes/flake-in-submodule.sh b/tests/functional/flakes/flake-in-submodule.sh
index 21a4b52de..21a4b52de 100644
--- a/tests/flakes/flake-in-submodule.sh
+++ b/tests/functional/flakes/flake-in-submodule.sh
diff --git a/tests/flakes/flakes.sh b/tests/functional/flakes/flakes.sh
index 128f759ea..128f759ea 100644
--- a/tests/flakes/flakes.sh
+++ b/tests/functional/flakes/flakes.sh
diff --git a/tests/flakes/follow-paths.sh b/tests/functional/flakes/follow-paths.sh
index dc97027ac..dc97027ac 100644
--- a/tests/flakes/follow-paths.sh
+++ b/tests/functional/flakes/follow-paths.sh
diff --git a/tests/flakes/init.sh b/tests/functional/flakes/init.sh
index 2d4c77ba1..2d4c77ba1 100644
--- a/tests/flakes/init.sh
+++ b/tests/functional/flakes/init.sh
diff --git a/tests/flakes/inputs.sh b/tests/functional/flakes/inputs.sh
index 80620488a..80620488a 100644
--- a/tests/flakes/inputs.sh
+++ b/tests/functional/flakes/inputs.sh
diff --git a/tests/flakes/mercurial.sh b/tests/functional/flakes/mercurial.sh
index 0622c79b7..0622c79b7 100644
--- a/tests/flakes/mercurial.sh
+++ b/tests/functional/flakes/mercurial.sh
diff --git a/tests/flakes/run.sh b/tests/functional/flakes/run.sh
index 9fa51d1c7..9fa51d1c7 100644
--- a/tests/flakes/run.sh
+++ b/tests/functional/flakes/run.sh
diff --git a/tests/flakes/search-root.sh b/tests/functional/flakes/search-root.sh
index d8586dc8a..d8586dc8a 100644
--- a/tests/flakes/search-root.sh
+++ b/tests/functional/flakes/search-root.sh
diff --git a/tests/flakes/show.sh b/tests/functional/flakes/show.sh
index a3d300552..a3d300552 100644
--- a/tests/flakes/show.sh
+++ b/tests/functional/flakes/show.sh
diff --git a/tests/flakes/unlocked-override.sh b/tests/functional/flakes/unlocked-override.sh
index 8abc8b7d3..8abc8b7d3 100644
--- a/tests/flakes/unlocked-override.sh
+++ b/tests/functional/flakes/unlocked-override.sh
diff --git a/tests/fmt.sh b/tests/functional/fmt.sh
index 3c1bd9989..3c1bd9989 100644
--- a/tests/fmt.sh
+++ b/tests/functional/fmt.sh
diff --git a/tests/fmt.simple.sh b/tests/functional/fmt.simple.sh
index 4c8c67ebb..4c8c67ebb 100755
--- a/tests/fmt.simple.sh
+++ b/tests/functional/fmt.simple.sh
diff --git a/tests/function-trace.sh b/tests/functional/function-trace.sh
index bd804bf18..bd804bf18 100755
--- a/tests/function-trace.sh
+++ b/tests/functional/function-trace.sh
diff --git a/tests/gc-auto.sh b/tests/functional/gc-auto.sh
index 521d9e539..521d9e539 100644
--- a/tests/gc-auto.sh
+++ b/tests/functional/gc-auto.sh
diff --git a/tests/gc-concurrent.builder.sh b/tests/functional/gc-concurrent.builder.sh
index bb6dcd4cf..bb6dcd4cf 100644
--- a/tests/gc-concurrent.builder.sh
+++ b/tests/functional/gc-concurrent.builder.sh
diff --git a/tests/gc-concurrent.nix b/tests/functional/gc-concurrent.nix
index 0aba1f983..0aba1f983 100644
--- a/tests/gc-concurrent.nix
+++ b/tests/functional/gc-concurrent.nix
diff --git a/tests/gc-concurrent.sh b/tests/functional/gc-concurrent.sh
index 2c6622c62..2c6622c62 100644
--- a/tests/gc-concurrent.sh
+++ b/tests/functional/gc-concurrent.sh
diff --git a/tests/gc-concurrent2.builder.sh b/tests/functional/gc-concurrent2.builder.sh
index 4f6c58b96..4f6c58b96 100644
--- a/tests/gc-concurrent2.builder.sh
+++ b/tests/functional/gc-concurrent2.builder.sh
diff --git a/tests/gc-non-blocking.sh b/tests/functional/gc-non-blocking.sh
index 0d781485d..0d781485d 100644
--- a/tests/gc-non-blocking.sh
+++ b/tests/functional/gc-non-blocking.sh
diff --git a/tests/gc-runtime.nix b/tests/functional/gc-runtime.nix
index ee5980bdf..ee5980bdf 100644
--- a/tests/gc-runtime.nix
+++ b/tests/functional/gc-runtime.nix
diff --git a/tests/gc-runtime.sh b/tests/functional/gc-runtime.sh
index dc1826a55..dc1826a55 100644
--- a/tests/gc-runtime.sh
+++ b/tests/functional/gc-runtime.sh
diff --git a/tests/gc.sh b/tests/functional/gc.sh
index ad09a8b39..ad09a8b39 100644
--- a/tests/gc.sh
+++ b/tests/functional/gc.sh
diff --git a/tests/hash-check.nix b/tests/functional/hash-check.nix
index 4a8e9b8a8..4a8e9b8a8 100644
--- a/tests/hash-check.nix
+++ b/tests/functional/hash-check.nix
diff --git a/tests/hash.sh b/tests/functional/hash.sh
index 34c1bb38a..34c1bb38a 100644
--- a/tests/hash.sh
+++ b/tests/functional/hash.sh
diff --git a/tests/hermetic.nix b/tests/functional/hermetic.nix
index 4c9d7a51f..4c9d7a51f 100644
--- a/tests/hermetic.nix
+++ b/tests/functional/hermetic.nix
diff --git a/tests/ifd.nix b/tests/functional/ifd.nix
index d0b9b54ad..d0b9b54ad 100644
--- a/tests/ifd.nix
+++ b/tests/functional/ifd.nix
diff --git a/tests/import-derivation.nix b/tests/functional/import-derivation.nix
index 44fa9a45d..44fa9a45d 100644
--- a/tests/import-derivation.nix
+++ b/tests/functional/import-derivation.nix
diff --git a/tests/import-derivation.sh b/tests/functional/import-derivation.sh
index 98d61ef49..98d61ef49 100644
--- a/tests/import-derivation.sh
+++ b/tests/functional/import-derivation.sh
diff --git a/tests/impure-derivations.nix b/tests/functional/impure-derivations.nix
index 98547e6c1..98547e6c1 100644
--- a/tests/impure-derivations.nix
+++ b/tests/functional/impure-derivations.nix
diff --git a/tests/impure-derivations.sh b/tests/functional/impure-derivations.sh
index 39d053a04..39d053a04 100644
--- a/tests/impure-derivations.sh
+++ b/tests/functional/impure-derivations.sh
diff --git a/tests/init.sh b/tests/functional/init.sh
index c420e8c9f..c420e8c9f 100755
--- a/tests/init.sh
+++ b/tests/functional/init.sh
diff --git a/tests/install-darwin.sh b/tests/functional/install-darwin.sh
index ea2b75323..ea2b75323 100755
--- a/tests/install-darwin.sh
+++ b/tests/functional/install-darwin.sh
diff --git a/tests/lang-test-infra.sh b/tests/functional/lang-test-infra.sh
index 30da8977b..30da8977b 100644
--- a/tests/lang-test-infra.sh
+++ b/tests/functional/lang-test-infra.sh
diff --git a/tests/lang.sh b/tests/functional/lang.sh
index 75dbbc38e..f4760eced 100755
--- a/tests/lang.sh
+++ b/tests/functional/lang.sh
@@ -134,7 +134,7 @@ else
echo ''
echo 'You can rerun this test with:'
echo ''
- echo ' _NIX_TEST_ACCEPT=1 make tests/lang.sh.test'
+ echo ' _NIX_TEST_ACCEPT=1 make tests/functional/lang.sh.test'
echo ''
echo 'to regenerate the files containing the expected output,'
echo 'and then view the git diff to decide whether a change is'
diff --git a/tests/lang/binary-data b/tests/functional/lang/binary-data
index 06d740502..06d740502 100644
--- a/tests/lang/binary-data
+++ b/tests/functional/lang/binary-data
Binary files differ
diff --git a/tests/lang/data b/tests/functional/lang/data
index 257cc5642..257cc5642 100644
--- a/tests/lang/data
+++ b/tests/functional/lang/data
diff --git a/tests/lang/dir1/a.nix b/tests/functional/lang/dir1/a.nix
index 231f150c5..231f150c5 100644
--- a/tests/lang/dir1/a.nix
+++ b/tests/functional/lang/dir1/a.nix
diff --git a/tests/lang/dir2/a.nix b/tests/functional/lang/dir2/a.nix
index 170df520a..170df520a 100644
--- a/tests/lang/dir2/a.nix
+++ b/tests/functional/lang/dir2/a.nix
diff --git a/tests/lang/dir2/b.nix b/tests/functional/lang/dir2/b.nix
index 19010cc35..19010cc35 100644
--- a/tests/lang/dir2/b.nix
+++ b/tests/functional/lang/dir2/b.nix
diff --git a/tests/lang/dir3/a.nix b/tests/functional/lang/dir3/a.nix
index 170df520a..170df520a 100644
--- a/tests/lang/dir3/a.nix
+++ b/tests/functional/lang/dir3/a.nix
diff --git a/tests/lang/dir3/b.nix b/tests/functional/lang/dir3/b.nix
index 170df520a..170df520a 100644
--- a/tests/lang/dir3/b.nix
+++ b/tests/functional/lang/dir3/b.nix
diff --git a/tests/lang/dir3/c.nix b/tests/functional/lang/dir3/c.nix
index cdf158597..cdf158597 100644
--- a/tests/lang/dir3/c.nix
+++ b/tests/functional/lang/dir3/c.nix
diff --git a/tests/lang/dir4/a.nix b/tests/functional/lang/dir4/a.nix
index 170df520a..170df520a 100644
--- a/tests/lang/dir4/a.nix
+++ b/tests/functional/lang/dir4/a.nix
diff --git a/tests/lang/dir4/c.nix b/tests/functional/lang/dir4/c.nix
index 170df520a..170df520a 100644
--- a/tests/lang/dir4/c.nix
+++ b/tests/functional/lang/dir4/c.nix
diff --git a/tests/lang/empty.exp b/tests/functional/lang/empty.exp
index e69de29bb..e69de29bb 100644
--- a/tests/lang/empty.exp
+++ b/tests/functional/lang/empty.exp
diff --git a/tests/lang/eval-fail-abort.err.exp b/tests/functional/lang/eval-fail-abort.err.exp
index 345232d3f..345232d3f 100644
--- a/tests/lang/eval-fail-abort.err.exp
+++ b/tests/functional/lang/eval-fail-abort.err.exp
diff --git a/tests/lang/eval-fail-abort.nix b/tests/functional/lang/eval-fail-abort.nix
index 75c51bceb..75c51bceb 100644
--- a/tests/lang/eval-fail-abort.nix
+++ b/tests/functional/lang/eval-fail-abort.nix
diff --git a/tests/lang/eval-fail-antiquoted-path.err.exp b/tests/functional/lang/eval-fail-antiquoted-path.err.exp
index 425deba42..425deba42 100644
--- a/tests/lang/eval-fail-antiquoted-path.err.exp
+++ b/tests/functional/lang/eval-fail-antiquoted-path.err.exp
diff --git a/tests/lang/eval-fail-assert.err.exp b/tests/functional/lang/eval-fail-assert.err.exp
index aeecd8167..aeecd8167 100644
--- a/tests/lang/eval-fail-assert.err.exp
+++ b/tests/functional/lang/eval-fail-assert.err.exp
diff --git a/tests/lang/eval-fail-assert.nix b/tests/functional/lang/eval-fail-assert.nix
index 3b7a1e8bf..3b7a1e8bf 100644
--- a/tests/lang/eval-fail-assert.nix
+++ b/tests/functional/lang/eval-fail-assert.nix
diff --git a/tests/lang/eval-fail-bad-antiquote-1.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-1.err.exp
index cf94f53bc..cf94f53bc 100644
--- a/tests/lang/eval-fail-bad-antiquote-1.err.exp
+++ b/tests/functional/lang/eval-fail-bad-antiquote-1.err.exp
diff --git a/tests/lang/eval-fail-bad-antiquote-2.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-2.err.exp
index c8fe39d12..c8fe39d12 100644
--- a/tests/lang/eval-fail-bad-antiquote-2.err.exp
+++ b/tests/functional/lang/eval-fail-bad-antiquote-2.err.exp
diff --git a/tests/lang/eval-fail-bad-antiquote-3.err.exp b/tests/functional/lang/eval-fail-bad-antiquote-3.err.exp
index fbefbc826..fbefbc826 100644
--- a/tests/lang/eval-fail-bad-antiquote-3.err.exp
+++ b/tests/functional/lang/eval-fail-bad-antiquote-3.err.exp
diff --git a/tests/lang/eval-fail-bad-string-interpolation-1.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp
index eb73e9a52..eb73e9a52 100644
--- a/tests/lang/eval-fail-bad-string-interpolation-1.err.exp
+++ b/tests/functional/lang/eval-fail-bad-string-interpolation-1.err.exp
diff --git a/tests/lang/eval-fail-bad-string-interpolation-1.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-1.nix
index ffe9c983c..ffe9c983c 100644
--- a/tests/lang/eval-fail-bad-string-interpolation-1.nix
+++ b/tests/functional/lang/eval-fail-bad-string-interpolation-1.nix
diff --git a/tests/lang/eval-fail-bad-string-interpolation-2.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp
index c8fe39d12..c8fe39d12 100644
--- a/tests/lang/eval-fail-bad-string-interpolation-2.err.exp
+++ b/tests/functional/lang/eval-fail-bad-string-interpolation-2.err.exp
diff --git a/tests/lang/eval-fail-bad-string-interpolation-2.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-2.nix
index 3745235ce..3745235ce 100644
--- a/tests/lang/eval-fail-bad-string-interpolation-2.nix
+++ b/tests/functional/lang/eval-fail-bad-string-interpolation-2.nix
diff --git a/tests/lang/eval-fail-bad-string-interpolation-3.err.exp b/tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp
index ac14f329b..ac14f329b 100644
--- a/tests/lang/eval-fail-bad-string-interpolation-3.err.exp
+++ b/tests/functional/lang/eval-fail-bad-string-interpolation-3.err.exp
diff --git a/tests/lang/eval-fail-bad-string-interpolation-3.nix b/tests/functional/lang/eval-fail-bad-string-interpolation-3.nix
index 65b9d4f50..65b9d4f50 100644
--- a/tests/lang/eval-fail-bad-string-interpolation-3.nix
+++ b/tests/functional/lang/eval-fail-bad-string-interpolation-3.nix
diff --git a/tests/lang/eval-fail-blackhole.err.exp b/tests/functional/lang/eval-fail-blackhole.err.exp
index f0618d8ac..f0618d8ac 100644
--- a/tests/lang/eval-fail-blackhole.err.exp
+++ b/tests/functional/lang/eval-fail-blackhole.err.exp
diff --git a/tests/lang/eval-fail-blackhole.nix b/tests/functional/lang/eval-fail-blackhole.nix
index 81133b511..81133b511 100644
--- a/tests/lang/eval-fail-blackhole.nix
+++ b/tests/functional/lang/eval-fail-blackhole.nix
diff --git a/tests/lang/eval-fail-deepseq.err.exp b/tests/functional/lang/eval-fail-deepseq.err.exp
index 5e204ba73..5e204ba73 100644
--- a/tests/lang/eval-fail-deepseq.err.exp
+++ b/tests/functional/lang/eval-fail-deepseq.err.exp
diff --git a/tests/lang/eval-fail-deepseq.nix b/tests/functional/lang/eval-fail-deepseq.nix
index 9baa49b06..9baa49b06 100644
--- a/tests/lang/eval-fail-deepseq.nix
+++ b/tests/functional/lang/eval-fail-deepseq.nix
diff --git a/tests/lang/eval-fail-dup-dynamic-attrs.err.exp b/tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp
index e01f8e6d0..e01f8e6d0 100644
--- a/tests/lang/eval-fail-dup-dynamic-attrs.err.exp
+++ b/tests/functional/lang/eval-fail-dup-dynamic-attrs.err.exp
diff --git a/tests/lang/eval-fail-dup-dynamic-attrs.nix b/tests/functional/lang/eval-fail-dup-dynamic-attrs.nix
index 7ea17f6c8..7ea17f6c8 100644
--- a/tests/lang/eval-fail-dup-dynamic-attrs.nix
+++ b/tests/functional/lang/eval-fail-dup-dynamic-attrs.nix
diff --git a/tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp
index 0069285fb..0069285fb 100644
--- a/tests/lang/eval-fail-foldlStrict-strict-op-application.err.exp
+++ b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.err.exp
diff --git a/tests/lang/eval-fail-foldlStrict-strict-op-application.nix b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix
index 1620cc76e..1620cc76e 100644
--- a/tests/lang/eval-fail-foldlStrict-strict-op-application.nix
+++ b/tests/functional/lang/eval-fail-foldlStrict-strict-op-application.nix
diff --git a/tests/lang/eval-fail-fromTOML-timestamps.err.exp b/tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp
index f6bd19f5a..f6bd19f5a 100644
--- a/tests/lang/eval-fail-fromTOML-timestamps.err.exp
+++ b/tests/functional/lang/eval-fail-fromTOML-timestamps.err.exp
diff --git a/tests/lang/eval-fail-fromTOML-timestamps.nix b/tests/functional/lang/eval-fail-fromTOML-timestamps.nix
index 74cff9470..74cff9470 100644
--- a/tests/lang/eval-fail-fromTOML-timestamps.nix
+++ b/tests/functional/lang/eval-fail-fromTOML-timestamps.nix
diff --git a/tests/lang/eval-fail-hashfile-missing.err.exp b/tests/functional/lang/eval-fail-hashfile-missing.err.exp
index 8e77dec1e..8e77dec1e 100644
--- a/tests/lang/eval-fail-hashfile-missing.err.exp
+++ b/tests/functional/lang/eval-fail-hashfile-missing.err.exp
diff --git a/tests/lang/eval-fail-hashfile-missing.nix b/tests/functional/lang/eval-fail-hashfile-missing.nix
index ce098b823..ce098b823 100644
--- a/tests/lang/eval-fail-hashfile-missing.nix
+++ b/tests/functional/lang/eval-fail-hashfile-missing.nix
diff --git a/tests/lang/eval-fail-list.err.exp b/tests/functional/lang/eval-fail-list.err.exp
index 24d682118..24d682118 100644
--- a/tests/lang/eval-fail-list.err.exp
+++ b/tests/functional/lang/eval-fail-list.err.exp
diff --git a/tests/lang/eval-fail-list.nix b/tests/functional/lang/eval-fail-list.nix
index fa749f2f7..fa749f2f7 100644
--- a/tests/lang/eval-fail-list.nix
+++ b/tests/functional/lang/eval-fail-list.nix
diff --git a/tests/lang/eval-fail-missing-arg.err.exp b/tests/functional/lang/eval-fail-missing-arg.err.exp
index 61fabf0d5..61fabf0d5 100644
--- a/tests/lang/eval-fail-missing-arg.err.exp
+++ b/tests/functional/lang/eval-fail-missing-arg.err.exp
diff --git a/tests/lang/eval-fail-missing-arg.nix b/tests/functional/lang/eval-fail-missing-arg.nix
index c4be9797c..c4be9797c 100644
--- a/tests/lang/eval-fail-missing-arg.nix
+++ b/tests/functional/lang/eval-fail-missing-arg.nix
diff --git a/tests/lang/eval-fail-nonexist-path.err.exp b/tests/functional/lang/eval-fail-nonexist-path.err.exp
index c8fe39d12..c8fe39d12 100644
--- a/tests/lang/eval-fail-nonexist-path.err.exp
+++ b/tests/functional/lang/eval-fail-nonexist-path.err.exp
diff --git a/tests/lang/eval-fail-nonexist-path.nix b/tests/functional/lang/eval-fail-nonexist-path.nix
index f2f08107b..f2f08107b 100644
--- a/tests/lang/eval-fail-nonexist-path.nix
+++ b/tests/functional/lang/eval-fail-nonexist-path.nix
diff --git a/tests/lang/eval-fail-path-slash.err.exp b/tests/functional/lang/eval-fail-path-slash.err.exp
index f0011c97f..f0011c97f 100644
--- a/tests/lang/eval-fail-path-slash.err.exp
+++ b/tests/functional/lang/eval-fail-path-slash.err.exp
diff --git a/tests/lang/eval-fail-path-slash.nix b/tests/functional/lang/eval-fail-path-slash.nix
index 8c2e104c7..8c2e104c7 100644
--- a/tests/lang/eval-fail-path-slash.nix
+++ b/tests/functional/lang/eval-fail-path-slash.nix
diff --git a/tests/lang/eval-fail-recursion.err.exp b/tests/functional/lang/eval-fail-recursion.err.exp
index af64133cb..af64133cb 100644
--- a/tests/lang/eval-fail-recursion.err.exp
+++ b/tests/functional/lang/eval-fail-recursion.err.exp
diff --git a/tests/lang/eval-fail-recursion.nix b/tests/functional/lang/eval-fail-recursion.nix
index 075b5ed06..075b5ed06 100644
--- a/tests/lang/eval-fail-recursion.nix
+++ b/tests/functional/lang/eval-fail-recursion.nix
diff --git a/tests/lang/eval-fail-remove.err.exp b/tests/functional/lang/eval-fail-remove.err.exp
index e82cdac98..e82cdac98 100644
--- a/tests/lang/eval-fail-remove.err.exp
+++ b/tests/functional/lang/eval-fail-remove.err.exp
diff --git a/tests/lang/eval-fail-remove.nix b/tests/functional/lang/eval-fail-remove.nix
index 539e0eb0a..539e0eb0a 100644
--- a/tests/lang/eval-fail-remove.nix
+++ b/tests/functional/lang/eval-fail-remove.nix
diff --git a/tests/lang/eval-fail-scope-5.err.exp b/tests/functional/lang/eval-fail-scope-5.err.exp
index 22b6166f8..22b6166f8 100644
--- a/tests/lang/eval-fail-scope-5.err.exp
+++ b/tests/functional/lang/eval-fail-scope-5.err.exp
diff --git a/tests/lang/eval-fail-scope-5.nix b/tests/functional/lang/eval-fail-scope-5.nix
index f89a65a99..f89a65a99 100644
--- a/tests/lang/eval-fail-scope-5.nix
+++ b/tests/functional/lang/eval-fail-scope-5.nix
diff --git a/tests/lang/eval-fail-seq.err.exp b/tests/functional/lang/eval-fail-seq.err.exp
index 33a7e9491..33a7e9491 100644
--- a/tests/lang/eval-fail-seq.err.exp
+++ b/tests/functional/lang/eval-fail-seq.err.exp
diff --git a/tests/lang/eval-fail-seq.nix b/tests/functional/lang/eval-fail-seq.nix
index cddbbfd32..cddbbfd32 100644
--- a/tests/lang/eval-fail-seq.nix
+++ b/tests/functional/lang/eval-fail-seq.nix
diff --git a/tests/lang/eval-fail-set-override.err.exp b/tests/functional/lang/eval-fail-set-override.err.exp
index beb29d678..beb29d678 100644
--- a/tests/lang/eval-fail-set-override.err.exp
+++ b/tests/functional/lang/eval-fail-set-override.err.exp
diff --git a/tests/lang/eval-fail-set-override.nix b/tests/functional/lang/eval-fail-set-override.nix
index 03551c186..03551c186 100644
--- a/tests/lang/eval-fail-set-override.nix
+++ b/tests/functional/lang/eval-fail-set-override.nix
diff --git a/tests/lang/eval-fail-set.err.exp b/tests/functional/lang/eval-fail-set.err.exp
index 0d0140508..0d0140508 100644
--- a/tests/lang/eval-fail-set.err.exp
+++ b/tests/functional/lang/eval-fail-set.err.exp
diff --git a/tests/lang/eval-fail-set.nix b/tests/functional/lang/eval-fail-set.nix
index c6b7980b6..c6b7980b6 100644
--- a/tests/lang/eval-fail-set.nix
+++ b/tests/functional/lang/eval-fail-set.nix
diff --git a/tests/lang/eval-fail-substring.err.exp b/tests/functional/lang/eval-fail-substring.err.exp
index dc26a00bd..dc26a00bd 100644
--- a/tests/lang/eval-fail-substring.err.exp
+++ b/tests/functional/lang/eval-fail-substring.err.exp
diff --git a/tests/lang/eval-fail-substring.nix b/tests/functional/lang/eval-fail-substring.nix
index f37c2bc0a..f37c2bc0a 100644
--- a/tests/lang/eval-fail-substring.nix
+++ b/tests/functional/lang/eval-fail-substring.nix
diff --git a/tests/lang/eval-fail-to-path.err.exp b/tests/functional/lang/eval-fail-to-path.err.exp
index 43ed2bdfc..43ed2bdfc 100644
--- a/tests/lang/eval-fail-to-path.err.exp
+++ b/tests/functional/lang/eval-fail-to-path.err.exp
diff --git a/tests/lang/eval-fail-to-path.nix b/tests/functional/lang/eval-fail-to-path.nix
index 5e322bc31..5e322bc31 100644
--- a/tests/lang/eval-fail-to-path.nix
+++ b/tests/functional/lang/eval-fail-to-path.nix
diff --git a/tests/lang/eval-fail-toJSON.err.exp b/tests/functional/lang/eval-fail-toJSON.err.exp
index 4e618c203..4e618c203 100644
--- a/tests/lang/eval-fail-toJSON.err.exp
+++ b/tests/functional/lang/eval-fail-toJSON.err.exp
diff --git a/tests/lang/eval-fail-toJSON.nix b/tests/functional/lang/eval-fail-toJSON.nix
index 8112e1c1f..8112e1c1f 100644
--- a/tests/lang/eval-fail-toJSON.nix
+++ b/tests/functional/lang/eval-fail-toJSON.nix
diff --git a/tests/lang/eval-fail-undeclared-arg.err.exp b/tests/functional/lang/eval-fail-undeclared-arg.err.exp
index 30db743c7..30db743c7 100644
--- a/tests/lang/eval-fail-undeclared-arg.err.exp
+++ b/tests/functional/lang/eval-fail-undeclared-arg.err.exp
diff --git a/tests/lang/eval-fail-undeclared-arg.nix b/tests/functional/lang/eval-fail-undeclared-arg.nix
index cafdf1636..cafdf1636 100644
--- a/tests/lang/eval-fail-undeclared-arg.nix
+++ b/tests/functional/lang/eval-fail-undeclared-arg.nix
diff --git a/tests/lang/eval-okay-any-all.exp b/tests/functional/lang/eval-okay-any-all.exp
index eb273f45b..eb273f45b 100644
--- a/tests/lang/eval-okay-any-all.exp
+++ b/tests/functional/lang/eval-okay-any-all.exp
diff --git a/tests/lang/eval-okay-any-all.nix b/tests/functional/lang/eval-okay-any-all.nix
index a3f26ea2a..a3f26ea2a 100644
--- a/tests/lang/eval-okay-any-all.nix
+++ b/tests/functional/lang/eval-okay-any-all.nix
diff --git a/tests/lang/eval-okay-arithmetic.exp b/tests/functional/lang/eval-okay-arithmetic.exp
index 5c54d10b7..5c54d10b7 100644
--- a/tests/lang/eval-okay-arithmetic.exp
+++ b/tests/functional/lang/eval-okay-arithmetic.exp
diff --git a/tests/lang/eval-okay-arithmetic.nix b/tests/functional/lang/eval-okay-arithmetic.nix
index 7e9e6a0b6..7e9e6a0b6 100644
--- a/tests/lang/eval-okay-arithmetic.nix
+++ b/tests/functional/lang/eval-okay-arithmetic.nix
diff --git a/tests/lang/eval-okay-attrnames.exp b/tests/functional/lang/eval-okay-attrnames.exp
index b4aa387e0..b4aa387e0 100644
--- a/tests/lang/eval-okay-attrnames.exp
+++ b/tests/functional/lang/eval-okay-attrnames.exp
diff --git a/tests/lang/eval-okay-attrnames.nix b/tests/functional/lang/eval-okay-attrnames.nix
index e5b26e9f2..e5b26e9f2 100644
--- a/tests/lang/eval-okay-attrnames.nix
+++ b/tests/functional/lang/eval-okay-attrnames.nix
diff --git a/tests/lang/eval-okay-attrs.exp b/tests/functional/lang/eval-okay-attrs.exp
index 45b0f829e..45b0f829e 100644
--- a/tests/lang/eval-okay-attrs.exp
+++ b/tests/functional/lang/eval-okay-attrs.exp
diff --git a/tests/lang/eval-okay-attrs.nix b/tests/functional/lang/eval-okay-attrs.nix
index 810b31a5d..810b31a5d 100644
--- a/tests/lang/eval-okay-attrs.nix
+++ b/tests/functional/lang/eval-okay-attrs.nix
diff --git a/tests/lang/eval-okay-attrs2.exp b/tests/functional/lang/eval-okay-attrs2.exp
index 45b0f829e..45b0f829e 100644
--- a/tests/lang/eval-okay-attrs2.exp
+++ b/tests/functional/lang/eval-okay-attrs2.exp
diff --git a/tests/lang/eval-okay-attrs2.nix b/tests/functional/lang/eval-okay-attrs2.nix
index 9e06b83ac..9e06b83ac 100644
--- a/tests/lang/eval-okay-attrs2.nix
+++ b/tests/functional/lang/eval-okay-attrs2.nix
diff --git a/tests/lang/eval-okay-attrs3.exp b/tests/functional/lang/eval-okay-attrs3.exp
index 19de4fdf7..19de4fdf7 100644
--- a/tests/lang/eval-okay-attrs3.exp
+++ b/tests/functional/lang/eval-okay-attrs3.exp
diff --git a/tests/lang/eval-okay-attrs3.nix b/tests/functional/lang/eval-okay-attrs3.nix
index f29de11fe..f29de11fe 100644
--- a/tests/lang/eval-okay-attrs3.nix
+++ b/tests/functional/lang/eval-okay-attrs3.nix
diff --git a/tests/lang/eval-okay-attrs4.exp b/tests/functional/lang/eval-okay-attrs4.exp
index 185173144..185173144 100644
--- a/tests/lang/eval-okay-attrs4.exp
+++ b/tests/functional/lang/eval-okay-attrs4.exp
diff --git a/tests/lang/eval-okay-attrs4.nix b/tests/functional/lang/eval-okay-attrs4.nix
index 43ec81210..43ec81210 100644
--- a/tests/lang/eval-okay-attrs4.nix
+++ b/tests/functional/lang/eval-okay-attrs4.nix
diff --git a/tests/lang/eval-okay-attrs5.exp b/tests/functional/lang/eval-okay-attrs5.exp
index ce0430d78..ce0430d78 100644
--- a/tests/lang/eval-okay-attrs5.exp
+++ b/tests/functional/lang/eval-okay-attrs5.exp
diff --git a/tests/lang/eval-okay-attrs5.nix b/tests/functional/lang/eval-okay-attrs5.nix
index a4584cd3b..a4584cd3b 100644
--- a/tests/lang/eval-okay-attrs5.nix
+++ b/tests/functional/lang/eval-okay-attrs5.nix
diff --git a/tests/lang/eval-okay-attrs6.exp b/tests/functional/lang/eval-okay-attrs6.exp
index b46938032..b46938032 100644
--- a/tests/lang/eval-okay-attrs6.exp
+++ b/tests/functional/lang/eval-okay-attrs6.exp
diff --git a/tests/lang/eval-okay-attrs6.nix b/tests/functional/lang/eval-okay-attrs6.nix
index 2e5c85483..2e5c85483 100644
--- a/tests/lang/eval-okay-attrs6.nix
+++ b/tests/functional/lang/eval-okay-attrs6.nix
diff --git a/tests/lang/eval-okay-autoargs.exp b/tests/functional/lang/eval-okay-autoargs.exp
index 7a8391786..7a8391786 100644
--- a/tests/lang/eval-okay-autoargs.exp
+++ b/tests/functional/lang/eval-okay-autoargs.exp
diff --git a/tests/lang/eval-okay-autoargs.flags b/tests/functional/lang/eval-okay-autoargs.flags
index ae3762254..ae3762254 100644
--- a/tests/lang/eval-okay-autoargs.flags
+++ b/tests/functional/lang/eval-okay-autoargs.flags
diff --git a/tests/lang/eval-okay-autoargs.nix b/tests/functional/lang/eval-okay-autoargs.nix
index 815f51b1d..815f51b1d 100644
--- a/tests/lang/eval-okay-autoargs.nix
+++ b/tests/functional/lang/eval-okay-autoargs.nix
diff --git a/tests/lang/eval-okay-backslash-newline-1.exp b/tests/functional/lang/eval-okay-backslash-newline-1.exp
index 3e754364c..3e754364c 100644
--- a/tests/lang/eval-okay-backslash-newline-1.exp
+++ b/tests/functional/lang/eval-okay-backslash-newline-1.exp
diff --git a/tests/lang/eval-okay-backslash-newline-1.nix b/tests/functional/lang/eval-okay-backslash-newline-1.nix
index 7fef3dddd..7fef3dddd 100644
--- a/tests/lang/eval-okay-backslash-newline-1.nix
+++ b/tests/functional/lang/eval-okay-backslash-newline-1.nix
diff --git a/tests/lang/eval-okay-backslash-newline-2.exp b/tests/functional/lang/eval-okay-backslash-newline-2.exp
index 3e754364c..3e754364c 100644
--- a/tests/lang/eval-okay-backslash-newline-2.exp
+++ b/tests/functional/lang/eval-okay-backslash-newline-2.exp
diff --git a/tests/lang/eval-okay-backslash-newline-2.nix b/tests/functional/lang/eval-okay-backslash-newline-2.nix
index 35ddf495c..35ddf495c 100644
--- a/tests/lang/eval-okay-backslash-newline-2.nix
+++ b/tests/functional/lang/eval-okay-backslash-newline-2.nix
diff --git a/tests/lang/eval-okay-builtins-add.exp b/tests/functional/lang/eval-okay-builtins-add.exp
index 0350b518a..0350b518a 100644
--- a/tests/lang/eval-okay-builtins-add.exp
+++ b/tests/functional/lang/eval-okay-builtins-add.exp
diff --git a/tests/lang/eval-okay-builtins-add.nix b/tests/functional/lang/eval-okay-builtins-add.nix
index c84181622..c84181622 100644
--- a/tests/lang/eval-okay-builtins-add.nix
+++ b/tests/functional/lang/eval-okay-builtins-add.nix
diff --git a/tests/lang/eval-okay-builtins.exp b/tests/functional/lang/eval-okay-builtins.exp
index 0661686d6..0661686d6 100644
--- a/tests/lang/eval-okay-builtins.exp
+++ b/tests/functional/lang/eval-okay-builtins.exp
diff --git a/tests/lang/eval-okay-builtins.nix b/tests/functional/lang/eval-okay-builtins.nix
index e9d65e88a..e9d65e88a 100644
--- a/tests/lang/eval-okay-builtins.nix
+++ b/tests/functional/lang/eval-okay-builtins.nix
diff --git a/tests/lang/eval-okay-callable-attrs.exp b/tests/functional/lang/eval-okay-callable-attrs.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-callable-attrs.exp
+++ b/tests/functional/lang/eval-okay-callable-attrs.exp
diff --git a/tests/lang/eval-okay-callable-attrs.nix b/tests/functional/lang/eval-okay-callable-attrs.nix
index 310a030df..310a030df 100644
--- a/tests/lang/eval-okay-callable-attrs.nix
+++ b/tests/functional/lang/eval-okay-callable-attrs.nix
diff --git a/tests/lang/eval-okay-catattrs.exp b/tests/functional/lang/eval-okay-catattrs.exp
index b4a1e66d6..b4a1e66d6 100644
--- a/tests/lang/eval-okay-catattrs.exp
+++ b/tests/functional/lang/eval-okay-catattrs.exp
diff --git a/tests/lang/eval-okay-catattrs.nix b/tests/functional/lang/eval-okay-catattrs.nix
index 2c3dc10da..2c3dc10da 100644
--- a/tests/lang/eval-okay-catattrs.nix
+++ b/tests/functional/lang/eval-okay-catattrs.nix
diff --git a/tests/lang/eval-okay-closure.exp b/tests/functional/lang/eval-okay-closure.exp
index e7dbf9781..e7dbf9781 100644
--- a/tests/lang/eval-okay-closure.exp
+++ b/tests/functional/lang/eval-okay-closure.exp
diff --git a/tests/lang/eval-okay-closure.exp.xml b/tests/functional/lang/eval-okay-closure.exp.xml
index dffc03a99..dffc03a99 100644
--- a/tests/lang/eval-okay-closure.exp.xml
+++ b/tests/functional/lang/eval-okay-closure.exp.xml
diff --git a/tests/lang/eval-okay-closure.nix b/tests/functional/lang/eval-okay-closure.nix
index cccd4dc35..cccd4dc35 100644
--- a/tests/lang/eval-okay-closure.nix
+++ b/tests/functional/lang/eval-okay-closure.nix
diff --git a/tests/lang/eval-okay-comments.exp b/tests/functional/lang/eval-okay-comments.exp
index 7182dc2f9..7182dc2f9 100644
--- a/tests/lang/eval-okay-comments.exp
+++ b/tests/functional/lang/eval-okay-comments.exp
diff --git a/tests/lang/eval-okay-comments.nix b/tests/functional/lang/eval-okay-comments.nix
index cb2cce218..cb2cce218 100644
--- a/tests/lang/eval-okay-comments.nix
+++ b/tests/functional/lang/eval-okay-comments.nix
diff --git a/tests/lang/eval-okay-concat.exp b/tests/functional/lang/eval-okay-concat.exp
index bb4bbd577..bb4bbd577 100644
--- a/tests/lang/eval-okay-concat.exp
+++ b/tests/functional/lang/eval-okay-concat.exp
diff --git a/tests/lang/eval-okay-concat.nix b/tests/functional/lang/eval-okay-concat.nix
index d158a9bf0..d158a9bf0 100644
--- a/tests/lang/eval-okay-concat.nix
+++ b/tests/functional/lang/eval-okay-concat.nix
diff --git a/tests/lang/eval-okay-concatmap.exp b/tests/functional/lang/eval-okay-concatmap.exp
index 3b8be7739..3b8be7739 100644
--- a/tests/lang/eval-okay-concatmap.exp
+++ b/tests/functional/lang/eval-okay-concatmap.exp
diff --git a/tests/lang/eval-okay-concatmap.nix b/tests/functional/lang/eval-okay-concatmap.nix
index 97da5d37a..97da5d37a 100644
--- a/tests/lang/eval-okay-concatmap.nix
+++ b/tests/functional/lang/eval-okay-concatmap.nix
diff --git a/tests/lang/eval-okay-concatstringssep.exp b/tests/functional/lang/eval-okay-concatstringssep.exp
index 93987647f..93987647f 100644
--- a/tests/lang/eval-okay-concatstringssep.exp
+++ b/tests/functional/lang/eval-okay-concatstringssep.exp
diff --git a/tests/lang/eval-okay-concatstringssep.nix b/tests/functional/lang/eval-okay-concatstringssep.nix
index adc4c41bd..adc4c41bd 100644
--- a/tests/lang/eval-okay-concatstringssep.nix
+++ b/tests/functional/lang/eval-okay-concatstringssep.nix
diff --git a/tests/lang/eval-okay-context-introspection.exp b/tests/functional/lang/eval-okay-context-introspection.exp
index 03b400cc8..03b400cc8 100644
--- a/tests/lang/eval-okay-context-introspection.exp
+++ b/tests/functional/lang/eval-okay-context-introspection.exp
diff --git a/tests/lang/eval-okay-context-introspection.nix b/tests/functional/lang/eval-okay-context-introspection.nix
index 50a78d946..50a78d946 100644
--- a/tests/lang/eval-okay-context-introspection.nix
+++ b/tests/functional/lang/eval-okay-context-introspection.nix
diff --git a/tests/lang/eval-okay-context.exp b/tests/functional/lang/eval-okay-context.exp
index 2f535bdbc..2f535bdbc 100644
--- a/tests/lang/eval-okay-context.exp
+++ b/tests/functional/lang/eval-okay-context.exp
diff --git a/tests/lang/eval-okay-context.nix b/tests/functional/lang/eval-okay-context.nix
index 7b9531cfe..7b9531cfe 100644
--- a/tests/lang/eval-okay-context.nix
+++ b/tests/functional/lang/eval-okay-context.nix
diff --git a/tests/lang/eval-okay-curpos.exp b/tests/functional/lang/eval-okay-curpos.exp
index 65fd65b4d..65fd65b4d 100644
--- a/tests/lang/eval-okay-curpos.exp
+++ b/tests/functional/lang/eval-okay-curpos.exp
diff --git a/tests/lang/eval-okay-curpos.nix b/tests/functional/lang/eval-okay-curpos.nix
index b79553df0..b79553df0 100644
--- a/tests/lang/eval-okay-curpos.nix
+++ b/tests/functional/lang/eval-okay-curpos.nix
diff --git a/tests/lang/eval-okay-deepseq.exp b/tests/functional/lang/eval-okay-deepseq.exp
index 8d38505c1..8d38505c1 100644
--- a/tests/lang/eval-okay-deepseq.exp
+++ b/tests/functional/lang/eval-okay-deepseq.exp
diff --git a/tests/lang/eval-okay-deepseq.nix b/tests/functional/lang/eval-okay-deepseq.nix
index 53aa4b1dc..53aa4b1dc 100644
--- a/tests/lang/eval-okay-deepseq.nix
+++ b/tests/functional/lang/eval-okay-deepseq.nix
diff --git a/tests/lang/eval-okay-delayed-with-inherit.exp b/tests/functional/lang/eval-okay-delayed-with-inherit.exp
index eaacb55c1..eaacb55c1 100644
--- a/tests/lang/eval-okay-delayed-with-inherit.exp
+++ b/tests/functional/lang/eval-okay-delayed-with-inherit.exp
diff --git a/tests/lang/eval-okay-delayed-with-inherit.nix b/tests/functional/lang/eval-okay-delayed-with-inherit.nix
index 84b388c27..84b388c27 100644
--- a/tests/lang/eval-okay-delayed-with-inherit.nix
+++ b/tests/functional/lang/eval-okay-delayed-with-inherit.nix
diff --git a/tests/lang/eval-okay-delayed-with.exp b/tests/functional/lang/eval-okay-delayed-with.exp
index 8e7c61ab8..8e7c61ab8 100644
--- a/tests/lang/eval-okay-delayed-with.exp
+++ b/tests/functional/lang/eval-okay-delayed-with.exp
diff --git a/tests/lang/eval-okay-delayed-with.nix b/tests/functional/lang/eval-okay-delayed-with.nix
index 3fb023e1c..3fb023e1c 100644
--- a/tests/lang/eval-okay-delayed-with.nix
+++ b/tests/functional/lang/eval-okay-delayed-with.nix
diff --git a/tests/lang/eval-okay-dynamic-attrs-2.exp b/tests/functional/lang/eval-okay-dynamic-attrs-2.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-dynamic-attrs-2.exp
+++ b/tests/functional/lang/eval-okay-dynamic-attrs-2.exp
diff --git a/tests/lang/eval-okay-dynamic-attrs-2.nix b/tests/functional/lang/eval-okay-dynamic-attrs-2.nix
index 6d57bf854..6d57bf854 100644
--- a/tests/lang/eval-okay-dynamic-attrs-2.nix
+++ b/tests/functional/lang/eval-okay-dynamic-attrs-2.nix
diff --git a/tests/lang/eval-okay-dynamic-attrs-bare.exp b/tests/functional/lang/eval-okay-dynamic-attrs-bare.exp
index df8750afc..df8750afc 100644
--- a/tests/lang/eval-okay-dynamic-attrs-bare.exp
+++ b/tests/functional/lang/eval-okay-dynamic-attrs-bare.exp
diff --git a/tests/lang/eval-okay-dynamic-attrs-bare.nix b/tests/functional/lang/eval-okay-dynamic-attrs-bare.nix
index 0dbe15e63..0dbe15e63 100644
--- a/tests/lang/eval-okay-dynamic-attrs-bare.nix
+++ b/tests/functional/lang/eval-okay-dynamic-attrs-bare.nix
diff --git a/tests/lang/eval-okay-dynamic-attrs.exp b/tests/functional/lang/eval-okay-dynamic-attrs.exp
index df8750afc..df8750afc 100644
--- a/tests/lang/eval-okay-dynamic-attrs.exp
+++ b/tests/functional/lang/eval-okay-dynamic-attrs.exp
diff --git a/tests/lang/eval-okay-dynamic-attrs.nix b/tests/functional/lang/eval-okay-dynamic-attrs.nix
index ee02ac7e6..ee02ac7e6 100644
--- a/tests/lang/eval-okay-dynamic-attrs.nix
+++ b/tests/functional/lang/eval-okay-dynamic-attrs.nix
diff --git a/tests/lang/eval-okay-elem.exp b/tests/functional/lang/eval-okay-elem.exp
index 3cf6c0e96..3cf6c0e96 100644
--- a/tests/lang/eval-okay-elem.exp
+++ b/tests/functional/lang/eval-okay-elem.exp
diff --git a/tests/lang/eval-okay-elem.nix b/tests/functional/lang/eval-okay-elem.nix
index 71ea7a4ed..71ea7a4ed 100644
--- a/tests/lang/eval-okay-elem.nix
+++ b/tests/functional/lang/eval-okay-elem.nix
diff --git a/tests/lang/eval-okay-empty-args.exp b/tests/functional/lang/eval-okay-empty-args.exp
index cb5537d5d..cb5537d5d 100644
--- a/tests/lang/eval-okay-empty-args.exp
+++ b/tests/functional/lang/eval-okay-empty-args.exp
diff --git a/tests/lang/eval-okay-empty-args.nix b/tests/functional/lang/eval-okay-empty-args.nix
index 78c133afd..78c133afd 100644
--- a/tests/lang/eval-okay-empty-args.nix
+++ b/tests/functional/lang/eval-okay-empty-args.nix
diff --git a/tests/lang/eval-okay-eq-derivations.exp b/tests/functional/lang/eval-okay-eq-derivations.exp
index ec04aab6a..ec04aab6a 100644
--- a/tests/lang/eval-okay-eq-derivations.exp
+++ b/tests/functional/lang/eval-okay-eq-derivations.exp
diff --git a/tests/lang/eval-okay-eq-derivations.nix b/tests/functional/lang/eval-okay-eq-derivations.nix
index d526cb4a2..d526cb4a2 100644
--- a/tests/lang/eval-okay-eq-derivations.nix
+++ b/tests/functional/lang/eval-okay-eq-derivations.nix
diff --git a/tests/lang/eval-okay-eq.exp b/tests/functional/lang/eval-okay-eq.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-eq.exp
+++ b/tests/functional/lang/eval-okay-eq.exp
diff --git a/tests/lang/eval-okay-eq.nix b/tests/functional/lang/eval-okay-eq.nix
index 73d200b38..73d200b38 100644
--- a/tests/lang/eval-okay-eq.nix
+++ b/tests/functional/lang/eval-okay-eq.nix
diff --git a/tests/lang/eval-okay-filter.exp b/tests/functional/lang/eval-okay-filter.exp
index 355d51c27..355d51c27 100644
--- a/tests/lang/eval-okay-filter.exp
+++ b/tests/functional/lang/eval-okay-filter.exp
diff --git a/tests/lang/eval-okay-filter.nix b/tests/functional/lang/eval-okay-filter.nix
index 85109b0d0..85109b0d0 100644
--- a/tests/lang/eval-okay-filter.nix
+++ b/tests/functional/lang/eval-okay-filter.nix
diff --git a/tests/lang/eval-okay-flake-ref-to-string.exp b/tests/functional/lang/eval-okay-flake-ref-to-string.exp
index 110f8442d..110f8442d 100644
--- a/tests/lang/eval-okay-flake-ref-to-string.exp
+++ b/tests/functional/lang/eval-okay-flake-ref-to-string.exp
diff --git a/tests/lang/eval-okay-flake-ref-to-string.nix b/tests/functional/lang/eval-okay-flake-ref-to-string.nix
index dbb4e5b2a..dbb4e5b2a 100644
--- a/tests/lang/eval-okay-flake-ref-to-string.nix
+++ b/tests/functional/lang/eval-okay-flake-ref-to-string.nix
diff --git a/tests/lang/eval-okay-flatten.exp b/tests/functional/lang/eval-okay-flatten.exp
index b979b2b8b..b979b2b8b 100644
--- a/tests/lang/eval-okay-flatten.exp
+++ b/tests/functional/lang/eval-okay-flatten.exp
diff --git a/tests/lang/eval-okay-flatten.nix b/tests/functional/lang/eval-okay-flatten.nix
index fe911e968..fe911e968 100644
--- a/tests/lang/eval-okay-flatten.nix
+++ b/tests/functional/lang/eval-okay-flatten.nix
diff --git a/tests/lang/eval-okay-float.exp b/tests/functional/lang/eval-okay-float.exp
index 3c50a8adc..3c50a8adc 100644
--- a/tests/lang/eval-okay-float.exp
+++ b/tests/functional/lang/eval-okay-float.exp
diff --git a/tests/lang/eval-okay-float.nix b/tests/functional/lang/eval-okay-float.nix
index b2702c7b1..b2702c7b1 100644
--- a/tests/lang/eval-okay-float.nix
+++ b/tests/functional/lang/eval-okay-float.nix
diff --git a/tests/lang/eval-okay-floor-ceil.exp b/tests/functional/lang/eval-okay-floor-ceil.exp
index 81f80420b..81f80420b 100644
--- a/tests/lang/eval-okay-floor-ceil.exp
+++ b/tests/functional/lang/eval-okay-floor-ceil.exp
diff --git a/tests/lang/eval-okay-floor-ceil.nix b/tests/functional/lang/eval-okay-floor-ceil.nix
index d76a0d86e..d76a0d86e 100644
--- a/tests/lang/eval-okay-floor-ceil.nix
+++ b/tests/functional/lang/eval-okay-floor-ceil.nix
diff --git a/tests/lang/eval-okay-foldlStrict-lazy-elements.exp b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp
index d81cc0710..d81cc0710 100644
--- a/tests/lang/eval-okay-foldlStrict-lazy-elements.exp
+++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.exp
diff --git a/tests/lang/eval-okay-foldlStrict-lazy-elements.nix b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix
index c666e07f3..c666e07f3 100644
--- a/tests/lang/eval-okay-foldlStrict-lazy-elements.nix
+++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-elements.nix
diff --git a/tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp
index d81cc0710..d81cc0710 100644
--- a/tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp
+++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.exp
diff --git a/tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix
index abcd5366a..abcd5366a 100644
--- a/tests/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix
+++ b/tests/functional/lang/eval-okay-foldlStrict-lazy-initial-accumulator.nix
diff --git a/tests/lang/eval-okay-foldlStrict.exp b/tests/functional/lang/eval-okay-foldlStrict.exp
index 837e12b40..837e12b40 100644
--- a/tests/lang/eval-okay-foldlStrict.exp
+++ b/tests/functional/lang/eval-okay-foldlStrict.exp
diff --git a/tests/lang/eval-okay-foldlStrict.nix b/tests/functional/lang/eval-okay-foldlStrict.nix
index 3b87188d2..3b87188d2 100644
--- a/tests/lang/eval-okay-foldlStrict.nix
+++ b/tests/functional/lang/eval-okay-foldlStrict.nix
diff --git a/tests/lang/eval-okay-fromTOML-timestamps.exp b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp
index 08b3c69a6..08b3c69a6 100644
--- a/tests/lang/eval-okay-fromTOML-timestamps.exp
+++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp
diff --git a/tests/lang/eval-okay-fromTOML-timestamps.flags b/tests/functional/lang/eval-okay-fromTOML-timestamps.flags
index 9ed39dc6b..9ed39dc6b 100644
--- a/tests/lang/eval-okay-fromTOML-timestamps.flags
+++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.flags
diff --git a/tests/lang/eval-okay-fromTOML-timestamps.nix b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix
index 74cff9470..74cff9470 100644
--- a/tests/lang/eval-okay-fromTOML-timestamps.nix
+++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix
diff --git a/tests/lang/eval-okay-fromTOML.exp b/tests/functional/lang/eval-okay-fromTOML.exp
index d0dd3af2c..d0dd3af2c 100644
--- a/tests/lang/eval-okay-fromTOML.exp
+++ b/tests/functional/lang/eval-okay-fromTOML.exp
diff --git a/tests/lang/eval-okay-fromTOML.nix b/tests/functional/lang/eval-okay-fromTOML.nix
index 963932689..963932689 100644
--- a/tests/lang/eval-okay-fromTOML.nix
+++ b/tests/functional/lang/eval-okay-fromTOML.nix
diff --git a/tests/lang/eval-okay-fromjson-escapes.exp b/tests/functional/lang/eval-okay-fromjson-escapes.exp
index add5505a8..add5505a8 100644
--- a/tests/lang/eval-okay-fromjson-escapes.exp
+++ b/tests/functional/lang/eval-okay-fromjson-escapes.exp
diff --git a/tests/lang/eval-okay-fromjson-escapes.nix b/tests/functional/lang/eval-okay-fromjson-escapes.nix
index f00713507..f00713507 100644
--- a/tests/lang/eval-okay-fromjson-escapes.nix
+++ b/tests/functional/lang/eval-okay-fromjson-escapes.nix
diff --git a/tests/lang/eval-okay-fromjson.exp b/tests/functional/lang/eval-okay-fromjson.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-fromjson.exp
+++ b/tests/functional/lang/eval-okay-fromjson.exp
diff --git a/tests/lang/eval-okay-fromjson.nix b/tests/functional/lang/eval-okay-fromjson.nix
index 4c526b9ae..4c526b9ae 100644
--- a/tests/lang/eval-okay-fromjson.nix
+++ b/tests/functional/lang/eval-okay-fromjson.nix
diff --git a/tests/lang/eval-okay-functionargs.exp b/tests/functional/lang/eval-okay-functionargs.exp
index c1c9f8ffa..c1c9f8ffa 100644
--- a/tests/lang/eval-okay-functionargs.exp
+++ b/tests/functional/lang/eval-okay-functionargs.exp
diff --git a/tests/lang/eval-okay-functionargs.exp.xml b/tests/functional/lang/eval-okay-functionargs.exp.xml
index 651f54c36..651f54c36 100644
--- a/tests/lang/eval-okay-functionargs.exp.xml
+++ b/tests/functional/lang/eval-okay-functionargs.exp.xml
diff --git a/tests/lang/eval-okay-functionargs.nix b/tests/functional/lang/eval-okay-functionargs.nix
index 68dca62ee..68dca62ee 100644
--- a/tests/lang/eval-okay-functionargs.nix
+++ b/tests/functional/lang/eval-okay-functionargs.nix
diff --git a/tests/lang/eval-okay-getattrpos-functionargs.exp b/tests/functional/lang/eval-okay-getattrpos-functionargs.exp
index 7f9ac40e8..7f9ac40e8 100644
--- a/tests/lang/eval-okay-getattrpos-functionargs.exp
+++ b/tests/functional/lang/eval-okay-getattrpos-functionargs.exp
diff --git a/tests/lang/eval-okay-getattrpos-functionargs.nix b/tests/functional/lang/eval-okay-getattrpos-functionargs.nix
index 11d6bb0e3..11d6bb0e3 100644
--- a/tests/lang/eval-okay-getattrpos-functionargs.nix
+++ b/tests/functional/lang/eval-okay-getattrpos-functionargs.nix
diff --git a/tests/lang/eval-okay-getattrpos-undefined.exp b/tests/functional/lang/eval-okay-getattrpos-undefined.exp
index 19765bd50..19765bd50 100644
--- a/tests/lang/eval-okay-getattrpos-undefined.exp
+++ b/tests/functional/lang/eval-okay-getattrpos-undefined.exp
diff --git a/tests/lang/eval-okay-getattrpos-undefined.nix b/tests/functional/lang/eval-okay-getattrpos-undefined.nix
index 14dd38f77..14dd38f77 100644
--- a/tests/lang/eval-okay-getattrpos-undefined.nix
+++ b/tests/functional/lang/eval-okay-getattrpos-undefined.nix
diff --git a/tests/lang/eval-okay-getattrpos.exp b/tests/functional/lang/eval-okay-getattrpos.exp
index 469249bbc..469249bbc 100644
--- a/tests/lang/eval-okay-getattrpos.exp
+++ b/tests/functional/lang/eval-okay-getattrpos.exp
diff --git a/tests/lang/eval-okay-getattrpos.nix b/tests/functional/lang/eval-okay-getattrpos.nix
index ca6b07961..ca6b07961 100644
--- a/tests/lang/eval-okay-getattrpos.nix
+++ b/tests/functional/lang/eval-okay-getattrpos.nix
diff --git a/tests/lang/eval-okay-getenv.exp b/tests/functional/lang/eval-okay-getenv.exp
index 14e24d419..14e24d419 100644
--- a/tests/lang/eval-okay-getenv.exp
+++ b/tests/functional/lang/eval-okay-getenv.exp
diff --git a/tests/lang/eval-okay-getenv.nix b/tests/functional/lang/eval-okay-getenv.nix
index 4cfec5f55..4cfec5f55 100644
--- a/tests/lang/eval-okay-getenv.nix
+++ b/tests/functional/lang/eval-okay-getenv.nix
diff --git a/tests/lang/eval-okay-groupBy.exp b/tests/functional/lang/eval-okay-groupBy.exp
index bfca5652a..bfca5652a 100644
--- a/tests/lang/eval-okay-groupBy.exp
+++ b/tests/functional/lang/eval-okay-groupBy.exp
diff --git a/tests/lang/eval-okay-groupBy.nix b/tests/functional/lang/eval-okay-groupBy.nix
index 862d89dbd..862d89dbd 100644
--- a/tests/lang/eval-okay-groupBy.nix
+++ b/tests/functional/lang/eval-okay-groupBy.nix
diff --git a/tests/lang/eval-okay-hash.exp b/tests/functional/lang/eval-okay-hash.exp
index e69de29bb..e69de29bb 100644
--- a/tests/lang/eval-okay-hash.exp
+++ b/tests/functional/lang/eval-okay-hash.exp
diff --git a/tests/lang/eval-okay-hashfile.exp b/tests/functional/lang/eval-okay-hashfile.exp
index ff1e8293e..ff1e8293e 100644
--- a/tests/lang/eval-okay-hashfile.exp
+++ b/tests/functional/lang/eval-okay-hashfile.exp
diff --git a/tests/lang/eval-okay-hashfile.nix b/tests/functional/lang/eval-okay-hashfile.nix
index aff5a1856..aff5a1856 100644
--- a/tests/lang/eval-okay-hashfile.nix
+++ b/tests/functional/lang/eval-okay-hashfile.nix
diff --git a/tests/lang/eval-okay-hashstring.exp b/tests/functional/lang/eval-okay-hashstring.exp
index d720a082d..d720a082d 100644
--- a/tests/lang/eval-okay-hashstring.exp
+++ b/tests/functional/lang/eval-okay-hashstring.exp
diff --git a/tests/lang/eval-okay-hashstring.nix b/tests/functional/lang/eval-okay-hashstring.nix
index b0f62b245..b0f62b245 100644
--- a/tests/lang/eval-okay-hashstring.nix
+++ b/tests/functional/lang/eval-okay-hashstring.nix
diff --git a/tests/lang/eval-okay-if.exp b/tests/functional/lang/eval-okay-if.exp
index 00750edc0..00750edc0 100644
--- a/tests/lang/eval-okay-if.exp
+++ b/tests/functional/lang/eval-okay-if.exp
diff --git a/tests/lang/eval-okay-if.nix b/tests/functional/lang/eval-okay-if.nix
index 23e4c74d5..23e4c74d5 100644
--- a/tests/lang/eval-okay-if.nix
+++ b/tests/functional/lang/eval-okay-if.nix
diff --git a/tests/lang/eval-okay-import.exp b/tests/functional/lang/eval-okay-import.exp
index c508125b5..c508125b5 100644
--- a/tests/lang/eval-okay-import.exp
+++ b/tests/functional/lang/eval-okay-import.exp
diff --git a/tests/lang/eval-okay-import.nix b/tests/functional/lang/eval-okay-import.nix
index 0b18d9413..0b18d9413 100644
--- a/tests/lang/eval-okay-import.nix
+++ b/tests/functional/lang/eval-okay-import.nix
diff --git a/tests/lang/eval-okay-ind-string.exp b/tests/functional/lang/eval-okay-ind-string.exp
index 7862331fa..7862331fa 100644
--- a/tests/lang/eval-okay-ind-string.exp
+++ b/tests/functional/lang/eval-okay-ind-string.exp
diff --git a/tests/lang/eval-okay-ind-string.nix b/tests/functional/lang/eval-okay-ind-string.nix
index 95d59b508..95d59b508 100644
--- a/tests/lang/eval-okay-ind-string.nix
+++ b/tests/functional/lang/eval-okay-ind-string.nix
diff --git a/tests/lang/eval-okay-intersectAttrs.exp b/tests/functional/lang/eval-okay-intersectAttrs.exp
index 50445bc0e..50445bc0e 100644
--- a/tests/lang/eval-okay-intersectAttrs.exp
+++ b/tests/functional/lang/eval-okay-intersectAttrs.exp
diff --git a/tests/lang/eval-okay-intersectAttrs.nix b/tests/functional/lang/eval-okay-intersectAttrs.nix
index 39d49938c..39d49938c 100644
--- a/tests/lang/eval-okay-intersectAttrs.nix
+++ b/tests/functional/lang/eval-okay-intersectAttrs.nix
diff --git a/tests/lang/eval-okay-let.exp b/tests/functional/lang/eval-okay-let.exp
index 14e24d419..14e24d419 100644
--- a/tests/lang/eval-okay-let.exp
+++ b/tests/functional/lang/eval-okay-let.exp
diff --git a/tests/lang/eval-okay-let.nix b/tests/functional/lang/eval-okay-let.nix
index fe118c528..fe118c528 100644
--- a/tests/lang/eval-okay-let.nix
+++ b/tests/functional/lang/eval-okay-let.nix
diff --git a/tests/lang/eval-okay-list.exp b/tests/functional/lang/eval-okay-list.exp
index f784f26d8..f784f26d8 100644
--- a/tests/lang/eval-okay-list.exp
+++ b/tests/functional/lang/eval-okay-list.exp
diff --git a/tests/lang/eval-okay-list.nix b/tests/functional/lang/eval-okay-list.nix
index d433bcf90..d433bcf90 100644
--- a/tests/lang/eval-okay-list.nix
+++ b/tests/functional/lang/eval-okay-list.nix
diff --git a/tests/lang/eval-okay-listtoattrs.exp b/tests/functional/lang/eval-okay-listtoattrs.exp
index 74abef7bc..74abef7bc 100644
--- a/tests/lang/eval-okay-listtoattrs.exp
+++ b/tests/functional/lang/eval-okay-listtoattrs.exp
diff --git a/tests/lang/eval-okay-listtoattrs.nix b/tests/functional/lang/eval-okay-listtoattrs.nix
index 4186e029b..4186e029b 100644
--- a/tests/lang/eval-okay-listtoattrs.nix
+++ b/tests/functional/lang/eval-okay-listtoattrs.nix
diff --git a/tests/lang/eval-okay-logic.exp b/tests/functional/lang/eval-okay-logic.exp
index d00491fd7..d00491fd7 100644
--- a/tests/lang/eval-okay-logic.exp
+++ b/tests/functional/lang/eval-okay-logic.exp
diff --git a/tests/lang/eval-okay-logic.nix b/tests/functional/lang/eval-okay-logic.nix
index fbb127944..fbb127944 100644
--- a/tests/lang/eval-okay-logic.nix
+++ b/tests/functional/lang/eval-okay-logic.nix
diff --git a/tests/lang/eval-okay-map.exp b/tests/functional/lang/eval-okay-map.exp
index dbb64f717..dbb64f717 100644
--- a/tests/lang/eval-okay-map.exp
+++ b/tests/functional/lang/eval-okay-map.exp
diff --git a/tests/lang/eval-okay-map.nix b/tests/functional/lang/eval-okay-map.nix
index a76c1d811..a76c1d811 100644
--- a/tests/lang/eval-okay-map.nix
+++ b/tests/functional/lang/eval-okay-map.nix
diff --git a/tests/lang/eval-okay-mapattrs.exp b/tests/functional/lang/eval-okay-mapattrs.exp
index 3f113f17b..3f113f17b 100644
--- a/tests/lang/eval-okay-mapattrs.exp
+++ b/tests/functional/lang/eval-okay-mapattrs.exp
diff --git a/tests/lang/eval-okay-mapattrs.nix b/tests/functional/lang/eval-okay-mapattrs.nix
index f075b6275..f075b6275 100644
--- a/tests/lang/eval-okay-mapattrs.nix
+++ b/tests/functional/lang/eval-okay-mapattrs.nix
diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.exp b/tests/functional/lang/eval-okay-merge-dynamic-attrs.exp
index 157d677ce..157d677ce 100644
--- a/tests/lang/eval-okay-merge-dynamic-attrs.exp
+++ b/tests/functional/lang/eval-okay-merge-dynamic-attrs.exp
diff --git a/tests/lang/eval-okay-merge-dynamic-attrs.nix b/tests/functional/lang/eval-okay-merge-dynamic-attrs.nix
index f459a554f..f459a554f 100644
--- a/tests/lang/eval-okay-merge-dynamic-attrs.nix
+++ b/tests/functional/lang/eval-okay-merge-dynamic-attrs.nix
diff --git a/tests/lang/eval-okay-nested-with.exp b/tests/functional/lang/eval-okay-nested-with.exp
index 0cfbf0888..0cfbf0888 100644
--- a/tests/lang/eval-okay-nested-with.exp
+++ b/tests/functional/lang/eval-okay-nested-with.exp
diff --git a/tests/lang/eval-okay-nested-with.nix b/tests/functional/lang/eval-okay-nested-with.nix
index ba9d79aa7..ba9d79aa7 100644
--- a/tests/lang/eval-okay-nested-with.nix
+++ b/tests/functional/lang/eval-okay-nested-with.nix
diff --git a/tests/lang/eval-okay-new-let.exp b/tests/functional/lang/eval-okay-new-let.exp
index f98b38807..f98b38807 100644
--- a/tests/lang/eval-okay-new-let.exp
+++ b/tests/functional/lang/eval-okay-new-let.exp
diff --git a/tests/lang/eval-okay-new-let.nix b/tests/functional/lang/eval-okay-new-let.nix
index 738123141..738123141 100644
--- a/tests/lang/eval-okay-new-let.nix
+++ b/tests/functional/lang/eval-okay-new-let.nix
diff --git a/tests/lang/eval-okay-null-dynamic-attrs.exp b/tests/functional/lang/eval-okay-null-dynamic-attrs.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-null-dynamic-attrs.exp
+++ b/tests/functional/lang/eval-okay-null-dynamic-attrs.exp
diff --git a/tests/lang/eval-okay-null-dynamic-attrs.nix b/tests/functional/lang/eval-okay-null-dynamic-attrs.nix
index b060c0bc9..b060c0bc9 100644
--- a/tests/lang/eval-okay-null-dynamic-attrs.nix
+++ b/tests/functional/lang/eval-okay-null-dynamic-attrs.nix
diff --git a/tests/lang/eval-okay-overrides.exp b/tests/functional/lang/eval-okay-overrides.exp
index 0cfbf0888..0cfbf0888 100644
--- a/tests/lang/eval-okay-overrides.exp
+++ b/tests/functional/lang/eval-okay-overrides.exp
diff --git a/tests/lang/eval-okay-overrides.nix b/tests/functional/lang/eval-okay-overrides.nix
index 719bdc9c0..719bdc9c0 100644
--- a/tests/lang/eval-okay-overrides.nix
+++ b/tests/functional/lang/eval-okay-overrides.nix
diff --git a/tests/lang/eval-okay-parse-flake-ref.exp b/tests/functional/lang/eval-okay-parse-flake-ref.exp
index fc17ba085..fc17ba085 100644
--- a/tests/lang/eval-okay-parse-flake-ref.exp
+++ b/tests/functional/lang/eval-okay-parse-flake-ref.exp
diff --git a/tests/lang/eval-okay-parse-flake-ref.nix b/tests/functional/lang/eval-okay-parse-flake-ref.nix
index db4ed2742..db4ed2742 100644
--- a/tests/lang/eval-okay-parse-flake-ref.nix
+++ b/tests/functional/lang/eval-okay-parse-flake-ref.nix
diff --git a/tests/lang/eval-okay-partition.exp b/tests/functional/lang/eval-okay-partition.exp
index cd8b8b020..cd8b8b020 100644
--- a/tests/lang/eval-okay-partition.exp
+++ b/tests/functional/lang/eval-okay-partition.exp
diff --git a/tests/lang/eval-okay-partition.nix b/tests/functional/lang/eval-okay-partition.nix
index 846d2ce49..846d2ce49 100644
--- a/tests/lang/eval-okay-partition.nix
+++ b/tests/functional/lang/eval-okay-partition.nix
diff --git a/tests/lang/eval-okay-path-string-interpolation.exp b/tests/functional/lang/eval-okay-path-string-interpolation.exp
index 5b8ea0243..5b8ea0243 100644
--- a/tests/lang/eval-okay-path-string-interpolation.exp
+++ b/tests/functional/lang/eval-okay-path-string-interpolation.exp
diff --git a/tests/lang/eval-okay-path-string-interpolation.nix b/tests/functional/lang/eval-okay-path-string-interpolation.nix
index 497d7c1c7..497d7c1c7 100644
--- a/tests/lang/eval-okay-path-string-interpolation.nix
+++ b/tests/functional/lang/eval-okay-path-string-interpolation.nix
diff --git a/tests/lang/eval-okay-path.exp b/tests/functional/lang/eval-okay-path.exp
index 3ce7f8283..3ce7f8283 100644
--- a/tests/lang/eval-okay-path.exp
+++ b/tests/functional/lang/eval-okay-path.exp
diff --git a/tests/lang/eval-okay-path.nix b/tests/functional/lang/eval-okay-path.nix
index e67168cf3..e67168cf3 100644
--- a/tests/lang/eval-okay-path.nix
+++ b/tests/functional/lang/eval-okay-path.nix
diff --git a/tests/lang/eval-okay-pathexists.exp b/tests/functional/lang/eval-okay-pathexists.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-pathexists.exp
+++ b/tests/functional/lang/eval-okay-pathexists.exp
diff --git a/tests/functional/lang/eval-okay-pathexists.nix b/tests/functional/lang/eval-okay-pathexists.nix
new file mode 100644
index 000000000..c5e7a62de
--- /dev/null
+++ b/tests/functional/lang/eval-okay-pathexists.nix
@@ -0,0 +1,29 @@
+builtins.pathExists (./lib.nix)
+&& builtins.pathExists (builtins.toPath ./lib.nix)
+&& builtins.pathExists (builtins.toString ./lib.nix)
+&& !builtins.pathExists (builtins.toString ./lib.nix + "/")
+&& !builtins.pathExists (builtins.toString ./lib.nix + "/.")
+# FIXME
+# && !builtins.pathExists (builtins.toString ./lib.nix + "/..")
+# && !builtins.pathExists (builtins.toString ./lib.nix + "/a/..")
+# && !builtins.pathExists (builtins.toString ./lib.nix + "/../lib.nix")
+&& !builtins.pathExists (builtins.toString ./lib.nix + "/./")
+&& !builtins.pathExists (builtins.toString ./lib.nix + "/./.")
+&& builtins.pathExists (builtins.toString ./.. + "/lang/lib.nix")
+&& !builtins.pathExists (builtins.toString ./.. + "lang/lib.nix")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/lib.nix")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/./lib.nix")
+&& builtins.pathExists (builtins.toString ./.)
+&& builtins.pathExists (builtins.toString ./. + "/")
+&& builtins.pathExists (builtins.toString ./. + "/../lang")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/.")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/./")
+&& builtins.pathExists (builtins.toString ./. + "/../lang//./")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/..")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/../")
+&& builtins.pathExists (builtins.toString ./. + "/../lang/..//")
+&& builtins.pathExists (builtins.toPath (builtins.toString ./lib.nix))
+&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix))
+&& builtins.pathExists ./lib.nix
+&& !builtins.pathExists ./bla.nix
diff --git a/tests/lang/eval-okay-patterns.exp b/tests/functional/lang/eval-okay-patterns.exp
index a4304010f..a4304010f 100644
--- a/tests/lang/eval-okay-patterns.exp
+++ b/tests/functional/lang/eval-okay-patterns.exp
diff --git a/tests/lang/eval-okay-patterns.nix b/tests/functional/lang/eval-okay-patterns.nix
index 96fd25a01..96fd25a01 100644
--- a/tests/lang/eval-okay-patterns.nix
+++ b/tests/functional/lang/eval-okay-patterns.nix
diff --git a/tests/lang/eval-okay-print.err.exp b/tests/functional/lang/eval-okay-print.err.exp
index 3fc99be3e..3fc99be3e 100644
--- a/tests/lang/eval-okay-print.err.exp
+++ b/tests/functional/lang/eval-okay-print.err.exp
diff --git a/tests/lang/eval-okay-print.exp b/tests/functional/lang/eval-okay-print.exp
index 0d960fb70..0d960fb70 100644
--- a/tests/lang/eval-okay-print.exp
+++ b/tests/functional/lang/eval-okay-print.exp
diff --git a/tests/lang/eval-okay-print.nix b/tests/functional/lang/eval-okay-print.nix
index d36ba4da3..d36ba4da3 100644
--- a/tests/lang/eval-okay-print.nix
+++ b/tests/functional/lang/eval-okay-print.nix
diff --git a/tests/lang/eval-okay-readDir.exp b/tests/functional/lang/eval-okay-readDir.exp
index 6413f6d4f..6413f6d4f 100644
--- a/tests/lang/eval-okay-readDir.exp
+++ b/tests/functional/lang/eval-okay-readDir.exp
diff --git a/tests/lang/eval-okay-readDir.nix b/tests/functional/lang/eval-okay-readDir.nix
index a7ec9292a..a7ec9292a 100644
--- a/tests/lang/eval-okay-readDir.nix
+++ b/tests/functional/lang/eval-okay-readDir.nix
diff --git a/tests/lang/eval-okay-readFileType.exp b/tests/functional/lang/eval-okay-readFileType.exp
index 6413f6d4f..6413f6d4f 100644
--- a/tests/lang/eval-okay-readFileType.exp
+++ b/tests/functional/lang/eval-okay-readFileType.exp
diff --git a/tests/lang/eval-okay-readFileType.nix b/tests/functional/lang/eval-okay-readFileType.nix
index 174fb6c3a..174fb6c3a 100644
--- a/tests/lang/eval-okay-readFileType.nix
+++ b/tests/functional/lang/eval-okay-readFileType.nix
diff --git a/tests/lang/eval-okay-readfile.exp b/tests/functional/lang/eval-okay-readfile.exp
index a2c87d0c4..a2c87d0c4 100644
--- a/tests/lang/eval-okay-readfile.exp
+++ b/tests/functional/lang/eval-okay-readfile.exp
diff --git a/tests/lang/eval-okay-readfile.nix b/tests/functional/lang/eval-okay-readfile.nix
index 82f7cb174..82f7cb174 100644
--- a/tests/lang/eval-okay-readfile.nix
+++ b/tests/functional/lang/eval-okay-readfile.nix
diff --git a/tests/lang/eval-okay-redefine-builtin.exp b/tests/functional/lang/eval-okay-redefine-builtin.exp
index c508d5366..c508d5366 100644
--- a/tests/lang/eval-okay-redefine-builtin.exp
+++ b/tests/functional/lang/eval-okay-redefine-builtin.exp
diff --git a/tests/lang/eval-okay-redefine-builtin.nix b/tests/functional/lang/eval-okay-redefine-builtin.nix
index df9fc3f37..df9fc3f37 100644
--- a/tests/lang/eval-okay-redefine-builtin.nix
+++ b/tests/functional/lang/eval-okay-redefine-builtin.nix
diff --git a/tests/lang/eval-okay-regex-match.exp b/tests/functional/lang/eval-okay-regex-match.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-regex-match.exp
+++ b/tests/functional/lang/eval-okay-regex-match.exp
diff --git a/tests/lang/eval-okay-regex-match.nix b/tests/functional/lang/eval-okay-regex-match.nix
index 273e25907..273e25907 100644
--- a/tests/lang/eval-okay-regex-match.nix
+++ b/tests/functional/lang/eval-okay-regex-match.nix
diff --git a/tests/lang/eval-okay-regex-split.exp b/tests/functional/lang/eval-okay-regex-split.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-regex-split.exp
+++ b/tests/functional/lang/eval-okay-regex-split.exp
diff --git a/tests/lang/eval-okay-regex-split.nix b/tests/functional/lang/eval-okay-regex-split.nix
index 0073e0577..0073e0577 100644
--- a/tests/lang/eval-okay-regex-split.nix
+++ b/tests/functional/lang/eval-okay-regex-split.nix
diff --git a/tests/lang/eval-okay-regression-20220122.exp b/tests/functional/lang/eval-okay-regression-20220122.exp
index 00750edc0..00750edc0 100644
--- a/tests/lang/eval-okay-regression-20220122.exp
+++ b/tests/functional/lang/eval-okay-regression-20220122.exp
diff --git a/tests/lang/eval-okay-regression-20220122.nix b/tests/functional/lang/eval-okay-regression-20220122.nix
index 694e9a13b..694e9a13b 100644
--- a/tests/lang/eval-okay-regression-20220122.nix
+++ b/tests/functional/lang/eval-okay-regression-20220122.nix
diff --git a/tests/lang/eval-okay-regression-20220125.exp b/tests/functional/lang/eval-okay-regression-20220125.exp
index 00750edc0..00750edc0 100644
--- a/tests/lang/eval-okay-regression-20220125.exp
+++ b/tests/functional/lang/eval-okay-regression-20220125.exp
diff --git a/tests/lang/eval-okay-regression-20220125.nix b/tests/functional/lang/eval-okay-regression-20220125.nix
index 485502373..485502373 100644
--- a/tests/lang/eval-okay-regression-20220125.nix
+++ b/tests/functional/lang/eval-okay-regression-20220125.nix
diff --git a/tests/lang/eval-okay-remove.exp b/tests/functional/lang/eval-okay-remove.exp
index 8d38505c1..8d38505c1 100644
--- a/tests/lang/eval-okay-remove.exp
+++ b/tests/functional/lang/eval-okay-remove.exp
diff --git a/tests/lang/eval-okay-remove.nix b/tests/functional/lang/eval-okay-remove.nix
index 4ad5ba897..4ad5ba897 100644
--- a/tests/lang/eval-okay-remove.nix
+++ b/tests/functional/lang/eval-okay-remove.nix
diff --git a/tests/lang/eval-okay-replacestrings.exp b/tests/functional/lang/eval-okay-replacestrings.exp
index eac67c5fe..eac67c5fe 100644
--- a/tests/lang/eval-okay-replacestrings.exp
+++ b/tests/functional/lang/eval-okay-replacestrings.exp
diff --git a/tests/lang/eval-okay-replacestrings.nix b/tests/functional/lang/eval-okay-replacestrings.nix
index a803e6519..a803e6519 100644
--- a/tests/lang/eval-okay-replacestrings.nix
+++ b/tests/functional/lang/eval-okay-replacestrings.nix
diff --git a/tests/lang/eval-okay-scope-1.exp b/tests/functional/lang/eval-okay-scope-1.exp
index 00750edc0..00750edc0 100644
--- a/tests/lang/eval-okay-scope-1.exp
+++ b/tests/functional/lang/eval-okay-scope-1.exp
diff --git a/tests/lang/eval-okay-scope-1.nix b/tests/functional/lang/eval-okay-scope-1.nix
index fa38a7174..fa38a7174 100644
--- a/tests/lang/eval-okay-scope-1.nix
+++ b/tests/functional/lang/eval-okay-scope-1.nix
diff --git a/tests/lang/eval-okay-scope-2.exp b/tests/functional/lang/eval-okay-scope-2.exp
index d00491fd7..d00491fd7 100644
--- a/tests/lang/eval-okay-scope-2.exp
+++ b/tests/functional/lang/eval-okay-scope-2.exp
diff --git a/tests/lang/eval-okay-scope-2.nix b/tests/functional/lang/eval-okay-scope-2.nix
index eb8b02bc4..eb8b02bc4 100644
--- a/tests/lang/eval-okay-scope-2.nix
+++ b/tests/functional/lang/eval-okay-scope-2.nix
diff --git a/tests/lang/eval-okay-scope-3.exp b/tests/functional/lang/eval-okay-scope-3.exp
index b8626c4cf..b8626c4cf 100644
--- a/tests/lang/eval-okay-scope-3.exp
+++ b/tests/functional/lang/eval-okay-scope-3.exp
diff --git a/tests/lang/eval-okay-scope-3.nix b/tests/functional/lang/eval-okay-scope-3.nix
index 10d6bc04d..10d6bc04d 100644
--- a/tests/lang/eval-okay-scope-3.nix
+++ b/tests/functional/lang/eval-okay-scope-3.nix
diff --git a/tests/lang/eval-okay-scope-4.exp b/tests/functional/lang/eval-okay-scope-4.exp
index 00ff03a46..00ff03a46 100644
--- a/tests/lang/eval-okay-scope-4.exp
+++ b/tests/functional/lang/eval-okay-scope-4.exp
diff --git a/tests/lang/eval-okay-scope-4.nix b/tests/functional/lang/eval-okay-scope-4.nix
index dc8243bc8..dc8243bc8 100644
--- a/tests/lang/eval-okay-scope-4.nix
+++ b/tests/functional/lang/eval-okay-scope-4.nix
diff --git a/tests/lang/eval-okay-scope-6.exp b/tests/functional/lang/eval-okay-scope-6.exp
index 00ff03a46..00ff03a46 100644
--- a/tests/lang/eval-okay-scope-6.exp
+++ b/tests/functional/lang/eval-okay-scope-6.exp
diff --git a/tests/lang/eval-okay-scope-6.nix b/tests/functional/lang/eval-okay-scope-6.nix
index 0995d4e7e..0995d4e7e 100644
--- a/tests/lang/eval-okay-scope-6.nix
+++ b/tests/functional/lang/eval-okay-scope-6.nix
diff --git a/tests/lang/eval-okay-scope-7.exp b/tests/functional/lang/eval-okay-scope-7.exp
index d00491fd7..d00491fd7 100644
--- a/tests/lang/eval-okay-scope-7.exp
+++ b/tests/functional/lang/eval-okay-scope-7.exp
diff --git a/tests/lang/eval-okay-scope-7.nix b/tests/functional/lang/eval-okay-scope-7.nix
index 4da02968f..4da02968f 100644
--- a/tests/lang/eval-okay-scope-7.nix
+++ b/tests/functional/lang/eval-okay-scope-7.nix
diff --git a/tests/lang/eval-okay-search-path.exp b/tests/functional/lang/eval-okay-search-path.exp
index 4519bc406..4519bc406 100644
--- a/tests/lang/eval-okay-search-path.exp
+++ b/tests/functional/lang/eval-okay-search-path.exp
diff --git a/tests/lang/eval-okay-search-path.flags b/tests/functional/lang/eval-okay-search-path.flags
index dfad1c611..dfad1c611 100644
--- a/tests/lang/eval-okay-search-path.flags
+++ b/tests/functional/lang/eval-okay-search-path.flags
diff --git a/tests/lang/eval-okay-search-path.nix b/tests/functional/lang/eval-okay-search-path.nix
index 6fe33decc..6fe33decc 100644
--- a/tests/lang/eval-okay-search-path.nix
+++ b/tests/functional/lang/eval-okay-search-path.nix
diff --git a/tests/lang/eval-okay-seq.exp b/tests/functional/lang/eval-okay-seq.exp
index 0cfbf0888..0cfbf0888 100644
--- a/tests/lang/eval-okay-seq.exp
+++ b/tests/functional/lang/eval-okay-seq.exp
diff --git a/tests/lang/eval-okay-seq.nix b/tests/functional/lang/eval-okay-seq.nix
index 0a9a21c03..0a9a21c03 100644
--- a/tests/lang/eval-okay-seq.nix
+++ b/tests/functional/lang/eval-okay-seq.nix
diff --git a/tests/lang/eval-okay-sort.exp b/tests/functional/lang/eval-okay-sort.exp
index 899119e20..899119e20 100644
--- a/tests/lang/eval-okay-sort.exp
+++ b/tests/functional/lang/eval-okay-sort.exp
diff --git a/tests/lang/eval-okay-sort.nix b/tests/functional/lang/eval-okay-sort.nix
index 50aa78e40..50aa78e40 100644
--- a/tests/lang/eval-okay-sort.nix
+++ b/tests/functional/lang/eval-okay-sort.nix
diff --git a/tests/lang/eval-okay-splitversion.exp b/tests/functional/lang/eval-okay-splitversion.exp
index 153ceb818..153ceb818 100644
--- a/tests/lang/eval-okay-splitversion.exp
+++ b/tests/functional/lang/eval-okay-splitversion.exp
diff --git a/tests/lang/eval-okay-splitversion.nix b/tests/functional/lang/eval-okay-splitversion.nix
index 9e5c99d2e..9e5c99d2e 100644
--- a/tests/lang/eval-okay-splitversion.nix
+++ b/tests/functional/lang/eval-okay-splitversion.nix
diff --git a/tests/lang/eval-okay-string.exp b/tests/functional/lang/eval-okay-string.exp
index 63f650f73..63f650f73 100644
--- a/tests/lang/eval-okay-string.exp
+++ b/tests/functional/lang/eval-okay-string.exp
diff --git a/tests/lang/eval-okay-string.nix b/tests/functional/lang/eval-okay-string.nix
index 47cc989ad..47cc989ad 100644
--- a/tests/lang/eval-okay-string.nix
+++ b/tests/functional/lang/eval-okay-string.nix
diff --git a/tests/lang/eval-okay-strings-as-attrs-names.exp b/tests/functional/lang/eval-okay-strings-as-attrs-names.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-strings-as-attrs-names.exp
+++ b/tests/functional/lang/eval-okay-strings-as-attrs-names.exp
diff --git a/tests/lang/eval-okay-strings-as-attrs-names.nix b/tests/functional/lang/eval-okay-strings-as-attrs-names.nix
index 5e40928db..5e40928db 100644
--- a/tests/lang/eval-okay-strings-as-attrs-names.nix
+++ b/tests/functional/lang/eval-okay-strings-as-attrs-names.nix
diff --git a/tests/lang/eval-okay-substring.exp b/tests/functional/lang/eval-okay-substring.exp
index 6aace04b0..6aace04b0 100644
--- a/tests/lang/eval-okay-substring.exp
+++ b/tests/functional/lang/eval-okay-substring.exp
diff --git a/tests/lang/eval-okay-substring.nix b/tests/functional/lang/eval-okay-substring.nix
index 424af00d9..424af00d9 100644
--- a/tests/lang/eval-okay-substring.nix
+++ b/tests/functional/lang/eval-okay-substring.nix
diff --git a/tests/lang/eval-okay-tail-call-1.exp-disabled b/tests/functional/lang/eval-okay-tail-call-1.exp-disabled
index f7393e847..f7393e847 100644
--- a/tests/lang/eval-okay-tail-call-1.exp-disabled
+++ b/tests/functional/lang/eval-okay-tail-call-1.exp-disabled
diff --git a/tests/lang/eval-okay-tail-call-1.nix b/tests/functional/lang/eval-okay-tail-call-1.nix
index a3962ce3f..a3962ce3f 100644
--- a/tests/lang/eval-okay-tail-call-1.nix
+++ b/tests/functional/lang/eval-okay-tail-call-1.nix
diff --git a/tests/lang/eval-okay-tojson.exp b/tests/functional/lang/eval-okay-tojson.exp
index e92aae323..e92aae323 100644
--- a/tests/lang/eval-okay-tojson.exp
+++ b/tests/functional/lang/eval-okay-tojson.exp
diff --git a/tests/lang/eval-okay-tojson.nix b/tests/functional/lang/eval-okay-tojson.nix
index ce67943be..ce67943be 100644
--- a/tests/lang/eval-okay-tojson.nix
+++ b/tests/functional/lang/eval-okay-tojson.nix
diff --git a/tests/lang/eval-okay-toxml.exp b/tests/functional/lang/eval-okay-toxml.exp
index 828220890..828220890 100644
--- a/tests/lang/eval-okay-toxml.exp
+++ b/tests/functional/lang/eval-okay-toxml.exp
diff --git a/tests/lang/eval-okay-toxml.nix b/tests/functional/lang/eval-okay-toxml.nix
index 068c97a6c..068c97a6c 100644
--- a/tests/lang/eval-okay-toxml.nix
+++ b/tests/functional/lang/eval-okay-toxml.nix
diff --git a/tests/lang/eval-okay-toxml2.exp b/tests/functional/lang/eval-okay-toxml2.exp
index 634a841eb..634a841eb 100644
--- a/tests/lang/eval-okay-toxml2.exp
+++ b/tests/functional/lang/eval-okay-toxml2.exp
diff --git a/tests/lang/eval-okay-toxml2.nix b/tests/functional/lang/eval-okay-toxml2.nix
index ff1791b30..ff1791b30 100644
--- a/tests/lang/eval-okay-toxml2.nix
+++ b/tests/functional/lang/eval-okay-toxml2.nix
diff --git a/tests/lang/eval-okay-tryeval.exp b/tests/functional/lang/eval-okay-tryeval.exp
index 2b2e6fa71..2b2e6fa71 100644
--- a/tests/lang/eval-okay-tryeval.exp
+++ b/tests/functional/lang/eval-okay-tryeval.exp
diff --git a/tests/lang/eval-okay-tryeval.nix b/tests/functional/lang/eval-okay-tryeval.nix
index 629bc440a..629bc440a 100644
--- a/tests/lang/eval-okay-tryeval.nix
+++ b/tests/functional/lang/eval-okay-tryeval.nix
diff --git a/tests/lang/eval-okay-types.exp b/tests/functional/lang/eval-okay-types.exp
index 92a153299..92a153299 100644
--- a/tests/lang/eval-okay-types.exp
+++ b/tests/functional/lang/eval-okay-types.exp
diff --git a/tests/lang/eval-okay-types.nix b/tests/functional/lang/eval-okay-types.nix
index 9b58be5d1..9b58be5d1 100644
--- a/tests/lang/eval-okay-types.nix
+++ b/tests/functional/lang/eval-okay-types.nix
diff --git a/tests/lang/eval-okay-versions.exp b/tests/functional/lang/eval-okay-versions.exp
index 27ba77dda..27ba77dda 100644
--- a/tests/lang/eval-okay-versions.exp
+++ b/tests/functional/lang/eval-okay-versions.exp
diff --git a/tests/lang/eval-okay-versions.nix b/tests/functional/lang/eval-okay-versions.nix
index e9111f5f4..e9111f5f4 100644
--- a/tests/lang/eval-okay-versions.nix
+++ b/tests/functional/lang/eval-okay-versions.nix
diff --git a/tests/lang/eval-okay-with.exp b/tests/functional/lang/eval-okay-with.exp
index 378c8dc80..378c8dc80 100644
--- a/tests/lang/eval-okay-with.exp
+++ b/tests/functional/lang/eval-okay-with.exp
diff --git a/tests/lang/eval-okay-with.nix b/tests/functional/lang/eval-okay-with.nix
index 033e8d3ab..033e8d3ab 100644
--- a/tests/lang/eval-okay-with.nix
+++ b/tests/functional/lang/eval-okay-with.nix
diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/functional/lang/eval-okay-xml.exp.xml
index 20099326c..20099326c 100644
--- a/tests/lang/eval-okay-xml.exp.xml
+++ b/tests/functional/lang/eval-okay-xml.exp.xml
diff --git a/tests/lang/eval-okay-xml.nix b/tests/functional/lang/eval-okay-xml.nix
index 9ee9f8a0b..9ee9f8a0b 100644
--- a/tests/lang/eval-okay-xml.nix
+++ b/tests/functional/lang/eval-okay-xml.nix
diff --git a/tests/lang/eval-okay-zipAttrsWith.exp b/tests/functional/lang/eval-okay-zipAttrsWith.exp
index 9c0b15d22..9c0b15d22 100644
--- a/tests/lang/eval-okay-zipAttrsWith.exp
+++ b/tests/functional/lang/eval-okay-zipAttrsWith.exp
diff --git a/tests/lang/eval-okay-zipAttrsWith.nix b/tests/functional/lang/eval-okay-zipAttrsWith.nix
index 877d4e5fa..877d4e5fa 100644
--- a/tests/lang/eval-okay-zipAttrsWith.nix
+++ b/tests/functional/lang/eval-okay-zipAttrsWith.nix
diff --git a/tests/lang/framework.sh b/tests/functional/lang/framework.sh
index 516bff8ad..516bff8ad 100644
--- a/tests/lang/framework.sh
+++ b/tests/functional/lang/framework.sh
diff --git a/tests/lang/imported.nix b/tests/functional/lang/imported.nix
index fb39ee4ef..fb39ee4ef 100644
--- a/tests/lang/imported.nix
+++ b/tests/functional/lang/imported.nix
diff --git a/tests/lang/imported2.nix b/tests/functional/lang/imported2.nix
index 6d0a2992b..6d0a2992b 100644
--- a/tests/lang/imported2.nix
+++ b/tests/functional/lang/imported2.nix
diff --git a/tests/lang/lib.nix b/tests/functional/lang/lib.nix
index 028a53831..028a53831 100644
--- a/tests/lang/lib.nix
+++ b/tests/functional/lang/lib.nix
diff --git a/tests/lang/parse-fail-dup-attrs-1.err.exp b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp
index 4fe6b7a1f..4fe6b7a1f 100644
--- a/tests/lang/parse-fail-dup-attrs-1.err.exp
+++ b/tests/functional/lang/parse-fail-dup-attrs-1.err.exp
diff --git a/tests/lang/parse-fail-dup-attrs-1.nix b/tests/functional/lang/parse-fail-dup-attrs-1.nix
index 2c02317d2..2c02317d2 100644
--- a/tests/lang/parse-fail-dup-attrs-1.nix
+++ b/tests/functional/lang/parse-fail-dup-attrs-1.nix
diff --git a/tests/lang/parse-fail-dup-attrs-2.err.exp b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp
index 3aba2891f..3aba2891f 100644
--- a/tests/lang/parse-fail-dup-attrs-2.err.exp
+++ b/tests/functional/lang/parse-fail-dup-attrs-2.err.exp
diff --git a/tests/lang/parse-fail-dup-attrs-2.nix b/tests/functional/lang/parse-fail-dup-attrs-2.nix
index 864d9865e..864d9865e 100644
--- a/tests/lang/parse-fail-dup-attrs-2.nix
+++ b/tests/functional/lang/parse-fail-dup-attrs-2.nix
diff --git a/tests/lang/parse-fail-dup-attrs-3.err.exp b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp
index 3aba2891f..3aba2891f 100644
--- a/tests/lang/parse-fail-dup-attrs-3.err.exp
+++ b/tests/functional/lang/parse-fail-dup-attrs-3.err.exp
diff --git a/tests/lang/parse-fail-dup-attrs-3.nix b/tests/functional/lang/parse-fail-dup-attrs-3.nix
index 114d19779..114d19779 100644
--- a/tests/lang/parse-fail-dup-attrs-3.nix
+++ b/tests/functional/lang/parse-fail-dup-attrs-3.nix
diff --git a/tests/lang/parse-fail-dup-attrs-4.err.exp b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp
index ff68446a1..ff68446a1 100644
--- a/tests/lang/parse-fail-dup-attrs-4.err.exp
+++ b/tests/functional/lang/parse-fail-dup-attrs-4.err.exp
diff --git a/tests/lang/parse-fail-dup-attrs-4.nix b/tests/functional/lang/parse-fail-dup-attrs-4.nix
index 77417432b..77417432b 100644
--- a/tests/lang/parse-fail-dup-attrs-4.nix
+++ b/tests/functional/lang/parse-fail-dup-attrs-4.nix
diff --git a/tests/lang/parse-fail-dup-attrs-6.err.exp b/tests/functional/lang/parse-fail-dup-attrs-6.err.exp
index 74823fc25..74823fc25 100644
--- a/tests/lang/parse-fail-dup-attrs-6.err.exp
+++ b/tests/functional/lang/parse-fail-dup-attrs-6.err.exp
diff --git a/tests/lang/parse-fail-dup-attrs-7.err.exp b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp
index 512a499ca..512a499ca 100644
--- a/tests/lang/parse-fail-dup-attrs-7.err.exp
+++ b/tests/functional/lang/parse-fail-dup-attrs-7.err.exp
diff --git a/tests/lang/parse-fail-dup-attrs-7.nix b/tests/functional/lang/parse-fail-dup-attrs-7.nix
index bbc3eb08c..bbc3eb08c 100644
--- a/tests/lang/parse-fail-dup-attrs-7.nix
+++ b/tests/functional/lang/parse-fail-dup-attrs-7.nix
diff --git a/tests/lang/parse-fail-dup-formals.err.exp b/tests/functional/lang/parse-fail-dup-formals.err.exp
index 1d566fb33..1d566fb33 100644
--- a/tests/lang/parse-fail-dup-formals.err.exp
+++ b/tests/functional/lang/parse-fail-dup-formals.err.exp
diff --git a/tests/lang/parse-fail-dup-formals.nix b/tests/functional/lang/parse-fail-dup-formals.nix
index a0edd91a9..a0edd91a9 100644
--- a/tests/lang/parse-fail-dup-formals.nix
+++ b/tests/functional/lang/parse-fail-dup-formals.nix
diff --git a/tests/lang/parse-fail-eof-in-string.err.exp b/tests/functional/lang/parse-fail-eof-in-string.err.exp
index f9fa72312..f9fa72312 100644
--- a/tests/lang/parse-fail-eof-in-string.err.exp
+++ b/tests/functional/lang/parse-fail-eof-in-string.err.exp
diff --git a/tests/lang/parse-fail-eof-in-string.nix b/tests/functional/lang/parse-fail-eof-in-string.nix
index 19775d2ec..19775d2ec 100644
--- a/tests/lang/parse-fail-eof-in-string.nix
+++ b/tests/functional/lang/parse-fail-eof-in-string.nix
diff --git a/tests/lang/parse-fail-mixed-nested-attrs1.err.exp b/tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp
index 32f776795..32f776795 100644
--- a/tests/lang/parse-fail-mixed-nested-attrs1.err.exp
+++ b/tests/functional/lang/parse-fail-mixed-nested-attrs1.err.exp
diff --git a/tests/lang/parse-fail-mixed-nested-attrs1.nix b/tests/functional/lang/parse-fail-mixed-nested-attrs1.nix
index 11e40e66f..11e40e66f 100644
--- a/tests/lang/parse-fail-mixed-nested-attrs1.nix
+++ b/tests/functional/lang/parse-fail-mixed-nested-attrs1.nix
diff --git a/tests/lang/parse-fail-mixed-nested-attrs2.err.exp b/tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp
index 0437cd50c..0437cd50c 100644
--- a/tests/lang/parse-fail-mixed-nested-attrs2.err.exp
+++ b/tests/functional/lang/parse-fail-mixed-nested-attrs2.err.exp
diff --git a/tests/lang/parse-fail-mixed-nested-attrs2.nix b/tests/functional/lang/parse-fail-mixed-nested-attrs2.nix
index 17da82e5f..17da82e5f 100644
--- a/tests/lang/parse-fail-mixed-nested-attrs2.nix
+++ b/tests/functional/lang/parse-fail-mixed-nested-attrs2.nix
diff --git a/tests/lang/parse-fail-patterns-1.err.exp b/tests/functional/lang/parse-fail-patterns-1.err.exp
index 634a04aaa..634a04aaa 100644
--- a/tests/lang/parse-fail-patterns-1.err.exp
+++ b/tests/functional/lang/parse-fail-patterns-1.err.exp
diff --git a/tests/lang/parse-fail-patterns-1.nix b/tests/functional/lang/parse-fail-patterns-1.nix
index 7b4061641..7b4061641 100644
--- a/tests/lang/parse-fail-patterns-1.nix
+++ b/tests/functional/lang/parse-fail-patterns-1.nix
diff --git a/tests/lang/parse-fail-regression-20060610.err.exp b/tests/functional/lang/parse-fail-regression-20060610.err.exp
index 167d01e85..167d01e85 100644
--- a/tests/lang/parse-fail-regression-20060610.err.exp
+++ b/tests/functional/lang/parse-fail-regression-20060610.err.exp
diff --git a/tests/lang/parse-fail-regression-20060610.nix b/tests/functional/lang/parse-fail-regression-20060610.nix
index b1934f7e1..b1934f7e1 100644
--- a/tests/lang/parse-fail-regression-20060610.nix
+++ b/tests/functional/lang/parse-fail-regression-20060610.nix
diff --git a/tests/lang/parse-fail-undef-var-2.err.exp b/tests/functional/lang/parse-fail-undef-var-2.err.exp
index 77c96bbd2..77c96bbd2 100644
--- a/tests/lang/parse-fail-undef-var-2.err.exp
+++ b/tests/functional/lang/parse-fail-undef-var-2.err.exp
diff --git a/tests/lang/parse-fail-undef-var-2.nix b/tests/functional/lang/parse-fail-undef-var-2.nix
index c10a52b1e..c10a52b1e 100644
--- a/tests/lang/parse-fail-undef-var-2.nix
+++ b/tests/functional/lang/parse-fail-undef-var-2.nix
diff --git a/tests/lang/parse-fail-undef-var.err.exp b/tests/functional/lang/parse-fail-undef-var.err.exp
index 48e88747f..48e88747f 100644
--- a/tests/lang/parse-fail-undef-var.err.exp
+++ b/tests/functional/lang/parse-fail-undef-var.err.exp
diff --git a/tests/lang/parse-fail-undef-var.nix b/tests/functional/lang/parse-fail-undef-var.nix
index 7b6300811..7b6300811 100644
--- a/tests/lang/parse-fail-undef-var.nix
+++ b/tests/functional/lang/parse-fail-undef-var.nix
diff --git a/tests/lang/parse-fail-utf8.err.exp b/tests/functional/lang/parse-fail-utf8.err.exp
index 6087479a3..6087479a3 100644
--- a/tests/lang/parse-fail-utf8.err.exp
+++ b/tests/functional/lang/parse-fail-utf8.err.exp
diff --git a/tests/lang/parse-fail-utf8.nix b/tests/functional/lang/parse-fail-utf8.nix
index 34948d48a..34948d48a 100644
--- a/tests/lang/parse-fail-utf8.nix
+++ b/tests/functional/lang/parse-fail-utf8.nix
diff --git a/tests/lang/parse-okay-1.exp b/tests/functional/lang/parse-okay-1.exp
index d5ab5f18a..d5ab5f18a 100644
--- a/tests/lang/parse-okay-1.exp
+++ b/tests/functional/lang/parse-okay-1.exp
diff --git a/tests/lang/parse-okay-1.nix b/tests/functional/lang/parse-okay-1.nix
index 23a58ed10..23a58ed10 100644
--- a/tests/lang/parse-okay-1.nix
+++ b/tests/functional/lang/parse-okay-1.nix
diff --git a/tests/lang/parse-okay-crlf.exp b/tests/functional/lang/parse-okay-crlf.exp
index 4213609fc..4213609fc 100644
--- a/tests/lang/parse-okay-crlf.exp
+++ b/tests/functional/lang/parse-okay-crlf.exp
diff --git a/tests/lang/parse-okay-crlf.nix b/tests/functional/lang/parse-okay-crlf.nix
index 21518d4c6..21518d4c6 100644
--- a/tests/lang/parse-okay-crlf.nix
+++ b/tests/functional/lang/parse-okay-crlf.nix
diff --git a/tests/lang/parse-okay-dup-attrs-5.exp b/tests/functional/lang/parse-okay-dup-attrs-5.exp
index 88b0b036f..88b0b036f 100644
--- a/tests/lang/parse-okay-dup-attrs-5.exp
+++ b/tests/functional/lang/parse-okay-dup-attrs-5.exp
diff --git a/tests/lang/parse-okay-dup-attrs-5.nix b/tests/functional/lang/parse-okay-dup-attrs-5.nix
index f4b9efd0c..f4b9efd0c 100644
--- a/tests/lang/parse-okay-dup-attrs-5.nix
+++ b/tests/functional/lang/parse-okay-dup-attrs-5.nix
diff --git a/tests/lang/parse-okay-dup-attrs-6.exp b/tests/functional/lang/parse-okay-dup-attrs-6.exp
index 88b0b036f..88b0b036f 100644
--- a/tests/lang/parse-okay-dup-attrs-6.exp
+++ b/tests/functional/lang/parse-okay-dup-attrs-6.exp
diff --git a/tests/lang/parse-okay-dup-attrs-6.nix b/tests/functional/lang/parse-okay-dup-attrs-6.nix
index ae6d7a769..ae6d7a769 100644
--- a/tests/lang/parse-okay-dup-attrs-6.nix
+++ b/tests/functional/lang/parse-okay-dup-attrs-6.nix
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-1.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp
index 89c66f760..89c66f760 100644
--- a/tests/lang/parse-okay-mixed-nested-attrs-1.exp
+++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.exp
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-1.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix
index fd1001c8c..fd1001c8c 100644
--- a/tests/lang/parse-okay-mixed-nested-attrs-1.nix
+++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-1.nix
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-2.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp
index 89c66f760..89c66f760 100644
--- a/tests/lang/parse-okay-mixed-nested-attrs-2.exp
+++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.exp
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-2.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix
index ad066b680..ad066b680 100644
--- a/tests/lang/parse-okay-mixed-nested-attrs-2.nix
+++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-2.nix
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-3.exp b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp
index b89a59734..b89a59734 100644
--- a/tests/lang/parse-okay-mixed-nested-attrs-3.exp
+++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.exp
diff --git a/tests/lang/parse-okay-mixed-nested-attrs-3.nix b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix
index 45a33e480..45a33e480 100644
--- a/tests/lang/parse-okay-mixed-nested-attrs-3.nix
+++ b/tests/functional/lang/parse-okay-mixed-nested-attrs-3.nix
diff --git a/tests/lang/parse-okay-regression-20041027.exp b/tests/functional/lang/parse-okay-regression-20041027.exp
index 9df7219e4..9df7219e4 100644
--- a/tests/lang/parse-okay-regression-20041027.exp
+++ b/tests/functional/lang/parse-okay-regression-20041027.exp
diff --git a/tests/lang/parse-okay-regression-20041027.nix b/tests/functional/lang/parse-okay-regression-20041027.nix
index ae2e256ee..ae2e256ee 100644
--- a/tests/lang/parse-okay-regression-20041027.nix
+++ b/tests/functional/lang/parse-okay-regression-20041027.nix
diff --git a/tests/lang/parse-okay-regression-751.exp b/tests/functional/lang/parse-okay-regression-751.exp
index e2ed886fe..e2ed886fe 100644
--- a/tests/lang/parse-okay-regression-751.exp
+++ b/tests/functional/lang/parse-okay-regression-751.exp
diff --git a/tests/lang/parse-okay-regression-751.nix b/tests/functional/lang/parse-okay-regression-751.nix
index 05c78b301..05c78b301 100644
--- a/tests/lang/parse-okay-regression-751.nix
+++ b/tests/functional/lang/parse-okay-regression-751.nix
diff --git a/tests/lang/parse-okay-subversion.exp b/tests/functional/lang/parse-okay-subversion.exp
index 4168ee8bf..4168ee8bf 100644
--- a/tests/lang/parse-okay-subversion.exp
+++ b/tests/functional/lang/parse-okay-subversion.exp
diff --git a/tests/lang/parse-okay-subversion.nix b/tests/functional/lang/parse-okay-subversion.nix
index 356272815..356272815 100644
--- a/tests/lang/parse-okay-subversion.nix
+++ b/tests/functional/lang/parse-okay-subversion.nix
diff --git a/tests/lang/parse-okay-url.exp b/tests/functional/lang/parse-okay-url.exp
index e5f0829b0..e5f0829b0 100644
--- a/tests/lang/parse-okay-url.exp
+++ b/tests/functional/lang/parse-okay-url.exp
diff --git a/tests/lang/parse-okay-url.nix b/tests/functional/lang/parse-okay-url.nix
index 08de27d0a..08de27d0a 100644
--- a/tests/lang/parse-okay-url.nix
+++ b/tests/functional/lang/parse-okay-url.nix
diff --git a/tests/lang/readDir/bar b/tests/functional/lang/readDir/bar
index e69de29bb..e69de29bb 100644
--- a/tests/lang/readDir/bar
+++ b/tests/functional/lang/readDir/bar
diff --git a/tests/lang/readDir/foo/git-hates-directories b/tests/functional/lang/readDir/foo/git-hates-directories
index e69de29bb..e69de29bb 100644
--- a/tests/lang/readDir/foo/git-hates-directories
+++ b/tests/functional/lang/readDir/foo/git-hates-directories
diff --git a/tests/lang/readDir/ldir b/tests/functional/lang/readDir/ldir
index 191028156..191028156 120000
--- a/tests/lang/readDir/ldir
+++ b/tests/functional/lang/readDir/ldir
diff --git a/tests/lang/readDir/linked b/tests/functional/lang/readDir/linked
index c503f86a0..c503f86a0 120000
--- a/tests/lang/readDir/linked
+++ b/tests/functional/lang/readDir/linked
diff --git a/tests/legacy-ssh-store.sh b/tests/functional/legacy-ssh-store.sh
index 71b716b84..71b716b84 100644
--- a/tests/legacy-ssh-store.sh
+++ b/tests/functional/legacy-ssh-store.sh
diff --git a/tests/linux-sandbox-cert-test.nix b/tests/functional/linux-sandbox-cert-test.nix
index 2fc083ea9..2fc083ea9 100644
--- a/tests/linux-sandbox-cert-test.nix
+++ b/tests/functional/linux-sandbox-cert-test.nix
diff --git a/tests/linux-sandbox.sh b/tests/functional/linux-sandbox.sh
index ff7d257bd..ff7d257bd 100644
--- a/tests/linux-sandbox.sh
+++ b/tests/functional/linux-sandbox.sh
diff --git a/tests/local-store.sh b/tests/functional/local-store.sh
index 89502f864..89502f864 100644
--- a/tests/local-store.sh
+++ b/tests/functional/local-store.sh
diff --git a/tests/local.mk b/tests/functional/local.mk
index 4edf31303..d60f1e08e 100644
--- a/tests/local.mk
+++ b/tests/functional/local.mk
@@ -103,7 +103,6 @@ nix_tests = \
case-hack.sh \
placeholders.sh \
ssh-relay.sh \
- plugins.sh \
build.sh \
build-delete.sh \
output-normalization.sh \
@@ -128,17 +127,20 @@ ifeq ($(HAVE_LIBCPUID), 1)
nix_tests += compute-levels.sh
endif
+ifeq ($(BUILD_SHARED_LIBS), 1)
+ nix_tests += plugins.sh
+endif
+
+$(d)/test-libstoreconsumer.sh.test $(d)/test-libstoreconsumer.sh.test-debug: \
+ $(d)/test-libstoreconsumer/test-libstoreconsumer
+$(d)/plugins.sh.test $(d)/plugins.sh.test-debug: \
+ $(d)/plugins/libplugintest.$(SO_EXT)
+
install-tests += $(foreach x, $(nix_tests), $(d)/$(x))
-clean-files += \
+test-clean-files := \
$(d)/common/vars-and-functions.sh \
$(d)/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)
-endif
+clean-files += $(test-clean-files)
+test-deps += $(test-clean-files)
diff --git a/tests/logging.sh b/tests/functional/logging.sh
index 1481b9b36..1481b9b36 100644
--- a/tests/logging.sh
+++ b/tests/functional/logging.sh
diff --git a/tests/misc.sh b/tests/functional/misc.sh
index af96d20bd..af96d20bd 100644
--- a/tests/misc.sh
+++ b/tests/functional/misc.sh
diff --git a/tests/multiple-outputs.nix b/tests/functional/multiple-outputs.nix
index 413d392e4..413d392e4 100644
--- a/tests/multiple-outputs.nix
+++ b/tests/functional/multiple-outputs.nix
diff --git a/tests/multiple-outputs.sh b/tests/functional/multiple-outputs.sh
index 330600d08..330600d08 100644
--- a/tests/multiple-outputs.sh
+++ b/tests/functional/multiple-outputs.sh
diff --git a/tests/nar-access.nix b/tests/functional/nar-access.nix
index 0e2a7f721..0e2a7f721 100644
--- a/tests/nar-access.nix
+++ b/tests/functional/nar-access.nix
diff --git a/tests/nar-access.sh b/tests/functional/nar-access.sh
index d487d58d2..d487d58d2 100644
--- a/tests/nar-access.sh
+++ b/tests/functional/nar-access.sh
diff --git a/tests/nested-sandboxing.sh b/tests/functional/nested-sandboxing.sh
index d9fa788aa..61fe043c6 100644
--- a/tests/nested-sandboxing.sh
+++ b/tests/functional/nested-sandboxing.sh
@@ -1,5 +1,5 @@
source common.sh
-# This test is run by `tests/nested-sandboxing/runner.nix` in an extra layer of sandboxing.
+# This test is run by `tests/functional/nested-sandboxing/runner.nix` in an extra layer of sandboxing.
[[ -d /nix/store ]] || skipTest "running this test without Nix's deps being drawn from /nix/store is not yet supported"
requireSandboxSupport
diff --git a/tests/nested-sandboxing/command.sh b/tests/functional/nested-sandboxing/command.sh
index 69366486c..69366486c 100644
--- a/tests/nested-sandboxing/command.sh
+++ b/tests/functional/nested-sandboxing/command.sh
diff --git a/tests/nested-sandboxing/runner.nix b/tests/functional/nested-sandboxing/runner.nix
index 9a5822c88..9a5822c88 100644
--- a/tests/nested-sandboxing/runner.nix
+++ b/tests/functional/nested-sandboxing/runner.nix
diff --git a/tests/nix-build-examples.nix b/tests/functional/nix-build-examples.nix
index e54dbbf62..e54dbbf62 100644
--- a/tests/nix-build-examples.nix
+++ b/tests/functional/nix-build-examples.nix
diff --git a/tests/nix-build.sh b/tests/functional/nix-build.sh
index 44a5a14cd..44a5a14cd 100644
--- a/tests/nix-build.sh
+++ b/tests/functional/nix-build.sh
diff --git a/tests/nix-channel.sh b/tests/functional/nix-channel.sh
index b5d935004..b5d935004 100644
--- a/tests/nix-channel.sh
+++ b/tests/functional/nix-channel.sh
diff --git a/tests/nix-collect-garbage-d.sh b/tests/functional/nix-collect-garbage-d.sh
index bf30f8938..bf30f8938 100644
--- a/tests/nix-collect-garbage-d.sh
+++ b/tests/functional/nix-collect-garbage-d.sh
diff --git a/tests/nix-copy-ssh-ng.sh b/tests/functional/nix-copy-ssh-ng.sh
index 45e53c9c0..45e53c9c0 100644
--- a/tests/nix-copy-ssh-ng.sh
+++ b/tests/functional/nix-copy-ssh-ng.sh
diff --git a/tests/nix-copy-ssh.sh b/tests/functional/nix-copy-ssh.sh
index eb801548d..eb801548d 100644
--- a/tests/nix-copy-ssh.sh
+++ b/tests/functional/nix-copy-ssh.sh
diff --git a/tests/nix-daemon-untrusting.sh b/tests/functional/nix-daemon-untrusting.sh
index bcdb70989..bcdb70989 100755
--- a/tests/nix-daemon-untrusting.sh
+++ b/tests/functional/nix-daemon-untrusting.sh
diff --git a/tests/nix-profile.sh b/tests/functional/nix-profile.sh
index 7c478a0cd..7c478a0cd 100644
--- a/tests/nix-profile.sh
+++ b/tests/functional/nix-profile.sh
diff --git a/tests/nix-shell.sh b/tests/functional/nix-shell.sh
index edaa1249b..edaa1249b 100644
--- a/tests/nix-shell.sh
+++ b/tests/functional/nix-shell.sh
diff --git a/tests/nix_path.sh b/tests/functional/nix_path.sh
index 2b222b4a1..2b222b4a1 100644
--- a/tests/nix_path.sh
+++ b/tests/functional/nix_path.sh
diff --git a/tests/optimise-store.sh b/tests/functional/optimise-store.sh
index 8c2d05cd5..8c2d05cd5 100644
--- a/tests/optimise-store.sh
+++ b/tests/functional/optimise-store.sh
diff --git a/tests/output-normalization.sh b/tests/functional/output-normalization.sh
index 0f6df5e31..0f6df5e31 100644
--- a/tests/output-normalization.sh
+++ b/tests/functional/output-normalization.sh
diff --git a/tests/parallel.builder.sh b/tests/functional/parallel.builder.sh
index d092bc5a6..d092bc5a6 100644
--- a/tests/parallel.builder.sh
+++ b/tests/functional/parallel.builder.sh
diff --git a/tests/parallel.nix b/tests/functional/parallel.nix
index 23f142059..23f142059 100644
--- a/tests/parallel.nix
+++ b/tests/functional/parallel.nix
diff --git a/tests/parallel.sh b/tests/functional/parallel.sh
index 3b7bbe5a2..3b7bbe5a2 100644
--- a/tests/parallel.sh
+++ b/tests/functional/parallel.sh
diff --git a/tests/pass-as-file.sh b/tests/functional/pass-as-file.sh
index 2c0bc5031..2c0bc5031 100644
--- a/tests/pass-as-file.sh
+++ b/tests/functional/pass-as-file.sh
diff --git a/tests/path-from-hash-part.sh b/tests/functional/path-from-hash-part.sh
index bdd104434..bdd104434 100644
--- a/tests/path-from-hash-part.sh
+++ b/tests/functional/path-from-hash-part.sh
diff --git a/tests/path.nix b/tests/functional/path.nix
index 883c3c41b..883c3c41b 100644
--- a/tests/path.nix
+++ b/tests/functional/path.nix
diff --git a/tests/placeholders.sh b/tests/functional/placeholders.sh
index cd1bb7bc2..cd1bb7bc2 100644
--- a/tests/placeholders.sh
+++ b/tests/functional/placeholders.sh
diff --git a/tests/plugins.sh b/tests/functional/plugins.sh
index baf71a362..baf71a362 100644
--- a/tests/plugins.sh
+++ b/tests/functional/plugins.sh
diff --git a/tests/plugins/local.mk b/tests/functional/plugins/local.mk
index 40350aa96..40350aa96 100644
--- a/tests/plugins/local.mk
+++ b/tests/functional/plugins/local.mk
diff --git a/tests/plugins/plugintest.cc b/tests/functional/plugins/plugintest.cc
index e02fd68d5..e02fd68d5 100644
--- a/tests/plugins/plugintest.cc
+++ b/tests/functional/plugins/plugintest.cc
diff --git a/tests/post-hook.sh b/tests/functional/post-hook.sh
index 752f8220c..752f8220c 100644
--- a/tests/post-hook.sh
+++ b/tests/functional/post-hook.sh
diff --git a/tests/pure-eval.nix b/tests/functional/pure-eval.nix
index ed25b3d45..ed25b3d45 100644
--- a/tests/pure-eval.nix
+++ b/tests/functional/pure-eval.nix
diff --git a/tests/pure-eval.sh b/tests/functional/pure-eval.sh
index 5334bf28e..5334bf28e 100644
--- a/tests/pure-eval.sh
+++ b/tests/functional/pure-eval.sh
diff --git a/tests/push-to-store-old.sh b/tests/functional/push-to-store-old.sh
index 4187958b2..4187958b2 100755
--- a/tests/push-to-store-old.sh
+++ b/tests/functional/push-to-store-old.sh
diff --git a/tests/push-to-store.sh b/tests/functional/push-to-store.sh
index 9e4e475e0..9e4e475e0 100755
--- a/tests/push-to-store.sh
+++ b/tests/functional/push-to-store.sh
diff --git a/tests/read-only-store.sh b/tests/functional/read-only-store.sh
index d63920c19..d63920c19 100644
--- a/tests/read-only-store.sh
+++ b/tests/functional/read-only-store.sh
diff --git a/tests/readfile-context.nix b/tests/functional/readfile-context.nix
index 54cd1afd9..54cd1afd9 100644
--- a/tests/readfile-context.nix
+++ b/tests/functional/readfile-context.nix
diff --git a/tests/readfile-context.sh b/tests/functional/readfile-context.sh
index 31e70ddb1..31e70ddb1 100644
--- a/tests/readfile-context.sh
+++ b/tests/functional/readfile-context.sh
diff --git a/tests/recursive.nix b/tests/functional/recursive.nix
index fa8cc04db..fa8cc04db 100644
--- a/tests/recursive.nix
+++ b/tests/functional/recursive.nix
diff --git a/tests/recursive.sh b/tests/functional/recursive.sh
index 0bf00f8fa..0bf00f8fa 100644
--- a/tests/recursive.sh
+++ b/tests/functional/recursive.sh
diff --git a/tests/referrers.sh b/tests/functional/referrers.sh
index 81323c280..81323c280 100644
--- a/tests/referrers.sh
+++ b/tests/functional/referrers.sh
diff --git a/tests/remote-store.sh b/tests/functional/remote-store.sh
index 7649964ef..7649964ef 100644
--- a/tests/remote-store.sh
+++ b/tests/functional/remote-store.sh
diff --git a/tests/repair.sh b/tests/functional/repair.sh
index c8f07b1c6..c8f07b1c6 100644
--- a/tests/repair.sh
+++ b/tests/functional/repair.sh
diff --git a/tests/repl.sh b/tests/functional/repl.sh
index bb8b60e50..bb8b60e50 100644
--- a/tests/repl.sh
+++ b/tests/functional/repl.sh
diff --git a/tests/restricted.nix b/tests/functional/restricted.nix
index e0ef58402..e0ef58402 100644
--- a/tests/restricted.nix
+++ b/tests/functional/restricted.nix
diff --git a/tests/restricted.sh b/tests/functional/restricted.sh
index 17f310a4b..197ae7a10 100644
--- a/tests/restricted.sh
+++ b/tests/functional/restricted.sh
@@ -9,10 +9,10 @@ nix-instantiate --restrict-eval ./simple.nix -I src=.
nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
(! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix')
-nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=..
+nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=../..
-(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel')
-nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel' -I src=../src
+(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel')
+nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../../src/nix-channel' -I src=../../src
(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>')
nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=.
diff --git a/tests/search.nix b/tests/functional/search.nix
index fea6e7a7a..fea6e7a7a 100644
--- a/tests/search.nix
+++ b/tests/functional/search.nix
diff --git a/tests/search.sh b/tests/functional/search.sh
index 8742f8736..8742f8736 100644
--- a/tests/search.sh
+++ b/tests/functional/search.sh
diff --git a/tests/secure-drv-outputs.nix b/tests/functional/secure-drv-outputs.nix
index b4ac8ff53..b4ac8ff53 100644
--- a/tests/secure-drv-outputs.nix
+++ b/tests/functional/secure-drv-outputs.nix
diff --git a/tests/secure-drv-outputs.sh b/tests/functional/secure-drv-outputs.sh
index 50a9c4428..50a9c4428 100644
--- a/tests/secure-drv-outputs.sh
+++ b/tests/functional/secure-drv-outputs.sh
diff --git a/tests/selfref-gc.sh b/tests/functional/selfref-gc.sh
index 3f1f50eea..3f1f50eea 100644
--- a/tests/selfref-gc.sh
+++ b/tests/functional/selfref-gc.sh
diff --git a/tests/shell-hello.nix b/tests/functional/shell-hello.nix
index 3fdd3501d..3fdd3501d 100644
--- a/tests/shell-hello.nix
+++ b/tests/functional/shell-hello.nix
diff --git a/tests/shell.nix b/tests/functional/shell.nix
index 92d94fbc2..92d94fbc2 100644
--- a/tests/shell.nix
+++ b/tests/functional/shell.nix
diff --git a/tests/shell.sh b/tests/functional/shell.sh
index d2f7cf14e..d2f7cf14e 100644
--- a/tests/shell.sh
+++ b/tests/functional/shell.sh
diff --git a/tests/shell.shebang.rb b/tests/functional/shell.shebang.rb
index ea67eb09c..ea67eb09c 100644
--- a/tests/shell.shebang.rb
+++ b/tests/functional/shell.shebang.rb
diff --git a/tests/shell.shebang.sh b/tests/functional/shell.shebang.sh
index f7132043d..f7132043d 100755
--- a/tests/shell.shebang.sh
+++ b/tests/functional/shell.shebang.sh
diff --git a/tests/signing.sh b/tests/functional/signing.sh
index 942b51630..942b51630 100644
--- a/tests/signing.sh
+++ b/tests/functional/signing.sh
diff --git a/tests/simple-failing.nix b/tests/functional/simple-failing.nix
index d176c9c51..d176c9c51 100644
--- a/tests/simple-failing.nix
+++ b/tests/functional/simple-failing.nix
diff --git a/tests/simple.builder.sh b/tests/functional/simple.builder.sh
index 569e8ca88..569e8ca88 100644
--- a/tests/simple.builder.sh
+++ b/tests/functional/simple.builder.sh
diff --git a/tests/simple.nix b/tests/functional/simple.nix
index 4223c0f23..4223c0f23 100644
--- a/tests/simple.nix
+++ b/tests/functional/simple.nix
diff --git a/tests/simple.sh b/tests/functional/simple.sh
index 50d44f93f..50d44f93f 100644
--- a/tests/simple.sh
+++ b/tests/functional/simple.sh
diff --git a/tests/ssh-relay.sh b/tests/functional/ssh-relay.sh
index 053b2f00d..053b2f00d 100644
--- a/tests/ssh-relay.sh
+++ b/tests/functional/ssh-relay.sh
diff --git a/tests/store-ping.sh b/tests/functional/store-ping.sh
index 9846c7d3d..9846c7d3d 100644
--- a/tests/store-ping.sh
+++ b/tests/functional/store-ping.sh
diff --git a/tests/structured-attrs-shell.nix b/tests/functional/structured-attrs-shell.nix
index 57c1e6bd2..57c1e6bd2 100644
--- a/tests/structured-attrs-shell.nix
+++ b/tests/functional/structured-attrs-shell.nix
diff --git a/tests/structured-attrs.nix b/tests/functional/structured-attrs.nix
index e93139a44..e93139a44 100644
--- a/tests/structured-attrs.nix
+++ b/tests/functional/structured-attrs.nix
diff --git a/tests/structured-attrs.sh b/tests/functional/structured-attrs.sh
index 378dbc735..378dbc735 100644
--- a/tests/structured-attrs.sh
+++ b/tests/functional/structured-attrs.sh
diff --git a/tests/substitute-with-invalid-ca.sh b/tests/functional/substitute-with-invalid-ca.sh
index 4d0b01e0f..4d0b01e0f 100644
--- a/tests/substitute-with-invalid-ca.sh
+++ b/tests/functional/substitute-with-invalid-ca.sh
diff --git a/tests/suggestions.sh b/tests/functional/suggestions.sh
index f18fefef9..f18fefef9 100644
--- a/tests/suggestions.sh
+++ b/tests/functional/suggestions.sh
diff --git a/tests/supplementary-groups.sh b/tests/functional/supplementary-groups.sh
index d18fb2414..d18fb2414 100644
--- a/tests/supplementary-groups.sh
+++ b/tests/functional/supplementary-groups.sh
diff --git a/tests/tarball.sh b/tests/functional/tarball.sh
index 6e621a28c..6e621a28c 100644
--- a/tests/tarball.sh
+++ b/tests/functional/tarball.sh
diff --git a/tests/test-infra.sh b/tests/functional/test-infra.sh
index 54ae120e7..54ae120e7 100644
--- a/tests/test-infra.sh
+++ b/tests/functional/test-infra.sh
diff --git a/tests/test-libstoreconsumer.sh b/tests/functional/test-libstoreconsumer.sh
index 8a77cf5a1..8a77cf5a1 100644
--- a/tests/test-libstoreconsumer.sh
+++ b/tests/functional/test-libstoreconsumer.sh
diff --git a/tests/test-libstoreconsumer/README.md b/tests/functional/test-libstoreconsumer/README.md
index ded69850f..ded69850f 100644
--- a/tests/test-libstoreconsumer/README.md
+++ b/tests/functional/test-libstoreconsumer/README.md
diff --git a/tests/test-libstoreconsumer/local.mk b/tests/functional/test-libstoreconsumer/local.mk
index edc140723..edc140723 100644
--- a/tests/test-libstoreconsumer/local.mk
+++ b/tests/functional/test-libstoreconsumer/local.mk
diff --git a/tests/test-libstoreconsumer/main.cc b/tests/functional/test-libstoreconsumer/main.cc
index c61489af6..c61489af6 100644
--- a/tests/test-libstoreconsumer/main.cc
+++ b/tests/functional/test-libstoreconsumer/main.cc
diff --git a/tests/timeout.nix b/tests/functional/timeout.nix
index d0e949e31..d0e949e31 100644
--- a/tests/timeout.nix
+++ b/tests/functional/timeout.nix
diff --git a/tests/timeout.sh b/tests/functional/timeout.sh
index b179b79a2..b179b79a2 100644
--- a/tests/timeout.sh
+++ b/tests/functional/timeout.sh
diff --git a/tests/toString-path.sh b/tests/functional/toString-path.sh
index 07eb87465..07eb87465 100644
--- a/tests/toString-path.sh
+++ b/tests/functional/toString-path.sh
diff --git a/tests/undefined-variable.nix b/tests/functional/undefined-variable.nix
index 579985497..579985497 100644
--- a/tests/undefined-variable.nix
+++ b/tests/functional/undefined-variable.nix
diff --git a/tests/user-envs-migration.sh b/tests/functional/user-envs-migration.sh
index 187372b16..187372b16 100644
--- a/tests/user-envs-migration.sh
+++ b/tests/functional/user-envs-migration.sh
diff --git a/tests/user-envs.builder.sh b/tests/functional/user-envs.builder.sh
index 5fafa797f..5fafa797f 100644
--- a/tests/user-envs.builder.sh
+++ b/tests/functional/user-envs.builder.sh
diff --git a/tests/user-envs.nix b/tests/functional/user-envs.nix
index 46f8b51dd..46f8b51dd 100644
--- a/tests/user-envs.nix
+++ b/tests/functional/user-envs.nix
diff --git a/tests/user-envs.sh b/tests/functional/user-envs.sh
index d1260ba04..d1260ba04 100644
--- a/tests/user-envs.sh
+++ b/tests/functional/user-envs.sh
diff --git a/tests/why-depends.sh b/tests/functional/why-depends.sh
index 9680bf80e..9680bf80e 100644
--- a/tests/why-depends.sh
+++ b/tests/functional/why-depends.sh
diff --git a/tests/zstd.sh b/tests/functional/zstd.sh
index ba7c20501..ba7c20501 100644
--- a/tests/zstd.sh
+++ b/tests/functional/zstd.sh
diff --git a/tests/lang/eval-okay-pathexists.nix b/tests/lang/eval-okay-pathexists.nix
deleted file mode 100644
index e1246e370..000000000
--- a/tests/lang/eval-okay-pathexists.nix
+++ /dev/null
@@ -1,8 +0,0 @@
-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
-&& !builtins.pathExists ./bla.nix
diff --git a/tests/nixos/containers/containers.nix b/tests/nixos/containers/containers.nix
index e721be48f..c8ee78a4a 100644
--- a/tests/nixos/containers/containers.nix
+++ b/tests/nixos/containers/containers.nix
@@ -56,8 +56,8 @@
host.fail("nix build -v --auto-allocate-uids --no-sandbox -L --offline --impure --file ${./id-test.nix} --argstr name id-test-6 --arg uidRange true")
# Run systemd-nspawn in a Nix build.
- #host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}")
- #host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]")
+ host.succeed("nix build -v --auto-allocate-uids --sandbox -L --offline --impure --file ${./systemd-nspawn.nix} --argstr nixpkgs ${nixpkgs}")
+ host.succeed("[[ $(cat ./result/msg) = 'Hello World' ]]")
'';
}
diff --git a/tests/nixos/containers/systemd-nspawn.nix b/tests/nixos/containers/systemd-nspawn.nix
index f54f32f2a..1dad4ebd7 100644
--- a/tests/nixos/containers/systemd-nspawn.nix
+++ b/tests/nixos/containers/systemd-nspawn.nix
@@ -73,6 +73,8 @@ runCommand "test"
--resolv-conf=off \
--bind-ro=/nix/store \
--bind=$out \
+ --bind=/proc:/run/host/proc \
+ --bind=/sys:/run/host/sys \
--private-network \
$toplevel/init
''
diff --git a/tests/unit/libexpr-support/local.mk b/tests/unit/libexpr-support/local.mk
new file mode 100644
index 000000000..ce0d9950c
--- /dev/null
+++ b/tests/unit/libexpr-support/local.mk
@@ -0,0 +1,19 @@
+libraries += libexpr-test-support
+
+libexpr-test-support_NAME = libnixexpr-test-support
+
+libexpr-test-support_DIR := $(d)
+
+libexpr-test-support_INSTALL_DIR :=
+
+libexpr-test-support_SOURCES := \
+ $(wildcard $(d)/tests/*.cc) \
+ $(wildcard $(d)/tests/value/*.cc)
+
+libexpr-test-support_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES)
+
+libexpr-test-support_LIBS = \
+ libstore-test-support libutil-test-support \
+ libexpr libstore libutil
+
+libexpr-test-support_LDFLAGS := -pthread -lrapidcheck
diff --git a/tests/unit/libexpr-support/tests/libexpr.hh b/tests/unit/libexpr-support/tests/libexpr.hh
new file mode 100644
index 000000000..b8e65aafe
--- /dev/null
+++ b/tests/unit/libexpr-support/tests/libexpr.hh
@@ -0,0 +1,140 @@
+#pragma once
+///@file
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "value.hh"
+#include "nixexpr.hh"
+#include "eval.hh"
+#include "eval-inline.hh"
+#include "store-api.hh"
+
+#include "tests/libstore.hh"
+
+namespace nix {
+ class LibExprTest : public LibStoreTest {
+ public:
+ static void SetUpTestSuite() {
+ LibStoreTest::SetUpTestSuite();
+ initGC();
+ }
+
+ protected:
+ LibExprTest()
+ : LibStoreTest()
+ , state({}, store)
+ {
+ }
+ Value eval(std::string input, bool forceValue = true) {
+ Value v;
+ Expr * e = state.parseExprFromString(input, state.rootPath(CanonPath::root));
+ assert(e);
+ state.eval(e, v);
+ if (forceValue)
+ state.forceValue(v, noPos);
+ return v;
+ }
+
+ Symbol createSymbol(const char * value) {
+ return state.symbols.create(value);
+ }
+
+ EvalState state;
+ };
+
+ MATCHER(IsListType, "") {
+ return arg != nList;
+ }
+
+ MATCHER(IsList, "") {
+ return arg.type() == nList;
+ }
+
+ MATCHER(IsString, "") {
+ return arg.type() == nString;
+ }
+
+ MATCHER(IsNull, "") {
+ return arg.type() == nNull;
+ }
+
+ MATCHER(IsThunk, "") {
+ return arg.type() == nThunk;
+ }
+
+ MATCHER(IsAttrs, "") {
+ return arg.type() == nAttrs;
+ }
+
+ MATCHER_P(IsStringEq, s, fmt("The string is equal to \"%1%\"", s)) {
+ if (arg.type() != nString) {
+ return false;
+ }
+ return std::string_view(arg.string.s) == s;
+ }
+
+ MATCHER_P(IsIntEq, v, fmt("The string is equal to \"%1%\"", v)) {
+ if (arg.type() != nInt) {
+ return false;
+ }
+ return arg.integer == v;
+ }
+
+ MATCHER_P(IsFloatEq, v, fmt("The float is equal to \"%1%\"", v)) {
+ if (arg.type() != nFloat) {
+ return false;
+ }
+ return arg.fpoint == v;
+ }
+
+ MATCHER(IsTrue, "") {
+ if (arg.type() != nBool) {
+ return false;
+ }
+ return arg.boolean == true;
+ }
+
+ MATCHER(IsFalse, "") {
+ if (arg.type() != nBool) {
+ return false;
+ }
+ return arg.boolean == false;
+ }
+
+ MATCHER_P(IsPathEq, p, fmt("Is a path equal to \"%1%\"", p)) {
+ if (arg.type() != nPath) {
+ *result_listener << "Expected a path got " << arg.type();
+ return false;
+ } else if (std::string_view(arg.string.s) != p) {
+ *result_listener << "Expected a path that equals \"" << p << "\" but got: " << arg.string.s;
+ return false;
+ }
+ return true;
+ }
+
+
+ MATCHER_P(IsListOfSize, n, fmt("Is a list of size [%1%]", n)) {
+ if (arg.type() != nList) {
+ *result_listener << "Expected list got " << arg.type();
+ return false;
+ } else if (arg.listSize() != (size_t)n) {
+ *result_listener << "Expected as list of size " << n << " got " << arg.listSize();
+ return false;
+ }
+ return true;
+ }
+
+ MATCHER_P(IsAttrsOfSize, n, fmt("Is a set of size [%1%]", n)) {
+ if (arg.type() != nAttrs) {
+ *result_listener << "Expected set got " << arg.type();
+ return false;
+ } else if (arg.attrs->size() != (size_t)n) {
+ *result_listener << "Expected a set with " << n << " attributes but got " << arg.attrs->size();
+ return false;
+ }
+ return true;
+ }
+
+
+} /* namespace nix */
diff --git a/tests/unit/libexpr-support/tests/value/context.cc b/tests/unit/libexpr-support/tests/value/context.cc
new file mode 100644
index 000000000..8658bdaef
--- /dev/null
+++ b/tests/unit/libexpr-support/tests/value/context.cc
@@ -0,0 +1,30 @@
+#include <rapidcheck.h>
+
+#include "tests/path.hh"
+#include "tests/value/context.hh"
+
+namespace rc {
+using namespace nix;
+
+Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arbitrary()
+{
+ return gen::just(NixStringContextElem::DrvDeep {
+ .drvPath = *gen::arbitrary<StorePath>(),
+ });
+}
+
+Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, std::variant_size_v<NixStringContextElem::Raw>)) {
+ case 0:
+ return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Opaque>());
+ case 1:
+ return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::DrvDeep>());
+ case 2:
+ return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Built>());
+ default:
+ assert(false);
+ }
+}
+
+}
diff --git a/tests/unit/libexpr-support/tests/value/context.hh b/tests/unit/libexpr-support/tests/value/context.hh
new file mode 100644
index 000000000..8c68c78bb
--- /dev/null
+++ b/tests/unit/libexpr-support/tests/value/context.hh
@@ -0,0 +1,31 @@
+#pragma once
+///@file
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include "value/context.hh"
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<NixStringContextElem::Opaque> {
+ static Gen<NixStringContextElem::Opaque> arbitrary();
+};
+
+template<>
+struct Arbitrary<NixStringContextElem::Built> {
+ static Gen<NixStringContextElem::Built> arbitrary();
+};
+
+template<>
+struct Arbitrary<NixStringContextElem::DrvDeep> {
+ static Gen<NixStringContextElem::DrvDeep> arbitrary();
+};
+
+template<>
+struct Arbitrary<NixStringContextElem> {
+ static Gen<NixStringContextElem> arbitrary();
+};
+
+}
diff --git a/tests/unit/libexpr/derived-path.cc b/tests/unit/libexpr/derived-path.cc
new file mode 100644
index 000000000..d5fc6f201
--- /dev/null
+++ b/tests/unit/libexpr/derived-path.cc
@@ -0,0 +1,68 @@
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include "tests/derived-path.hh"
+#include "tests/libexpr.hh"
+
+namespace nix {
+
+// Testing of trivial expressions
+class DerivedPathExpressionTest : public LibExprTest {};
+
+// FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is
+// no a real fixture.
+//
+// See https://github.com/emil-e/rapidcheck/blob/master/doc/gtest.md#rc_gtest_fixture_propfixture-name-args
+TEST_F(DerivedPathExpressionTest, force_init)
+{
+}
+
+#ifndef COVERAGE
+
+RC_GTEST_FIXTURE_PROP(
+ DerivedPathExpressionTest,
+ prop_opaque_path_round_trip,
+ (const SingleDerivedPath::Opaque & o))
+{
+ auto * v = state.allocValue();
+ state.mkStorePathString(o.path, *v);
+ auto d = state.coerceToSingleDerivedPath(noPos, *v, "");
+ RC_ASSERT(SingleDerivedPath { o } == d);
+}
+
+// TODO use DerivedPath::Built for parameter once it supports a single output
+// path only.
+
+RC_GTEST_FIXTURE_PROP(
+ DerivedPathExpressionTest,
+ prop_derived_path_built_placeholder_round_trip,
+ (const SingleDerivedPath::Built & b))
+{
+ /**
+ * We set these in tests rather than the regular globals so we don't have
+ * to worry about race conditions if the tests run concurrently.
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+ mockXpSettings.set("experimental-features", "ca-derivations");
+
+ auto * v = state.allocValue();
+ state.mkOutputString(*v, b, std::nullopt, mockXpSettings);
+ auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "");
+ RC_ASSERT(SingleDerivedPath { b } == d);
+}
+
+RC_GTEST_FIXTURE_PROP(
+ DerivedPathExpressionTest,
+ prop_derived_path_built_out_path_round_trip,
+ (const SingleDerivedPath::Built & b, const StorePath & outPath))
+{
+ auto * v = state.allocValue();
+ state.mkOutputString(*v, b, outPath);
+ auto [d, _] = state.coerceToSingleDerivedPathUnchecked(noPos, *v, "");
+ RC_ASSERT(SingleDerivedPath { b } == d);
+}
+
+#endif
+
+} /* namespace nix */
diff --git a/tests/unit/libexpr/error_traces.cc b/tests/unit/libexpr/error_traces.cc
new file mode 100644
index 000000000..285651256
--- /dev/null
+++ b/tests/unit/libexpr/error_traces.cc
@@ -0,0 +1,1298 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "tests/libexpr.hh"
+
+namespace nix {
+
+ using namespace testing;
+
+ // Testing eval of PrimOp's
+ class ErrorTraceTest : public LibExprTest { };
+
+ TEST_F(ErrorTraceTest, TraceBuilder) {
+ ASSERT_THROW(
+ state.error("Not much").debugThrow<EvalError>(),
+ EvalError
+ );
+
+ ASSERT_THROW(
+ state.error("Not much").withTrace(noPos, "No more").debugThrow<EvalError>(),
+ EvalError
+ );
+
+ ASSERT_THROW(
+ try {
+ try {
+ state.error("Not much").withTrace(noPos, "No more").debugThrow<EvalError>();
+ } catch (Error & e) {
+ e.addTrace(state.positions[noPos], "Something", "");
+ throw;
+ }
+ } catch (BaseError & e) {
+ ASSERT_EQ(PrintToString(e.info().msg),
+ PrintToString(hintfmt("Not much")));
+ auto trace = e.info().traces.rbegin();
+ ASSERT_EQ(e.info().traces.size(), 2);
+ ASSERT_EQ(PrintToString(trace->hint),
+ PrintToString(hintfmt("No more")));
+ trace++;
+ ASSERT_EQ(PrintToString(trace->hint),
+ PrintToString(hintfmt("Something")));
+ throw;
+ }
+ , EvalError
+ );
+ }
+
+ TEST_F(ErrorTraceTest, NestedThrows) {
+ try {
+ state.error("Not much").withTrace(noPos, "No more").debugThrow<EvalError>();
+ } catch (BaseError & e) {
+ try {
+ state.error("Not much more").debugThrow<EvalError>();
+ } catch (Error & e2) {
+ e.addTrace(state.positions[noPos], "Something", "");
+ //e2.addTrace(state.positions[noPos], "Something", "");
+ ASSERT_TRUE(e.info().traces.size() == 2);
+ ASSERT_TRUE(e2.info().traces.size() == 0);
+ ASSERT_FALSE(&e.info() == &e2.info());
+ }
+ }
+ }
+
+#define ASSERT_TRACE1(args, type, message) \
+ ASSERT_THROW( \
+ std::string expr(args); \
+ std::string name = expr.substr(0, expr.find(" ")); \
+ try { \
+ Value v = eval("builtins." args); \
+ state.forceValueDeep(v); \
+ } catch (BaseError & e) { \
+ ASSERT_EQ(PrintToString(e.info().msg), \
+ PrintToString(message)); \
+ ASSERT_EQ(e.info().traces.size(), 1) << "while testing " args << std::endl << e.what(); \
+ auto trace = e.info().traces.rbegin(); \
+ ASSERT_EQ(PrintToString(trace->hint), \
+ PrintToString(hintfmt("while calling the '%s' builtin", name))); \
+ throw; \
+ } \
+ , type \
+ )
+
+#define ASSERT_TRACE2(args, type, message, context) \
+ ASSERT_THROW( \
+ std::string expr(args); \
+ std::string name = expr.substr(0, expr.find(" ")); \
+ try { \
+ Value v = eval("builtins." args); \
+ state.forceValueDeep(v); \
+ } catch (BaseError & e) { \
+ ASSERT_EQ(PrintToString(e.info().msg), \
+ PrintToString(message)); \
+ ASSERT_EQ(e.info().traces.size(), 2) << "while testing " args << std::endl << e.what(); \
+ auto trace = e.info().traces.rbegin(); \
+ ASSERT_EQ(PrintToString(trace->hint), \
+ PrintToString(context)); \
+ ++trace; \
+ ASSERT_EQ(PrintToString(trace->hint), \
+ PrintToString(hintfmt("while calling the '%s' builtin", name))); \
+ throw; \
+ } \
+ , type \
+ )
+
+ TEST_F(ErrorTraceTest, genericClosure) {
+ ASSERT_TRACE2("genericClosure 1",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure {}",
+ TypeError,
+ hintfmt("attribute '%s' missing", "startSet"),
+ hintfmt("in the attrset passed as argument to builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure { startSet = 1; }",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the 'startSet' attribute passed as argument to builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = true; }",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "a Boolean"),
+ hintfmt("while evaluating the 'operator' attribute passed as argument to builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: true; }",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a Boolean"),
+ hintfmt("while evaluating the return value of the `operator` passed to builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: [ true ]; }",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a Boolean"),
+ hintfmt("while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: [ {} ]; }",
+ TypeError,
+ hintfmt("attribute '%s' missing", "key"),
+ hintfmt("in one of the attrsets generated by (or initially passed to) builtins.genericClosure"));
+
+ ASSERT_TRACE2("genericClosure { startSet = [{ key = 1;}]; operator = item: [{ key = ''a''; }]; }",
+ EvalError,
+ hintfmt("cannot compare %s with %s", "a string", "an integer"),
+ hintfmt("while comparing the `key` attributes of two genericClosure elements"));
+
+ ASSERT_TRACE2("genericClosure { startSet = [ true ]; operator = item: [{ key = ''a''; }]; }",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a Boolean"),
+ hintfmt("while evaluating one of the elements generated by (or initially passed to) builtins.genericClosure"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, replaceStrings) {
+ ASSERT_TRACE2("replaceStrings 0 0 {}",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.replaceStrings"));
+
+ ASSERT_TRACE2("replaceStrings [] 0 {}",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the second argument passed to builtins.replaceStrings"));
+
+ ASSERT_TRACE1("replaceStrings [ 0 ] [] {}",
+ EvalError,
+ hintfmt("'from' and 'to' arguments passed to builtins.replaceStrings have different lengths"));
+
+ ASSERT_TRACE2("replaceStrings [ 1 ] [ \"new\" ] {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating one of the strings to replace passed to builtins.replaceStrings"));
+
+ ASSERT_TRACE2("replaceStrings [ \"oo\" ] [ true ] \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a Boolean"),
+ hintfmt("while evaluating one of the replacement strings passed to builtins.replaceStrings"));
+
+ ASSERT_TRACE2("replaceStrings [ \"old\" ] [ \"new\" ] {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a set"),
+ hintfmt("while evaluating the third argument passed to builtins.replaceStrings"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, scopedImport) {
+ }
+
+
+ TEST_F(ErrorTraceTest, import) {
+ }
+
+
+ TEST_F(ErrorTraceTest, typeOf) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isNull) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isFunction) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isInt) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isFloat) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isString) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isBool) {
+ }
+
+
+ TEST_F(ErrorTraceTest, isPath) {
+ }
+
+
+ TEST_F(ErrorTraceTest, break) {
+ }
+
+
+ TEST_F(ErrorTraceTest, abort) {
+ }
+
+
+ TEST_F(ErrorTraceTest, throw) {
+ }
+
+
+ TEST_F(ErrorTraceTest, addErrorContext) {
+ }
+
+
+ TEST_F(ErrorTraceTest, ceil) {
+ ASSERT_TRACE2("ceil \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a float was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.ceil"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, floor) {
+ ASSERT_TRACE2("floor \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a float was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.floor"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, tryEval) {
+ }
+
+
+ TEST_F(ErrorTraceTest, getEnv) {
+ ASSERT_TRACE2("getEnv [ ]",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.getEnv"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, seq) {
+ }
+
+
+ TEST_F(ErrorTraceTest, deepSeq) {
+ }
+
+
+ TEST_F(ErrorTraceTest, trace) {
+ }
+
+
+ TEST_F(ErrorTraceTest, placeholder) {
+ ASSERT_TRACE2("placeholder []",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.placeholder"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, toPath) {
+ ASSERT_TRACE2("toPath []",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.toPath"));
+
+ ASSERT_TRACE2("toPath \"foo\"",
+ EvalError,
+ hintfmt("string '%s' doesn't represent an absolute path", "foo"),
+ hintfmt("while evaluating the first argument passed to builtins.toPath"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, storePath) {
+ ASSERT_TRACE2("storePath true",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a Boolean"),
+ hintfmt("while evaluating the first argument passed to builtins.storePath"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, pathExists) {
+ ASSERT_TRACE2("pathExists []",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a list"),
+ hintfmt("while realising the context of a path"));
+
+ ASSERT_TRACE2("pathExists \"zorglub\"",
+ EvalError,
+ hintfmt("string '%s' doesn't represent an absolute path", "zorglub"),
+ hintfmt("while realising the context of a path"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, baseNameOf) {
+ ASSERT_TRACE2("baseNameOf []",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.baseNameOf"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, dirOf) {
+ }
+
+
+ TEST_F(ErrorTraceTest, readFile) {
+ }
+
+
+ TEST_F(ErrorTraceTest, findFile) {
+ }
+
+
+ TEST_F(ErrorTraceTest, hashFile) {
+ }
+
+
+ TEST_F(ErrorTraceTest, readDir) {
+ }
+
+
+ TEST_F(ErrorTraceTest, toXML) {
+ }
+
+
+ TEST_F(ErrorTraceTest, toJSON) {
+ }
+
+
+ TEST_F(ErrorTraceTest, fromJSON) {
+ }
+
+
+ TEST_F(ErrorTraceTest, toFile) {
+ }
+
+
+ TEST_F(ErrorTraceTest, filterSource) {
+ ASSERT_TRACE2("filterSource [] []",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a list"),
+ hintfmt("while evaluating the second argument (the path to filter) passed to builtins.filterSource"));
+
+ ASSERT_TRACE2("filterSource [] \"foo\"",
+ EvalError,
+ hintfmt("string '%s' doesn't represent an absolute path", "foo"),
+ hintfmt("while evaluating the second argument (the path to filter) passed to builtins.filterSource"));
+
+ ASSERT_TRACE2("filterSource [] ./.",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.filterSource"));
+
+ // Usupported by store "dummy"
+
+ // ASSERT_TRACE2("filterSource (_: 1) ./.",
+ // TypeError,
+ // hintfmt("attempt to call something which is not a function but %s", "an integer"),
+ // hintfmt("while adding path '/home/layus/projects/nix'"));
+
+ // ASSERT_TRACE2("filterSource (_: _: 1) ./.",
+ // TypeError,
+ // hintfmt("value is %s while a Boolean was expected", "an integer"),
+ // hintfmt("while evaluating the return value of the path filter function"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, path) {
+ }
+
+
+ TEST_F(ErrorTraceTest, attrNames) {
+ ASSERT_TRACE2("attrNames []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the argument passed to builtins.attrNames"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, attrValues) {
+ ASSERT_TRACE2("attrValues []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the argument passed to builtins.attrValues"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, getAttr) {
+ ASSERT_TRACE2("getAttr [] []",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.getAttr"));
+
+ ASSERT_TRACE2("getAttr \"foo\" []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the second argument passed to builtins.getAttr"));
+
+ ASSERT_TRACE2("getAttr \"foo\" {}",
+ TypeError,
+ hintfmt("attribute '%s' missing", "foo"),
+ hintfmt("in the attribute set under consideration"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, unsafeGetAttrPos) {
+ }
+
+
+ TEST_F(ErrorTraceTest, hasAttr) {
+ ASSERT_TRACE2("hasAttr [] []",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.hasAttr"));
+
+ ASSERT_TRACE2("hasAttr \"foo\" []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the second argument passed to builtins.hasAttr"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, isAttrs) {
+ }
+
+
+ TEST_F(ErrorTraceTest, removeAttrs) {
+ ASSERT_TRACE2("removeAttrs \"\" \"\"",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.removeAttrs"));
+
+ ASSERT_TRACE2("removeAttrs \"\" [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.removeAttrs"));
+
+ ASSERT_TRACE2("removeAttrs \"\" [ \"1\" ]",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.removeAttrs"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, listToAttrs) {
+ ASSERT_TRACE2("listToAttrs 1",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the argument passed to builtins.listToAttrs"));
+
+ ASSERT_TRACE2("listToAttrs [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "an integer"),
+ hintfmt("while evaluating an element of the list passed to builtins.listToAttrs"));
+
+ ASSERT_TRACE2("listToAttrs [ {} ]",
+ TypeError,
+ hintfmt("attribute '%s' missing", "name"),
+ hintfmt("in a {name=...; value=...;} pair"));
+
+ ASSERT_TRACE2("listToAttrs [ { name = 1; } ]",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the `name` attribute of an element of the list passed to builtins.listToAttrs"));
+
+ ASSERT_TRACE2("listToAttrs [ { name = \"foo\"; } ]",
+ TypeError,
+ hintfmt("attribute '%s' missing", "value"),
+ hintfmt("in a {name=...; value=...;} pair"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, intersectAttrs) {
+ ASSERT_TRACE2("intersectAttrs [] []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.intersectAttrs"));
+
+ ASSERT_TRACE2("intersectAttrs {} []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the second argument passed to builtins.intersectAttrs"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, catAttrs) {
+ ASSERT_TRACE2("catAttrs [] {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.catAttrs"));
+
+ ASSERT_TRACE2("catAttrs \"foo\" {}",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a set"),
+ hintfmt("while evaluating the second argument passed to builtins.catAttrs"));
+
+ ASSERT_TRACE2("catAttrs \"foo\" [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "an integer"),
+ hintfmt("while evaluating an element in the list passed as second argument to builtins.catAttrs"));
+
+ ASSERT_TRACE2("catAttrs \"foo\" [ { foo = 1; } 1 { bar = 5;} ]",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "an integer"),
+ hintfmt("while evaluating an element in the list passed as second argument to builtins.catAttrs"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, functionArgs) {
+ ASSERT_TRACE1("functionArgs {}",
+ TypeError,
+ hintfmt("'functionArgs' requires a function"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, mapAttrs) {
+ ASSERT_TRACE2("mapAttrs [] []",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a list"),
+ hintfmt("while evaluating the second argument passed to builtins.mapAttrs"));
+
+ // XXX: defered
+ // ASSERT_TRACE2("mapAttrs \"\" { foo.bar = 1; }",
+ // TypeError,
+ // hintfmt("attempt to call something which is not a function but %s", "a string"),
+ // hintfmt("while evaluating the attribute 'foo'"));
+
+ // ASSERT_TRACE2("mapAttrs (x: x + \"1\") { foo.bar = 1; }",
+ // TypeError,
+ // hintfmt("attempt to call something which is not a function but %s", "a string"),
+ // hintfmt("while evaluating the attribute 'foo'"));
+
+ // ASSERT_TRACE2("mapAttrs (x: y: x + 1) { foo.bar = 1; }",
+ // TypeError,
+ // hintfmt("cannot coerce %s to a string", "an integer"),
+ // hintfmt("while evaluating a path segment"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, zipAttrsWith) {
+ ASSERT_TRACE2("zipAttrsWith [] [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "a list"),
+ hintfmt("while evaluating the first argument passed to builtins.zipAttrsWith"));
+
+ ASSERT_TRACE2("zipAttrsWith (_: 1) [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "an integer"),
+ hintfmt("while evaluating a value of the list passed as second argument to builtins.zipAttrsWith"));
+
+ // XXX: How to properly tell that the fucntion takes two arguments ?
+ // The same question also applies to sort, and maybe others.
+ // Due to lazyness, we only create a thunk, and it fails later on.
+ // ASSERT_TRACE2("zipAttrsWith (_: 1) [ { foo = 1; } ]",
+ // TypeError,
+ // hintfmt("attempt to call something which is not a function but %s", "an integer"),
+ // hintfmt("while evaluating the attribute 'foo'"));
+
+ // XXX: Also deferred deeply
+ // ASSERT_TRACE2("zipAttrsWith (a: b: a + b) [ { foo = 1; } { foo = 2; } ]",
+ // TypeError,
+ // hintfmt("cannot coerce %s to a string", "a list"),
+ // hintfmt("while evaluating a path segment"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, isList) {
+ }
+
+
+ TEST_F(ErrorTraceTest, elemAt) {
+ ASSERT_TRACE2("elemAt \"foo\" (-1)",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.elemAt"));
+
+ ASSERT_TRACE1("elemAt [] (-1)",
+ Error,
+ hintfmt("list index %d is out of bounds", -1));
+
+ ASSERT_TRACE1("elemAt [\"foo\"] 3",
+ Error,
+ hintfmt("list index %d is out of bounds", 3));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, head) {
+ ASSERT_TRACE2("head 1",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.elemAt"));
+
+ ASSERT_TRACE1("head []",
+ Error,
+ hintfmt("list index %d is out of bounds", 0));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, tail) {
+ ASSERT_TRACE2("tail 1",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.tail"));
+
+ ASSERT_TRACE1("tail []",
+ Error,
+ hintfmt("'tail' called on an empty list"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, map) {
+ ASSERT_TRACE2("map 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.map"));
+
+ ASSERT_TRACE2("map 1 [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.map"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, filter) {
+ ASSERT_TRACE2("filter 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.filter"));
+
+ ASSERT_TRACE2("filter 1 [ \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.filter"));
+
+ ASSERT_TRACE2("filter (_: 5) [ \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the return value of the filtering function passed to builtins.filter"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, elem) {
+ ASSERT_TRACE2("elem 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.elem"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, concatLists) {
+ ASSERT_TRACE2("concatLists 1",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.concatLists"));
+
+ ASSERT_TRACE2("concatLists [ 1 ]",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating a value of the list passed to builtins.concatLists"));
+
+ ASSERT_TRACE2("concatLists [ [1] \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating a value of the list passed to builtins.concatLists"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, length) {
+ ASSERT_TRACE2("length 1",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.length"));
+
+ ASSERT_TRACE2("length \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the first argument passed to builtins.length"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, foldlPrime) {
+ ASSERT_TRACE2("foldl' 1 \"foo\" true",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.foldlStrict"));
+
+ ASSERT_TRACE2("foldl' (_: 1) \"foo\" true",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a Boolean"),
+ hintfmt("while evaluating the third argument passed to builtins.foldlStrict"));
+
+ ASSERT_TRACE1("foldl' (_: 1) \"foo\" [ true ]",
+ TypeError,
+ hintfmt("attempt to call something which is not a function but %s", "an integer"));
+
+ ASSERT_TRACE2("foldl' (a: b: a && b) \"foo\" [ true ]",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "a string"),
+ hintfmt("in the left operand of the AND (&&) operator"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, any) {
+ ASSERT_TRACE2("any 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.any"));
+
+ ASSERT_TRACE2("any (_: 1) \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.any"));
+
+ ASSERT_TRACE2("any (_: 1) [ \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the return value of the function passed to builtins.any"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, all) {
+ ASSERT_TRACE2("all 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.all"));
+
+ ASSERT_TRACE2("all (_: 1) \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.all"));
+
+ ASSERT_TRACE2("all (_: 1) [ \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the return value of the function passed to builtins.all"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, genList) {
+ ASSERT_TRACE2("genList 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.genList"));
+
+ ASSERT_TRACE2("genList 1 2",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.genList", "an integer"));
+
+ // XXX: defered
+ // ASSERT_TRACE2("genList (x: x + \"foo\") 2 #TODO",
+ // TypeError,
+ // hintfmt("cannot add %s to an integer", "a string"),
+ // hintfmt("while evaluating anonymous lambda"));
+
+ ASSERT_TRACE1("genList false (-3)",
+ EvalError,
+ hintfmt("cannot create list of size %d", -3));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, sort) {
+ ASSERT_TRACE2("sort 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.sort"));
+
+ ASSERT_TRACE2("sort 1 [ \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.sort"));
+
+ ASSERT_TRACE1("sort (_: 1) [ \"foo\" \"bar\" ]",
+ TypeError,
+ hintfmt("attempt to call something which is not a function but %s", "an integer"));
+
+ ASSERT_TRACE2("sort (_: _: 1) [ \"foo\" \"bar\" ]",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the return value of the sorting function passed to builtins.sort"));
+
+ // XXX: Trace too deep, need better asserts
+ // ASSERT_TRACE1("sort (a: b: a <= b) [ \"foo\" {} ] # TODO",
+ // TypeError,
+ // hintfmt("cannot compare %s with %s", "a string", "a set"));
+
+ // ASSERT_TRACE1("sort (a: b: a <= b) [ {} {} ] # TODO",
+ // TypeError,
+ // hintfmt("cannot compare %s with %s; values of that type are incomparable", "a set", "a set"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, partition) {
+ ASSERT_TRACE2("partition 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.partition"));
+
+ ASSERT_TRACE2("partition (_: 1) \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.partition"));
+
+ ASSERT_TRACE2("partition (_: 1) [ \"foo\" ]",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the return value of the partition function passed to builtins.partition"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, groupBy) {
+ ASSERT_TRACE2("groupBy 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.groupBy"));
+
+ ASSERT_TRACE2("groupBy (_: 1) \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.groupBy"));
+
+ ASSERT_TRACE2("groupBy (x: x) [ \"foo\" \"bar\" 1 ]",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the return value of the grouping function passed to builtins.groupBy"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, concatMap) {
+ ASSERT_TRACE2("concatMap 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a function was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.concatMap"));
+
+ ASSERT_TRACE2("concatMap (x: 1) \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the second argument passed to builtins.concatMap"));
+
+ ASSERT_TRACE2("concatMap (x: 1) [ \"foo\" ] # TODO",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "an integer"),
+ hintfmt("while evaluating the return value of the function passed to buitlins.concatMap"));
+
+ ASSERT_TRACE2("concatMap (x: \"foo\") [ 1 2 ] # TODO",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the return value of the function passed to buitlins.concatMap"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, add) {
+ ASSERT_TRACE2("add \"foo\" 1",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the first argument of the addition"));
+
+ ASSERT_TRACE2("add 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the second argument of the addition"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, sub) {
+ ASSERT_TRACE2("sub \"foo\" 1",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the first argument of the subtraction"));
+
+ ASSERT_TRACE2("sub 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the second argument of the subtraction"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, mul) {
+ ASSERT_TRACE2("mul \"foo\" 1",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the first argument of the multiplication"));
+
+ ASSERT_TRACE2("mul 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the second argument of the multiplication"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, div) {
+ ASSERT_TRACE2("div \"foo\" 1 # TODO: an integer was expected -> a number",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the first operand of the division"));
+
+ ASSERT_TRACE2("div 1 \"foo\"",
+ TypeError,
+ hintfmt("value is %s while a float was expected", "a string"),
+ hintfmt("while evaluating the second operand of the division"));
+
+ ASSERT_TRACE1("div \"foo\" 0",
+ EvalError,
+ hintfmt("division by zero"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, bitAnd) {
+ ASSERT_TRACE2("bitAnd 1.1 2",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a float"),
+ hintfmt("while evaluating the first argument passed to builtins.bitAnd"));
+
+ ASSERT_TRACE2("bitAnd 1 2.2",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a float"),
+ hintfmt("while evaluating the second argument passed to builtins.bitAnd"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, bitOr) {
+ ASSERT_TRACE2("bitOr 1.1 2",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a float"),
+ hintfmt("while evaluating the first argument passed to builtins.bitOr"));
+
+ ASSERT_TRACE2("bitOr 1 2.2",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a float"),
+ hintfmt("while evaluating the second argument passed to builtins.bitOr"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, bitXor) {
+ ASSERT_TRACE2("bitXor 1.1 2",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a float"),
+ hintfmt("while evaluating the first argument passed to builtins.bitXor"));
+
+ ASSERT_TRACE2("bitXor 1 2.2",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a float"),
+ hintfmt("while evaluating the second argument passed to builtins.bitXor"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, lessThan) {
+ ASSERT_TRACE1("lessThan 1 \"foo\"",
+ EvalError,
+ hintfmt("cannot compare %s with %s", "an integer", "a string"));
+
+ ASSERT_TRACE1("lessThan {} {}",
+ EvalError,
+ hintfmt("cannot compare %s with %s; values of that type are incomparable", "a set", "a set"));
+
+ ASSERT_TRACE2("lessThan [ 1 2 ] [ \"foo\" ]",
+ EvalError,
+ hintfmt("cannot compare %s with %s", "an integer", "a string"),
+ hintfmt("while comparing two list elements"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, toString) {
+ ASSERT_TRACE2("toString { a = 1; }",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating the first argument passed to builtins.toString"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, substring) {
+ ASSERT_TRACE2("substring {} \"foo\" true",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a set"),
+ hintfmt("while evaluating the first argument (the start offset) passed to builtins.substring"));
+
+ ASSERT_TRACE2("substring 3 \"foo\" true",
+ TypeError,
+ hintfmt("value is %s while an integer was expected", "a string"),
+ hintfmt("while evaluating the second argument (the substring length) passed to builtins.substring"));
+
+ ASSERT_TRACE2("substring 0 3 {}",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating the third argument (the string) passed to builtins.substring"));
+
+ ASSERT_TRACE1("substring (-3) 3 \"sometext\"",
+ EvalError,
+ hintfmt("negative start position in 'substring'"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, stringLength) {
+ ASSERT_TRACE2("stringLength {} # TODO: context is missing ???",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating the argument passed to builtins.stringLength"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, hashString) {
+ ASSERT_TRACE2("hashString 1 {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.hashString"));
+
+ ASSERT_TRACE1("hashString \"foo\" \"content\"",
+ UsageError,
+ hintfmt("unknown hash algorithm '%s'", "foo"));
+
+ ASSERT_TRACE2("hashString \"sha256\" {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a set"),
+ hintfmt("while evaluating the second argument passed to builtins.hashString"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, match) {
+ ASSERT_TRACE2("match 1 {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.match"));
+
+ ASSERT_TRACE2("match \"foo\" {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a set"),
+ hintfmt("while evaluating the second argument passed to builtins.match"));
+
+ ASSERT_TRACE1("match \"(.*\" \"\"",
+ EvalError,
+ hintfmt("invalid regular expression '%s'", "(.*"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, split) {
+ ASSERT_TRACE2("split 1 {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.split"));
+
+ ASSERT_TRACE2("split \"foo\" {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a set"),
+ hintfmt("while evaluating the second argument passed to builtins.split"));
+
+ ASSERT_TRACE1("split \"f(o*o\" \"1foo2\"",
+ EvalError,
+ hintfmt("invalid regular expression '%s'", "f(o*o"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, concatStringsSep) {
+ ASSERT_TRACE2("concatStringsSep 1 {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument (the separator string) passed to builtins.concatStringsSep"));
+
+ ASSERT_TRACE2("concatStringsSep \"foo\" {}",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a set"),
+ hintfmt("while evaluating the second argument (the list of strings to concat) passed to builtins.concatStringsSep"));
+
+ ASSERT_TRACE2("concatStringsSep \"foo\" [ 1 2 {} ] # TODO: coerce to string is buggy",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "an integer"),
+ hintfmt("while evaluating one element of the list of strings to concat passed to builtins.concatStringsSep"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, parseDrvName) {
+ ASSERT_TRACE2("parseDrvName 1",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.parseDrvName"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, compareVersions) {
+ ASSERT_TRACE2("compareVersions 1 {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.compareVersions"));
+
+ ASSERT_TRACE2("compareVersions \"abd\" {}",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "a set"),
+ hintfmt("while evaluating the second argument passed to builtins.compareVersions"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, splitVersion) {
+ ASSERT_TRACE2("splitVersion 1",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the first argument passed to builtins.splitVersion"));
+
+ }
+
+
+ TEST_F(ErrorTraceTest, traceVerbose) {
+ }
+
+
+ /* // Needs different ASSERTs
+ TEST_F(ErrorTraceTest, derivationStrict) {
+ ASSERT_TRACE2("derivationStrict \"\"",
+ TypeError,
+ hintfmt("value is %s while a set was expected", "a string"),
+ hintfmt("while evaluating the argument passed to builtins.derivationStrict"));
+
+ ASSERT_TRACE2("derivationStrict {}",
+ TypeError,
+ hintfmt("attribute '%s' missing", "name"),
+ hintfmt("in the attrset passed as argument to builtins.derivationStrict"));
+
+ ASSERT_TRACE2("derivationStrict { name = 1; }",
+ TypeError,
+ hintfmt("value is %s while a string was expected", "an integer"),
+ hintfmt("while evaluating the `name` attribute passed to builtins.derivationStrict"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; }",
+ TypeError,
+ hintfmt("required attribute 'builder' missing"),
+ hintfmt("while evaluating derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __structuredAttrs = 15; }",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the `__structuredAttrs` attribute passed to builtins.derivationStrict"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; __ignoreNulls = 15; }",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "an integer"),
+ hintfmt("while evaluating the `__ignoreNulls` attribute passed to builtins.derivationStrict"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = 15; }",
+ TypeError,
+ hintfmt("invalid value '15' for 'outputHashMode' attribute"),
+ hintfmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; outputHashMode = \"custom\"; }",
+ TypeError,
+ hintfmt("invalid value 'custom' for 'outputHashMode' attribute"),
+ hintfmt("while evaluating the attribute 'outputHashMode' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = {}; }",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating the attribute 'system' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = {}; }",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating the attribute 'outputs' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"drv\"; }",
+ TypeError,
+ hintfmt("invalid derivation output name 'drv'"),
+ hintfmt("while evaluating the attribute 'outputs' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = []; }",
+ TypeError,
+ hintfmt("derivation cannot have an empty set of outputs"),
+ hintfmt("while evaluating the attribute 'outputs' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"drv\" ]; }",
+ TypeError,
+ hintfmt("invalid derivation output name 'drv'"),
+ hintfmt("while evaluating the attribute 'outputs' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = [ \"out\" \"out\" ]; }",
+ TypeError,
+ hintfmt("duplicate derivation output 'out'"),
+ hintfmt("while evaluating the attribute 'outputs' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __contentAddressed = \"true\"; }",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "a string"),
+ hintfmt("while evaluating the attribute '__contentAddressed' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "a string"),
+ hintfmt("while evaluating the attribute '__impure' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; __impure = \"true\"; }",
+ TypeError,
+ hintfmt("value is %s while a Boolean was expected", "a string"),
+ hintfmt("while evaluating the attribute '__impure' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = \"foo\"; }",
+ TypeError,
+ hintfmt("value is %s while a list was expected", "a string"),
+ hintfmt("while evaluating the attribute 'args' of derivation 'foo'"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ {} ]; }",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating an element of the argument list"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; args = [ \"a\" {} ]; }",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating an element of the argument list"));
+
+ ASSERT_TRACE2("derivationStrict { name = \"foo\"; builder = 1; system = 1; outputs = \"out\"; FOO = {}; }",
+ TypeError,
+ hintfmt("cannot coerce %s to a string", "a set"),
+ hintfmt("while evaluating the attribute 'FOO' of derivation 'foo'"));
+
+ }
+ */
+
+} /* namespace nix */
diff --git a/tests/unit/libexpr/flakeref.cc b/tests/unit/libexpr/flakeref.cc
new file mode 100644
index 000000000..2b7809b93
--- /dev/null
+++ b/tests/unit/libexpr/flakeref.cc
@@ -0,0 +1,22 @@
+#include <gtest/gtest.h>
+
+#include "flake/flakeref.hh"
+
+namespace nix {
+
+/* ----------- tests for flake/flakeref.hh --------------------------------------------------*/
+
+ /* ----------------------------------------------------------------------------
+ * to_string
+ * --------------------------------------------------------------------------*/
+
+ TEST(to_string, doesntReencodeUrl) {
+ auto s = "http://localhost:8181/test/+3d.tar.gz";
+ auto flakeref = parseFlakeRef(s);
+ auto parsed = flakeref.to_string();
+ auto expected = "http://localhost:8181/test/%2B3d.tar.gz";
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+}
diff --git a/tests/unit/libexpr/json.cc b/tests/unit/libexpr/json.cc
new file mode 100644
index 000000000..7586bdd9b
--- /dev/null
+++ b/tests/unit/libexpr/json.cc
@@ -0,0 +1,68 @@
+#include "tests/libexpr.hh"
+#include "value-to-json.hh"
+
+namespace nix {
+// Testing the conversion to JSON
+
+ class JSONValueTest : public LibExprTest {
+ protected:
+ std::string getJSONValue(Value& value) {
+ std::stringstream ss;
+ NixStringContext ps;
+ printValueAsJSON(state, true, value, noPos, ss, ps);
+ return ss.str();
+ }
+ };
+
+ TEST_F(JSONValueTest, null) {
+ Value v;
+ v.mkNull();
+ ASSERT_EQ(getJSONValue(v), "null");
+ }
+
+ TEST_F(JSONValueTest, BoolFalse) {
+ Value v;
+ v.mkBool(false);
+ ASSERT_EQ(getJSONValue(v),"false");
+ }
+
+ TEST_F(JSONValueTest, BoolTrue) {
+ Value v;
+ v.mkBool(true);
+ ASSERT_EQ(getJSONValue(v), "true");
+ }
+
+ TEST_F(JSONValueTest, IntPositive) {
+ Value v;
+ v.mkInt(100);
+ ASSERT_EQ(getJSONValue(v), "100");
+ }
+
+ TEST_F(JSONValueTest, IntNegative) {
+ Value v;
+ v.mkInt(-100);
+ ASSERT_EQ(getJSONValue(v), "-100");
+ }
+
+ TEST_F(JSONValueTest, String) {
+ Value v;
+ v.mkString("test");
+ ASSERT_EQ(getJSONValue(v), "\"test\"");
+ }
+
+ TEST_F(JSONValueTest, StringQuotes) {
+ Value v;
+
+ v.mkString("test\"");
+ ASSERT_EQ(getJSONValue(v), "\"test\\\"\"");
+ }
+
+ // The dummy store doesn't support writing files. Fails with this exception message:
+ // C++ exception with description "error: operation 'addToStoreFromDump' is
+ // not supported by store 'dummy'" thrown in the test body.
+ TEST_F(JSONValueTest, DISABLED_Path) {
+ Value v;
+ v.mkPath("test");
+ ASSERT_EQ(getJSONValue(v), "\"/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x\"");
+ }
+} /* namespace nix */
diff --git a/tests/unit/libexpr/local.mk b/tests/unit/libexpr/local.mk
new file mode 100644
index 000000000..068dd7a17
--- /dev/null
+++ b/tests/unit/libexpr/local.mk
@@ -0,0 +1,32 @@
+check: libexpr-tests_RUN
+
+programs += libexpr-tests
+
+libexpr-tests_NAME := libnixexpr-tests
+
+libexpr-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data
+
+libexpr-tests_DIR := $(d)
+
+libexpr-tests_INSTALL_DIR :=
+
+libexpr-tests_SOURCES := \
+ $(wildcard $(d)/*.cc) \
+ $(wildcard $(d)/value/*.cc)
+
+libexpr-tests_EXTRA_INCLUDES = \
+ -I tests/unit/libexpr-support \
+ -I tests/unit/libstore-support \
+ -I tests/unit/libutil-support \
+ -I src/libexpr \
+ -I src/libfetchers \
+ -I src/libstore \
+ -I src/libutil
+
+libexpr-tests_CXXFLAGS += $(libexpr-tests_EXTRA_INCLUDES)
+
+libexpr-tests_LIBS = \
+ libexpr-test-support libstore-test-support libutils-test-support \
+ libexpr libfetchers libstore libutil
+
+libexpr-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS) -lgmock
diff --git a/tests/unit/libexpr/primops.cc b/tests/unit/libexpr/primops.cc
new file mode 100644
index 000000000..ce3b5d11f
--- /dev/null
+++ b/tests/unit/libexpr/primops.cc
@@ -0,0 +1,832 @@
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "tests/libexpr.hh"
+
+namespace nix {
+ class CaptureLogger : public Logger
+ {
+ std::ostringstream oss;
+
+ public:
+ CaptureLogger() {}
+
+ std::string get() const {
+ return oss.str();
+ }
+
+ void log(Verbosity lvl, std::string_view s) override {
+ oss << s << std::endl;
+ }
+
+ void logEI(const ErrorInfo & ei) override {
+ showErrorInfo(oss, ei, loggerSettings.showTrace.get());
+ }
+ };
+
+ class CaptureLogging {
+ Logger * oldLogger;
+ std::unique_ptr<CaptureLogger> tempLogger;
+ public:
+ CaptureLogging() : tempLogger(std::make_unique<CaptureLogger>()) {
+ oldLogger = logger;
+ logger = tempLogger.get();
+ }
+
+ ~CaptureLogging() {
+ logger = oldLogger;
+ }
+
+ std::string get() const {
+ return tempLogger->get();
+ }
+ };
+
+
+ // Testing eval of PrimOp's
+ class PrimOpTest : public LibExprTest {};
+
+
+ TEST_F(PrimOpTest, throw) {
+ ASSERT_THROW(eval("throw \"foo\""), ThrownError);
+ }
+
+ TEST_F(PrimOpTest, abort) {
+ ASSERT_THROW(eval("abort \"abort\""), Abort);
+ }
+
+ TEST_F(PrimOpTest, ceil) {
+ auto v = eval("builtins.ceil 1.9");
+ ASSERT_THAT(v, IsIntEq(2));
+ }
+
+ TEST_F(PrimOpTest, floor) {
+ auto v = eval("builtins.floor 1.9");
+ ASSERT_THAT(v, IsIntEq(1));
+ }
+
+ TEST_F(PrimOpTest, tryEvalFailure) {
+ auto v = eval("builtins.tryEval (throw \"\")");
+ ASSERT_THAT(v, IsAttrsOfSize(2));
+ auto s = createSymbol("success");
+ auto p = v.attrs->get(s);
+ ASSERT_NE(p, nullptr);
+ ASSERT_THAT(*p->value, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, tryEvalSuccess) {
+ auto v = eval("builtins.tryEval 123");
+ ASSERT_THAT(v, IsAttrs());
+ auto s = createSymbol("success");
+ auto p = v.attrs->get(s);
+ ASSERT_NE(p, nullptr);
+ ASSERT_THAT(*p->value, IsTrue());
+ s = createSymbol("value");
+ p = v.attrs->get(s);
+ ASSERT_NE(p, nullptr);
+ ASSERT_THAT(*p->value, IsIntEq(123));
+ }
+
+ TEST_F(PrimOpTest, getEnv) {
+ setenv("_NIX_UNIT_TEST_ENV_VALUE", "test value", 1);
+ auto v = eval("builtins.getEnv \"_NIX_UNIT_TEST_ENV_VALUE\"");
+ ASSERT_THAT(v, IsStringEq("test value"));
+ }
+
+ TEST_F(PrimOpTest, seq) {
+ ASSERT_THROW(eval("let x = throw \"test\"; in builtins.seq x { }"), ThrownError);
+ }
+
+ TEST_F(PrimOpTest, seqNotDeep) {
+ auto v = eval("let x = { z = throw \"test\"; }; in builtins.seq x { }");
+ ASSERT_THAT(v, IsAttrs());
+ }
+
+ TEST_F(PrimOpTest, deepSeq) {
+ ASSERT_THROW(eval("let x = { z = throw \"test\"; }; in builtins.deepSeq x { }"), ThrownError);
+ }
+
+ TEST_F(PrimOpTest, trace) {
+ CaptureLogging l;
+ auto v = eval("builtins.trace \"test string 123\" 123");
+ ASSERT_THAT(v, IsIntEq(123));
+ auto text = l.get();
+ ASSERT_NE(text.find("test string 123"), std::string::npos);
+ }
+
+ TEST_F(PrimOpTest, placeholder) {
+ auto v = eval("builtins.placeholder \"out\"");
+ ASSERT_THAT(v, IsStringEq("/1rz4g4znpzjwh1xymhjpm42vipw92pr73vdgl6xs1hycac8kf2n9"));
+ }
+
+ TEST_F(PrimOpTest, baseNameOf) {
+ auto v = eval("builtins.baseNameOf /some/path");
+ ASSERT_THAT(v, IsStringEq("path"));
+ }
+
+ TEST_F(PrimOpTest, dirOf) {
+ auto v = eval("builtins.dirOf /some/path");
+ ASSERT_THAT(v, IsPathEq("/some"));
+ }
+
+ TEST_F(PrimOpTest, attrValues) {
+ auto v = eval("builtins.attrValues { x = \"foo\"; a = 1; }");
+ ASSERT_THAT(v, IsListOfSize(2));
+ ASSERT_THAT(*v.listElems()[0], IsIntEq(1));
+ ASSERT_THAT(*v.listElems()[1], IsStringEq("foo"));
+ }
+
+ TEST_F(PrimOpTest, getAttr) {
+ auto v = eval("builtins.getAttr \"x\" { x = \"foo\"; }");
+ ASSERT_THAT(v, IsStringEq("foo"));
+ }
+
+ TEST_F(PrimOpTest, getAttrNotFound) {
+ // FIXME: TypeError is really bad here, also the error wording is worse
+ // than on Nix <=2.3
+ ASSERT_THROW(eval("builtins.getAttr \"y\" { }"), TypeError);
+ }
+
+ TEST_F(PrimOpTest, unsafeGetAttrPos) {
+ // The `y` attribute is at position
+ const char* expr = "builtins.unsafeGetAttrPos \"y\" { y = \"x\"; }";
+ auto v = eval(expr);
+ ASSERT_THAT(v, IsNull());
+ }
+
+ TEST_F(PrimOpTest, hasAttr) {
+ auto v = eval("builtins.hasAttr \"x\" { x = 1; }");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, hasAttrNotFound) {
+ auto v = eval("builtins.hasAttr \"x\" { }");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, isAttrs) {
+ auto v = eval("builtins.isAttrs {}");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, isAttrsFalse) {
+ auto v = eval("builtins.isAttrs null");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, removeAttrs) {
+ auto v = eval("builtins.removeAttrs { x = 1; } [\"x\"]");
+ ASSERT_THAT(v, IsAttrsOfSize(0));
+ }
+
+ TEST_F(PrimOpTest, removeAttrsRetains) {
+ auto v = eval("builtins.removeAttrs { x = 1; y = 2; } [\"x\"]");
+ ASSERT_THAT(v, IsAttrsOfSize(1));
+ ASSERT_NE(v.attrs->find(createSymbol("y")), nullptr);
+ }
+
+ TEST_F(PrimOpTest, listToAttrsEmptyList) {
+ auto v = eval("builtins.listToAttrs []");
+ ASSERT_THAT(v, IsAttrsOfSize(0));
+ ASSERT_EQ(v.type(), nAttrs);
+ ASSERT_EQ(v.attrs->size(), 0);
+ }
+
+ TEST_F(PrimOpTest, listToAttrsNotFieldName) {
+ ASSERT_THROW(eval("builtins.listToAttrs [{}]"), Error);
+ }
+
+ TEST_F(PrimOpTest, listToAttrs) {
+ auto v = eval("builtins.listToAttrs [ { name = \"key\"; value = 123; } ]");
+ ASSERT_THAT(v, IsAttrsOfSize(1));
+ auto key = v.attrs->find(createSymbol("key"));
+ ASSERT_NE(key, nullptr);
+ ASSERT_THAT(*key->value, IsIntEq(123));
+ }
+
+ TEST_F(PrimOpTest, intersectAttrs) {
+ auto v = eval("builtins.intersectAttrs { a = 1; b = 2; } { b = 3; c = 4; }");
+ ASSERT_THAT(v, IsAttrsOfSize(1));
+ auto b = v.attrs->find(createSymbol("b"));
+ ASSERT_NE(b, nullptr);
+ ASSERT_THAT(*b->value, IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, catAttrs) {
+ auto v = eval("builtins.catAttrs \"a\" [{a = 1;} {b = 0;} {a = 2;}]");
+ ASSERT_THAT(v, IsListOfSize(2));
+ ASSERT_THAT(*v.listElems()[0], IsIntEq(1));
+ ASSERT_THAT(*v.listElems()[1], IsIntEq(2));
+ }
+
+ TEST_F(PrimOpTest, functionArgs) {
+ auto v = eval("builtins.functionArgs ({ x, y ? 123}: 1)");
+ ASSERT_THAT(v, IsAttrsOfSize(2));
+
+ auto x = v.attrs->find(createSymbol("x"));
+ ASSERT_NE(x, nullptr);
+ ASSERT_THAT(*x->value, IsFalse());
+
+ auto y = v.attrs->find(createSymbol("y"));
+ ASSERT_NE(y, nullptr);
+ ASSERT_THAT(*y->value, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, mapAttrs) {
+ auto v = eval("builtins.mapAttrs (name: value: value * 10) { a = 1; b = 2; }");
+ ASSERT_THAT(v, IsAttrsOfSize(2));
+
+ auto a = v.attrs->find(createSymbol("a"));
+ ASSERT_NE(a, nullptr);
+ ASSERT_THAT(*a->value, IsThunk());
+ state.forceValue(*a->value, noPos);
+ ASSERT_THAT(*a->value, IsIntEq(10));
+
+ auto b = v.attrs->find(createSymbol("b"));
+ ASSERT_NE(b, nullptr);
+ ASSERT_THAT(*b->value, IsThunk());
+ state.forceValue(*b->value, noPos);
+ ASSERT_THAT(*b->value, IsIntEq(20));
+ }
+
+ TEST_F(PrimOpTest, isList) {
+ auto v = eval("builtins.isList []");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, isListFalse) {
+ auto v = eval("builtins.isList null");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, elemtAt) {
+ auto v = eval("builtins.elemAt [0 1 2 3] 3");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, elemtAtOutOfBounds) {
+ ASSERT_THROW(eval("builtins.elemAt [0 1 2 3] 5"), Error);
+ }
+
+ TEST_F(PrimOpTest, head) {
+ auto v = eval("builtins.head [ 3 2 1 0 ]");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, headEmpty) {
+ ASSERT_THROW(eval("builtins.head [ ]"), Error);
+ }
+
+ TEST_F(PrimOpTest, headWrongType) {
+ ASSERT_THROW(eval("builtins.head { }"), Error);
+ }
+
+ TEST_F(PrimOpTest, tail) {
+ auto v = eval("builtins.tail [ 3 2 1 0 ]");
+ ASSERT_THAT(v, IsListOfSize(3));
+ for (const auto [n, elem] : enumerate(v.listItems()))
+ ASSERT_THAT(*elem, IsIntEq(2 - static_cast<int>(n)));
+ }
+
+ TEST_F(PrimOpTest, tailEmpty) {
+ ASSERT_THROW(eval("builtins.tail []"), Error);
+ }
+
+ TEST_F(PrimOpTest, map) {
+ auto v = eval("map (x: \"foo\" + x) [ \"bar\" \"bla\" \"abc\" ]");
+ ASSERT_THAT(v, IsListOfSize(3));
+ auto elem = v.listElems()[0];
+ ASSERT_THAT(*elem, IsThunk());
+ state.forceValue(*elem, noPos);
+ ASSERT_THAT(*elem, IsStringEq("foobar"));
+
+ elem = v.listElems()[1];
+ ASSERT_THAT(*elem, IsThunk());
+ state.forceValue(*elem, noPos);
+ ASSERT_THAT(*elem, IsStringEq("foobla"));
+
+ elem = v.listElems()[2];
+ ASSERT_THAT(*elem, IsThunk());
+ state.forceValue(*elem, noPos);
+ ASSERT_THAT(*elem, IsStringEq("fooabc"));
+ }
+
+ TEST_F(PrimOpTest, filter) {
+ auto v = eval("builtins.filter (x: x == 2) [ 3 2 3 2 3 2 ]");
+ ASSERT_THAT(v, IsListOfSize(3));
+ for (const auto elem : v.listItems())
+ ASSERT_THAT(*elem, IsIntEq(2));
+ }
+
+ TEST_F(PrimOpTest, elemTrue) {
+ auto v = eval("builtins.elem 3 [ 1 2 3 4 5 ]");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, elemFalse) {
+ auto v = eval("builtins.elem 6 [ 1 2 3 4 5 ]");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, concatLists) {
+ auto v = eval("builtins.concatLists [[1 2] [3 4]]");
+ ASSERT_THAT(v, IsListOfSize(4));
+ for (const auto [i, elem] : enumerate(v.listItems()))
+ ASSERT_THAT(*elem, IsIntEq(static_cast<int>(i)+1));
+ }
+
+ TEST_F(PrimOpTest, length) {
+ auto v = eval("builtins.length [ 1 2 3 ]");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, foldStrict) {
+ auto v = eval("builtins.foldl' (a: b: a + b) 0 [1 2 3]");
+ ASSERT_THAT(v, IsIntEq(6));
+ }
+
+ TEST_F(PrimOpTest, anyTrue) {
+ auto v = eval("builtins.any (x: x == 2) [ 1 2 3 ]");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, anyFalse) {
+ auto v = eval("builtins.any (x: x == 5) [ 1 2 3 ]");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, allTrue) {
+ auto v = eval("builtins.all (x: x > 0) [ 1 2 3 ]");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, allFalse) {
+ auto v = eval("builtins.all (x: x <= 0) [ 1 2 3 ]");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, genList) {
+ auto v = eval("builtins.genList (x: x + 1) 3");
+ ASSERT_EQ(v.type(), nList);
+ ASSERT_EQ(v.listSize(), 3);
+ for (const auto [i, elem] : enumerate(v.listItems())) {
+ ASSERT_THAT(*elem, IsThunk());
+ state.forceValue(*elem, noPos);
+ ASSERT_THAT(*elem, IsIntEq(static_cast<int>(i)+1));
+ }
+ }
+
+ TEST_F(PrimOpTest, sortLessThan) {
+ auto v = eval("builtins.sort builtins.lessThan [ 483 249 526 147 42 77 ]");
+ ASSERT_EQ(v.type(), nList);
+ ASSERT_EQ(v.listSize(), 6);
+
+ const std::vector<int> numbers = { 42, 77, 147, 249, 483, 526 };
+ for (const auto [n, elem] : enumerate(v.listItems()))
+ ASSERT_THAT(*elem, IsIntEq(numbers[n]));
+ }
+
+ TEST_F(PrimOpTest, partition) {
+ auto v = eval("builtins.partition (x: x > 10) [1 23 9 3 42]");
+ ASSERT_THAT(v, IsAttrsOfSize(2));
+
+ auto right = v.attrs->get(createSymbol("right"));
+ ASSERT_NE(right, nullptr);
+ ASSERT_THAT(*right->value, IsListOfSize(2));
+ ASSERT_THAT(*right->value->listElems()[0], IsIntEq(23));
+ ASSERT_THAT(*right->value->listElems()[1], IsIntEq(42));
+
+ auto wrong = v.attrs->get(createSymbol("wrong"));
+ ASSERT_NE(wrong, nullptr);
+ ASSERT_EQ(wrong->value->type(), nList);
+ ASSERT_EQ(wrong->value->listSize(), 3);
+ ASSERT_THAT(*wrong->value, IsListOfSize(3));
+ ASSERT_THAT(*wrong->value->listElems()[0], IsIntEq(1));
+ ASSERT_THAT(*wrong->value->listElems()[1], IsIntEq(9));
+ ASSERT_THAT(*wrong->value->listElems()[2], IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, concatMap) {
+ auto v = eval("builtins.concatMap (x: x ++ [0]) [ [1 2] [3 4] ]");
+ ASSERT_EQ(v.type(), nList);
+ ASSERT_EQ(v.listSize(), 6);
+
+ const std::vector<int> numbers = { 1, 2, 0, 3, 4, 0 };
+ for (const auto [n, elem] : enumerate(v.listItems()))
+ ASSERT_THAT(*elem, IsIntEq(numbers[n]));
+ }
+
+ TEST_F(PrimOpTest, addInt) {
+ auto v = eval("builtins.add 3 5");
+ ASSERT_THAT(v, IsIntEq(8));
+ }
+
+ TEST_F(PrimOpTest, addFloat) {
+ auto v = eval("builtins.add 3.0 5.0");
+ ASSERT_THAT(v, IsFloatEq(8.0));
+ }
+
+ TEST_F(PrimOpTest, addFloatToInt) {
+ auto v = eval("builtins.add 3.0 5");
+ ASSERT_THAT(v, IsFloatEq(8.0));
+
+ v = eval("builtins.add 3 5.0");
+ ASSERT_THAT(v, IsFloatEq(8.0));
+ }
+
+ TEST_F(PrimOpTest, subInt) {
+ auto v = eval("builtins.sub 5 2");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, subFloat) {
+ auto v = eval("builtins.sub 5.0 2.0");
+ ASSERT_THAT(v, IsFloatEq(3.0));
+ }
+
+ TEST_F(PrimOpTest, subFloatFromInt) {
+ auto v = eval("builtins.sub 5.0 2");
+ ASSERT_THAT(v, IsFloatEq(3.0));
+
+ v = eval("builtins.sub 4 2.0");
+ ASSERT_THAT(v, IsFloatEq(2.0));
+ }
+
+ TEST_F(PrimOpTest, mulInt) {
+ auto v = eval("builtins.mul 3 5");
+ ASSERT_THAT(v, IsIntEq(15));
+ }
+
+ TEST_F(PrimOpTest, mulFloat) {
+ auto v = eval("builtins.mul 3.0 5.0");
+ ASSERT_THAT(v, IsFloatEq(15.0));
+ }
+
+ TEST_F(PrimOpTest, mulFloatMixed) {
+ auto v = eval("builtins.mul 3 5.0");
+ ASSERT_THAT(v, IsFloatEq(15.0));
+
+ v = eval("builtins.mul 2.0 5");
+ ASSERT_THAT(v, IsFloatEq(10.0));
+ }
+
+ TEST_F(PrimOpTest, divInt) {
+ auto v = eval("builtins.div 5 (-1)");
+ ASSERT_THAT(v, IsIntEq(-5));
+ }
+
+ TEST_F(PrimOpTest, divIntZero) {
+ ASSERT_THROW(eval("builtins.div 5 0"), EvalError);
+ }
+
+ TEST_F(PrimOpTest, divFloat) {
+ auto v = eval("builtins.div 5.0 (-1)");
+ ASSERT_THAT(v, IsFloatEq(-5.0));
+ }
+
+ TEST_F(PrimOpTest, divFloatZero) {
+ ASSERT_THROW(eval("builtins.div 5.0 0.0"), EvalError);
+ }
+
+ TEST_F(PrimOpTest, bitOr) {
+ auto v = eval("builtins.bitOr 1 2");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+
+ TEST_F(PrimOpTest, bitXor) {
+ auto v = eval("builtins.bitXor 3 2");
+ ASSERT_THAT(v, IsIntEq(1));
+ }
+
+ TEST_F(PrimOpTest, lessThanFalse) {
+ auto v = eval("builtins.lessThan 3 1");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(PrimOpTest, lessThanTrue) {
+ auto v = eval("builtins.lessThan 1 3");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(PrimOpTest, toStringAttrsThrows) {
+ ASSERT_THROW(eval("builtins.toString {}"), EvalError);
+ }
+
+ TEST_F(PrimOpTest, toStringLambdaThrows) {
+ ASSERT_THROW(eval("builtins.toString (x: x)"), EvalError);
+ }
+
+ class ToStringPrimOpTest :
+ public PrimOpTest,
+ public testing::WithParamInterface<std::tuple<std::string, std::string_view>>
+ {};
+
+ TEST_P(ToStringPrimOpTest, toString) {
+ const auto [input, output] = GetParam();
+ auto v = eval(input);
+ ASSERT_THAT(v, IsStringEq(output));
+ }
+
+#define CASE(input, output) (std::make_tuple(std::string_view("builtins.toString " input), std::string_view(output)))
+ INSTANTIATE_TEST_SUITE_P(
+ toString,
+ ToStringPrimOpTest,
+ testing::Values(
+ CASE(R"("foo")", "foo"),
+ CASE(R"(1)", "1"),
+ CASE(R"([1 2 3])", "1 2 3"),
+ CASE(R"(.123)", "0.123000"),
+ CASE(R"(true)", "1"),
+ CASE(R"(false)", ""),
+ CASE(R"(null)", ""),
+ CASE(R"({ v = "bar"; __toString = self: self.v; })", "bar"),
+ CASE(R"({ v = "bar"; __toString = self: self.v; outPath = "foo"; })", "bar"),
+ CASE(R"({ outPath = "foo"; })", "foo"),
+ CASE(R"(./test)", "/test")
+ )
+ );
+#undef CASE
+
+ TEST_F(PrimOpTest, substring){
+ auto v = eval("builtins.substring 0 3 \"nixos\"");
+ ASSERT_THAT(v, IsStringEq("nix"));
+ }
+
+ TEST_F(PrimOpTest, substringSmallerString){
+ auto v = eval("builtins.substring 0 3 \"n\"");
+ ASSERT_THAT(v, IsStringEq("n"));
+ }
+
+ TEST_F(PrimOpTest, substringEmptyString){
+ auto v = eval("builtins.substring 1 3 \"\"");
+ ASSERT_THAT(v, IsStringEq(""));
+ }
+
+ TEST_F(PrimOpTest, stringLength) {
+ auto v = eval("builtins.stringLength \"123\"");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+ TEST_F(PrimOpTest, hashStringMd5) {
+ auto v = eval("builtins.hashString \"md5\" \"asdf\"");
+ ASSERT_THAT(v, IsStringEq("912ec803b2ce49e4a541068d495ab570"));
+ }
+
+ TEST_F(PrimOpTest, hashStringSha1) {
+ auto v = eval("builtins.hashString \"sha1\" \"asdf\"");
+ ASSERT_THAT(v, IsStringEq("3da541559918a808c2402bba5012f6c60b27661c"));
+ }
+
+ TEST_F(PrimOpTest, hashStringSha256) {
+ auto v = eval("builtins.hashString \"sha256\" \"asdf\"");
+ ASSERT_THAT(v, IsStringEq("f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"));
+ }
+
+ TEST_F(PrimOpTest, hashStringSha512) {
+ auto v = eval("builtins.hashString \"sha512\" \"asdf\"");
+ ASSERT_THAT(v, IsStringEq("401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429080fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1"));
+ }
+
+ TEST_F(PrimOpTest, hashStringInvalidHashType) {
+ ASSERT_THROW(eval("builtins.hashString \"foobar\" \"asdf\""), Error);
+ }
+
+ TEST_F(PrimOpTest, nixPath) {
+ auto v = eval("builtins.nixPath");
+ ASSERT_EQ(v.type(), nList);
+ // We can't test much more as currently the EvalSettings are a global
+ // that we can't easily swap / replace
+ }
+
+ TEST_F(PrimOpTest, langVersion) {
+ auto v = eval("builtins.langVersion");
+ ASSERT_EQ(v.type(), nInt);
+ }
+
+ TEST_F(PrimOpTest, storeDir) {
+ auto v = eval("builtins.storeDir");
+ ASSERT_THAT(v, IsStringEq(settings.nixStore));
+ }
+
+ TEST_F(PrimOpTest, nixVersion) {
+ auto v = eval("builtins.nixVersion");
+ ASSERT_THAT(v, IsStringEq(nixVersion));
+ }
+
+ TEST_F(PrimOpTest, currentSystem) {
+ auto v = eval("builtins.currentSystem");
+ ASSERT_THAT(v, IsStringEq(settings.thisSystem.get()));
+ }
+
+ TEST_F(PrimOpTest, derivation) {
+ auto v = eval("derivation");
+ ASSERT_EQ(v.type(), nFunction);
+ ASSERT_TRUE(v.isLambda());
+ ASSERT_NE(v.lambda.fun, nullptr);
+ ASSERT_TRUE(v.lambda.fun->hasFormals());
+ }
+
+ TEST_F(PrimOpTest, currentTime) {
+ auto v = eval("builtins.currentTime");
+ ASSERT_EQ(v.type(), nInt);
+ ASSERT_TRUE(v.integer > 0);
+ }
+
+ TEST_F(PrimOpTest, splitVersion) {
+ auto v = eval("builtins.splitVersion \"1.2.3git\"");
+ ASSERT_THAT(v, IsListOfSize(4));
+
+ const std::vector<std::string_view> strings = { "1", "2", "3", "git" };
+ for (const auto [n, p] : enumerate(v.listItems()))
+ ASSERT_THAT(*p, IsStringEq(strings[n]));
+ }
+
+ class CompareVersionsPrimOpTest :
+ public PrimOpTest,
+ public testing::WithParamInterface<std::tuple<std::string, const int>>
+ {};
+
+ TEST_P(CompareVersionsPrimOpTest, compareVersions) {
+ auto [expression, expectation] = GetParam();
+ auto v = eval(expression);
+ ASSERT_THAT(v, IsIntEq(expectation));
+ }
+
+#define CASE(a, b, expected) (std::make_tuple("builtins.compareVersions \"" #a "\" \"" #b "\"", expected))
+ INSTANTIATE_TEST_SUITE_P(
+ compareVersions,
+ CompareVersionsPrimOpTest,
+ testing::Values(
+ // The first two are weird cases. Intuition tells they should
+ // be the same but they aren't.
+ CASE(1.0, 1.0.0, -1),
+ CASE(1.0.0, 1.0, 1),
+ // the following are from the nix-env manual:
+ CASE(1.0, 2.3, -1),
+ CASE(2.1, 2.3, -1),
+ CASE(2.3, 2.3, 0),
+ CASE(2.5, 2.3, 1),
+ CASE(3.1, 2.3, 1),
+ CASE(2.3.1, 2.3, 1),
+ CASE(2.3.1, 2.3a, 1),
+ CASE(2.3pre1, 2.3, -1),
+ CASE(2.3pre3, 2.3pre12, -1),
+ CASE(2.3a, 2.3c, -1),
+ CASE(2.3pre1, 2.3c, -1),
+ CASE(2.3pre1, 2.3q, -1)
+ )
+ );
+#undef CASE
+
+
+ class ParseDrvNamePrimOpTest :
+ public PrimOpTest,
+ public testing::WithParamInterface<std::tuple<std::string, std::string_view, std::string_view>>
+ {};
+
+ TEST_P(ParseDrvNamePrimOpTest, parseDrvName) {
+ auto [input, expectedName, expectedVersion] = GetParam();
+ const auto expr = fmt("builtins.parseDrvName \"%1%\"", input);
+ auto v = eval(expr);
+ ASSERT_THAT(v, IsAttrsOfSize(2));
+
+ auto name = v.attrs->find(createSymbol("name"));
+ ASSERT_TRUE(name);
+ ASSERT_THAT(*name->value, IsStringEq(expectedName));
+
+ auto version = v.attrs->find(createSymbol("version"));
+ ASSERT_TRUE(version);
+ ASSERT_THAT(*version->value, IsStringEq(expectedVersion));
+ }
+
+ INSTANTIATE_TEST_SUITE_P(
+ parseDrvName,
+ ParseDrvNamePrimOpTest,
+ testing::Values(
+ std::make_tuple("nix-0.12pre12876", "nix", "0.12pre12876"),
+ std::make_tuple("a-b-c-1234pre5+git", "a-b-c", "1234pre5+git")
+ )
+ );
+
+ TEST_F(PrimOpTest, replaceStrings) {
+ // FIXME: add a test that verifies the string context is as expected
+ auto v = eval("builtins.replaceStrings [\"oo\" \"a\"] [\"a\" \"i\"] \"foobar\"");
+ ASSERT_EQ(v.type(), nString);
+ ASSERT_EQ(v.string.s, std::string_view("fabir"));
+ }
+
+ TEST_F(PrimOpTest, concatStringsSep) {
+ // FIXME: add a test that verifies the string context is as expected
+ auto v = eval("builtins.concatStringsSep \"%\" [\"foo\" \"bar\" \"baz\"]");
+ ASSERT_EQ(v.type(), nString);
+ ASSERT_EQ(std::string_view(v.string.s), "foo%bar%baz");
+ }
+
+ TEST_F(PrimOpTest, split1) {
+ // v = [ "" [ "a" ] "c" ]
+ auto v = eval("builtins.split \"(a)b\" \"abc\"");
+ ASSERT_THAT(v, IsListOfSize(3));
+
+ ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
+
+ ASSERT_THAT(*v.listElems()[1], IsListOfSize(1));
+ ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
+
+ ASSERT_THAT(*v.listElems()[2], IsStringEq("c"));
+ }
+
+ TEST_F(PrimOpTest, split2) {
+ // v is expected to be a list [ "" [ "a" ] "b" [ "c"] "" ]
+ auto v = eval("builtins.split \"([ac])\" \"abc\"");
+ ASSERT_THAT(v, IsListOfSize(5));
+
+ ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
+
+ ASSERT_THAT(*v.listElems()[1], IsListOfSize(1));
+ ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
+
+ ASSERT_THAT(*v.listElems()[2], IsStringEq("b"));
+
+ ASSERT_THAT(*v.listElems()[3], IsListOfSize(1));
+ ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsStringEq("c"));
+
+ ASSERT_THAT(*v.listElems()[4], IsStringEq(""));
+ }
+
+ TEST_F(PrimOpTest, split3) {
+ auto v = eval("builtins.split \"(a)|(c)\" \"abc\"");
+ ASSERT_THAT(v, IsListOfSize(5));
+
+ // First list element
+ ASSERT_THAT(*v.listElems()[0], IsStringEq(""));
+
+ // 2nd list element is a list [ "" null ]
+ ASSERT_THAT(*v.listElems()[1], IsListOfSize(2));
+ ASSERT_THAT(*v.listElems()[1]->listElems()[0], IsStringEq("a"));
+ ASSERT_THAT(*v.listElems()[1]->listElems()[1], IsNull());
+
+ // 3rd element
+ ASSERT_THAT(*v.listElems()[2], IsStringEq("b"));
+
+ // 4th element is a list: [ null "c" ]
+ ASSERT_THAT(*v.listElems()[3], IsListOfSize(2));
+ ASSERT_THAT(*v.listElems()[3]->listElems()[0], IsNull());
+ ASSERT_THAT(*v.listElems()[3]->listElems()[1], IsStringEq("c"));
+
+ // 5th element is the empty string
+ ASSERT_THAT(*v.listElems()[4], IsStringEq(""));
+ }
+
+ TEST_F(PrimOpTest, split4) {
+ auto v = eval("builtins.split \"([[:upper:]]+)\" \" FOO \"");
+ ASSERT_THAT(v, IsListOfSize(3));
+ auto first = v.listElems()[0];
+ auto second = v.listElems()[1];
+ auto third = v.listElems()[2];
+
+ ASSERT_THAT(*first, IsStringEq(" "));
+
+ ASSERT_THAT(*second, IsListOfSize(1));
+ ASSERT_THAT(*second->listElems()[0], IsStringEq("FOO"));
+
+ ASSERT_THAT(*third, IsStringEq(" "));
+ }
+
+ TEST_F(PrimOpTest, match1) {
+ auto v = eval("builtins.match \"ab\" \"abc\"");
+ ASSERT_THAT(v, IsNull());
+ }
+
+ TEST_F(PrimOpTest, match2) {
+ auto v = eval("builtins.match \"abc\" \"abc\"");
+ ASSERT_THAT(v, IsListOfSize(0));
+ }
+
+ TEST_F(PrimOpTest, match3) {
+ auto v = eval("builtins.match \"a(b)(c)\" \"abc\"");
+ ASSERT_THAT(v, IsListOfSize(2));
+ ASSERT_THAT(*v.listElems()[0], IsStringEq("b"));
+ ASSERT_THAT(*v.listElems()[1], IsStringEq("c"));
+ }
+
+ TEST_F(PrimOpTest, match4) {
+ auto v = eval("builtins.match \"[[:space:]]+([[:upper:]]+)[[:space:]]+\" \" FOO \"");
+ ASSERT_THAT(v, IsListOfSize(1));
+ ASSERT_THAT(*v.listElems()[0], IsStringEq("FOO"));
+ }
+
+ TEST_F(PrimOpTest, attrNames) {
+ auto v = eval("builtins.attrNames { x = 1; y = 2; z = 3; a = 2; }");
+ ASSERT_THAT(v, IsListOfSize(4));
+
+ // ensure that the list is sorted
+ const std::vector<std::string_view> expected { "a", "x", "y", "z" };
+ for (const auto [n, elem] : enumerate(v.listItems()))
+ ASSERT_THAT(*elem, IsStringEq(expected[n]));
+ }
+
+ TEST_F(PrimOpTest, genericClosure_not_strict) {
+ // Operator should not be used when startSet is empty
+ auto v = eval("builtins.genericClosure { startSet = []; }");
+ ASSERT_THAT(v, IsListOfSize(0));
+ }
+} /* namespace nix */
diff --git a/tests/unit/libexpr/search-path.cc b/tests/unit/libexpr/search-path.cc
new file mode 100644
index 000000000..dbe7ab95f
--- /dev/null
+++ b/tests/unit/libexpr/search-path.cc
@@ -0,0 +1,90 @@
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "search-path.hh"
+
+namespace nix {
+
+TEST(SearchPathElem, parse_justPath) {
+ ASSERT_EQ(
+ SearchPath::Elem::parse("foo"),
+ (SearchPath::Elem {
+ .prefix = SearchPath::Prefix { .s = "" },
+ .path = SearchPath::Path { .s = "foo" },
+ }));
+}
+
+TEST(SearchPathElem, parse_emptyPrefix) {
+ ASSERT_EQ(
+ SearchPath::Elem::parse("=foo"),
+ (SearchPath::Elem {
+ .prefix = SearchPath::Prefix { .s = "" },
+ .path = SearchPath::Path { .s = "foo" },
+ }));
+}
+
+TEST(SearchPathElem, parse_oneEq) {
+ ASSERT_EQ(
+ SearchPath::Elem::parse("foo=bar"),
+ (SearchPath::Elem {
+ .prefix = SearchPath::Prefix { .s = "foo" },
+ .path = SearchPath::Path { .s = "bar" },
+ }));
+}
+
+TEST(SearchPathElem, parse_twoEqs) {
+ ASSERT_EQ(
+ SearchPath::Elem::parse("foo=bar=baz"),
+ (SearchPath::Elem {
+ .prefix = SearchPath::Prefix { .s = "foo" },
+ .path = SearchPath::Path { .s = "bar=baz" },
+ }));
+}
+
+
+TEST(SearchPathElem, suffixIfPotentialMatch_justPath) {
+ SearchPath::Prefix prefix { .s = "" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("any/thing"), std::optional { "any/thing" });
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_misleadingPrefix1) {
+ SearchPath::Prefix prefix { .s = "foo" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX"), std::nullopt);
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_misleadingPrefix2) {
+ SearchPath::Prefix prefix { .s = "foo" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("fooX/bar"), std::nullopt);
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_partialPrefix) {
+ SearchPath::Prefix prefix { .s = "fooX" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::nullopt);
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_exactPrefix) {
+ SearchPath::Prefix prefix { .s = "foo" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("foo"), std::optional { "" });
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_multiKey) {
+ SearchPath::Prefix prefix { .s = "foo/bar" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "baz" });
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_trailingSlash) {
+ SearchPath::Prefix prefix { .s = "foo" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/"), std::optional { "" });
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_trailingDoubleSlash) {
+ SearchPath::Prefix prefix { .s = "foo" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("foo//"), std::optional { "/" });
+}
+
+TEST(SearchPathElem, suffixIfPotentialMatch_trailingPath) {
+ SearchPath::Prefix prefix { .s = "foo" };
+ ASSERT_EQ(prefix.suffixIfPotentialMatch("foo/bar/baz"), std::optional { "bar/baz" });
+}
+
+}
diff --git a/tests/unit/libexpr/trivial.cc b/tests/unit/libexpr/trivial.cc
new file mode 100644
index 000000000..171727ac7
--- /dev/null
+++ b/tests/unit/libexpr/trivial.cc
@@ -0,0 +1,196 @@
+#include "tests/libexpr.hh"
+
+namespace nix {
+ // Testing of trivial expressions
+ class TrivialExpressionTest : public LibExprTest {};
+
+ TEST_F(TrivialExpressionTest, true) {
+ auto v = eval("true");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(TrivialExpressionTest, false) {
+ auto v = eval("false");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(TrivialExpressionTest, null) {
+ auto v = eval("null");
+ ASSERT_THAT(v, IsNull());
+ }
+
+ TEST_F(TrivialExpressionTest, 1) {
+ auto v = eval("1");
+ ASSERT_THAT(v, IsIntEq(1));
+ }
+
+ TEST_F(TrivialExpressionTest, 1plus1) {
+ auto v = eval("1+1");
+ ASSERT_THAT(v, IsIntEq(2));
+ }
+
+ TEST_F(TrivialExpressionTest, minus1) {
+ auto v = eval("-1");
+ ASSERT_THAT(v, IsIntEq(-1));
+ }
+
+ TEST_F(TrivialExpressionTest, 1minus1) {
+ auto v = eval("1-1");
+ ASSERT_THAT(v, IsIntEq(0));
+ }
+
+ TEST_F(TrivialExpressionTest, lambdaAdd) {
+ auto v = eval("let add = a: b: a + b; in add 1 2");
+ ASSERT_THAT(v, IsIntEq(3));
+ }
+
+ TEST_F(TrivialExpressionTest, list) {
+ auto v = eval("[]");
+ ASSERT_THAT(v, IsListOfSize(0));
+ }
+
+ TEST_F(TrivialExpressionTest, attrs) {
+ auto v = eval("{}");
+ ASSERT_THAT(v, IsAttrsOfSize(0));
+ }
+
+ TEST_F(TrivialExpressionTest, float) {
+ auto v = eval("1.234");
+ ASSERT_THAT(v, IsFloatEq(1.234));
+ }
+
+ TEST_F(TrivialExpressionTest, updateAttrs) {
+ auto v = eval("{ a = 1; } // { b = 2; a = 3; }");
+ ASSERT_THAT(v, IsAttrsOfSize(2));
+ auto a = v.attrs->find(createSymbol("a"));
+ ASSERT_NE(a, nullptr);
+ ASSERT_THAT(*a->value, IsIntEq(3));
+
+ auto b = v.attrs->find(createSymbol("b"));
+ ASSERT_NE(b, nullptr);
+ ASSERT_THAT(*b->value, IsIntEq(2));
+ }
+
+ TEST_F(TrivialExpressionTest, hasAttrOpFalse) {
+ auto v = eval("{} ? a");
+ ASSERT_THAT(v, IsFalse());
+ }
+
+ TEST_F(TrivialExpressionTest, hasAttrOpTrue) {
+ auto v = eval("{ a = 123; } ? a");
+ ASSERT_THAT(v, IsTrue());
+ }
+
+ TEST_F(TrivialExpressionTest, withFound) {
+ auto v = eval("with { a = 23; }; a");
+ ASSERT_THAT(v, IsIntEq(23));
+ }
+
+ TEST_F(TrivialExpressionTest, withNotFound) {
+ ASSERT_THROW(eval("with {}; a"), Error);
+ }
+
+ TEST_F(TrivialExpressionTest, withOverride) {
+ auto v = eval("with { a = 23; }; with { a = 42; }; a");
+ ASSERT_THAT(v, IsIntEq(42));
+ }
+
+ TEST_F(TrivialExpressionTest, letOverWith) {
+ auto v = eval("let a = 23; in with { a = 1; }; a");
+ ASSERT_THAT(v, IsIntEq(23));
+ }
+
+ TEST_F(TrivialExpressionTest, multipleLet) {
+ auto v = eval("let a = 23; in let a = 42; in a");
+ ASSERT_THAT(v, IsIntEq(42));
+ }
+
+ TEST_F(TrivialExpressionTest, defaultFunctionArgs) {
+ auto v = eval("({ a ? 123 }: a) {}");
+ ASSERT_THAT(v, IsIntEq(123));
+ }
+
+ TEST_F(TrivialExpressionTest, defaultFunctionArgsOverride) {
+ auto v = eval("({ a ? 123 }: a) { a = 5; }");
+ ASSERT_THAT(v, IsIntEq(5));
+ }
+
+ TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureBack) {
+ auto v = eval("({ a ? 123 }@args: args) {}");
+ ASSERT_THAT(v, IsAttrsOfSize(0));
+ }
+
+ TEST_F(TrivialExpressionTest, defaultFunctionArgsCaptureFront) {
+ auto v = eval("(args@{ a ? 123 }: args) {}");
+ ASSERT_THAT(v, IsAttrsOfSize(0));
+ }
+
+ TEST_F(TrivialExpressionTest, assertThrows) {
+ ASSERT_THROW(eval("let x = arg: assert arg == 1; 123; in x 2"), Error);
+ }
+
+ TEST_F(TrivialExpressionTest, assertPassed) {
+ auto v = eval("let x = arg: assert arg == 1; 123; in x 1");
+ ASSERT_THAT(v, IsIntEq(123));
+ }
+
+ class AttrSetMergeTrvialExpressionTest :
+ public TrivialExpressionTest,
+ public testing::WithParamInterface<const char*>
+ {};
+
+ TEST_P(AttrSetMergeTrvialExpressionTest, attrsetMergeLazy) {
+ // Usually Nix rejects duplicate keys in an attrset but it does allow
+ // so if it is an attribute set that contains disjoint sets of keys.
+ // The below is equivalent to `{a.b = 1; a.c = 2; }`.
+ // The attribute set `a` will be a Thunk at first as the attribuets
+ // have to be merged (or otherwise computed) and that is done in a lazy
+ // manner.
+
+ auto expr = GetParam();
+ auto v = eval(expr);
+ ASSERT_THAT(v, IsAttrsOfSize(1));
+
+ auto a = v.attrs->find(createSymbol("a"));
+ ASSERT_NE(a, nullptr);
+
+ ASSERT_THAT(*a->value, IsThunk());
+ state.forceValue(*a->value, noPos);
+
+ ASSERT_THAT(*a->value, IsAttrsOfSize(2));
+
+ auto b = a->value->attrs->find(createSymbol("b"));
+ ASSERT_NE(b, nullptr);
+ ASSERT_THAT(*b->value, IsIntEq(1));
+
+ auto c = a->value->attrs->find(createSymbol("c"));
+ ASSERT_NE(c, nullptr);
+ ASSERT_THAT(*c->value, IsIntEq(2));
+ }
+
+ INSTANTIATE_TEST_SUITE_P(
+ attrsetMergeLazy,
+ AttrSetMergeTrvialExpressionTest,
+ testing::Values(
+ "{ a.b = 1; a.c = 2; }",
+ "{ a = { b = 1; }; a = { c = 2; }; }"
+ )
+ );
+
+ TEST_F(TrivialExpressionTest, functor) {
+ auto v = eval("{ __functor = self: arg: self.v + arg; v = 10; } 5");
+ ASSERT_THAT(v, IsIntEq(15));
+ }
+
+ TEST_F(TrivialExpressionTest, bindOr) {
+ auto v = eval("{ or = 1; }");
+ ASSERT_THAT(v, IsAttrsOfSize(1));
+ auto b = v.attrs->find(createSymbol("or"));
+ ASSERT_NE(b, nullptr);
+ ASSERT_THAT(*b->value, IsIntEq(1));
+ }
+
+ TEST_F(TrivialExpressionTest, orCantBeUsed) {
+ ASSERT_THROW(eval("let or = 1; in or"), Error);
+ }
+} /* namespace nix */
diff --git a/tests/unit/libexpr/value/context.cc b/tests/unit/libexpr/value/context.cc
new file mode 100644
index 000000000..761286dbd
--- /dev/null
+++ b/tests/unit/libexpr/value/context.cc
@@ -0,0 +1,132 @@
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include "tests/path.hh"
+#include "tests/libexpr.hh"
+#include "tests/value/context.hh"
+
+namespace nix {
+
+// Test a few cases of invalid string context elements.
+
+TEST(NixStringContextElemTest, empty_invalid) {
+ EXPECT_THROW(
+ NixStringContextElem::parse(""),
+ BadNixStringContextElem);
+}
+
+TEST(NixStringContextElemTest, single_bang_invalid) {
+ EXPECT_THROW(
+ NixStringContextElem::parse("!"),
+ BadNixStringContextElem);
+}
+
+TEST(NixStringContextElemTest, double_bang_invalid) {
+ EXPECT_THROW(
+ NixStringContextElem::parse("!!/"),
+ BadStorePath);
+}
+
+TEST(NixStringContextElemTest, eq_slash_invalid) {
+ EXPECT_THROW(
+ NixStringContextElem::parse("=/"),
+ BadStorePath);
+}
+
+TEST(NixStringContextElemTest, slash_invalid) {
+ EXPECT_THROW(
+ NixStringContextElem::parse("/"),
+ BadStorePath);
+}
+
+/**
+ * Round trip (string <-> data structure) test for
+ * `NixStringContextElem::Opaque`.
+ */
+TEST(NixStringContextElemTest, opaque) {
+ std::string_view opaque = "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
+ auto elem = NixStringContextElem::parse(opaque);
+ auto * p = std::get_if<NixStringContextElem::Opaque>(&elem.raw);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->path, StorePath { opaque });
+ ASSERT_EQ(elem.to_string(), opaque);
+}
+
+/**
+ * Round trip (string <-> data structure) test for
+ * `NixStringContextElem::DrvDeep`.
+ */
+TEST(NixStringContextElemTest, drvDeep) {
+ std::string_view drvDeep = "=g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
+ auto elem = NixStringContextElem::parse(drvDeep);
+ auto * p = std::get_if<NixStringContextElem::DrvDeep>(&elem.raw);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->drvPath, StorePath { drvDeep.substr(1) });
+ ASSERT_EQ(elem.to_string(), drvDeep);
+}
+
+/**
+ * Round trip (string <-> data structure) test for a simpler
+ * `NixStringContextElem::Built`.
+ */
+TEST(NixStringContextElemTest, built_opaque) {
+ std::string_view built = "!foo!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
+ auto elem = NixStringContextElem::parse(built);
+ auto * p = std::get_if<NixStringContextElem::Built>(&elem.raw);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->output, "foo");
+ ASSERT_EQ(*p->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
+ .path = StorePath { built.substr(5) },
+ }));
+ ASSERT_EQ(elem.to_string(), built);
+}
+
+/**
+ * Round trip (string <-> data structure) test for a more complex,
+ * inductive `NixStringContextElem::Built`.
+ */
+TEST(NixStringContextElemTest, built_built) {
+ /**
+ * We set these in tests rather than the regular globals so we don't have
+ * to worry about race conditions if the tests run concurrently.
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+ mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
+
+ std::string_view built = "!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv";
+ auto elem = NixStringContextElem::parse(built, mockXpSettings);
+ auto * p = std::get_if<NixStringContextElem::Built>(&elem.raw);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->output, "foo");
+ auto * drvPath = std::get_if<SingleDerivedPath::Built>(&*p->drvPath);
+ ASSERT_TRUE(drvPath);
+ ASSERT_EQ(drvPath->output, "bar");
+ ASSERT_EQ(*drvPath->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
+ .path = StorePath { built.substr(9) },
+ }));
+ ASSERT_EQ(elem.to_string(), built);
+}
+
+/**
+ * Without the right experimental features enabled, we cannot parse a
+ * complex inductive string context element.
+ */
+TEST(NixStringContextElemTest, built_built_xp) {
+ ASSERT_THROW(
+ NixStringContextElem::parse("!foo!bar!g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv"), MissingExperimentalFeature);
+}
+
+#ifndef COVERAGE
+
+RC_GTEST_PROP(
+ NixStringContextElemTest,
+ prop_round_rip,
+ (const NixStringContextElem & o))
+{
+ RC_ASSERT(o == NixStringContextElem::parse(o.to_string()));
+}
+
+#endif
+
+}
diff --git a/tests/unit/libexpr/value/print.cc b/tests/unit/libexpr/value/print.cc
new file mode 100644
index 000000000..5e96e12ec
--- /dev/null
+++ b/tests/unit/libexpr/value/print.cc
@@ -0,0 +1,236 @@
+#include "tests/libexpr.hh"
+
+#include "value.hh"
+
+namespace nix {
+
+using namespace testing;
+
+struct ValuePrintingTests : LibExprTest
+{
+ template<class... A>
+ void test(Value v, std::string_view expected, A... args)
+ {
+ std::stringstream out;
+ v.print(state.symbols, out, args...);
+ ASSERT_EQ(out.str(), expected);
+ }
+};
+
+TEST_F(ValuePrintingTests, tInt)
+{
+ Value vInt;
+ vInt.mkInt(10);
+ test(vInt, "10");
+}
+
+TEST_F(ValuePrintingTests, tBool)
+{
+ Value vBool;
+ vBool.mkBool(true);
+ test(vBool, "true");
+}
+
+TEST_F(ValuePrintingTests, tString)
+{
+ Value vString;
+ vString.mkString("some-string");
+ test(vString, "\"some-string\"");
+}
+
+TEST_F(ValuePrintingTests, tPath)
+{
+ Value vPath;
+ vPath.mkString("/foo");
+ test(vPath, "\"/foo\"");
+}
+
+TEST_F(ValuePrintingTests, tNull)
+{
+ Value vNull;
+ vNull.mkNull();
+ test(vNull, "null");
+}
+
+TEST_F(ValuePrintingTests, tAttrs)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ BindingsBuilder builder(state, state.allocBindings(10));
+ builder.insert(state.symbols.create("one"), &vOne);
+ builder.insert(state.symbols.create("two"), &vTwo);
+
+ Value vAttrs;
+ vAttrs.mkAttrs(builder.finish());
+
+ test(vAttrs, "{ one = 1; two = 2; }");
+}
+
+TEST_F(ValuePrintingTests, tList)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ Value vList;
+ state.mkList(vList, 5);
+ vList.bigList.elems[0] = &vOne;
+ vList.bigList.elems[1] = &vTwo;
+ vList.bigList.size = 3;
+
+ test(vList, "[ 1 2 (nullptr) ]");
+}
+
+TEST_F(ValuePrintingTests, vThunk)
+{
+ Value vThunk;
+ vThunk.mkThunk(nullptr, nullptr);
+
+ test(vThunk, "<CODE>");
+}
+
+TEST_F(ValuePrintingTests, vApp)
+{
+ Value vApp;
+ vApp.mkApp(nullptr, nullptr);
+
+ test(vApp, "<CODE>");
+}
+
+TEST_F(ValuePrintingTests, vLambda)
+{
+ Value vLambda;
+ vLambda.mkLambda(nullptr, nullptr);
+
+ test(vLambda, "<LAMBDA>");
+}
+
+TEST_F(ValuePrintingTests, vPrimOp)
+{
+ Value vPrimOp;
+ vPrimOp.mkPrimOp(nullptr);
+
+ test(vPrimOp, "<PRIMOP>");
+}
+
+TEST_F(ValuePrintingTests, vPrimOpApp)
+{
+ Value vPrimOpApp;
+ vPrimOpApp.mkPrimOpApp(nullptr, nullptr);
+
+ test(vPrimOpApp, "<PRIMOP-APP>");
+}
+
+TEST_F(ValuePrintingTests, vExternal)
+{
+ struct MyExternal : ExternalValueBase
+ {
+ public:
+ std::string showType() const override
+ {
+ return "";
+ }
+ std::string typeOf() const override
+ {
+ return "";
+ }
+ virtual std::ostream & print(std::ostream & str) const override
+ {
+ str << "testing-external!";
+ return str;
+ }
+ } myExternal;
+ Value vExternal;
+ vExternal.mkExternal(&myExternal);
+
+ test(vExternal, "testing-external!");
+}
+
+TEST_F(ValuePrintingTests, vFloat)
+{
+ Value vFloat;
+ vFloat.mkFloat(2.0);
+
+ test(vFloat, "2");
+}
+
+TEST_F(ValuePrintingTests, vBlackhole)
+{
+ Value vBlackhole;
+ vBlackhole.mkBlackhole();
+ test(vBlackhole, "«potential infinite recursion»");
+}
+
+TEST_F(ValuePrintingTests, depthAttrs)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ BindingsBuilder builder(state, state.allocBindings(10));
+ builder.insert(state.symbols.create("one"), &vOne);
+ builder.insert(state.symbols.create("two"), &vTwo);
+
+ Value vAttrs;
+ vAttrs.mkAttrs(builder.finish());
+
+ BindingsBuilder builder2(state, state.allocBindings(10));
+ builder2.insert(state.symbols.create("one"), &vOne);
+ builder2.insert(state.symbols.create("two"), &vTwo);
+ builder2.insert(state.symbols.create("nested"), &vAttrs);
+
+ Value vNested;
+ vNested.mkAttrs(builder2.finish());
+
+ test(vNested, "{ nested = «too deep»; one = «too deep»; two = «too deep»; }", false, 1);
+ test(vNested, "{ nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; }", false, 2);
+ test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 3);
+ test(vNested, "{ nested = { one = 1; two = 2; }; one = 1; two = 2; }", false, 4);
+}
+
+TEST_F(ValuePrintingTests, depthList)
+{
+ Value vOne;
+ vOne.mkInt(1);
+
+ Value vTwo;
+ vTwo.mkInt(2);
+
+ BindingsBuilder builder(state, state.allocBindings(10));
+ builder.insert(state.symbols.create("one"), &vOne);
+ builder.insert(state.symbols.create("two"), &vTwo);
+
+ Value vAttrs;
+ vAttrs.mkAttrs(builder.finish());
+
+ BindingsBuilder builder2(state, state.allocBindings(10));
+ builder2.insert(state.symbols.create("one"), &vOne);
+ builder2.insert(state.symbols.create("two"), &vTwo);
+ builder2.insert(state.symbols.create("nested"), &vAttrs);
+
+ Value vNested;
+ vNested.mkAttrs(builder2.finish());
+
+ Value vList;
+ state.mkList(vList, 5);
+ vList.bigList.elems[0] = &vOne;
+ vList.bigList.elems[1] = &vTwo;
+ vList.bigList.elems[2] = &vNested;
+ vList.bigList.size = 3;
+
+ test(vList, "[ «too deep» «too deep» «too deep» ]", false, 1);
+ test(vList, "[ 1 2 { nested = «too deep»; one = «too deep»; two = «too deep»; } ]", false, 2);
+ test(vList, "[ 1 2 { nested = { one = «too deep»; two = «too deep»; }; one = 1; two = 2; } ]", false, 3);
+ test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 4);
+ test(vList, "[ 1 2 { nested = { one = 1; two = 2; }; one = 1; two = 2; } ]", false, 5);
+}
+
+} // namespace nix
diff --git a/tests/unit/libstore-support/local.mk b/tests/unit/libstore-support/local.mk
new file mode 100644
index 000000000..59c3e09ca
--- /dev/null
+++ b/tests/unit/libstore-support/local.mk
@@ -0,0 +1,17 @@
+libraries += libstore-test-support
+
+libstore-test-support_NAME = libnixstore-test-support
+
+libstore-test-support_DIR := $(d)
+
+libstore-test-support_INSTALL_DIR :=
+
+libstore-test-support_SOURCES := $(wildcard $(d)/tests/*.cc)
+
+libstore-test-support_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES)
+
+libstore-test-support_LIBS = \
+ libutil-test-support \
+ libstore libutil
+
+libstore-test-support_LDFLAGS := -pthread -lrapidcheck
diff --git a/tests/unit/libstore-support/tests/derived-path.cc b/tests/unit/libstore-support/tests/derived-path.cc
new file mode 100644
index 000000000..091706dba
--- /dev/null
+++ b/tests/unit/libstore-support/tests/derived-path.cc
@@ -0,0 +1,57 @@
+#include <regex>
+
+#include <rapidcheck.h>
+
+#include "tests/derived-path.hh"
+
+namespace rc {
+using namespace nix;
+
+Gen<DerivedPath::Opaque> Arbitrary<DerivedPath::Opaque>::arbitrary()
+{
+ return gen::just(DerivedPath::Opaque {
+ .path = *gen::arbitrary<StorePath>(),
+ });
+}
+
+Gen<SingleDerivedPath::Built> Arbitrary<SingleDerivedPath::Built>::arbitrary()
+{
+ return gen::just(SingleDerivedPath::Built {
+ .drvPath = make_ref<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath>()),
+ .output = (*gen::arbitrary<StorePathName>()).name,
+ });
+}
+
+Gen<DerivedPath::Built> Arbitrary<DerivedPath::Built>::arbitrary()
+{
+ return gen::just(DerivedPath::Built {
+ .drvPath = make_ref<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath>()),
+ .outputs = *gen::arbitrary<OutputsSpec>(),
+ });
+}
+
+Gen<SingleDerivedPath> Arbitrary<SingleDerivedPath>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, std::variant_size_v<SingleDerivedPath::Raw>)) {
+ case 0:
+ return gen::just<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath::Opaque>());
+ case 1:
+ return gen::just<SingleDerivedPath>(*gen::arbitrary<SingleDerivedPath::Built>());
+ default:
+ assert(false);
+ }
+}
+
+Gen<DerivedPath> Arbitrary<DerivedPath>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, std::variant_size_v<DerivedPath::Raw>)) {
+ case 0:
+ return gen::just<DerivedPath>(*gen::arbitrary<DerivedPath::Opaque>());
+ case 1:
+ return gen::just<DerivedPath>(*gen::arbitrary<DerivedPath::Built>());
+ default:
+ assert(false);
+ }
+}
+
+}
diff --git a/tests/unit/libstore-support/tests/derived-path.hh b/tests/unit/libstore-support/tests/derived-path.hh
new file mode 100644
index 000000000..98d61f228
--- /dev/null
+++ b/tests/unit/libstore-support/tests/derived-path.hh
@@ -0,0 +1,39 @@
+#pragma once
+///@file
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <derived-path.hh>
+
+#include "tests/path.hh"
+#include "tests/outputs-spec.hh"
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<SingleDerivedPath::Opaque> {
+ static Gen<SingleDerivedPath::Opaque> arbitrary();
+};
+
+template<>
+struct Arbitrary<SingleDerivedPath::Built> {
+ static Gen<SingleDerivedPath::Built> arbitrary();
+};
+
+template<>
+struct Arbitrary<SingleDerivedPath> {
+ static Gen<SingleDerivedPath> arbitrary();
+};
+
+template<>
+struct Arbitrary<DerivedPath::Built> {
+ static Gen<DerivedPath::Built> arbitrary();
+};
+
+template<>
+struct Arbitrary<DerivedPath> {
+ static Gen<DerivedPath> arbitrary();
+};
+
+}
diff --git a/tests/unit/libstore-support/tests/libstore.hh b/tests/unit/libstore-support/tests/libstore.hh
new file mode 100644
index 000000000..ef93457b5
--- /dev/null
+++ b/tests/unit/libstore-support/tests/libstore.hh
@@ -0,0 +1,26 @@
+#pragma once
+///@file
+
+#include <gtest/gtest.h>
+#include <gmock/gmock.h>
+
+#include "store-api.hh"
+
+namespace nix {
+
+class LibStoreTest : public ::testing::Test {
+ public:
+ static void SetUpTestSuite() {
+ initLibStore();
+ }
+
+ protected:
+ LibStoreTest()
+ : store(openStore("dummy://"))
+ { }
+
+ ref<Store> store;
+};
+
+
+} /* namespace nix */
diff --git a/tests/unit/libstore-support/tests/outputs-spec.cc b/tests/unit/libstore-support/tests/outputs-spec.cc
new file mode 100644
index 000000000..e9d602203
--- /dev/null
+++ b/tests/unit/libstore-support/tests/outputs-spec.cc
@@ -0,0 +1,24 @@
+#include "tests/outputs-spec.hh"
+
+#include <rapidcheck.h>
+
+namespace rc {
+using namespace nix;
+
+Gen<OutputsSpec> Arbitrary<OutputsSpec>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, std::variant_size_v<OutputsSpec::Raw>)) {
+ case 0:
+ return gen::just((OutputsSpec) OutputsSpec::All { });
+ case 1:
+ return gen::just((OutputsSpec) OutputsSpec::Names {
+ *gen::nonEmpty(gen::container<StringSet>(gen::map(
+ gen::arbitrary<StorePathName>(),
+ [](StorePathName n) { return n.name; }))),
+ });
+ default:
+ assert(false);
+ }
+}
+
+}
diff --git a/tests/unit/libstore-support/tests/outputs-spec.hh b/tests/unit/libstore-support/tests/outputs-spec.hh
new file mode 100644
index 000000000..f5bf9042d
--- /dev/null
+++ b/tests/unit/libstore-support/tests/outputs-spec.hh
@@ -0,0 +1,18 @@
+#pragma once
+///@file
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <outputs-spec.hh>
+
+#include "tests/path.hh"
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<OutputsSpec> {
+ static Gen<OutputsSpec> arbitrary();
+};
+
+}
diff --git a/tests/unit/libstore-support/tests/path.cc b/tests/unit/libstore-support/tests/path.cc
new file mode 100644
index 000000000..e5f169e94
--- /dev/null
+++ b/tests/unit/libstore-support/tests/path.cc
@@ -0,0 +1,82 @@
+#include <regex>
+
+#include <rapidcheck.h>
+
+#include "path-regex.hh"
+#include "store-api.hh"
+
+#include "tests/hash.hh"
+#include "tests/path.hh"
+
+namespace nix {
+
+void showValue(const StorePath & p, std::ostream & os)
+{
+ os << p.to_string();
+}
+
+}
+
+namespace rc {
+using namespace nix;
+
+Gen<StorePathName> Arbitrary<StorePathName>::arbitrary()
+{
+ auto len = *gen::inRange<size_t>(
+ 1,
+ StorePath::MaxPathLen - StorePath::HashLen);
+
+ std::string pre;
+ pre.reserve(len);
+
+ for (size_t c = 0; c < len; ++c) {
+ switch (auto i = *gen::inRange<uint8_t>(0, 10 + 2 * 26 + 6)) {
+ case 0 ... 9:
+ pre += '0' + i;
+ case 10 ... 35:
+ pre += 'A' + (i - 10);
+ break;
+ case 36 ... 61:
+ pre += 'a' + (i - 36);
+ break;
+ case 62:
+ pre += '+';
+ break;
+ case 63:
+ pre += '-';
+ break;
+ case 64:
+ // names aren't permitted to start with a period,
+ // so just fall through to the next case here
+ if (c != 0) {
+ pre += '.';
+ break;
+ }
+ case 65:
+ pre += '_';
+ break;
+ case 66:
+ pre += '?';
+ break;
+ case 67:
+ pre += '=';
+ break;
+ default:
+ assert(false);
+ }
+ }
+
+ return gen::just(StorePathName {
+ .name = std::move(pre),
+ });
+}
+
+Gen<StorePath> Arbitrary<StorePath>::arbitrary()
+{
+ return gen::just(StorePath {
+ *gen::arbitrary<Hash>(),
+ (*gen::arbitrary<StorePathName>()).name,
+ });
+}
+
+} // namespace rc
diff --git a/tests/unit/libstore-support/tests/path.hh b/tests/unit/libstore-support/tests/path.hh
new file mode 100644
index 000000000..4751b3373
--- /dev/null
+++ b/tests/unit/libstore-support/tests/path.hh
@@ -0,0 +1,32 @@
+#pragma once
+///@file
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <path.hh>
+
+namespace nix {
+
+struct StorePathName {
+ std::string name;
+};
+
+// For rapidcheck
+void showValue(const StorePath & p, std::ostream & os);
+
+}
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<StorePathName> {
+ static Gen<StorePathName> arbitrary();
+};
+
+template<>
+struct Arbitrary<StorePath> {
+ static Gen<StorePath> arbitrary();
+};
+
+}
diff --git a/tests/unit/libstore/derivation.cc b/tests/unit/libstore/derivation.cc
new file mode 100644
index 000000000..c360c9707
--- /dev/null
+++ b/tests/unit/libstore/derivation.cc
@@ -0,0 +1,369 @@
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+
+#include "experimental-features.hh"
+#include "derivations.hh"
+
+#include "tests/libstore.hh"
+
+namespace nix {
+
+class DerivationTest : public LibStoreTest
+{
+public:
+ /**
+ * We set these in tests rather than the regular globals so we don't have
+ * to worry about race conditions if the tests run concurrently.
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+};
+
+class CaDerivationTest : public DerivationTest
+{
+ void SetUp() override
+ {
+ mockXpSettings.set("experimental-features", "ca-derivations");
+ }
+};
+
+class DynDerivationTest : public DerivationTest
+{
+ void SetUp() override
+ {
+ mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
+ }
+};
+
+class ImpureDerivationTest : public DerivationTest
+{
+ void SetUp() override
+ {
+ mockXpSettings.set("experimental-features", "impure-derivations");
+ }
+};
+
+TEST_F(DerivationTest, BadATerm_version) {
+ ASSERT_THROW(
+ parseDerivation(
+ *store,
+ R"(DrvWithVersion("invalid-version",[],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",["cat","dog"])],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
+ "whatever",
+ mockXpSettings),
+ FormatError);
+}
+
+TEST_F(DynDerivationTest, BadATerm_oldVersionDynDeps) {
+ ASSERT_THROW(
+ parseDerivation(
+ *store,
+ R"(Derive([],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",(["cat","dog"],[("cat",["kitten"]),("goose",["gosling"])]))],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
+ "dyn-dep-derivation",
+ mockXpSettings),
+ FormatError);
+}
+
+#define TEST_JSON(FIXTURE, NAME, STR, VAL, DRV_NAME, OUTPUT_NAME) \
+ TEST_F(FIXTURE, DerivationOutput_ ## NAME ## _to_json) { \
+ using nlohmann::literals::operator "" _json; \
+ ASSERT_EQ( \
+ STR ## _json, \
+ (DerivationOutput { VAL }).toJSON( \
+ *store, \
+ DRV_NAME, \
+ OUTPUT_NAME)); \
+ } \
+ \
+ TEST_F(FIXTURE, DerivationOutput_ ## NAME ## _from_json) { \
+ using nlohmann::literals::operator "" _json; \
+ ASSERT_EQ( \
+ DerivationOutput { VAL }, \
+ DerivationOutput::fromJSON( \
+ *store, \
+ DRV_NAME, \
+ OUTPUT_NAME, \
+ STR ## _json, \
+ mockXpSettings)); \
+ }
+
+TEST_JSON(DerivationTest, inputAddressed,
+ R"({
+ "path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
+ })",
+ (DerivationOutput::InputAddressed {
+ .path = store->parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"),
+ }),
+ "drv-name", "output-name")
+
+TEST_JSON(DerivationTest, caFixedFlat,
+ R"({
+ "hashAlgo": "sha256",
+ "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
+ "path": "/nix/store/rhcg9h16sqvlbpsa6dqm57sbr2al6nzg-drv-name-output-name"
+ })",
+ (DerivationOutput::CAFixed {
+ .ca = {
+ .method = FileIngestionMethod::Flat,
+ .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
+ },
+ }),
+ "drv-name", "output-name")
+
+TEST_JSON(DerivationTest, caFixedNAR,
+ R"({
+ "hashAlgo": "r:sha256",
+ "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
+ "path": "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-drv-name-output-name"
+ })",
+ (DerivationOutput::CAFixed {
+ .ca = {
+ .method = FileIngestionMethod::Recursive,
+ .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
+ },
+ }),
+ "drv-name", "output-name")
+
+TEST_JSON(DynDerivationTest, caFixedText,
+ R"({
+ "hashAlgo": "text:sha256",
+ "hash": "894517c9163c896ec31a2adbd33c0681fd5f45b2c0ef08a64c92a03fb97f390f",
+ "path": "/nix/store/6s1zwabh956jvhv4w9xcdb5jiyanyxg1-drv-name-output-name"
+ })",
+ (DerivationOutput::CAFixed {
+ .ca = {
+ .hash = Hash::parseAnyPrefixed("sha256-iUUXyRY8iW7DGirb0zwGgf1fRbLA7wimTJKgP7l/OQ8="),
+ },
+ }),
+ "drv-name", "output-name")
+
+TEST_JSON(CaDerivationTest, caFloating,
+ R"({
+ "hashAlgo": "r:sha256"
+ })",
+ (DerivationOutput::CAFloating {
+ .method = FileIngestionMethod::Recursive,
+ .hashType = htSHA256,
+ }),
+ "drv-name", "output-name")
+
+TEST_JSON(DerivationTest, deferred,
+ R"({ })",
+ DerivationOutput::Deferred { },
+ "drv-name", "output-name")
+
+TEST_JSON(ImpureDerivationTest, impure,
+ R"({
+ "hashAlgo": "r:sha256",
+ "impure": true
+ })",
+ (DerivationOutput::Impure {
+ .method = FileIngestionMethod::Recursive,
+ .hashType = htSHA256,
+ }),
+ "drv-name", "output-name")
+
+#undef TEST_JSON
+
+#define TEST_JSON(FIXTURE, NAME, STR, VAL) \
+ TEST_F(FIXTURE, Derivation_ ## NAME ## _to_json) { \
+ using nlohmann::literals::operator "" _json; \
+ ASSERT_EQ( \
+ STR ## _json, \
+ (VAL).toJSON(*store)); \
+ } \
+ \
+ TEST_F(FIXTURE, Derivation_ ## NAME ## _from_json) { \
+ using nlohmann::literals::operator "" _json; \
+ ASSERT_EQ( \
+ (VAL), \
+ Derivation::fromJSON( \
+ *store, \
+ STR ## _json, \
+ mockXpSettings)); \
+ }
+
+#define TEST_ATERM(FIXTURE, NAME, STR, VAL, DRV_NAME) \
+ TEST_F(FIXTURE, Derivation_ ## NAME ## _to_aterm) { \
+ ASSERT_EQ( \
+ STR, \
+ (VAL).unparse(*store, false)); \
+ } \
+ \
+ TEST_F(FIXTURE, Derivation_ ## NAME ## _from_aterm) { \
+ auto parsed = parseDerivation( \
+ *store, \
+ STR, \
+ DRV_NAME, \
+ mockXpSettings); \
+ ASSERT_EQ( \
+ (VAL).toJSON(*store), \
+ parsed.toJSON(*store)); \
+ ASSERT_EQ( \
+ (VAL), \
+ parsed); \
+ }
+
+Derivation makeSimpleDrv(const Store & store) {
+ Derivation drv;
+ drv.name = "simple-derivation";
+ drv.inputSrcs = {
+ store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
+ };
+ drv.inputDrvs = {
+ .map = {
+ {
+ store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
+ {
+ .value = {
+ "cat",
+ "dog",
+ },
+ },
+ },
+ },
+ };
+ drv.platform = "wasm-sel4";
+ drv.builder = "foo";
+ drv.args = {
+ "bar",
+ "baz",
+ };
+ drv.env = {
+ {
+ "BIG_BAD",
+ "WOLF",
+ },
+ };
+ return drv;
+}
+
+TEST_JSON(DerivationTest, simple,
+ R"({
+ "name": "simple-derivation",
+ "inputSrcs": [
+ "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
+ ],
+ "inputDrvs": {
+ "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
+ "dynamicOutputs": {},
+ "outputs": [
+ "cat",
+ "dog"
+ ]
+ }
+ },
+ "system": "wasm-sel4",
+ "builder": "foo",
+ "args": [
+ "bar",
+ "baz"
+ ],
+ "env": {
+ "BIG_BAD": "WOLF"
+ },
+ "outputs": {}
+ })",
+ makeSimpleDrv(*store))
+
+TEST_ATERM(DerivationTest, simple,
+ R"(Derive([],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",["cat","dog"])],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
+ makeSimpleDrv(*store),
+ "simple-derivation")
+
+Derivation makeDynDepDerivation(const Store & store) {
+ Derivation drv;
+ drv.name = "dyn-dep-derivation";
+ drv.inputSrcs = {
+ store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"),
+ };
+ drv.inputDrvs = {
+ .map = {
+ {
+ store.parseStorePath("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv"),
+ DerivedPathMap<StringSet>::ChildNode {
+ .value = {
+ "cat",
+ "dog",
+ },
+ .childMap = {
+ {
+ "cat",
+ DerivedPathMap<StringSet>::ChildNode {
+ .value = {
+ "kitten",
+ },
+ },
+ },
+ {
+ "goose",
+ DerivedPathMap<StringSet>::ChildNode {
+ .value = {
+ "gosling",
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+ drv.platform = "wasm-sel4";
+ drv.builder = "foo";
+ drv.args = {
+ "bar",
+ "baz",
+ };
+ drv.env = {
+ {
+ "BIG_BAD",
+ "WOLF",
+ },
+ };
+ return drv;
+}
+
+TEST_JSON(DynDerivationTest, dynDerivationDeps,
+ R"({
+ "name": "dyn-dep-derivation",
+ "inputSrcs": [
+ "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"
+ ],
+ "inputDrvs": {
+ "/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv": {
+ "dynamicOutputs": {
+ "cat": {
+ "dynamicOutputs": {},
+ "outputs": ["kitten"]
+ },
+ "goose": {
+ "dynamicOutputs": {},
+ "outputs": ["gosling"]
+ }
+ },
+ "outputs": [
+ "cat",
+ "dog"
+ ]
+ }
+ },
+ "system": "wasm-sel4",
+ "builder": "foo",
+ "args": [
+ "bar",
+ "baz"
+ ],
+ "env": {
+ "BIG_BAD": "WOLF"
+ },
+ "outputs": {}
+ })",
+ makeDynDepDerivation(*store))
+
+TEST_ATERM(DynDerivationTest, dynDerivationDeps,
+ R"(DrvWithVersion("xp-dyn-drv",[],[("/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep2.drv",(["cat","dog"],[("cat",["kitten"]),("goose",["gosling"])]))],["/nix/store/c015dhfh5l0lp6wxyvdn7bmwhbbr6hr9-dep1"],"wasm-sel4","foo",["bar","baz"],[("BIG_BAD","WOLF")]))",
+ makeDynDepDerivation(*store),
+ "dyn-dep-derivation")
+
+#undef TEST_JSON
+#undef TEST_ATERM
+
+}
diff --git a/tests/unit/libstore/derived-path.cc b/tests/unit/libstore/derived-path.cc
new file mode 100644
index 000000000..c62d79a78
--- /dev/null
+++ b/tests/unit/libstore/derived-path.cc
@@ -0,0 +1,100 @@
+#include <regex>
+
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include "tests/derived-path.hh"
+#include "tests/libstore.hh"
+
+namespace nix {
+
+class DerivedPathTest : public LibStoreTest
+{
+};
+
+/**
+ * Round trip (string <-> data structure) test for
+ * `DerivedPath::Opaque`.
+ */
+TEST_F(DerivedPathTest, opaque) {
+ std::string_view opaque = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x";
+ auto elem = DerivedPath::parse(*store, opaque);
+ auto * p = std::get_if<DerivedPath::Opaque>(&elem);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->path, store->parseStorePath(opaque));
+ ASSERT_EQ(elem.to_string(*store), opaque);
+}
+
+/**
+ * Round trip (string <-> data structure) test for a simpler
+ * `DerivedPath::Built`.
+ */
+TEST_F(DerivedPathTest, built_opaque) {
+ std::string_view built = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv^bar,foo";
+ auto elem = DerivedPath::parse(*store, built);
+ auto * p = std::get_if<DerivedPath::Built>(&elem);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->outputs, ((OutputsSpec) OutputsSpec::Names { "foo", "bar" }));
+ ASSERT_EQ(*p->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
+ .path = store->parseStorePath(built.substr(0, 49)),
+ }));
+ ASSERT_EQ(elem.to_string(*store), built);
+}
+
+/**
+ * Round trip (string <-> data structure) test for a more complex,
+ * inductive `DerivedPath::Built`.
+ */
+TEST_F(DerivedPathTest, built_built) {
+ /**
+ * We set these in tests rather than the regular globals so we don't have
+ * to worry about race conditions if the tests run concurrently.
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+ mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
+
+ std::string_view built = "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv^foo^bar,baz";
+ auto elem = DerivedPath::parse(*store, built, mockXpSettings);
+ auto * p = std::get_if<DerivedPath::Built>(&elem);
+ ASSERT_TRUE(p);
+ ASSERT_EQ(p->outputs, ((OutputsSpec) OutputsSpec::Names { "bar", "baz" }));
+ auto * drvPath = std::get_if<SingleDerivedPath::Built>(&*p->drvPath);
+ ASSERT_TRUE(drvPath);
+ ASSERT_EQ(drvPath->output, "foo");
+ ASSERT_EQ(*drvPath->drvPath, ((SingleDerivedPath) SingleDerivedPath::Opaque {
+ .path = store->parseStorePath(built.substr(0, 49)),
+ }));
+ ASSERT_EQ(elem.to_string(*store), built);
+}
+
+/**
+ * Without the right experimental features enabled, we cannot parse a
+ * complex inductive derived path.
+ */
+TEST_F(DerivedPathTest, built_built_xp) {
+ ASSERT_THROW(
+ DerivedPath::parse(*store, "/nix/store/g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-x.drv^foo^bar,baz"),
+ MissingExperimentalFeature);
+}
+
+#ifndef COVERAGE
+
+RC_GTEST_FIXTURE_PROP(
+ DerivedPathTest,
+ prop_legacy_round_rip,
+ (const DerivedPath & o))
+{
+ RC_ASSERT(o == DerivedPath::parseLegacy(*store, o.to_string_legacy(*store)));
+}
+
+RC_GTEST_FIXTURE_PROP(
+ DerivedPathTest,
+ prop_round_rip,
+ (const DerivedPath & o))
+{
+ RC_ASSERT(o == DerivedPath::parse(*store, o.to_string(*store)));
+}
+
+#endif
+
+}
diff --git a/tests/unit/libstore/downstream-placeholder.cc b/tests/unit/libstore/downstream-placeholder.cc
new file mode 100644
index 000000000..fd29530ac
--- /dev/null
+++ b/tests/unit/libstore/downstream-placeholder.cc
@@ -0,0 +1,41 @@
+#include <gtest/gtest.h>
+
+#include "downstream-placeholder.hh"
+
+namespace nix {
+
+TEST(DownstreamPlaceholder, unknownCaOutput) {
+ /**
+ * We set these in tests rather than the regular globals so we don't have
+ * to worry about race conditions if the tests run concurrently.
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+ mockXpSettings.set("experimental-features", "ca-derivations");
+
+ ASSERT_EQ(
+ DownstreamPlaceholder::unknownCaOutput(
+ StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv" },
+ "out",
+ mockXpSettings).render(),
+ "/0c6rn30q4frawknapgwq386zq358m8r6msvywcvc89n6m5p2dgbz");
+}
+
+TEST(DownstreamPlaceholder, unknownDerivation) {
+ /**
+ * Same reason as above
+ */
+ ExperimentalFeatureSettings mockXpSettings;
+ mockXpSettings.set("experimental-features", "dynamic-derivations ca-derivations");
+
+ ASSERT_EQ(
+ DownstreamPlaceholder::unknownDerivation(
+ DownstreamPlaceholder::unknownCaOutput(
+ StorePath { "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q-foo.drv.drv" },
+ "out",
+ mockXpSettings),
+ "out",
+ mockXpSettings).render(),
+ "/0gn6agqxjyyalf0dpihgyf49xq5hqxgw100f0wydnj6yqrhqsb3w");
+}
+
+}
diff --git a/tests/unit/libstore/local.mk b/tests/unit/libstore/local.mk
new file mode 100644
index 000000000..fb7385ec1
--- /dev/null
+++ b/tests/unit/libstore/local.mk
@@ -0,0 +1,27 @@
+check: libstore-tests_RUN
+
+programs += libstore-tests
+
+libstore-tests_NAME = libnixstore-tests
+
+libstore-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data
+
+libstore-tests_DIR := $(d)
+
+libstore-tests_INSTALL_DIR :=
+
+libstore-tests_SOURCES := $(wildcard $(d)/*.cc)
+
+libstore-tests_EXTRA_INCLUDES = \
+ -I tests/unit/libstore-support \
+ -I tests/unit/libutil-support \
+ -I src/libstore \
+ -I src/libutil
+
+libstore-tests_CXXFLAGS += $(libstore-tests_EXTRA_INCLUDES)
+
+libstore-tests_LIBS = \
+ libstore-test-support libutil-test-support \
+ libstore libutil
+
+libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS)
diff --git a/tests/unit/libstore/machines.cc b/tests/unit/libstore/machines.cc
new file mode 100644
index 000000000..72b2c3ac6
--- /dev/null
+++ b/tests/unit/libstore/machines.cc
@@ -0,0 +1,169 @@
+#include "machines.hh"
+#include "globals.hh"
+
+#include <gmock/gmock-matchers.h>
+
+using testing::Contains;
+using testing::ElementsAre;
+using testing::EndsWith;
+using testing::Eq;
+using testing::Field;
+using testing::SizeIs;
+
+using nix::absPath;
+using nix::FormatError;
+using nix::getMachines;
+using nix::Machine;
+using nix::Machines;
+using nix::pathExists;
+using nix::Settings;
+using nix::settings;
+
+class Environment : public ::testing::Environment {
+ public:
+ void SetUp() override { settings.thisSystem = "TEST_ARCH-TEST_OS"; }
+};
+
+testing::Environment* const foo_env =
+ testing::AddGlobalTestEnvironment(new Environment);
+
+TEST(machines, getMachinesWithEmptyBuilders) {
+ settings.builders = "";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(0));
+}
+
+TEST(machines, getMachinesUriOnly) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(1));
+ EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl")));
+ EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS")));
+ EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0)));
+ EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1)));
+ EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(1)));
+ EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, SizeIs(0)));
+ EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, SizeIs(0)));
+ EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, SizeIs(0)));
+}
+
+TEST(machines, getMachinesDefaults) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl - - - - - - -";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(1));
+ EXPECT_THAT(actual[0], Field(&Machine::storeUri, Eq("ssh://nix@scratchy.labs.cs.uu.nl")));
+ EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("TEST_ARCH-TEST_OS")));
+ EXPECT_THAT(actual[0], Field(&Machine::sshKey, SizeIs(0)));
+ EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(1)));
+ EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(1)));
+ EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, SizeIs(0)));
+ EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, SizeIs(0)));
+ EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, SizeIs(0)));
+}
+
+TEST(machines, getMachinesWithNewLineSeparator) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl\nnix@itchy.labs.cs.uu.nl";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(2));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl"))));
+}
+
+TEST(machines, getMachinesWithSemicolonSeparator) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl ; nix@itchy.labs.cs.uu.nl";
+ Machines actual = getMachines();
+ EXPECT_THAT(actual, SizeIs(2));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl"))));
+}
+
+TEST(machines, getMachinesWithCorrectCompleteSingleBuilder) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl i686-linux "
+ "/home/nix/.ssh/id_scratchy_auto 8 3 kvm "
+ "benchmark SSH+HOST+PUBLIC+KEY+BASE64+ENCODED==";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(1));
+ EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")));
+ EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux")));
+ EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto")));
+ EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8)));
+ EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(3)));
+ EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("kvm")));
+ EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("benchmark")));
+ EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, Eq("SSH+HOST+PUBLIC+KEY+BASE64+ENCODED==")));
+}
+
+TEST(machines,
+ getMachinesWithCorrectCompleteSingleBuilderWithTabColumnDelimiter) {
+ settings.builders =
+ "nix@scratchy.labs.cs.uu.nl\ti686-linux\t/home/nix/.ssh/"
+ "id_scratchy_auto\t8\t3\tkvm\tbenchmark\tSSH+HOST+PUBLIC+"
+ "KEY+BASE64+ENCODED==";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(1));
+ EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")));
+ EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("i686-linux")));
+ EXPECT_THAT(actual[0], Field(&Machine::sshKey, Eq("/home/nix/.ssh/id_scratchy_auto")));
+ EXPECT_THAT(actual[0], Field(&Machine::maxJobs, Eq(8)));
+ EXPECT_THAT(actual[0], Field(&Machine::speedFactor, Eq(3)));
+ EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("kvm")));
+ EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("benchmark")));
+ EXPECT_THAT(actual[0], Field(&Machine::sshPublicHostKey, Eq("SSH+HOST+PUBLIC+KEY+BASE64+ENCODED==")));
+}
+
+TEST(machines, getMachinesWithMultiOptions) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl Arch1,Arch2 - - - "
+ "SupportedFeature1,SupportedFeature2 "
+ "MandatoryFeature1,MandatoryFeature2";
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(1));
+ EXPECT_THAT(actual[0], Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl")));
+ EXPECT_THAT(actual[0], Field(&Machine::systemTypes, ElementsAre("Arch1", "Arch2")));
+ EXPECT_THAT(actual[0], Field(&Machine::supportedFeatures, ElementsAre("SupportedFeature1", "SupportedFeature2")));
+ EXPECT_THAT(actual[0], Field(&Machine::mandatoryFeatures, ElementsAre("MandatoryFeature1", "MandatoryFeature2")));
+}
+
+TEST(machines, getMachinesWithIncorrectFormat) {
+ settings.builders = "nix@scratchy.labs.cs.uu.nl - - eight";
+ EXPECT_THROW(getMachines(), FormatError);
+ settings.builders = "nix@scratchy.labs.cs.uu.nl - - -1";
+ EXPECT_THROW(getMachines(), FormatError);
+ settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 three";
+ EXPECT_THROW(getMachines(), FormatError);
+ settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 -3";
+ EXPECT_THROW(getMachines(), FormatError);
+ settings.builders = "nix@scratchy.labs.cs.uu.nl - - 8 3 - - BAD_BASE64";
+ EXPECT_THROW(getMachines(), FormatError);
+}
+
+TEST(machines, getMachinesWithCorrectFileReference) {
+ auto path = absPath("tests/unit/libstore/test-data/machines.valid");
+ ASSERT_TRUE(pathExists(path));
+
+ settings.builders = std::string("@") + path;
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(3));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@scratchy.labs.cs.uu.nl"))));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@itchy.labs.cs.uu.nl"))));
+ EXPECT_THAT(actual, Contains(Field(&Machine::storeUri, EndsWith("nix@poochie.labs.cs.uu.nl"))));
+}
+
+TEST(machines, getMachinesWithCorrectFileReferenceToEmptyFile) {
+ auto path = "/dev/null";
+ ASSERT_TRUE(pathExists(path));
+
+ settings.builders = std::string("@") + path;
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(0));
+}
+
+TEST(machines, getMachinesWithIncorrectFileReference) {
+ settings.builders = std::string("@") + absPath("/not/a/file");
+ Machines actual = getMachines();
+ ASSERT_THAT(actual, SizeIs(0));
+}
+
+TEST(machines, getMachinesWithCorrectFileReferenceToIncorrectFile) {
+ settings.builders = std::string("@") + absPath("tests/unit/libstore/test-data/machines.bad_format");
+ EXPECT_THROW(getMachines(), FormatError);
+}
diff --git a/tests/unit/libstore/nar-info-disk-cache.cc b/tests/unit/libstore/nar-info-disk-cache.cc
new file mode 100644
index 000000000..b4bdb8329
--- /dev/null
+++ b/tests/unit/libstore/nar-info-disk-cache.cc
@@ -0,0 +1,123 @@
+#include "nar-info-disk-cache.hh"
+
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+#include "sqlite.hh"
+#include <sqlite3.h>
+
+
+namespace nix {
+
+TEST(NarInfoDiskCacheImpl, create_and_read) {
+ // This is a large single test to avoid some setup overhead.
+
+ int prio = 12345;
+ bool wantMassQuery = true;
+
+ Path tmpDir = createTempDir();
+ AutoDelete delTmpDir(tmpDir);
+ Path dbPath(tmpDir + "/test-narinfo-disk-cache.sqlite");
+
+ int savedId;
+ int barId;
+ SQLite db;
+ SQLiteStmt getIds;
+
+ {
+ auto cache = getTestNarInfoDiskCache(dbPath);
+
+ // Set up "background noise" and check that different caches receive different ids
+ {
+ auto bc1 = cache->createCache("https://bar", "/nix/storedir", wantMassQuery, prio);
+ auto bc2 = cache->createCache("https://xyz", "/nix/storedir", false, 12);
+ ASSERT_NE(bc1, bc2);
+ barId = bc1;
+ }
+
+ // Check that the fields are saved and returned correctly. This does not test
+ // the select statement yet, because of in-memory caching.
+ savedId = cache->createCache("http://foo", "/nix/storedir", wantMassQuery, prio);;
+ {
+ auto r = cache->upToDateCacheExists("http://foo");
+ ASSERT_TRUE(r);
+ ASSERT_EQ(r->priority, prio);
+ ASSERT_EQ(r->wantMassQuery, wantMassQuery);
+ ASSERT_EQ(savedId, r->id);
+ }
+
+ // We're going to pay special attention to the id field because we had a bug
+ // that changed it.
+ db = SQLite(dbPath);
+ getIds.create(db, "select id from BinaryCaches where url = 'http://foo'");
+
+ {
+ auto q(getIds.use());
+ ASSERT_TRUE(q.next());
+ ASSERT_EQ(savedId, q.getInt(0));
+ ASSERT_FALSE(q.next());
+ }
+
+ // Pretend that the caches are older, but keep one up to date, as "background noise"
+ db.exec("update BinaryCaches set timestamp = timestamp - 1 - 7 * 24 * 3600 where url <> 'https://xyz';");
+
+ // This shows that the in-memory cache works
+ {
+ auto r = cache->upToDateCacheExists("http://foo");
+ ASSERT_TRUE(r);
+ ASSERT_EQ(r->priority, prio);
+ ASSERT_EQ(r->wantMassQuery, wantMassQuery);
+ }
+ }
+
+ {
+ // We can't clear the in-memory cache, so we use a new cache object. This is
+ // more realistic anyway.
+ auto cache2 = getTestNarInfoDiskCache(dbPath);
+
+ {
+ auto r = cache2->upToDateCacheExists("http://foo");
+ ASSERT_FALSE(r);
+ }
+
+ // "Update", same data, check that the id number is reused
+ cache2->createCache("http://foo", "/nix/storedir", wantMassQuery, prio);
+
+ {
+ auto r = cache2->upToDateCacheExists("http://foo");
+ ASSERT_TRUE(r);
+ ASSERT_EQ(r->priority, prio);
+ ASSERT_EQ(r->wantMassQuery, wantMassQuery);
+ ASSERT_EQ(r->id, savedId);
+ }
+
+ {
+ auto q(getIds.use());
+ ASSERT_TRUE(q.next());
+ auto currentId = q.getInt(0);
+ ASSERT_FALSE(q.next());
+ ASSERT_EQ(currentId, savedId);
+ }
+
+ // Check that the fields can be modified, and the id remains the same
+ {
+ auto r0 = cache2->upToDateCacheExists("https://bar");
+ ASSERT_FALSE(r0);
+
+ cache2->createCache("https://bar", "/nix/storedir", !wantMassQuery, prio + 10);
+ auto r = cache2->upToDateCacheExists("https://bar");
+ ASSERT_EQ(r->wantMassQuery, !wantMassQuery);
+ ASSERT_EQ(r->priority, prio + 10);
+ ASSERT_EQ(r->id, barId);
+ }
+
+ // // Force update (no use case yet; we only retrieve cache metadata when stale based on timestamp)
+ // {
+ // cache2->createCache("https://bar", "/nix/storedir", wantMassQuery, prio + 20);
+ // auto r = cache2->upToDateCacheExists("https://bar");
+ // ASSERT_EQ(r->wantMassQuery, wantMassQuery);
+ // ASSERT_EQ(r->priority, prio + 20);
+ // }
+ }
+}
+
+}
diff --git a/tests/unit/libstore/outputs-spec.cc b/tests/unit/libstore/outputs-spec.cc
new file mode 100644
index 000000000..456196be1
--- /dev/null
+++ b/tests/unit/libstore/outputs-spec.cc
@@ -0,0 +1,214 @@
+#include "tests/outputs-spec.hh"
+
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+namespace nix {
+
+#ifndef NDEBUG
+TEST(OutputsSpec, no_empty_names) {
+ ASSERT_DEATH(OutputsSpec::Names { std::set<std::string> { } }, "");
+}
+#endif
+
+#define TEST_DONT_PARSE(NAME, STR) \
+ TEST(OutputsSpec, bad_ ## NAME) { \
+ std::optional OutputsSpecOpt = \
+ OutputsSpec::parseOpt(STR); \
+ ASSERT_FALSE(OutputsSpecOpt); \
+ }
+
+TEST_DONT_PARSE(empty, "")
+TEST_DONT_PARSE(garbage, "&*()")
+TEST_DONT_PARSE(double_star, "**")
+TEST_DONT_PARSE(star_first, "*,foo")
+TEST_DONT_PARSE(star_second, "foo,*")
+
+#undef TEST_DONT_PARSE
+
+TEST(OutputsSpec, all) {
+ std::string_view str = "*";
+ OutputsSpec expected = OutputsSpec::All { };
+ ASSERT_EQ(OutputsSpec::parse(str), expected);
+ ASSERT_EQ(expected.to_string(), str);
+}
+
+TEST(OutputsSpec, names_out) {
+ std::string_view str = "out";
+ OutputsSpec expected = OutputsSpec::Names { "out" };
+ ASSERT_EQ(OutputsSpec::parse(str), expected);
+ ASSERT_EQ(expected.to_string(), str);
+}
+
+TEST(OutputsSpec, names_underscore) {
+ std::string_view str = "a_b";
+ OutputsSpec expected = OutputsSpec::Names { "a_b" };
+ ASSERT_EQ(OutputsSpec::parse(str), expected);
+ ASSERT_EQ(expected.to_string(), str);
+}
+
+TEST(OutputsSpec, names_numberic) {
+ std::string_view str = "01";
+ OutputsSpec expected = OutputsSpec::Names { "01" };
+ ASSERT_EQ(OutputsSpec::parse(str), expected);
+ ASSERT_EQ(expected.to_string(), str);
+}
+
+TEST(OutputsSpec, names_out_bin) {
+ OutputsSpec expected = OutputsSpec::Names { "out", "bin" };
+ ASSERT_EQ(OutputsSpec::parse("out,bin"), expected);
+ // N.B. This normalization is OK.
+ ASSERT_EQ(expected.to_string(), "bin,out");
+}
+
+#define TEST_SUBSET(X, THIS, THAT) \
+ X((OutputsSpec { THIS }).isSubsetOf(THAT));
+
+TEST(OutputsSpec, subsets_all_all) {
+ TEST_SUBSET(ASSERT_TRUE, OutputsSpec::All { }, OutputsSpec::All { });
+}
+
+TEST(OutputsSpec, subsets_names_all) {
+ TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names { "a" }, OutputsSpec::All { });
+}
+
+TEST(OutputsSpec, subsets_names_names_eq) {
+ TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names { "a" }, OutputsSpec::Names { "a" });
+}
+
+TEST(OutputsSpec, subsets_names_names_noneq) {
+ TEST_SUBSET(ASSERT_TRUE, OutputsSpec::Names { "a" }, (OutputsSpec::Names { "a", "b" }));
+}
+
+TEST(OutputsSpec, not_subsets_all_names) {
+ TEST_SUBSET(ASSERT_FALSE, OutputsSpec::All { }, OutputsSpec::Names { "a" });
+}
+
+TEST(OutputsSpec, not_subsets_names_names) {
+ TEST_SUBSET(ASSERT_FALSE, (OutputsSpec::Names { "a", "b" }), (OutputsSpec::Names { "a" }));
+}
+
+#undef TEST_SUBSET
+
+#define TEST_UNION(RES, THIS, THAT) \
+ ASSERT_EQ(OutputsSpec { RES }, (OutputsSpec { THIS }).union_(THAT));
+
+TEST(OutputsSpec, union_all_all) {
+ TEST_UNION(OutputsSpec::All { }, OutputsSpec::All { }, OutputsSpec::All { });
+}
+
+TEST(OutputsSpec, union_all_names) {
+ TEST_UNION(OutputsSpec::All { }, OutputsSpec::All { }, OutputsSpec::Names { "a" });
+}
+
+TEST(OutputsSpec, union_names_all) {
+ TEST_UNION(OutputsSpec::All { }, OutputsSpec::Names { "a" }, OutputsSpec::All { });
+}
+
+TEST(OutputsSpec, union_names_names) {
+ TEST_UNION((OutputsSpec::Names { "a", "b" }), OutputsSpec::Names { "a" }, OutputsSpec::Names { "b" });
+}
+
+#undef TEST_UNION
+
+#define TEST_DONT_PARSE(NAME, STR) \
+ TEST(ExtendedOutputsSpec, bad_ ## NAME) { \
+ std::optional extendedOutputsSpecOpt = \
+ ExtendedOutputsSpec::parseOpt(STR); \
+ ASSERT_FALSE(extendedOutputsSpecOpt); \
+ }
+
+TEST_DONT_PARSE(carot_empty, "^")
+TEST_DONT_PARSE(prefix_carot_empty, "foo^")
+TEST_DONT_PARSE(garbage, "^&*()")
+TEST_DONT_PARSE(double_star, "^**")
+TEST_DONT_PARSE(star_first, "^*,foo")
+TEST_DONT_PARSE(star_second, "^foo,*")
+
+#undef TEST_DONT_PARSE
+
+TEST(ExtendedOutputsSpec, defeault) {
+ std::string_view str = "foo";
+ auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
+ ASSERT_EQ(prefix, "foo");
+ ExtendedOutputsSpec expected = ExtendedOutputsSpec::Default { };
+ ASSERT_EQ(extendedOutputsSpec, expected);
+ ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
+}
+
+TEST(ExtendedOutputsSpec, all) {
+ std::string_view str = "foo^*";
+ auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
+ ASSERT_EQ(prefix, "foo");
+ ExtendedOutputsSpec expected = OutputsSpec::All { };
+ ASSERT_EQ(extendedOutputsSpec, expected);
+ ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
+}
+
+TEST(ExtendedOutputsSpec, out) {
+ std::string_view str = "foo^out";
+ auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse(str);
+ ASSERT_EQ(prefix, "foo");
+ ExtendedOutputsSpec expected = OutputsSpec::Names { "out" };
+ ASSERT_EQ(extendedOutputsSpec, expected);
+ ASSERT_EQ(std::string { prefix } + expected.to_string(), str);
+}
+
+TEST(ExtendedOutputsSpec, out_bin) {
+ auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^out,bin");
+ ASSERT_EQ(prefix, "foo");
+ ExtendedOutputsSpec expected = OutputsSpec::Names { "out", "bin" };
+ ASSERT_EQ(extendedOutputsSpec, expected);
+ ASSERT_EQ(std::string { prefix } + expected.to_string(), "foo^bin,out");
+}
+
+TEST(ExtendedOutputsSpec, many_carrot) {
+ auto [prefix, extendedOutputsSpec] = ExtendedOutputsSpec::parse("foo^bar^out,bin");
+ ASSERT_EQ(prefix, "foo^bar");
+ ExtendedOutputsSpec expected = OutputsSpec::Names { "out", "bin" };
+ ASSERT_EQ(extendedOutputsSpec, expected);
+ ASSERT_EQ(std::string { prefix } + expected.to_string(), "foo^bar^bin,out");
+}
+
+
+#define TEST_JSON(TYPE, NAME, STR, VAL) \
+ \
+ TEST(TYPE, NAME ## _to_json) { \
+ using nlohmann::literals::operator "" _json; \
+ ASSERT_EQ( \
+ STR ## _json, \
+ ((nlohmann::json) TYPE { VAL })); \
+ } \
+ \
+ TEST(TYPE, NAME ## _from_json) { \
+ using nlohmann::literals::operator "" _json; \
+ ASSERT_EQ( \
+ TYPE { VAL }, \
+ (STR ## _json).get<TYPE>()); \
+ }
+
+TEST_JSON(OutputsSpec, all, R"(["*"])", OutputsSpec::All { })
+TEST_JSON(OutputsSpec, name, R"(["a"])", OutputsSpec::Names { "a" })
+TEST_JSON(OutputsSpec, names, R"(["a","b"])", (OutputsSpec::Names { "a", "b" }))
+
+TEST_JSON(ExtendedOutputsSpec, def, R"(null)", ExtendedOutputsSpec::Default { })
+TEST_JSON(ExtendedOutputsSpec, all, R"(["*"])", ExtendedOutputsSpec::Explicit { OutputsSpec::All { } })
+TEST_JSON(ExtendedOutputsSpec, name, R"(["a"])", ExtendedOutputsSpec::Explicit { OutputsSpec::Names { "a" } })
+TEST_JSON(ExtendedOutputsSpec, names, R"(["a","b"])", (ExtendedOutputsSpec::Explicit { OutputsSpec::Names { "a", "b" } }))
+
+#undef TEST_JSON
+
+#ifndef COVERAGE
+
+RC_GTEST_PROP(
+ OutputsSpec,
+ prop_round_rip,
+ (const OutputsSpec & o))
+{
+ RC_ASSERT(o == OutputsSpec::parse(o.to_string()));
+}
+
+#endif
+
+}
diff --git a/tests/unit/libstore/path.cc b/tests/unit/libstore/path.cc
new file mode 100644
index 000000000..30631b5fd
--- /dev/null
+++ b/tests/unit/libstore/path.cc
@@ -0,0 +1,89 @@
+#include <regex>
+
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include "path-regex.hh"
+#include "store-api.hh"
+
+#include "tests/hash.hh"
+#include "tests/libstore.hh"
+#include "tests/path.hh"
+
+namespace nix {
+
+#define STORE_DIR "/nix/store/"
+#define HASH_PART "g1w7hy3qg1w7hy3qg1w7hy3qg1w7hy3q"
+
+class StorePathTest : public LibStoreTest
+{
+};
+
+static std::regex nameRegex { std::string { nameRegexStr } };
+
+#define TEST_DONT_PARSE(NAME, STR) \
+ TEST_F(StorePathTest, bad_ ## NAME) { \
+ std::string_view str = \
+ STORE_DIR HASH_PART "-" STR; \
+ ASSERT_THROW( \
+ store->parseStorePath(str), \
+ BadStorePath); \
+ std::string name { STR }; \
+ EXPECT_FALSE(std::regex_match(name, nameRegex)); \
+ }
+
+TEST_DONT_PARSE(empty, "")
+TEST_DONT_PARSE(garbage, "&*()")
+TEST_DONT_PARSE(double_star, "**")
+TEST_DONT_PARSE(star_first, "*,foo")
+TEST_DONT_PARSE(star_second, "foo,*")
+TEST_DONT_PARSE(bang, "foo!o")
+TEST_DONT_PARSE(dotfile, ".gitignore")
+
+#undef TEST_DONT_PARSE
+
+#define TEST_DO_PARSE(NAME, STR) \
+ TEST_F(StorePathTest, good_ ## NAME) { \
+ std::string_view str = \
+ STORE_DIR HASH_PART "-" STR; \
+ auto p = store->parseStorePath(str); \
+ std::string name { p.name() }; \
+ EXPECT_TRUE(std::regex_match(name, nameRegex)); \
+ }
+
+// 0-9 a-z A-Z + - . _ ? =
+
+TEST_DO_PARSE(numbers, "02345")
+TEST_DO_PARSE(lower_case, "foo")
+TEST_DO_PARSE(upper_case, "FOO")
+TEST_DO_PARSE(plus, "foo+bar")
+TEST_DO_PARSE(dash, "foo-dev")
+TEST_DO_PARSE(underscore, "foo_bar")
+TEST_DO_PARSE(period, "foo.txt")
+TEST_DO_PARSE(question_mark, "foo?why")
+TEST_DO_PARSE(equals_sign, "foo=foo")
+
+#undef TEST_DO_PARSE
+
+#ifndef COVERAGE
+
+RC_GTEST_FIXTURE_PROP(
+ StorePathTest,
+ prop_regex_accept,
+ (const StorePath & p))
+{
+ RC_ASSERT(std::regex_match(std::string { p.name() }, nameRegex));
+}
+
+RC_GTEST_FIXTURE_PROP(
+ StorePathTest,
+ prop_round_rip,
+ (const StorePath & p))
+{
+ RC_ASSERT(p == store->parseStorePath(store->printStorePath(p)));
+}
+
+#endif
+
+}
diff --git a/tests/unit/libstore/references.cc b/tests/unit/libstore/references.cc
new file mode 100644
index 000000000..d91d1cedd
--- /dev/null
+++ b/tests/unit/libstore/references.cc
@@ -0,0 +1,45 @@
+#include "references.hh"
+
+#include <gtest/gtest.h>
+
+namespace nix {
+
+TEST(references, scan)
+{
+ std::string hash1 = "dc04vv14dak1c1r48qa0m23vr9jy8sm0";
+ std::string hash2 = "zc842j0rz61mjsp3h3wp5ly71ak6qgdn";
+
+ {
+ RefScanSink scanner(StringSet{hash1});
+ auto s = "foobar";
+ scanner(s);
+ ASSERT_EQ(scanner.getResult(), StringSet{});
+ }
+
+ {
+ RefScanSink scanner(StringSet{hash1});
+ auto s = "foobar" + hash1 + "xyzzy";
+ scanner(s);
+ ASSERT_EQ(scanner.getResult(), StringSet{hash1});
+ }
+
+ {
+ RefScanSink scanner(StringSet{hash1, hash2});
+ auto s = "foobar" + hash1 + "xyzzy" + hash2;
+ scanner(((std::string_view) s).substr(0, 10));
+ scanner(((std::string_view) s).substr(10, 5));
+ scanner(((std::string_view) s).substr(15, 5));
+ scanner(((std::string_view) s).substr(20));
+ ASSERT_EQ(scanner.getResult(), StringSet({hash1, hash2}));
+ }
+
+ {
+ RefScanSink scanner(StringSet{hash1, hash2});
+ auto s = "foobar" + hash1 + "xyzzy" + hash2;
+ for (auto & i : s)
+ scanner(std::string(1, i));
+ ASSERT_EQ(scanner.getResult(), StringSet({hash1, hash2}));
+ }
+}
+
+}
diff --git a/tests/unit/libstore/test-data/machines.bad_format b/tests/unit/libstore/test-data/machines.bad_format
new file mode 100644
index 000000000..7255a1216
--- /dev/null
+++ b/tests/unit/libstore/test-data/machines.bad_format
@@ -0,0 +1 @@
+nix@scratchy.labs.cs.uu.nl - - eight
diff --git a/tests/unit/libstore/test-data/machines.valid b/tests/unit/libstore/test-data/machines.valid
new file mode 100644
index 000000000..1a6c8017c
--- /dev/null
+++ b/tests/unit/libstore/test-data/machines.valid
@@ -0,0 +1,3 @@
+nix@scratchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 1 kvm
+nix@itchy.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 8 2
+nix@poochie.labs.cs.uu.nl i686-linux /home/nix/.ssh/id_scratchy_auto 1 2 kvm benchmark c3NoLXJzYSBBQUFBQjNOemFDMXljMkVBQUFBREFRQUJBQUFDQVFDWWV5R1laNTNzd1VjMUZNSHBWL1BCcXlKaFR5S1JoRkpWWVRpRHlQN2h5c1JGa0w4VDlLOGdhL2Y2L3c3QjN2SjNHSFRIUFkybENiUEdZbGNLd2h6M2ZRbFNNOEViNi95b3ZLajdvM1FsMEx5Y0dzdGJvRmcwWkZKNldncUxsR0ltS0NobUlxOGZ3TW5ZTWUxbnRQeTBUZFZjSU1tOTV3YzF3SjBMd2c3cEVMRmtHazdkeTVvYnM4a3lGZ0pORDVRSmFwQWJjeWp4Z1QzdzdMcktNZ2xzeWhhd01JNVpkMGZsQTVudW5OZ3pid3plYVhLaUsyTW0vdGJXYTU1YTd4QmNYdHpIZGlPSWdSajJlRWxaMGh5bk10YjBmcklsdmxIcEtLaVFaZ3pQdCtIVXQ2bXpRMkRVME52MGYyYnNSU0krOGpJU2pQcmdlcVVHRldMUzVIUTg2N2xSMlpiaWtyclhZNTdqbVFEZk5DRHY1VFBHZU9UekFEd2pjMDc2aFZ3VFJCd3VTZFhtaWNxTS95b3lrWitkV1dnZ25MenE5QU1tdlNZcDhmZkZDcS9CSDBZNUFXWTFHay9vS3hMVTNaOWt3ZDd2UWNFQWFCQ2dxdnVZRGdTaHE1RlhndDM3OVZESWtEL05ZSTg2QXVvajVDRmVNTzlRM2pJSlRadlh6c1VldjVoSnA2djcxSVh5ODVtbTY5R20zcXdicVE1SjVQZDU1Um56SitpaW5BNjZxTEFSc0Y4amNsSnd5ekFXclBoYU9DRVY2bjVMeVhVazhzMW9EVVR4V1pWN25rVkFTbHJ0MllGcjN5dzdjRTRXQVhsemhHcDhocmdLMVVkMUlyeDVnZWRaSnBWcy9uNWVybmJFMUxmb2x5UHUvRUFIWlh6VGd4dHVDUFNobXc9PQo=
diff --git a/tests/unit/libutil-support/local.mk b/tests/unit/libutil-support/local.mk
new file mode 100644
index 000000000..b4c8f2475
--- /dev/null
+++ b/tests/unit/libutil-support/local.mk
@@ -0,0 +1,15 @@
+libraries += libutil-test-support
+
+libutil-test-support_NAME = libnixutil-test-support
+
+libutil-test-support_DIR := $(d)
+
+libutil-test-support_INSTALL_DIR :=
+
+libutil-test-support_SOURCES := $(wildcard $(d)/tests/*.cc)
+
+libutil-test-support_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES)
+
+libutil-test-support_LIBS = libutil
+
+libutil-test-support_LDFLAGS := -pthread -lrapidcheck
diff --git a/tests/unit/libutil-support/tests/hash.cc b/tests/unit/libutil-support/tests/hash.cc
new file mode 100644
index 000000000..577e9890e
--- /dev/null
+++ b/tests/unit/libutil-support/tests/hash.cc
@@ -0,0 +1,20 @@
+#include <regex>
+
+#include <rapidcheck.h>
+
+#include "hash.hh"
+
+#include "tests/hash.hh"
+
+namespace rc {
+using namespace nix;
+
+Gen<Hash> Arbitrary<Hash>::arbitrary()
+{
+ Hash hash(htSHA1);
+ for (size_t i = 0; i < hash.hashSize; ++i)
+ hash.hash[i] = *gen::arbitrary<uint8_t>();
+ return gen::just(hash);
+}
+
+}
diff --git a/tests/unit/libutil-support/tests/hash.hh b/tests/unit/libutil-support/tests/hash.hh
new file mode 100644
index 000000000..1f9fa59ae
--- /dev/null
+++ b/tests/unit/libutil-support/tests/hash.hh
@@ -0,0 +1,16 @@
+#pragma once
+///@file
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <hash.hh>
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<Hash> {
+ static Gen<Hash> arbitrary();
+};
+
+}
diff --git a/tests/unit/libutil/canon-path.cc b/tests/unit/libutil/canon-path.cc
new file mode 100644
index 000000000..fc94ccc3d
--- /dev/null
+++ b/tests/unit/libutil/canon-path.cc
@@ -0,0 +1,162 @@
+#include "canon-path.hh"
+
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ TEST(CanonPath, basic) {
+ {
+ CanonPath p("/");
+ ASSERT_EQ(p.abs(), "/");
+ ASSERT_EQ(p.rel(), "");
+ ASSERT_EQ(p.baseName(), std::nullopt);
+ ASSERT_EQ(p.dirOf(), std::nullopt);
+ ASSERT_FALSE(p.parent());
+ }
+
+ {
+ CanonPath p("/foo//");
+ ASSERT_EQ(p.abs(), "/foo");
+ ASSERT_EQ(p.rel(), "foo");
+ ASSERT_EQ(*p.baseName(), "foo");
+ ASSERT_EQ(*p.dirOf(), ""); // FIXME: do we want this?
+ ASSERT_EQ(p.parent()->abs(), "/");
+ }
+
+ {
+ CanonPath p("foo/bar");
+ ASSERT_EQ(p.abs(), "/foo/bar");
+ ASSERT_EQ(p.rel(), "foo/bar");
+ ASSERT_EQ(*p.baseName(), "bar");
+ ASSERT_EQ(*p.dirOf(), "/foo");
+ ASSERT_EQ(p.parent()->abs(), "/foo");
+ }
+
+ {
+ CanonPath p("foo//bar/");
+ ASSERT_EQ(p.abs(), "/foo/bar");
+ ASSERT_EQ(p.rel(), "foo/bar");
+ ASSERT_EQ(*p.baseName(), "bar");
+ ASSERT_EQ(*p.dirOf(), "/foo");
+ }
+ }
+
+ TEST(CanonPath, pop) {
+ CanonPath p("foo/bar/x");
+ ASSERT_EQ(p.abs(), "/foo/bar/x");
+ p.pop();
+ ASSERT_EQ(p.abs(), "/foo/bar");
+ p.pop();
+ ASSERT_EQ(p.abs(), "/foo");
+ p.pop();
+ ASSERT_EQ(p.abs(), "/");
+ }
+
+ TEST(CanonPath, removePrefix) {
+ CanonPath p1("foo/bar");
+ CanonPath p2("foo/bar/a/b/c");
+ ASSERT_EQ(p2.removePrefix(p1).abs(), "/a/b/c");
+ ASSERT_EQ(p1.removePrefix(p1).abs(), "/");
+ ASSERT_EQ(p1.removePrefix(CanonPath("/")).abs(), "/foo/bar");
+ }
+
+ TEST(CanonPath, iter) {
+ {
+ CanonPath p("a//foo/bar//");
+ std::vector<std::string_view> ss;
+ for (auto & c : p) ss.push_back(c);
+ ASSERT_EQ(ss, std::vector<std::string_view>({"a", "foo", "bar"}));
+ }
+
+ {
+ CanonPath p("/");
+ std::vector<std::string_view> ss;
+ for (auto & c : p) ss.push_back(c);
+ ASSERT_EQ(ss, std::vector<std::string_view>());
+ }
+ }
+
+ TEST(CanonPath, concat) {
+ {
+ CanonPath p1("a//foo/bar//");
+ CanonPath p2("xyzzy/bla");
+ ASSERT_EQ((p1 + p2).abs(), "/a/foo/bar/xyzzy/bla");
+ }
+
+ {
+ CanonPath p1("/");
+ CanonPath p2("/a/b");
+ ASSERT_EQ((p1 + p2).abs(), "/a/b");
+ }
+
+ {
+ CanonPath p1("/a/b");
+ CanonPath p2("/");
+ ASSERT_EQ((p1 + p2).abs(), "/a/b");
+ }
+
+ {
+ CanonPath p("/foo/bar");
+ ASSERT_EQ((p + "x").abs(), "/foo/bar/x");
+ }
+
+ {
+ CanonPath p("/");
+ ASSERT_EQ((p + "foo" + "bar").abs(), "/foo/bar");
+ }
+ }
+
+ TEST(CanonPath, within) {
+ ASSERT_TRUE(CanonPath("foo").isWithin(CanonPath("foo")));
+ ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("bar")));
+ ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("fo")));
+ ASSERT_TRUE(CanonPath("foo/bar").isWithin(CanonPath("foo")));
+ ASSERT_FALSE(CanonPath("foo").isWithin(CanonPath("foo/bar")));
+ ASSERT_TRUE(CanonPath("/foo/bar/default.nix").isWithin(CanonPath("/")));
+ ASSERT_TRUE(CanonPath("/").isWithin(CanonPath("/")));
+ }
+
+ TEST(CanonPath, sort) {
+ ASSERT_FALSE(CanonPath("foo") < CanonPath("foo"));
+ ASSERT_TRUE (CanonPath("foo") < CanonPath("foo/bar"));
+ ASSERT_TRUE (CanonPath("foo/bar") < CanonPath("foo!"));
+ ASSERT_FALSE(CanonPath("foo!") < CanonPath("foo"));
+ ASSERT_TRUE (CanonPath("foo") < CanonPath("foo!"));
+ }
+
+ TEST(CanonPath, allowed) {
+ std::set<CanonPath> allowed {
+ CanonPath("foo/bar"),
+ CanonPath("foo!"),
+ CanonPath("xyzzy"),
+ CanonPath("a/b/c"),
+ };
+
+ ASSERT_TRUE (CanonPath("foo/bar").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("foo/bar/bla").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("foo").isAllowed(allowed));
+ ASSERT_FALSE(CanonPath("bar").isAllowed(allowed));
+ ASSERT_FALSE(CanonPath("bar/a").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("a").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("a/b").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("a/b/c").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("a/b/c/d").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("a/b/c/d/e").isAllowed(allowed));
+ ASSERT_FALSE(CanonPath("a/b/a").isAllowed(allowed));
+ ASSERT_FALSE(CanonPath("a/b/d").isAllowed(allowed));
+ ASSERT_FALSE(CanonPath("aaa").isAllowed(allowed));
+ ASSERT_FALSE(CanonPath("zzz").isAllowed(allowed));
+ ASSERT_TRUE (CanonPath("/").isAllowed(allowed));
+ }
+
+ TEST(CanonPath, makeRelative) {
+ CanonPath d("/foo/bar");
+ ASSERT_EQ(d.makeRelative(CanonPath("/foo/bar")), ".");
+ ASSERT_EQ(d.makeRelative(CanonPath("/foo")), "..");
+ ASSERT_EQ(d.makeRelative(CanonPath("/")), "../..");
+ ASSERT_EQ(d.makeRelative(CanonPath("/foo/bar/xyzzy")), "xyzzy");
+ ASSERT_EQ(d.makeRelative(CanonPath("/foo/bar/xyzzy/bla")), "xyzzy/bla");
+ ASSERT_EQ(d.makeRelative(CanonPath("/foo/xyzzy/bla")), "../xyzzy/bla");
+ ASSERT_EQ(d.makeRelative(CanonPath("/xyzzy/bla")), "../../xyzzy/bla");
+ }
+}
diff --git a/tests/unit/libutil/chunked-vector.cc b/tests/unit/libutil/chunked-vector.cc
new file mode 100644
index 000000000..868d11f6f
--- /dev/null
+++ b/tests/unit/libutil/chunked-vector.cc
@@ -0,0 +1,54 @@
+#include "chunked-vector.hh"
+
+#include <gtest/gtest.h>
+
+namespace nix {
+ TEST(ChunkedVector, InitEmpty) {
+ auto v = ChunkedVector<int, 2>(100);
+ ASSERT_EQ(v.size(), 0);
+ }
+
+ TEST(ChunkedVector, GrowsCorrectly) {
+ auto v = ChunkedVector<int, 2>(100);
+ for (auto i = 1; i < 20; i++) {
+ v.add(i);
+ ASSERT_EQ(v.size(), i);
+ }
+ }
+
+ TEST(ChunkedVector, AddAndGet) {
+ auto v = ChunkedVector<int, 2>(100);
+ for (auto i = 1; i < 20; i++) {
+ auto [i2, idx] = v.add(i);
+ auto & i3 = v[idx];
+ ASSERT_EQ(i, i2);
+ ASSERT_EQ(&i2, &i3);
+ }
+ }
+
+ TEST(ChunkedVector, ForEach) {
+ auto v = ChunkedVector<int, 2>(100);
+ for (auto i = 1; i < 20; i++) {
+ v.add(i);
+ }
+ int count = 0;
+ v.forEach([&count](int elt) {
+ count++;
+ });
+ ASSERT_EQ(count, v.size());
+ }
+
+ TEST(ChunkedVector, OverflowOK) {
+ // Similar to the AddAndGet, but intentionnally use a small
+ // initial ChunkedVector to force it to overflow
+ auto v = ChunkedVector<int, 2>(2);
+ for (auto i = 1; i < 20; i++) {
+ auto [i2, idx] = v.add(i);
+ auto & i3 = v[idx];
+ ASSERT_EQ(i, i2);
+ ASSERT_EQ(&i2, &i3);
+ }
+ }
+
+}
+
diff --git a/tests/unit/libutil/closure.cc b/tests/unit/libutil/closure.cc
new file mode 100644
index 000000000..7597e7807
--- /dev/null
+++ b/tests/unit/libutil/closure.cc
@@ -0,0 +1,70 @@
+#include "closure.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+using namespace std;
+
+map<string, set<string>> testGraph = {
+ { "A", { "B", "C", "G" } },
+ { "B", { "A" } }, // Loops back to A
+ { "C", { "F" } }, // Indirect reference
+ { "D", { "A" } }, // Not reachable, but has backreferences
+ { "E", {} }, // Just not reachable
+ { "F", {} },
+ { "G", { "G" } }, // Self reference
+};
+
+TEST(closure, correctClosure) {
+ set<string> aClosure;
+ set<string> expectedClosure = {"A", "B", "C", "F", "G"};
+ computeClosure<string>(
+ {"A"},
+ aClosure,
+ [&](const string currentNode, function<void(promise<set<string>> &)> processEdges) {
+ promise<set<string>> promisedNodes;
+ promisedNodes.set_value(testGraph[currentNode]);
+ processEdges(promisedNodes);
+ }
+ );
+
+ ASSERT_EQ(aClosure, expectedClosure);
+}
+
+TEST(closure, properlyHandlesDirectExceptions) {
+ struct TestExn {};
+ set<string> aClosure;
+ EXPECT_THROW(
+ computeClosure<string>(
+ {"A"},
+ aClosure,
+ [&](const string currentNode, function<void(promise<set<string>> &)> processEdges) {
+ throw TestExn();
+ }
+ ),
+ TestExn
+ );
+}
+
+TEST(closure, properlyHandlesExceptionsInPromise) {
+ struct TestExn {};
+ set<string> aClosure;
+ EXPECT_THROW(
+ computeClosure<string>(
+ {"A"},
+ aClosure,
+ [&](const string currentNode, function<void(promise<set<string>> &)> processEdges) {
+ promise<set<string>> promise;
+ try {
+ throw TestExn();
+ } catch (...) {
+ promise.set_exception(std::current_exception());
+ }
+ processEdges(promise);
+ }
+ ),
+ TestExn
+ );
+}
+
+}
diff --git a/tests/unit/libutil/compression.cc b/tests/unit/libutil/compression.cc
new file mode 100644
index 000000000..bbbf3500f
--- /dev/null
+++ b/tests/unit/libutil/compression.cc
@@ -0,0 +1,96 @@
+#include "compression.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * compress / decompress
+ * --------------------------------------------------------------------------*/
+
+ TEST(compress, compressWithUnknownMethod) {
+ ASSERT_THROW(compress("invalid-method", "something-to-compress"), UnknownCompressionMethod);
+ }
+
+ TEST(compress, noneMethodDoesNothingToTheInput) {
+ auto o = compress("none", "this-is-a-test");
+
+ ASSERT_EQ(o, "this-is-a-test");
+ }
+
+ TEST(decompress, decompressNoneCompressed) {
+ auto method = "none";
+ auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto o = decompress(method, str);
+
+ ASSERT_EQ(o, str);
+ }
+
+ TEST(decompress, decompressEmptyCompressed) {
+ // Empty-method decompression used e.g. by S3 store
+ // (Content-Encoding == "").
+ auto method = "";
+ auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto o = decompress(method, str);
+
+ ASSERT_EQ(o, str);
+ }
+
+ TEST(decompress, decompressXzCompressed) {
+ auto method = "xz";
+ auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto o = decompress(method, compress(method, str));
+
+ ASSERT_EQ(o, str);
+ }
+
+ TEST(decompress, decompressBzip2Compressed) {
+ auto method = "bzip2";
+ auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto o = decompress(method, compress(method, str));
+
+ ASSERT_EQ(o, str);
+ }
+
+ TEST(decompress, decompressBrCompressed) {
+ auto method = "br";
+ auto str = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto o = decompress(method, compress(method, str));
+
+ ASSERT_EQ(o, str);
+ }
+
+ TEST(decompress, decompressInvalidInputThrowsCompressionError) {
+ auto method = "bzip2";
+ auto str = "this is a string that does not qualify as valid bzip2 data";
+
+ ASSERT_THROW(decompress(method, str), CompressionError);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * compression sinks
+ * --------------------------------------------------------------------------*/
+
+ TEST(makeCompressionSink, noneSinkDoesNothingToInput) {
+ StringSink strSink;
+ auto inputString = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto sink = makeCompressionSink("none", strSink);
+ (*sink)(inputString);
+ sink->finish();
+
+ ASSERT_STREQ(strSink.s.c_str(), inputString);
+ }
+
+ TEST(makeCompressionSink, compressAndDecompress) {
+ StringSink strSink;
+ auto inputString = "slfja;sljfklsa;jfklsjfkl;sdjfkl;sadjfkl;sdjf;lsdfjsadlf";
+ auto decompressionSink = makeDecompressionSink("bzip2", strSink);
+ auto sink = makeCompressionSink("bzip2", *decompressionSink);
+
+ (*sink)(inputString);
+ sink->finish();
+ decompressionSink->finish();
+
+ ASSERT_STREQ(strSink.s.c_str(), inputString);
+ }
+
+}
diff --git a/tests/unit/libutil/config.cc b/tests/unit/libutil/config.cc
new file mode 100644
index 000000000..886e70da5
--- /dev/null
+++ b/tests/unit/libutil/config.cc
@@ -0,0 +1,295 @@
+#include "config.hh"
+#include "args.hh"
+
+#include <sstream>
+#include <gtest/gtest.h>
+#include <nlohmann/json.hpp>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * Config
+ * --------------------------------------------------------------------------*/
+
+ TEST(Config, setUndefinedSetting) {
+ Config config;
+ ASSERT_EQ(config.set("undefined-key", "value"), false);
+ }
+
+ TEST(Config, setDefinedSetting) {
+ Config config;
+ std::string value;
+ Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
+ ASSERT_EQ(config.set("name-of-the-setting", "value"), true);
+ }
+
+ TEST(Config, getDefinedSetting) {
+ Config config;
+ std::string value;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
+
+ config.getSettings(settings, /* overriddenOnly = */ false);
+ const auto iter = settings.find("name-of-the-setting");
+ ASSERT_NE(iter, settings.end());
+ ASSERT_EQ(iter->second.value, "");
+ ASSERT_EQ(iter->second.description, "description\n");
+ }
+
+ TEST(Config, getDefinedOverriddenSettingNotSet) {
+ Config config;
+ std::string value;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> foo{&config, value, "name-of-the-setting", "description"};
+
+ config.getSettings(settings, /* overriddenOnly = */ true);
+ const auto e = settings.find("name-of-the-setting");
+ ASSERT_EQ(e, settings.end());
+ }
+
+ TEST(Config, getDefinedSettingSet1) {
+ Config config;
+ std::string value;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, value, "name-of-the-setting", "description"};
+
+ setting.assign("value");
+
+ config.getSettings(settings, /* overriddenOnly = */ false);
+ const auto iter = settings.find("name-of-the-setting");
+ ASSERT_NE(iter, settings.end());
+ ASSERT_EQ(iter->second.value, "value");
+ ASSERT_EQ(iter->second.description, "description\n");
+ }
+
+ TEST(Config, getDefinedSettingSet2) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+
+ ASSERT_TRUE(config.set("name-of-the-setting", "value"));
+
+ config.getSettings(settings, /* overriddenOnly = */ false);
+ const auto e = settings.find("name-of-the-setting");
+ ASSERT_NE(e, settings.end());
+ ASSERT_EQ(e->second.value, "value");
+ ASSERT_EQ(e->second.description, "description\n");
+ }
+
+ TEST(Config, addSetting) {
+ class TestSetting : public AbstractSetting {
+ public:
+ TestSetting() : AbstractSetting("test", "test", {}) {}
+ void set(const std::string & value, bool append) override {}
+ std::string to_string() const override { return {}; }
+ bool isAppendable() override { return false; }
+ };
+
+ Config config;
+ TestSetting setting;
+
+ ASSERT_FALSE(config.set("test", "value"));
+ config.addSetting(&setting);
+ ASSERT_TRUE(config.set("test", "value"));
+ ASSERT_FALSE(config.set("extra-test", "value"));
+ }
+
+ TEST(Config, withInitialValue) {
+ const StringMap initials = {
+ { "key", "value" },
+ };
+ Config config(initials);
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+ config.getSettings(settings, /* overriddenOnly = */ false);
+ ASSERT_EQ(settings.find("key"), settings.end());
+ }
+
+ Setting<std::string> setting{&config, "default-value", "key", "description"};
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+ config.getSettings(settings, /* overriddenOnly = */ false);
+ ASSERT_EQ(settings["key"].value, "value");
+ }
+ }
+
+ TEST(Config, resetOverridden) {
+ Config config;
+ config.resetOverridden();
+ }
+
+ TEST(Config, resetOverriddenWithSetting) {
+ Config config;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+
+ setting.set("foo");
+ ASSERT_EQ(setting.get(), "foo");
+ config.getSettings(settings, /* overriddenOnly = */ true);
+ ASSERT_TRUE(settings.empty());
+ }
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+
+ setting.override("bar");
+ ASSERT_TRUE(setting.overridden);
+ ASSERT_EQ(setting.get(), "bar");
+ config.getSettings(settings, /* overriddenOnly = */ true);
+ ASSERT_FALSE(settings.empty());
+ }
+
+ {
+ std::map<std::string, Config::SettingInfo> settings;
+
+ config.resetOverridden();
+ ASSERT_FALSE(setting.overridden);
+ config.getSettings(settings, /* overriddenOnly = */ true);
+ ASSERT_TRUE(settings.empty());
+ }
+ }
+
+ TEST(Config, toJSONOnEmptyConfig) {
+ ASSERT_EQ(Config().toJSON().dump(), "{}");
+ }
+
+ TEST(Config, toJSONOnNonEmptyConfig) {
+ using nlohmann::literals::operator "" _json;
+ Config config;
+ Setting<std::string> setting{
+ &config,
+ "",
+ "name-of-the-setting",
+ "description",
+ };
+ setting.assign("value");
+
+ ASSERT_EQ(config.toJSON(),
+ R"#({
+ "name-of-the-setting": {
+ "aliases": [],
+ "defaultValue": "",
+ "description": "description\n",
+ "documentDefault": true,
+ "value": "value",
+ "experimentalFeature": null
+ }
+ })#"_json);
+ }
+
+ TEST(Config, toJSONOnNonEmptyConfigWithExperimentalSetting) {
+ using nlohmann::literals::operator "" _json;
+ Config config;
+ Setting<std::string> setting{
+ &config,
+ "",
+ "name-of-the-setting",
+ "description",
+ {},
+ true,
+ Xp::Flakes,
+ };
+ setting.assign("value");
+
+ ASSERT_EQ(config.toJSON(),
+ R"#({
+ "name-of-the-setting": {
+ "aliases": [],
+ "defaultValue": "",
+ "description": "description\n",
+ "documentDefault": true,
+ "value": "value",
+ "experimentalFeature": "flakes"
+ }
+ })#"_json);
+ }
+
+ TEST(Config, setSettingAlias) {
+ Config config;
+ Setting<std::string> setting{&config, "", "some-int", "best number", { "another-int" }};
+ ASSERT_TRUE(config.set("some-int", "1"));
+ ASSERT_EQ(setting.get(), "1");
+ ASSERT_TRUE(config.set("another-int", "2"));
+ ASSERT_EQ(setting.get(), "2");
+ ASSERT_TRUE(config.set("some-int", "3"));
+ ASSERT_EQ(setting.get(), "3");
+ }
+
+ /* FIXME: The reapplyUnknownSettings method doesn't seem to do anything
+ * useful (these days). Whenever we add a new setting to Config the
+ * unknown settings are always considered. In which case is this function
+ * actually useful? Is there some way to register a Setting without calling
+ * addSetting? */
+ TEST(Config, DISABLED_reapplyUnknownSettings) {
+ Config config;
+ ASSERT_FALSE(config.set("name-of-the-setting", "unknownvalue"));
+ Setting<std::string> setting{&config, "default", "name-of-the-setting", "description"};
+ ASSERT_EQ(setting.get(), "default");
+ config.reapplyUnknownSettings();
+ ASSERT_EQ(setting.get(), "unknownvalue");
+ }
+
+ TEST(Config, applyConfigEmpty) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ config.applyConfig("");
+ config.getSettings(settings);
+ ASSERT_TRUE(settings.empty());
+ }
+
+ TEST(Config, applyConfigEmptyWithComment) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ config.applyConfig("# just a comment");
+ config.getSettings(settings);
+ ASSERT_TRUE(settings.empty());
+ }
+
+ TEST(Config, applyConfigAssignment) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+ config.applyConfig(
+ "name-of-the-setting = value-from-file #useful comment\n"
+ "# name-of-the-setting = foo\n"
+ );
+ config.getSettings(settings);
+ ASSERT_FALSE(settings.empty());
+ ASSERT_EQ(settings["name-of-the-setting"].value, "value-from-file");
+ }
+
+ TEST(Config, applyConfigWithReassignedSetting) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+ config.applyConfig(
+ "name-of-the-setting = first-value\n"
+ "name-of-the-setting = second-value\n"
+ );
+ config.getSettings(settings);
+ ASSERT_FALSE(settings.empty());
+ ASSERT_EQ(settings["name-of-the-setting"].value, "second-value");
+ }
+
+ TEST(Config, applyConfigFailsOnMissingIncludes) {
+ Config config;
+ std::map<std::string, Config::SettingInfo> settings;
+ Setting<std::string> setting{&config, "", "name-of-the-setting", "description"};
+
+ ASSERT_THROW(config.applyConfig(
+ "name-of-the-setting = value-from-file\n"
+ "# name-of-the-setting = foo\n"
+ "include /nix/store/does/not/exist.nix"
+ ), Error);
+ }
+
+ TEST(Config, applyConfigInvalidThrows) {
+ Config config;
+ ASSERT_THROW(config.applyConfig("value == key"), UsageError);
+ ASSERT_THROW(config.applyConfig("value "), UsageError);
+ }
+}
diff --git a/tests/unit/libutil/git.cc b/tests/unit/libutil/git.cc
new file mode 100644
index 000000000..5b5715fc2
--- /dev/null
+++ b/tests/unit/libutil/git.cc
@@ -0,0 +1,33 @@
+#include "git.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ TEST(GitLsRemote, parseSymrefLineWithReference) {
+ auto line = "ref: refs/head/main HEAD";
+ auto res = git::parseLsRemoteLine(line);
+ ASSERT_TRUE(res.has_value());
+ ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic);
+ ASSERT_EQ(res->target, "refs/head/main");
+ ASSERT_EQ(res->reference, "HEAD");
+ }
+
+ TEST(GitLsRemote, parseSymrefLineWithNoReference) {
+ auto line = "ref: refs/head/main";
+ auto res = git::parseLsRemoteLine(line);
+ ASSERT_TRUE(res.has_value());
+ ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Symbolic);
+ ASSERT_EQ(res->target, "refs/head/main");
+ ASSERT_EQ(res->reference, std::nullopt);
+ }
+
+ TEST(GitLsRemote, parseObjectRefLine) {
+ auto line = "abc123 refs/head/main";
+ auto res = git::parseLsRemoteLine(line);
+ ASSERT_TRUE(res.has_value());
+ ASSERT_EQ(res->kind, git::LsRemoteRefLine::Kind::Object);
+ ASSERT_EQ(res->target, "abc123");
+ ASSERT_EQ(res->reference, "refs/head/main");
+ }
+}
+
diff --git a/tests/unit/libutil/hash.cc b/tests/unit/libutil/hash.cc
new file mode 100644
index 000000000..1f40edc95
--- /dev/null
+++ b/tests/unit/libutil/hash.cc
@@ -0,0 +1,77 @@
+#include <regex>
+
+#include <gtest/gtest.h>
+
+#include "hash.hh"
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * hashString
+ * --------------------------------------------------------------------------*/
+
+ TEST(hashString, testKnownMD5Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc1321
+ auto s1 = "";
+ auto hash = hashString(HashType::htMD5, s1);
+ ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:d41d8cd98f00b204e9800998ecf8427e");
+ }
+
+ TEST(hashString, testKnownMD5Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc1321
+ auto s2 = "abc";
+ auto hash = hashString(HashType::htMD5, s2);
+ ASSERT_EQ(hash.to_string(Base::Base16, true), "md5:900150983cd24fb0d6963f7d28e17f72");
+ }
+
+ TEST(hashString, testKnownSHA1Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc3174
+ auto s = "abc";
+ auto hash = hashString(HashType::htSHA1, s);
+ ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:a9993e364706816aba3e25717850c26c9cd0d89d");
+ }
+
+ TEST(hashString, testKnownSHA1Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc3174
+ auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ auto hash = hashString(HashType::htSHA1, s);
+ ASSERT_EQ(hash.to_string(Base::Base16, true),"sha1:84983e441c3bd26ebaae4aa1f95129e5e54670f1");
+ }
+
+ TEST(hashString, testKnownSHA256Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abc";
+
+ auto hash = hashString(HashType::htSHA256, s);
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
+ "sha256:ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
+ }
+
+ TEST(hashString, testKnownSHA256Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
+ auto hash = hashString(HashType::htSHA256, s);
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
+ "sha256:248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1");
+ }
+
+ TEST(hashString, testKnownSHA512Hashes1) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abc";
+ auto hash = hashString(HashType::htSHA512, s);
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
+ "sha512:ddaf35a193617abacc417349ae20413112e6fa4e89a9"
+ "7ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd"
+ "454d4423643ce80e2a9ac94fa54ca49f");
+ }
+ TEST(hashString, testKnownSHA512Hashes2) {
+ // values taken from: https://tools.ietf.org/html/rfc4634
+ auto s = "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
+
+ auto hash = hashString(HashType::htSHA512, s);
+ ASSERT_EQ(hash.to_string(Base::Base16, true),
+ "sha512:8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa1"
+ "7299aeadb6889018501d289e4900f7e4331b99dec4b5433a"
+ "c7d329eeb6dd26545e96e55b874be909");
+ }
+}
diff --git a/tests/unit/libutil/hilite.cc b/tests/unit/libutil/hilite.cc
new file mode 100644
index 000000000..1ff5980d5
--- /dev/null
+++ b/tests/unit/libutil/hilite.cc
@@ -0,0 +1,66 @@
+#include "hilite.hh"
+
+#include <gtest/gtest.h>
+
+namespace nix {
+/* ----------- tests for fmt.hh -------------------------------------------------*/
+
+ TEST(hiliteMatches, noHighlight) {
+ ASSERT_STREQ(hiliteMatches("Hello, world!", std::vector<std::smatch>(), "(", ")").c_str(), "Hello, world!");
+ }
+
+ TEST(hiliteMatches, simpleHighlight) {
+ std::string str = "Hello, world!";
+ std::regex re = std::regex("world");
+ auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
+ ASSERT_STREQ(
+ hiliteMatches(str, matches, "(", ")").c_str(),
+ "Hello, (world)!"
+ );
+ }
+
+ TEST(hiliteMatches, multipleMatches) {
+ std::string str = "Hello, world, world, world, world, world, world, Hello!";
+ std::regex re = std::regex("world");
+ auto matches = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
+ ASSERT_STREQ(
+ hiliteMatches(str, matches, "(", ")").c_str(),
+ "Hello, (world), (world), (world), (world), (world), (world), Hello!"
+ );
+ }
+
+ TEST(hiliteMatches, overlappingMatches) {
+ std::string str = "world, Hello, world, Hello, world, Hello, world, Hello, world!";
+ std::regex re = std::regex("Hello, world");
+ std::regex re2 = std::regex("world, Hello");
+ auto v = std::vector(std::sregex_iterator(str.begin(), str.end(), re), std::sregex_iterator());
+ for(auto it = std::sregex_iterator(str.begin(), str.end(), re2); it != std::sregex_iterator(); ++it) {
+ v.push_back(*it);
+ }
+ ASSERT_STREQ(
+ hiliteMatches(str, v, "(", ")").c_str(),
+ "(world, Hello, world, Hello, world, Hello, world, Hello, world)!"
+ );
+ }
+
+ TEST(hiliteMatches, complexOverlappingMatches) {
+ std::string str = "legacyPackages.x86_64-linux.git-crypt";
+ std::vector regexes = {
+ std::regex("t-cry"),
+ std::regex("ux\\.git-cry"),
+ std::regex("git-c"),
+ std::regex("pt"),
+ };
+ std::vector<std::smatch> matches;
+ for(auto regex : regexes)
+ {
+ for(auto it = std::sregex_iterator(str.begin(), str.end(), regex); it != std::sregex_iterator(); ++it) {
+ matches.push_back(*it);
+ }
+ }
+ ASSERT_STREQ(
+ hiliteMatches(str, matches, "(", ")").c_str(),
+ "legacyPackages.x86_64-lin(ux.git-crypt)"
+ );
+ }
+}
diff --git a/tests/unit/libutil/local.mk b/tests/unit/libutil/local.mk
new file mode 100644
index 000000000..6de96c0dc
--- /dev/null
+++ b/tests/unit/libutil/local.mk
@@ -0,0 +1,23 @@
+check: libutil-tests_RUN
+
+programs += libutil-tests
+
+libutil-tests_NAME = libnixutil-tests
+
+libutil-tests_ENV := _NIX_TEST_UNIT_DATA=$(d)/data
+
+libutil-tests_DIR := $(d)
+
+libutil-tests_INSTALL_DIR :=
+
+libutil-tests_SOURCES := $(wildcard $(d)/*.cc)
+
+libutil-tests_EXTRA_INCLUDES = \
+ -I tests/unit/libutil-support \
+ -I src/libutil
+
+libutil-tests_CXXFLAGS += $(libutil-tests_EXTRA_INCLUDES)
+
+libutil-tests_LIBS = libutil-test-support libutil
+
+libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS)
diff --git a/tests/unit/libutil/logging.cc b/tests/unit/libutil/logging.cc
new file mode 100644
index 000000000..2ffdc2e9b
--- /dev/null
+++ b/tests/unit/libutil/logging.cc
@@ -0,0 +1,370 @@
+#if 0
+
+#include "logging.hh"
+#include "nixexpr.hh"
+#include "util.hh"
+#include <fstream>
+
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * logEI
+ * --------------------------------------------------------------------------*/
+
+ const char *test_file =
+ "previous line of code\n"
+ "this is the problem line of code\n"
+ "next line of code\n";
+ const char *one_liner =
+ "this is the other problem line of code";
+
+ TEST(logEI, catpuresBasicProperties) {
+
+ MakeError(TestError, Error);
+ ErrorInfo::programName = std::optional("error-unit-test");
+
+ try {
+ throw TestError("an error for testing purposes");
+ } catch (Error &e) {
+ testing::internal::CaptureStderr();
+ logger->logEI(e.info());
+ auto str = testing::internal::GetCapturedStderr();
+
+ ASSERT_STREQ(str.c_str(),"\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError --- error-unit-test\x1B[0m\nan error for testing purposes\n");
+ }
+ }
+
+ TEST(logEI, jsonOutput) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create("random.nix");
+ testing::internal::CaptureStderr();
+
+ makeJSONLogger(*logger)->logEI({
+ .name = "error name",
+ .msg = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .errPos = Pos(foFile, problem_file, 02, 13)
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "@nix {\"action\":\"msg\",\"column\":13,\"file\":\"random.nix\",\"level\":0,\"line\":2,\"msg\":\"\\u001b[31;1merror:\\u001b[0m\\u001b[34;1m --- error name --- error-unit-test\\u001b[0m\\n\\u001b[34;1mat: \\u001b[33;1m(2:13)\\u001b[34;1m in file: \\u001b[0mrandom.nix\\n\\nerror without any code lines.\\n\\nthis hint has \\u001b[33;1myellow\\u001b[0m templated \\u001b[33;1mvalues\\u001b[0m!!\",\"raw_msg\":\"this hint has \\u001b[33;1myellow\\u001b[0m templated \\u001b[33;1mvalues\\u001b[0m!!\"}\n");
+ }
+
+ TEST(logEI, appendingHintsToPreviousError) {
+
+ MakeError(TestError, Error);
+ ErrorInfo::programName = std::optional("error-unit-test");
+
+ try {
+ auto e = Error("initial error");
+ throw TestError(e.info());
+ } catch (Error &e) {
+ ErrorInfo ei = e.info();
+ ei.msg = hintfmt("%s; subsequent error message.", normaltxt(e.info().msg.str()));
+
+ testing::internal::CaptureStderr();
+ logger->logEI(ei);
+ auto str = testing::internal::GetCapturedStderr();
+
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- TestError --- error-unit-test\x1B[0m\ninitial error; subsequent error message.\n");
+ }
+
+ }
+
+ TEST(logEI, picksUpSysErrorExitCode) {
+
+ MakeError(TestError, Error);
+ ErrorInfo::programName = std::optional("error-unit-test");
+
+ try {
+ auto x = readFile(-1);
+ }
+ catch (SysError &e) {
+ testing::internal::CaptureStderr();
+ logError(e.info());
+ auto str = testing::internal::GetCapturedStderr();
+
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- SysError --- error-unit-test\x1B[0m\nstatting file: \x1B[33;1mBad file descriptor\x1B[0m\n");
+ }
+ }
+
+ TEST(logEI, loggingErrorOnInfoLevel) {
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlInfo,
+ .name = "Info name",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1minfo:\x1B[0m\x1B[34;1m --- Info name --- error-unit-test\x1B[0m\nInfo description\n");
+ }
+
+ TEST(logEI, loggingErrorOnTalkativeLevel) {
+ verbosity = lvlTalkative;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlTalkative,
+ .name = "Talkative name",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1mtalk:\x1B[0m\x1B[34;1m --- Talkative name --- error-unit-test\x1B[0m\nTalkative description\n");
+ }
+
+ TEST(logEI, loggingErrorOnChattyLevel) {
+ verbosity = lvlChatty;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlChatty,
+ .name = "Chatty name",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1mchat:\x1B[0m\x1B[34;1m --- Chatty name --- error-unit-test\x1B[0m\nTalkative description\n");
+ }
+
+ TEST(logEI, loggingErrorOnDebugLevel) {
+ verbosity = lvlDebug;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlDebug,
+ .name = "Debug name",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mdebug:\x1B[0m\x1B[34;1m --- Debug name --- error-unit-test\x1B[0m\nDebug description\n");
+ }
+
+ TEST(logEI, loggingErrorOnVomitLevel) {
+ verbosity = lvlVomit;
+
+ testing::internal::CaptureStderr();
+
+ logger->logEI({ .level = lvlVomit,
+ .name = "Vomit name",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[32;1mvomit:\x1B[0m\x1B[34;1m --- Vomit name --- error-unit-test\x1B[0m\nVomit description\n");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * logError
+ * --------------------------------------------------------------------------*/
+
+ TEST(logError, logErrorWithoutHintOrCode) {
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "name",
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nerror description\n");
+ }
+
+ TEST(logError, logErrorWithPreviousAndNextLinesOfCode) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create(test_file);
+
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "error name",
+ .msg = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .errPos = Pos(foString, problem_file, 02, 13),
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nerror with code lines\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+ TEST(logError, logErrorWithInvalidFile) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create("invalid filename");
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "error name",
+ .msg = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .errPos = Pos(foFile, problem_file, 02, 13)
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m in file: \x1B[0minvalid filename\n\nerror without any code lines.\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+ TEST(logError, logErrorWithOnlyHintAndName) {
+ testing::internal::CaptureStderr();
+
+ logError({
+ .name = "error name",
+ .msg = hintfmt("hint %1%", "only"),
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- error name --- error-unit-test\x1B[0m\nhint \x1B[33;1monly\x1B[0m\n");
+
+ }
+
+ /* ----------------------------------------------------------------------------
+ * logWarning
+ * --------------------------------------------------------------------------*/
+
+ TEST(logWarning, logWarningWithNameDescriptionAndHint) {
+ testing::internal::CaptureStderr();
+
+ logWarning({
+ .name = "name",
+ .msg = hintfmt("there was a %1%", "warning"),
+ });
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- name --- error-unit-test\x1B[0m\nwarning description\n\nthere was a \x1B[33;1mwarning\x1B[0m\n");
+ }
+
+ TEST(logWarning, logWarningWithFileLineNumAndCode) {
+
+ SymbolTable testTable;
+ auto problem_file = testTable.create(test_file);
+
+ testing::internal::CaptureStderr();
+
+ logWarning({
+ .name = "warning name",
+ .msg = hintfmt("this hint has %1% templated %2%!!",
+ "yellow",
+ "values"),
+ .errPos = Pos(foStdin, problem_file, 2, 13),
+ });
+
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[33;1mwarning:\x1B[0m\x1B[34;1m --- warning name --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from stdin\x1B[0m\n\nwarning description\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nthis hint has \x1B[33;1myellow\x1B[0m templated \x1B[33;1mvalues\x1B[0m!!\n");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * traces
+ * --------------------------------------------------------------------------*/
+
+ TEST(addTrace, showTracesWithShowTrace) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create(test_file);
+ auto oneliner_file = testTable.create(one_liner);
+ auto invalidfilename = testTable.create("invalid filename");
+
+ auto e = AssertionError(ErrorInfo {
+ .name = "wat",
+ .msg = hintfmt("it has been %1% days since our last error", "zero"),
+ .errPos = Pos(foString, problem_file, 2, 13),
+ });
+
+ e.addTrace(Pos(foStdin, oneliner_file, 1, 19), "while trying to compute %1%", 42);
+ e.addTrace(std::nullopt, "while doing something without a %1%", "pos");
+ e.addTrace(Pos(foFile, invalidfilename, 100, 1), "missing %s", "nix file");
+
+ testing::internal::CaptureStderr();
+
+ loggerSettings.showTrace.assign(true);
+
+ logError(e.info());
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nshow-traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n\x1B[34;1m---- show-trace ----\x1B[0m\n\x1B[34;1mtrace: \x1B[0mwhile trying to compute \x1B[33;1m42\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(1:19)\x1B[34;1m from stdin\x1B[0m\n\n 1| this is the other problem line of code\n | \x1B[31;1m^\x1B[0m\n\n\x1B[34;1mtrace: \x1B[0mwhile doing something without a \x1B[33;1mpos\x1B[0m\n\x1B[34;1mtrace: \x1B[0mmissing \x1B[33;1mnix file\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(100:1)\x1B[34;1m in file: \x1B[0minvalid filename\n");
+ }
+
+ TEST(addTrace, hideTracesWithoutShowTrace) {
+ SymbolTable testTable;
+ auto problem_file = testTable.create(test_file);
+ auto oneliner_file = testTable.create(one_liner);
+ auto invalidfilename = testTable.create("invalid filename");
+
+ auto e = AssertionError(ErrorInfo {
+ .name = "wat",
+ .msg = hintfmt("it has been %1% days since our last error", "zero"),
+ .errPos = Pos(foString, problem_file, 2, 13),
+ });
+
+ e.addTrace(Pos(foStdin, oneliner_file, 1, 19), "while trying to compute %1%", 42);
+ e.addTrace(std::nullopt, "while doing something without a %1%", "pos");
+ e.addTrace(Pos(foFile, invalidfilename, 100, 1), "missing %s", "nix file");
+
+ testing::internal::CaptureStderr();
+
+ loggerSettings.showTrace.assign(false);
+
+ logError(e.info());
+
+ auto str = testing::internal::GetCapturedStderr();
+ ASSERT_STREQ(str.c_str(), "\x1B[31;1merror:\x1B[0m\x1B[34;1m --- AssertionError --- error-unit-test\x1B[0m\n\x1B[34;1mat: \x1B[33;1m(2:13)\x1B[34;1m from string\x1B[0m\n\nhide traces\n\n 1| previous line of code\n 2| this is the problem line of code\n | \x1B[31;1m^\x1B[0m\n 3| next line of code\n\nit has been \x1B[33;1mzero\x1B[0m days since our last error\n");
+ }
+
+
+ /* ----------------------------------------------------------------------------
+ * hintfmt
+ * --------------------------------------------------------------------------*/
+
+ TEST(hintfmt, percentStringWithoutArgs) {
+
+ const char *teststr = "this is 100%s correct!";
+
+ ASSERT_STREQ(
+ hintfmt(teststr).str().c_str(),
+ teststr);
+
+ }
+
+ TEST(hintfmt, fmtToHintfmt) {
+
+ ASSERT_STREQ(
+ hintfmt(fmt("the color of this this text is %1%", "not yellow")).str().c_str(),
+ "the color of this this text is not yellow");
+
+ }
+
+ TEST(hintfmt, tooFewArguments) {
+
+ ASSERT_STREQ(
+ hintfmt("only one arg %1% %2%", "fulfilled").str().c_str(),
+ "only one arg " ANSI_WARNING "fulfilled" ANSI_NORMAL " ");
+
+ }
+
+ TEST(hintfmt, tooManyArguments) {
+
+ ASSERT_STREQ(
+ hintfmt("what about this %1% %2%", "%3%", "one", "two").str().c_str(),
+ "what about this " ANSI_WARNING "%3%" ANSI_NORMAL " " ANSI_YELLOW "one" ANSI_NORMAL);
+
+ }
+
+ /* ----------------------------------------------------------------------------
+ * ErrPos
+ * --------------------------------------------------------------------------*/
+
+ TEST(errpos, invalidPos) {
+
+ // contains an invalid symbol, which we should not dereference!
+ Pos invalid;
+
+ // constructing without access violation.
+ ErrPos ep(invalid);
+
+ // assignment without access violation.
+ ep = invalid;
+
+ }
+
+}
+
+#endif
diff --git a/tests/unit/libutil/lru-cache.cc b/tests/unit/libutil/lru-cache.cc
new file mode 100644
index 000000000..091d3d5ed
--- /dev/null
+++ b/tests/unit/libutil/lru-cache.cc
@@ -0,0 +1,130 @@
+#include "lru-cache.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * size
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, sizeOfEmptyCacheIsZero) {
+ LRUCache<std::string, std::string> c(10);
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ TEST(LRUCache, sizeOfSingleElementCacheIsOne) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("foo", "bar");
+ ASSERT_EQ(c.size(), 1);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * upsert / get
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, getFromEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ auto val = c.get("x");
+ ASSERT_EQ(val.has_value(), false);
+ }
+
+ TEST(LRUCache, getExistingValue) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("foo", "bar");
+ auto val = c.get("foo");
+ ASSERT_EQ(val, "bar");
+ }
+
+ TEST(LRUCache, getNonExistingValueFromNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("foo", "bar");
+ auto val = c.get("another");
+ ASSERT_EQ(val.has_value(), false);
+ }
+
+ TEST(LRUCache, upsertOnZeroCapacityCache) {
+ LRUCache<std::string, std::string> c(0);
+ c.upsert("foo", "bar");
+ auto val = c.get("foo");
+ ASSERT_EQ(val.has_value(), false);
+ }
+
+ TEST(LRUCache, updateExistingValue) {
+ LRUCache<std::string, std::string> c(1);
+ c.upsert("foo", "bar");
+
+ auto val = c.get("foo");
+ ASSERT_EQ(val.value_or("error"), "bar");
+ ASSERT_EQ(c.size(), 1);
+
+ c.upsert("foo", "changed");
+ val = c.get("foo");
+ ASSERT_EQ(val.value_or("error"), "changed");
+ ASSERT_EQ(c.size(), 1);
+ }
+
+ TEST(LRUCache, overwriteOldestWhenCapacityIsReached) {
+ LRUCache<std::string, std::string> c(3);
+ c.upsert("one", "eins");
+ c.upsert("two", "zwei");
+ c.upsert("three", "drei");
+
+ ASSERT_EQ(c.size(), 3);
+ ASSERT_EQ(c.get("one").value_or("error"), "eins");
+
+ // exceed capacity
+ c.upsert("another", "whatever");
+
+ ASSERT_EQ(c.size(), 3);
+ // Retrieving "one" makes it the most recent element thus
+ // two will be the oldest one and thus replaced.
+ ASSERT_EQ(c.get("two").has_value(), false);
+ ASSERT_EQ(c.get("another").value(), "whatever");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * clear
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, clearEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.clear();
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ TEST(LRUCache, clearNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("one", "eins");
+ c.upsert("two", "zwei");
+ c.upsert("three", "drei");
+ ASSERT_EQ(c.size(), 3);
+ c.clear();
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * erase
+ * --------------------------------------------------------------------------*/
+
+ TEST(LRUCache, eraseFromEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ ASSERT_EQ(c.erase("foo"), false);
+ ASSERT_EQ(c.size(), 0);
+ }
+
+ TEST(LRUCache, eraseMissingFromNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("one", "eins");
+ ASSERT_EQ(c.erase("foo"), false);
+ ASSERT_EQ(c.size(), 1);
+ ASSERT_EQ(c.get("one").value_or("error"), "eins");
+ }
+
+ TEST(LRUCache, eraseFromNonEmptyCache) {
+ LRUCache<std::string, std::string> c(10);
+ c.upsert("one", "eins");
+ ASSERT_EQ(c.erase("one"), true);
+ ASSERT_EQ(c.size(), 0);
+ ASSERT_EQ(c.get("one").value_or("empty"), "empty");
+ }
+}
diff --git a/tests/unit/libutil/pool.cc b/tests/unit/libutil/pool.cc
new file mode 100644
index 000000000..127e42dda
--- /dev/null
+++ b/tests/unit/libutil/pool.cc
@@ -0,0 +1,127 @@
+#include "pool.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ struct TestResource
+ {
+
+ TestResource() {
+ static int counter = 0;
+ num = counter++;
+ }
+
+ int dummyValue = 1;
+ bool good = true;
+ int num;
+ };
+
+ /* ----------------------------------------------------------------------------
+ * Pool
+ * --------------------------------------------------------------------------*/
+
+ TEST(Pool, freshPoolHasZeroCountAndSpecifiedCapacity) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ ASSERT_EQ(pool.count(), 0);
+ ASSERT_EQ(pool.capacity(), 1);
+ }
+
+ TEST(Pool, freshPoolCanGetAResource) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+ ASSERT_EQ(pool.count(), 0);
+
+ TestResource r = *(pool.get());
+
+ ASSERT_EQ(pool.count(), 1);
+ ASSERT_EQ(pool.capacity(), 1);
+ ASSERT_EQ(r.dummyValue, 1);
+ ASSERT_EQ(r.good, true);
+ }
+
+ TEST(Pool, capacityCanBeIncremented) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+ ASSERT_EQ(pool.capacity(), 1);
+ pool.incCapacity();
+ ASSERT_EQ(pool.capacity(), 2);
+ }
+
+ TEST(Pool, capacityCanBeDecremented) {
+ auto isGood = [](const ref<TestResource> & r) { return r->good; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+ ASSERT_EQ(pool.capacity(), 1);
+ pool.decCapacity();
+ ASSERT_EQ(pool.capacity(), 0);
+ }
+
+ TEST(Pool, flushBadDropsOutOfScopeResources) {
+ auto isGood = [](const ref<TestResource> & r) { return false; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ {
+ auto _r = pool.get();
+ ASSERT_EQ(pool.count(), 1);
+ }
+
+ pool.flushBad();
+ ASSERT_EQ(pool.count(), 0);
+ }
+
+ // Test that the resources we allocate are being reused when they are still good.
+ TEST(Pool, reuseResource) {
+ auto isGood = [](const ref<TestResource> & r) { return true; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ // Compare the instance counter between the two handles. We expect them to be equal
+ // as the pool should hand out the same (still) good one again.
+ int counter = -1;
+ {
+ Pool<TestResource>::Handle h = pool.get();
+ counter = h->num;
+ } // the first handle goes out of scope
+
+ { // the second handle should contain the same resource (with the same counter value)
+ Pool<TestResource>::Handle h = pool.get();
+ ASSERT_EQ(h->num, counter);
+ }
+ }
+
+ // Test that the resources we allocate are being thrown away when they are no longer good.
+ TEST(Pool, badResourceIsNotReused) {
+ auto isGood = [](const ref<TestResource> & r) { return false; };
+ auto createResource = []() { return make_ref<TestResource>(); };
+
+ Pool<TestResource> pool = Pool<TestResource>((size_t)1, createResource, isGood);
+
+ // Compare the instance counter between the two handles. We expect them
+ // to *not* be equal as the pool should hand out a new instance after
+ // the first one was returned.
+ int counter = -1;
+ {
+ Pool<TestResource>::Handle h = pool.get();
+ counter = h->num;
+ } // the first handle goes out of scope
+
+ {
+ // the second handle should contain a different resource (with a
+ //different counter value)
+ Pool<TestResource>::Handle h = pool.get();
+ ASSERT_NE(h->num, counter);
+ }
+ }
+}
diff --git a/tests/unit/libutil/references.cc b/tests/unit/libutil/references.cc
new file mode 100644
index 000000000..a517d9aa1
--- /dev/null
+++ b/tests/unit/libutil/references.cc
@@ -0,0 +1,46 @@
+#include "references.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+using std::string;
+
+struct RewriteParams {
+ string originalString, finalString;
+ StringMap rewrites;
+
+ friend std::ostream& operator<<(std::ostream& os, const RewriteParams& bar) {
+ StringSet strRewrites;
+ for (auto & [from, to] : bar.rewrites)
+ strRewrites.insert(from + "->" + to);
+ return os <<
+ "OriginalString: " << bar.originalString << std::endl <<
+ "Rewrites: " << concatStringsSep(",", strRewrites) << std::endl <<
+ "Expected result: " << bar.finalString;
+ }
+};
+
+class RewriteTest : public ::testing::TestWithParam<RewriteParams> {
+};
+
+TEST_P(RewriteTest, IdentityRewriteIsIdentity) {
+ RewriteParams param = GetParam();
+ StringSink rewritten;
+ auto rewriter = RewritingSink(param.rewrites, rewritten);
+ rewriter(param.originalString);
+ rewriter.flush();
+ ASSERT_EQ(rewritten.s, param.finalString);
+}
+
+INSTANTIATE_TEST_CASE_P(
+ references,
+ RewriteTest,
+ ::testing::Values(
+ RewriteParams{ "foooo", "baroo", {{"foo", "bar"}, {"bar", "baz"}}},
+ RewriteParams{ "foooo", "bazoo", {{"fou", "bar"}, {"foo", "baz"}}},
+ RewriteParams{ "foooo", "foooo", {}}
+ )
+);
+
+}
+
diff --git a/tests/unit/libutil/suggestions.cc b/tests/unit/libutil/suggestions.cc
new file mode 100644
index 000000000..279994abc
--- /dev/null
+++ b/tests/unit/libutil/suggestions.cc
@@ -0,0 +1,43 @@
+#include "suggestions.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+ struct LevenshteinDistanceParam {
+ std::string s1, s2;
+ int distance;
+ };
+
+ class LevenshteinDistanceTest :
+ public testing::TestWithParam<LevenshteinDistanceParam> {
+ };
+
+ TEST_P(LevenshteinDistanceTest, CorrectlyComputed) {
+ auto params = GetParam();
+
+ ASSERT_EQ(levenshteinDistance(params.s1, params.s2), params.distance);
+ ASSERT_EQ(levenshteinDistance(params.s2, params.s1), params.distance);
+ }
+
+ INSTANTIATE_TEST_SUITE_P(LevenshteinDistance, LevenshteinDistanceTest,
+ testing::Values(
+ LevenshteinDistanceParam{"foo", "foo", 0},
+ LevenshteinDistanceParam{"foo", "", 3},
+ LevenshteinDistanceParam{"", "", 0},
+ LevenshteinDistanceParam{"foo", "fo", 1},
+ LevenshteinDistanceParam{"foo", "oo", 1},
+ LevenshteinDistanceParam{"foo", "fao", 1},
+ LevenshteinDistanceParam{"foo", "abc", 3}
+ )
+ );
+
+ TEST(Suggestions, Trim) {
+ auto suggestions = Suggestions::bestMatches({"foooo", "bar", "fo", "gao"}, "foo");
+ auto onlyOne = suggestions.trim(1);
+ ASSERT_EQ(onlyOne.suggestions.size(), 1);
+ ASSERT_TRUE(onlyOne.suggestions.begin()->suggestion == "fo");
+
+ auto closest = suggestions.trim(999, 2);
+ ASSERT_EQ(closest.suggestions.size(), 3);
+ }
+}
diff --git a/tests/unit/libutil/tests.cc b/tests/unit/libutil/tests.cc
new file mode 100644
index 000000000..f3c1e8248
--- /dev/null
+++ b/tests/unit/libutil/tests.cc
@@ -0,0 +1,659 @@
+#include "util.hh"
+#include "types.hh"
+
+#include <limits.h>
+#include <gtest/gtest.h>
+
+#include <numeric>
+
+namespace nix {
+
+/* ----------- tests for util.hh ------------------------------------------------*/
+
+ /* ----------------------------------------------------------------------------
+ * absPath
+ * --------------------------------------------------------------------------*/
+
+ TEST(absPath, doesntChangeRoot) {
+ auto p = absPath("/");
+
+ ASSERT_EQ(p, "/");
+ }
+
+
+
+
+ TEST(absPath, turnsEmptyPathIntoCWD) {
+ char cwd[PATH_MAX+1];
+ auto p = absPath("");
+
+ ASSERT_EQ(p, getcwd((char*)&cwd, PATH_MAX));
+ }
+
+ TEST(absPath, usesOptionalBasePathWhenGiven) {
+ char _cwd[PATH_MAX+1];
+ char* cwd = getcwd((char*)&_cwd, PATH_MAX);
+
+ auto p = absPath("", cwd);
+
+ ASSERT_EQ(p, cwd);
+ }
+
+ TEST(absPath, isIdempotent) {
+ char _cwd[PATH_MAX+1];
+ char* cwd = getcwd((char*)&_cwd, PATH_MAX);
+ auto p1 = absPath(cwd);
+ auto p2 = absPath(p1);
+
+ ASSERT_EQ(p1, p2);
+ }
+
+
+ TEST(absPath, pathIsCanonicalised) {
+ auto path = "/some/path/with/trailing/dot/.";
+ auto p1 = absPath(path);
+ auto p2 = absPath(p1);
+
+ ASSERT_EQ(p1, "/some/path/with/trailing/dot");
+ ASSERT_EQ(p1, p2);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * canonPath
+ * --------------------------------------------------------------------------*/
+
+ TEST(canonPath, removesTrailingSlashes) {
+ auto path = "/this/is/a/path//";
+ auto p = canonPath(path);
+
+ ASSERT_EQ(p, "/this/is/a/path");
+ }
+
+ TEST(canonPath, removesDots) {
+ auto path = "/this/./is/a/path/./";
+ auto p = canonPath(path);
+
+ ASSERT_EQ(p, "/this/is/a/path");
+ }
+
+ TEST(canonPath, removesDots2) {
+ auto path = "/this/a/../is/a////path/foo/..";
+ auto p = canonPath(path);
+
+ ASSERT_EQ(p, "/this/is/a/path");
+ }
+
+ TEST(canonPath, requiresAbsolutePath) {
+ ASSERT_ANY_THROW(canonPath("."));
+ ASSERT_ANY_THROW(canonPath(".."));
+ ASSERT_ANY_THROW(canonPath("../"));
+ ASSERT_DEATH({ canonPath(""); }, "path != \"\"");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * dirOf
+ * --------------------------------------------------------------------------*/
+
+ TEST(dirOf, returnsEmptyStringForRoot) {
+ auto p = dirOf("/");
+
+ ASSERT_EQ(p, "/");
+ }
+
+ TEST(dirOf, returnsFirstPathComponent) {
+ auto p1 = dirOf("/dir/");
+ ASSERT_EQ(p1, "/dir");
+ auto p2 = dirOf("/dir");
+ ASSERT_EQ(p2, "/");
+ auto p3 = dirOf("/dir/..");
+ ASSERT_EQ(p3, "/dir");
+ auto p4 = dirOf("/dir/../");
+ ASSERT_EQ(p4, "/dir/..");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * baseNameOf
+ * --------------------------------------------------------------------------*/
+
+ TEST(baseNameOf, emptyPath) {
+ auto p1 = baseNameOf("");
+ ASSERT_EQ(p1, "");
+ }
+
+ TEST(baseNameOf, pathOnRoot) {
+ auto p1 = baseNameOf("/dir");
+ ASSERT_EQ(p1, "dir");
+ }
+
+ TEST(baseNameOf, relativePath) {
+ auto p1 = baseNameOf("dir/foo");
+ ASSERT_EQ(p1, "foo");
+ }
+
+ TEST(baseNameOf, pathWithTrailingSlashRoot) {
+ auto p1 = baseNameOf("/");
+ ASSERT_EQ(p1, "");
+ }
+
+ TEST(baseNameOf, trailingSlash) {
+ auto p1 = baseNameOf("/dir/");
+ ASSERT_EQ(p1, "dir");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * isInDir
+ * --------------------------------------------------------------------------*/
+
+ TEST(isInDir, trivialCase) {
+ auto p1 = isInDir("/foo/bar", "/foo");
+ ASSERT_EQ(p1, true);
+ }
+
+ TEST(isInDir, notInDir) {
+ auto p1 = isInDir("/zes/foo/bar", "/foo");
+ ASSERT_EQ(p1, false);
+ }
+
+ // XXX: hm, bug or feature? :) Looking at the implementation
+ // this might be problematic.
+ TEST(isInDir, emptyDir) {
+ auto p1 = isInDir("/zes/foo/bar", "");
+ ASSERT_EQ(p1, true);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * isDirOrInDir
+ * --------------------------------------------------------------------------*/
+
+ TEST(isDirOrInDir, trueForSameDirectory) {
+ ASSERT_EQ(isDirOrInDir("/nix", "/nix"), true);
+ ASSERT_EQ(isDirOrInDir("/", "/"), true);
+ }
+
+ TEST(isDirOrInDir, trueForEmptyPaths) {
+ ASSERT_EQ(isDirOrInDir("", ""), true);
+ }
+
+ TEST(isDirOrInDir, falseForDisjunctPaths) {
+ ASSERT_EQ(isDirOrInDir("/foo", "/bar"), false);
+ }
+
+ TEST(isDirOrInDir, relativePaths) {
+ ASSERT_EQ(isDirOrInDir("/foo/..", "/foo"), true);
+ }
+
+ // XXX: while it is possible to use "." or ".." in the
+ // first argument this doesn't seem to work in the second.
+ TEST(isDirOrInDir, DISABLED_shouldWork) {
+ ASSERT_EQ(isDirOrInDir("/foo/..", "/foo/."), true);
+
+ }
+
+ /* ----------------------------------------------------------------------------
+ * pathExists
+ * --------------------------------------------------------------------------*/
+
+ TEST(pathExists, rootExists) {
+ ASSERT_TRUE(pathExists("/"));
+ }
+
+ TEST(pathExists, cwdExists) {
+ ASSERT_TRUE(pathExists("."));
+ }
+
+ TEST(pathExists, bogusPathDoesNotExist) {
+ ASSERT_FALSE(pathExists("/schnitzel/darmstadt/pommes"));
+ }
+
+ /* ----------------------------------------------------------------------------
+ * concatStringsSep
+ * --------------------------------------------------------------------------*/
+
+ TEST(concatStringsSep, buildCommaSeparatedString) {
+ Strings strings;
+ strings.push_back("this");
+ strings.push_back("is");
+ strings.push_back("great");
+
+ ASSERT_EQ(concatStringsSep(",", strings), "this,is,great");
+ }
+
+ TEST(concatStringsSep, buildStringWithEmptySeparator) {
+ Strings strings;
+ strings.push_back("this");
+ strings.push_back("is");
+ strings.push_back("great");
+
+ ASSERT_EQ(concatStringsSep("", strings), "thisisgreat");
+ }
+
+ TEST(concatStringsSep, buildSingleString) {
+ Strings strings;
+ strings.push_back("this");
+
+ ASSERT_EQ(concatStringsSep(",", strings), "this");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * hasPrefix
+ * --------------------------------------------------------------------------*/
+
+ TEST(hasPrefix, emptyStringHasNoPrefix) {
+ ASSERT_FALSE(hasPrefix("", "foo"));
+ }
+
+ TEST(hasPrefix, emptyStringIsAlwaysPrefix) {
+ ASSERT_TRUE(hasPrefix("foo", ""));
+ ASSERT_TRUE(hasPrefix("jshjkfhsadf", ""));
+ }
+
+ TEST(hasPrefix, trivialCase) {
+ ASSERT_TRUE(hasPrefix("foobar", "foo"));
+ }
+
+ /* ----------------------------------------------------------------------------
+ * hasSuffix
+ * --------------------------------------------------------------------------*/
+
+ TEST(hasSuffix, emptyStringHasNoSuffix) {
+ ASSERT_FALSE(hasSuffix("", "foo"));
+ }
+
+ TEST(hasSuffix, trivialCase) {
+ ASSERT_TRUE(hasSuffix("foo", "foo"));
+ ASSERT_TRUE(hasSuffix("foobar", "bar"));
+ }
+
+ /* ----------------------------------------------------------------------------
+ * base64Encode
+ * --------------------------------------------------------------------------*/
+
+ TEST(base64Encode, emptyString) {
+ ASSERT_EQ(base64Encode(""), "");
+ }
+
+ TEST(base64Encode, encodesAString) {
+ ASSERT_EQ(base64Encode("quod erat demonstrandum"), "cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0=");
+ }
+
+ TEST(base64Encode, encodeAndDecode) {
+ auto s = "quod erat demonstrandum";
+ auto encoded = base64Encode(s);
+ auto decoded = base64Decode(encoded);
+
+ ASSERT_EQ(decoded, s);
+ }
+
+ TEST(base64Encode, encodeAndDecodeNonPrintable) {
+ char s[256];
+ std::iota(std::rbegin(s), std::rend(s), 0);
+
+ auto encoded = base64Encode(s);
+ auto decoded = base64Decode(encoded);
+
+ EXPECT_EQ(decoded.length(), 255);
+ ASSERT_EQ(decoded, s);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * base64Decode
+ * --------------------------------------------------------------------------*/
+
+ TEST(base64Decode, emptyString) {
+ ASSERT_EQ(base64Decode(""), "");
+ }
+
+ TEST(base64Decode, decodeAString) {
+ ASSERT_EQ(base64Decode("cXVvZCBlcmF0IGRlbW9uc3RyYW5kdW0="), "quod erat demonstrandum");
+ }
+
+ TEST(base64Decode, decodeThrowsOnInvalidChar) {
+ ASSERT_THROW(base64Decode("cXVvZCBlcm_0IGRlbW9uc3RyYW5kdW0="), Error);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * getLine
+ * --------------------------------------------------------------------------*/
+
+ TEST(getLine, all) {
+ {
+ auto [line, rest] = getLine("foo\nbar\nxyzzy");
+ ASSERT_EQ(line, "foo");
+ ASSERT_EQ(rest, "bar\nxyzzy");
+ }
+
+ {
+ auto [line, rest] = getLine("foo\r\nbar\r\nxyzzy");
+ ASSERT_EQ(line, "foo");
+ ASSERT_EQ(rest, "bar\r\nxyzzy");
+ }
+
+ {
+ auto [line, rest] = getLine("foo\n");
+ ASSERT_EQ(line, "foo");
+ ASSERT_EQ(rest, "");
+ }
+
+ {
+ auto [line, rest] = getLine("foo");
+ ASSERT_EQ(line, "foo");
+ ASSERT_EQ(rest, "");
+ }
+
+ {
+ auto [line, rest] = getLine("");
+ ASSERT_EQ(line, "");
+ ASSERT_EQ(rest, "");
+ }
+ }
+
+ /* ----------------------------------------------------------------------------
+ * toLower
+ * --------------------------------------------------------------------------*/
+
+ TEST(toLower, emptyString) {
+ ASSERT_EQ(toLower(""), "");
+ }
+
+ TEST(toLower, nonLetters) {
+ auto s = "!@(*$#)(@#=\\234_";
+ ASSERT_EQ(toLower(s), s);
+ }
+
+ // std::tolower() doesn't handle unicode characters. In the context of
+ // store paths this isn't relevant but doesn't hurt to record this behavior
+ // here.
+ TEST(toLower, umlauts) {
+ auto s = "ÄÖÜ";
+ ASSERT_EQ(toLower(s), "ÄÖÜ");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * string2Float
+ * --------------------------------------------------------------------------*/
+
+ TEST(string2Float, emptyString) {
+ ASSERT_EQ(string2Float<double>(""), std::nullopt);
+ }
+
+ TEST(string2Float, trivialConversions) {
+ ASSERT_EQ(string2Float<double>("1.0"), 1.0);
+
+ ASSERT_EQ(string2Float<double>("0.0"), 0.0);
+
+ ASSERT_EQ(string2Float<double>("-100.25"), -100.25);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * string2Int
+ * --------------------------------------------------------------------------*/
+
+ TEST(string2Int, emptyString) {
+ ASSERT_EQ(string2Int<int>(""), std::nullopt);
+ }
+
+ TEST(string2Int, trivialConversions) {
+ ASSERT_EQ(string2Int<int>("1"), 1);
+
+ ASSERT_EQ(string2Int<int>("0"), 0);
+
+ ASSERT_EQ(string2Int<int>("-100"), -100);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * statusOk
+ * --------------------------------------------------------------------------*/
+
+ TEST(statusOk, zeroIsOk) {
+ ASSERT_EQ(statusOk(0), true);
+ ASSERT_EQ(statusOk(1), false);
+ }
+
+
+ /* ----------------------------------------------------------------------------
+ * rewriteStrings
+ * --------------------------------------------------------------------------*/
+
+ TEST(rewriteStrings, emptyString) {
+ StringMap rewrites;
+ rewrites["this"] = "that";
+
+ ASSERT_EQ(rewriteStrings("", rewrites), "");
+ }
+
+ TEST(rewriteStrings, emptyRewrites) {
+ StringMap rewrites;
+
+ ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that");
+ }
+
+ TEST(rewriteStrings, successfulRewrite) {
+ StringMap rewrites;
+ rewrites["this"] = "that";
+
+ ASSERT_EQ(rewriteStrings("this and that", rewrites), "that and that");
+ }
+
+ TEST(rewriteStrings, doesntOccur) {
+ StringMap rewrites;
+ rewrites["foo"] = "bar";
+
+ ASSERT_EQ(rewriteStrings("this and that", rewrites), "this and that");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * replaceStrings
+ * --------------------------------------------------------------------------*/
+
+ TEST(replaceStrings, emptyString) {
+ ASSERT_EQ(replaceStrings("", "this", "that"), "");
+ ASSERT_EQ(replaceStrings("this and that", "", ""), "this and that");
+ }
+
+ TEST(replaceStrings, successfulReplace) {
+ ASSERT_EQ(replaceStrings("this and that", "this", "that"), "that and that");
+ }
+
+ TEST(replaceStrings, doesntOccur) {
+ ASSERT_EQ(replaceStrings("this and that", "foo", "bar"), "this and that");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * trim
+ * --------------------------------------------------------------------------*/
+
+ TEST(trim, emptyString) {
+ ASSERT_EQ(trim(""), "");
+ }
+
+ TEST(trim, removesWhitespace) {
+ ASSERT_EQ(trim("foo"), "foo");
+ ASSERT_EQ(trim(" foo "), "foo");
+ ASSERT_EQ(trim(" foo bar baz"), "foo bar baz");
+ ASSERT_EQ(trim(" \t foo bar baz\n"), "foo bar baz");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * chomp
+ * --------------------------------------------------------------------------*/
+
+ TEST(chomp, emptyString) {
+ ASSERT_EQ(chomp(""), "");
+ }
+
+ TEST(chomp, removesWhitespace) {
+ ASSERT_EQ(chomp("foo"), "foo");
+ ASSERT_EQ(chomp("foo "), "foo");
+ ASSERT_EQ(chomp(" foo "), " foo");
+ ASSERT_EQ(chomp(" foo bar baz "), " foo bar baz");
+ ASSERT_EQ(chomp("\t foo bar baz\n"), "\t foo bar baz");
+ }
+
+ /* ----------------------------------------------------------------------------
+ * quoteStrings
+ * --------------------------------------------------------------------------*/
+
+ TEST(quoteStrings, empty) {
+ Strings s = { };
+ Strings expected = { };
+
+ ASSERT_EQ(quoteStrings(s), expected);
+ }
+
+ TEST(quoteStrings, emptyStrings) {
+ Strings s = { "", "", "" };
+ Strings expected = { "''", "''", "''" };
+ ASSERT_EQ(quoteStrings(s), expected);
+
+ }
+
+ TEST(quoteStrings, trivialQuote) {
+ Strings s = { "foo", "bar", "baz" };
+ Strings expected = { "'foo'", "'bar'", "'baz'" };
+
+ ASSERT_EQ(quoteStrings(s), expected);
+ }
+
+ TEST(quoteStrings, quotedStrings) {
+ Strings s = { "'foo'", "'bar'", "'baz'" };
+ Strings expected = { "''foo''", "''bar''", "''baz''" };
+
+ ASSERT_EQ(quoteStrings(s), expected);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * tokenizeString
+ * --------------------------------------------------------------------------*/
+
+ TEST(tokenizeString, empty) {
+ Strings expected = { };
+
+ ASSERT_EQ(tokenizeString<Strings>(""), expected);
+ }
+
+ TEST(tokenizeString, tokenizeSpacesWithDefaults) {
+ auto s = "foo bar baz";
+ Strings expected = { "foo", "bar", "baz" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s), expected);
+ }
+
+ TEST(tokenizeString, tokenizeTabsWithDefaults) {
+ auto s = "foo\tbar\tbaz";
+ Strings expected = { "foo", "bar", "baz" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s), expected);
+ }
+
+ TEST(tokenizeString, tokenizeTabsSpacesWithDefaults) {
+ auto s = "foo\t bar\t baz";
+ Strings expected = { "foo", "bar", "baz" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s), expected);
+ }
+
+ TEST(tokenizeString, tokenizeTabsSpacesNewlineWithDefaults) {
+ auto s = "foo\t\n bar\t\n baz";
+ Strings expected = { "foo", "bar", "baz" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s), expected);
+ }
+
+ TEST(tokenizeString, tokenizeTabsSpacesNewlineRetWithDefaults) {
+ auto s = "foo\t\n\r bar\t\n\r baz";
+ Strings expected = { "foo", "bar", "baz" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s), expected);
+
+ auto s2 = "foo \t\n\r bar \t\n\r baz";
+ Strings expected2 = { "foo", "bar", "baz" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s2), expected2);
+ }
+
+ TEST(tokenizeString, tokenizeWithCustomSep) {
+ auto s = "foo\n,bar\n,baz\n";
+ Strings expected = { "foo\n", "bar\n", "baz\n" };
+
+ ASSERT_EQ(tokenizeString<Strings>(s, ","), expected);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * get
+ * --------------------------------------------------------------------------*/
+
+ TEST(get, emptyContainer) {
+ StringMap s = { };
+ auto expected = nullptr;
+
+ ASSERT_EQ(get(s, "one"), expected);
+ }
+
+ TEST(get, getFromContainer) {
+ StringMap s;
+ s["one"] = "yi";
+ s["two"] = "er";
+ auto expected = "yi";
+
+ ASSERT_EQ(*get(s, "one"), expected);
+ }
+
+ TEST(getOr, emptyContainer) {
+ StringMap s = { };
+ auto expected = "yi";
+
+ ASSERT_EQ(getOr(s, "one", "yi"), expected);
+ }
+
+ TEST(getOr, getFromContainer) {
+ StringMap s;
+ s["one"] = "yi";
+ s["two"] = "er";
+ auto expected = "yi";
+
+ ASSERT_EQ(getOr(s, "one", "nope"), expected);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * filterANSIEscapes
+ * --------------------------------------------------------------------------*/
+
+ TEST(filterANSIEscapes, emptyString) {
+ auto s = "";
+ auto expected = "";
+
+ ASSERT_EQ(filterANSIEscapes(s), expected);
+ }
+
+ TEST(filterANSIEscapes, doesntChangePrintableChars) {
+ auto s = "09 2q304ruyhr slk2-19024 kjsadh sar f";
+
+ ASSERT_EQ(filterANSIEscapes(s), s);
+ }
+
+ TEST(filterANSIEscapes, filtersColorCodes) {
+ auto s = "\u001b[30m A \u001b[31m B \u001b[32m C \u001b[33m D \u001b[0m";
+
+ ASSERT_EQ(filterANSIEscapes(s, true, 2), " A" );
+ ASSERT_EQ(filterANSIEscapes(s, true, 3), " A " );
+ ASSERT_EQ(filterANSIEscapes(s, true, 4), " A " );
+ ASSERT_EQ(filterANSIEscapes(s, true, 5), " A B" );
+ ASSERT_EQ(filterANSIEscapes(s, true, 8), " A B C" );
+ }
+
+ TEST(filterANSIEscapes, expandsTabs) {
+ auto s = "foo\tbar\tbaz";
+
+ ASSERT_EQ(filterANSIEscapes(s, true), "foo bar baz" );
+ }
+
+ TEST(filterANSIEscapes, utf8) {
+ ASSERT_EQ(filterANSIEscapes("foobar", true, 5), "fooba");
+ ASSERT_EQ(filterANSIEscapes("fóóbär", true, 6), "fóóbär");
+ ASSERT_EQ(filterANSIEscapes("fóóbär", true, 5), "fóóbä");
+ ASSERT_EQ(filterANSIEscapes("fóóbär", true, 3), "fóó");
+ ASSERT_EQ(filterANSIEscapes("f€€bär", true, 4), "f€€b");
+ ASSERT_EQ(filterANSIEscapes("f𐍈𐍈bär", true, 4), "f𐍈𐍈b");
+ }
+
+}
diff --git a/tests/unit/libutil/url.cc b/tests/unit/libutil/url.cc
new file mode 100644
index 000000000..a908631e6
--- /dev/null
+++ b/tests/unit/libutil/url.cc
@@ -0,0 +1,338 @@
+#include "url.hh"
+#include <gtest/gtest.h>
+
+namespace nix {
+
+/* ----------- tests for url.hh --------------------------------------------------*/
+
+ std::string print_map(std::map<std::string, std::string> m) {
+ std::map<std::string, std::string>::iterator it;
+ std::string s = "{ ";
+ for (it = m.begin(); it != m.end(); ++it) {
+ s += "{ ";
+ s += it->first;
+ s += " = ";
+ s += it->second;
+ s += " } ";
+ }
+ s += "}";
+ return s;
+ }
+
+
+ std::ostream& operator<<(std::ostream& os, const ParsedURL& p) {
+ return os << "\n"
+ << "url: " << p.url << "\n"
+ << "base: " << p.base << "\n"
+ << "scheme: " << p.scheme << "\n"
+ << "authority: " << p.authority.value() << "\n"
+ << "path: " << p.path << "\n"
+ << "query: " << print_map(p.query) << "\n"
+ << "fragment: " << p.fragment << "\n";
+ }
+
+ TEST(parseURL, parsesSimpleHttpUrl) {
+ auto s = "http://www.example.org/file.tar.gz";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "http://www.example.org/file.tar.gz",
+ .base = "http://www.example.org/file.tar.gz",
+ .scheme = "http",
+ .authority = "www.example.org",
+ .path = "/file.tar.gz",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parsesSimpleHttpsUrl) {
+ auto s = "https://www.example.org/file.tar.gz";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "https://www.example.org/file.tar.gz",
+ .base = "https://www.example.org/file.tar.gz",
+ .scheme = "https",
+ .authority = "www.example.org",
+ .path = "/file.tar.gz",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parsesSimpleHttpUrlWithQueryAndFragment) {
+ auto s = "https://www.example.org/file.tar.gz?download=fast&when=now#hello";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "https://www.example.org/file.tar.gz",
+ .base = "https://www.example.org/file.tar.gz",
+ .scheme = "https",
+ .authority = "www.example.org",
+ .path = "/file.tar.gz",
+ .query = (StringMap) { { "download", "fast" }, { "when", "now" } },
+ .fragment = "hello",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parsesSimpleHttpUrlWithComplexFragment) {
+ auto s = "http://www.example.org/file.tar.gz?field=value#?foo=bar%23";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "http://www.example.org/file.tar.gz",
+ .base = "http://www.example.org/file.tar.gz",
+ .scheme = "http",
+ .authority = "www.example.org",
+ .path = "/file.tar.gz",
+ .query = (StringMap) { { "field", "value" } },
+ .fragment = "?foo=bar#",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parsesFilePlusHttpsUrl) {
+ auto s = "file+https://www.example.org/video.mp4";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "file+https://www.example.org/video.mp4",
+ .base = "https://www.example.org/video.mp4",
+ .scheme = "file+https",
+ .authority = "www.example.org",
+ .path = "/video.mp4",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, rejectsAuthorityInUrlsWithFileTransportation) {
+ auto s = "file://www.example.org/video.mp4";
+ ASSERT_THROW(parseURL(s), Error);
+ }
+
+ TEST(parseURL, parseIPv4Address) {
+ auto s = "http://127.0.0.1:8080/file.tar.gz?download=fast&when=now#hello";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "http://127.0.0.1:8080/file.tar.gz",
+ .base = "https://127.0.0.1:8080/file.tar.gz",
+ .scheme = "http",
+ .authority = "127.0.0.1:8080",
+ .path = "/file.tar.gz",
+ .query = (StringMap) { { "download", "fast" }, { "when", "now" } },
+ .fragment = "hello",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parseScopedRFC4007IPv6Address) {
+ auto s = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080",
+ .base = "http://[fe80::818c:da4d:8975:415c\%enp0s25]:8080",
+ .scheme = "http",
+ .authority = "[fe80::818c:da4d:8975:415c\%enp0s25]:8080",
+ .path = "",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+
+ }
+
+ TEST(parseURL, parseIPv6Address) {
+ auto s = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080",
+ .base = "http://[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080",
+ .scheme = "http",
+ .authority = "[2a02:8071:8192:c100:311d:192d:81ac:11ea]:8080",
+ .path = "",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+
+ }
+
+ TEST(parseURL, parseEmptyQueryParams) {
+ auto s = "http://127.0.0.1:8080/file.tar.gz?&&&&&";
+ auto parsed = parseURL(s);
+ ASSERT_EQ(parsed.query, (StringMap) { });
+ }
+
+ TEST(parseURL, parseUserPassword) {
+ auto s = "http://user:pass@www.example.org:8080/file.tar.gz";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "http://user:pass@www.example.org/file.tar.gz",
+ .base = "http://user:pass@www.example.org/file.tar.gz",
+ .scheme = "http",
+ .authority = "user:pass@www.example.org:8080",
+ .path = "/file.tar.gz",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parseFileURLWithQueryAndFragment) {
+ auto s = "file:///none/of//your/business";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "",
+ .base = "",
+ .scheme = "file",
+ .authority = "",
+ .path = "/none/of//your/business",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+
+ }
+
+ TEST(parseURL, parsedUrlsIsEqualToItself) {
+ auto s = "http://www.example.org/file.tar.gz";
+ auto url = parseURL(s);
+
+ ASSERT_TRUE(url == url);
+ }
+
+ TEST(parseURL, parseFTPUrl) {
+ auto s = "ftp://ftp.nixos.org/downloads/nixos.iso";
+ auto parsed = parseURL(s);
+
+ ParsedURL expected {
+ .url = "ftp://ftp.nixos.org/downloads/nixos.iso",
+ .base = "ftp://ftp.nixos.org/downloads/nixos.iso",
+ .scheme = "ftp",
+ .authority = "ftp.nixos.org",
+ .path = "/downloads/nixos.iso",
+ .query = (StringMap) { },
+ .fragment = "",
+ };
+
+ ASSERT_EQ(parsed, expected);
+ }
+
+ TEST(parseURL, parsesAnythingInUriFormat) {
+ auto s = "whatever://github.com/NixOS/nixpkgs.git";
+ auto parsed = parseURL(s);
+ }
+
+ TEST(parseURL, parsesAnythingInUriFormatWithoutDoubleSlash) {
+ auto s = "whatever:github.com/NixOS/nixpkgs.git";
+ auto parsed = parseURL(s);
+ }
+
+ TEST(parseURL, emptyStringIsInvalidURL) {
+ ASSERT_THROW(parseURL(""), Error);
+ }
+
+ /* ----------------------------------------------------------------------------
+ * decodeQuery
+ * --------------------------------------------------------------------------*/
+
+ TEST(decodeQuery, emptyStringYieldsEmptyMap) {
+ auto d = decodeQuery("");
+ ASSERT_EQ(d, (StringMap) { });
+ }
+
+ TEST(decodeQuery, simpleDecode) {
+ auto d = decodeQuery("yi=one&er=two");
+ ASSERT_EQ(d, ((StringMap) { { "yi", "one" }, { "er", "two" } }));
+ }
+
+ TEST(decodeQuery, decodeUrlEncodedArgs) {
+ auto d = decodeQuery("arg=%3D%3D%40%3D%3D");
+ ASSERT_EQ(d, ((StringMap) { { "arg", "==@==" } }));
+ }
+
+ TEST(decodeQuery, decodeArgWithEmptyValue) {
+ auto d = decodeQuery("arg=");
+ ASSERT_EQ(d, ((StringMap) { { "arg", ""} }));
+ }
+
+ /* ----------------------------------------------------------------------------
+ * percentDecode
+ * --------------------------------------------------------------------------*/
+
+ TEST(percentDecode, decodesUrlEncodedString) {
+ std::string s = "==@==";
+ std::string d = percentDecode("%3D%3D%40%3D%3D");
+ ASSERT_EQ(d, s);
+ }
+
+ TEST(percentDecode, multipleDecodesAreIdempotent) {
+ std::string once = percentDecode("%3D%3D%40%3D%3D");
+ std::string twice = percentDecode(once);
+
+ ASSERT_EQ(once, twice);
+ }
+
+ TEST(percentDecode, trailingPercent) {
+ std::string s = "==@==%";
+ std::string d = percentDecode("%3D%3D%40%3D%3D%25");
+
+ ASSERT_EQ(d, s);
+ }
+
+
+ /* ----------------------------------------------------------------------------
+ * percentEncode
+ * --------------------------------------------------------------------------*/
+
+ TEST(percentEncode, encodesUrlEncodedString) {
+ std::string s = percentEncode("==@==");
+ std::string d = "%3D%3D%40%3D%3D";
+ ASSERT_EQ(d, s);
+ }
+
+ TEST(percentEncode, keepArgument) {
+ std::string a = percentEncode("abd / def");
+ std::string b = percentEncode("abd / def", "/");
+ ASSERT_EQ(a, "abd%20%2F%20def");
+ ASSERT_EQ(b, "abd%20/%20def");
+ }
+
+ TEST(percentEncode, inverseOfDecode) {
+ std::string original = "%3D%3D%40%3D%3D";
+ std::string once = percentEncode(original);
+ std::string back = percentDecode(once);
+
+ ASSERT_EQ(back, original);
+ }
+
+ TEST(percentEncode, trailingPercent) {
+ std::string s = percentEncode("==@==%");
+ std::string d = "%3D%3D%40%3D%3D%25";
+
+ ASSERT_EQ(d, s);
+ }
+
+}
diff --git a/tests/unit/libutil/xml-writer.cc b/tests/unit/libutil/xml-writer.cc
new file mode 100644
index 000000000..adcde25c9
--- /dev/null
+++ b/tests/unit/libutil/xml-writer.cc
@@ -0,0 +1,105 @@
+#include "xml-writer.hh"
+#include <gtest/gtest.h>
+#include <sstream>
+
+namespace nix {
+
+ /* ----------------------------------------------------------------------------
+ * XMLWriter
+ * --------------------------------------------------------------------------*/
+
+ TEST(XMLWriter, emptyObject) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n");
+ }
+
+ TEST(XMLWriter, objectWithEmptyElement) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ t.openElement("foobar");
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithAttrs) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {
+ { "foo", "bar" }
+ };
+ t.openElement("foobar", attrs);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\"></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithEmptyAttrs) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {};
+ t.openElement("foobar", attrs);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithAttrsEscaping) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {
+ { "<key>", "<value>" }
+ };
+ t.openElement("foobar", attrs);
+ }
+
+ // XXX: While "<value>" is escaped, "<key>" isn't which I think is a bug.
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar <key>=\"&lt;value&gt;\"></foobar>");
+ }
+
+ TEST(XMLWriter, objectWithElementWithAttrsIndented) {
+ std::stringstream out;
+ {
+ XMLWriter t(true, out);
+ XMLAttrs attrs = {
+ { "foo", "bar" }
+ };
+ t.openElement("foobar", attrs);
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\">\n</foobar>\n");
+ }
+
+ TEST(XMLWriter, writeEmptyElement) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ t.writeEmptyElement("foobar");
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar />");
+ }
+
+ TEST(XMLWriter, writeEmptyElementWithAttributes) {
+ std::stringstream out;
+ {
+ XMLWriter t(false, out);
+ XMLAttrs attrs = {
+ { "foo", "bar" }
+ };
+ t.writeEmptyElement("foobar", attrs);
+
+ }
+
+ ASSERT_EQ(out.str(), "<?xml version='1.0' encoding='utf-8'?>\n<foobar foo=\"bar\" />");
+ }
+
+}