about summary refs log tree commit diff
path: root/tests
diff options
context:
space:
mode:
Diffstat (limited to 'tests')
-rw-r--r--tests/add.sh28
-rw-r--r--tests/binary-cache.sh89
-rw-r--r--tests/binary-patching.nix18
-rw-r--r--tests/binary-patching.sh61
-rwxr-xr-xtests/build-hook.hook.sh23
-rw-r--r--tests/build-hook.nix22
-rw-r--r--tests/build-hook.sh10
-rw-r--r--tests/check-refs.nix58
-rw-r--r--tests/check-refs.sh36
-rw-r--r--tests/common.sh.in84
-rw-r--r--tests/config.nix20
-rw-r--r--tests/dependencies.builder0.sh12
-rw-r--r--tests/dependencies.builder1.sh2
-rw-r--r--tests/dependencies.builder2.sh2
-rw-r--r--tests/dependencies.nix23
-rw-r--r--tests/dependencies.sh52
-rw-r--r--tests/export-graph.nix29
-rw-r--r--tests/export-graph.sh30
-rw-r--r--tests/export.sh31
-rw-r--r--tests/fallback.sh20
-rw-r--r--tests/fetchurl.nix6
-rw-r--r--tests/fetchurl.sh9
-rw-r--r--tests/filter-source.nix12
-rw-r--r--tests/filter-source.sh20
-rw-r--r--tests/fixed.builder1.sh3
-rw-r--r--tests/fixed.builder2.sh6
-rw-r--r--tests/fixed.nix50
-rw-r--r--tests/fixed.sh52
-rw-r--r--tests/gc-concurrent.builder.sh13
-rw-r--r--tests/gc-concurrent.nix27
-rw-r--r--tests/gc-concurrent.sh58
-rw-r--r--tests/gc-concurrent2.builder.sh7
-rw-r--r--tests/gc-runtime.nix17
-rw-r--r--tests/gc-runtime.sh38
-rw-r--r--tests/gc.sh38
-rw-r--r--tests/hash-check.nix29
-rw-r--r--tests/hash.sh62
-rw-r--r--tests/import-derivation.nix23
-rw-r--r--tests/import-derivation.sh12
-rw-r--r--tests/init.sh32
-rw-r--r--tests/install-package.sh21
-rw-r--r--tests/lang.sh64
-rw-r--r--tests/lang/dir1/a.nix1
-rw-r--r--tests/lang/dir2/a.nix1
-rw-r--r--tests/lang/dir2/b.nix1
-rw-r--r--tests/lang/dir3/a.nix1
-rw-r--r--tests/lang/dir3/b.nix1
-rw-r--r--tests/lang/dir3/c.nix1
-rw-r--r--tests/lang/dir4/a.nix1
-rw-r--r--tests/lang/dir4/c.nix1
-rw-r--r--tests/lang/eval-fail-abort.nix1
-rw-r--r--tests/lang/eval-fail-antiquoted-path.nix4
-rw-r--r--tests/lang/eval-fail-assert.nix5
-rw-r--r--tests/lang/eval-fail-bad-antiquote-1.nix1
-rw-r--r--tests/lang/eval-fail-bad-antiquote-2.nix1
-rw-r--r--tests/lang/eval-fail-bad-antiquote-3.nix1
-rw-r--r--tests/lang/eval-fail-blackhole.nix5
-rw-r--r--tests/lang/eval-fail-missing-arg.nix1
-rw-r--r--tests/lang/eval-fail-remove.nix5
-rw-r--r--tests/lang/eval-fail-scope-5.nix10
-rw-r--r--tests/lang/eval-fail-substring.nix1
-rw-r--r--tests/lang/eval-fail-to-path.nix1
-rw-r--r--tests/lang/eval-fail-undeclared-arg.nix1
-rw-r--r--tests/lang/eval-okay-arithmetic.exp1
-rw-r--r--tests/lang/eval-okay-arithmetic.nix60
-rw-r--r--tests/lang/eval-okay-attrnames.exp1
-rw-r--r--tests/lang/eval-okay-attrnames.nix11
-rw-r--r--tests/lang/eval-okay-attrs.exp1
-rw-r--r--tests/lang/eval-okay-attrs.nix5
-rw-r--r--tests/lang/eval-okay-attrs2.exp1
-rw-r--r--tests/lang/eval-okay-attrs2.nix10
-rw-r--r--tests/lang/eval-okay-attrs3.exp1
-rw-r--r--tests/lang/eval-okay-attrs3.nix22
-rw-r--r--tests/lang/eval-okay-attrs4.exp1
-rw-r--r--tests/lang/eval-okay-attrs4.nix7
-rw-r--r--tests/lang/eval-okay-attrs5.exp1
-rw-r--r--tests/lang/eval-okay-attrs5.nix21
-rw-r--r--tests/lang/eval-okay-autoargs.exp1
-rw-r--r--tests/lang/eval-okay-autoargs.flags1
-rw-r--r--tests/lang/eval-okay-autoargs.nix15
-rw-r--r--tests/lang/eval-okay-builtins.exp1
-rw-r--r--tests/lang/eval-okay-builtins.nix12
-rw-r--r--tests/lang/eval-okay-closure.exp.xml343
-rw-r--r--tests/lang/eval-okay-closure.nix13
-rw-r--r--tests/lang/eval-okay-concat.exp1
-rw-r--r--tests/lang/eval-okay-concat.nix1
-rw-r--r--tests/lang/eval-okay-context.exp1
-rw-r--r--tests/lang/eval-okay-context.nix6
-rw-r--r--tests/lang/eval-okay-curpos.exp1
-rw-r--r--tests/lang/eval-okay-curpos.nix5
-rw-r--r--tests/lang/eval-okay-delayed-with-inherit.exp1
-rw-r--r--tests/lang/eval-okay-delayed-with-inherit.nix24
-rw-r--r--tests/lang/eval-okay-delayed-with.exp1
-rw-r--r--tests/lang/eval-okay-delayed-with.nix29
-rw-r--r--tests/lang/eval-okay-dynamic-attrs-2.exp1
-rw-r--r--tests/lang/eval-okay-dynamic-attrs-2.nix1
-rw-r--r--tests/lang/eval-okay-dynamic-attrs-bare.exp1
-rw-r--r--tests/lang/eval-okay-dynamic-attrs-bare.nix17
-rw-r--r--tests/lang/eval-okay-dynamic-attrs.exp1
-rw-r--r--tests/lang/eval-okay-dynamic-attrs.nix17
-rw-r--r--tests/lang/eval-okay-empty-args.exp1
-rw-r--r--tests/lang/eval-okay-empty-args.nix1
-rw-r--r--tests/lang/eval-okay-eq-derivations.exp1
-rw-r--r--tests/lang/eval-okay-eq-derivations.nix10
-rw-r--r--tests/lang/eval-okay-eq.exp.disabled1
-rw-r--r--tests/lang/eval-okay-eq.nix3
-rw-r--r--tests/lang/eval-okay-flatten.exp1
-rw-r--r--tests/lang/eval-okay-flatten.nix8
-rw-r--r--tests/lang/eval-okay-functionargs.exp.xml15
-rw-r--r--tests/lang/eval-okay-functionargs.nix80
-rw-r--r--tests/lang/eval-okay-getattrpos.exp1
-rw-r--r--tests/lang/eval-okay-getattrpos.nix6
-rw-r--r--tests/lang/eval-okay-getenv.exp1
-rw-r--r--tests/lang/eval-okay-getenv.nix1
-rw-r--r--tests/lang/eval-okay-hash.exp1
-rw-r--r--tests/lang/eval-okay-hash.nix7
-rw-r--r--tests/lang/eval-okay-if.exp1
-rw-r--r--tests/lang/eval-okay-if.nix1
-rw-r--r--tests/lang/eval-okay-ind-string.exp1
-rw-r--r--tests/lang/eval-okay-ind-string.nix120
-rw-r--r--tests/lang/eval-okay-let.exp1
-rw-r--r--tests/lang/eval-okay-let.nix5
-rw-r--r--tests/lang/eval-okay-list.exp1
-rw-r--r--tests/lang/eval-okay-list.nix7
-rw-r--r--tests/lang/eval-okay-listtoattrs.exp1
-rw-r--r--tests/lang/eval-okay-listtoattrs.nix11
-rw-r--r--tests/lang/eval-okay-logic.exp1
-rw-r--r--tests/lang/eval-okay-logic.nix1
-rw-r--r--tests/lang/eval-okay-map.exp1
-rw-r--r--tests/lang/eval-okay-map.nix3
-rw-r--r--tests/lang/eval-okay-new-let.exp1
-rw-r--r--tests/lang/eval-okay-new-let.nix14
-rw-r--r--tests/lang/eval-okay-overrides.exp1
-rw-r--r--tests/lang/eval-okay-overrides.nix9
-rw-r--r--tests/lang/eval-okay-pathexists.exp1
-rw-r--r--tests/lang/eval-okay-pathexists.nix5
-rw-r--r--tests/lang/eval-okay-patterns.exp1
-rw-r--r--tests/lang/eval-okay-patterns.nix16
-rw-r--r--tests/lang/eval-okay-readfile.exp1
-rw-r--r--tests/lang/eval-okay-readfile.nix1
-rw-r--r--tests/lang/eval-okay-redefine-builtin.exp1
-rw-r--r--tests/lang/eval-okay-redefine-builtin.nix3
-rw-r--r--tests/lang/eval-okay-remove.exp1
-rw-r--r--tests/lang/eval-okay-remove.nix5
-rw-r--r--tests/lang/eval-okay-scope-1.exp1
-rw-r--r--tests/lang/eval-okay-scope-1.nix6
-rw-r--r--tests/lang/eval-okay-scope-2.exp1
-rw-r--r--tests/lang/eval-okay-scope-2.nix6
-rw-r--r--tests/lang/eval-okay-scope-3.exp1
-rw-r--r--tests/lang/eval-okay-scope-3.nix6
-rw-r--r--tests/lang/eval-okay-scope-4.exp1
-rw-r--r--tests/lang/eval-okay-scope-4.nix10
-rw-r--r--tests/lang/eval-okay-scope-6.exp1
-rw-r--r--tests/lang/eval-okay-scope-6.nix7
-rw-r--r--tests/lang/eval-okay-scope-7.exp1
-rw-r--r--tests/lang/eval-okay-scope-7.nix6
-rw-r--r--tests/lang/eval-okay-search-path.exp1
-rw-r--r--tests/lang/eval-okay-search-path.flags1
-rw-r--r--tests/lang/eval-okay-search-path.nix3
-rw-r--r--tests/lang/eval-okay-string.exp1
-rw-r--r--tests/lang/eval-okay-string.nix10
-rw-r--r--tests/lang/eval-okay-strings-as-attrs-names.exp1
-rw-r--r--tests/lang/eval-okay-strings-as-attrs-names.nix20
-rw-r--r--tests/lang/eval-okay-substring.exp1
-rw-r--r--tests/lang/eval-okay-substring.nix21
-rw-r--r--tests/lang/eval-okay-tail-call-1.exp-disabled1
-rw-r--r--tests/lang/eval-okay-tail-call-1.nix3
-rw-r--r--tests/lang/eval-okay-tojson.exp1
-rw-r--r--tests/lang/eval-okay-tojson.nix11
-rw-r--r--tests/lang/eval-okay-toxml.exp1
-rw-r--r--tests/lang/eval-okay-toxml.nix3
-rw-r--r--tests/lang/eval-okay-toxml2.exp1
-rw-r--r--tests/lang/eval-okay-toxml2.nix1
-rw-r--r--tests/lang/eval-okay-tryeval.exp1
-rw-r--r--tests/lang/eval-okay-tryeval.nix5
-rw-r--r--tests/lang/eval-okay-types.exp1
-rw-r--r--tests/lang/eval-okay-types.nix23
-rw-r--r--tests/lang/eval-okay-versions.exp1
-rw-r--r--tests/lang/eval-okay-versions.nix40
-rw-r--r--tests/lang/eval-okay-with.exp1
-rw-r--r--tests/lang/eval-okay-with.nix19
-rw-r--r--tests/lang/eval-okay-xml.exp.xml49
-rw-r--r--tests/lang/eval-okay-xml.nix19
-rw-r--r--tests/lang/lib.nix56
-rw-r--r--tests/lang/parse-fail-dup-attrs-1.nix4
-rw-r--r--tests/lang/parse-fail-dup-attrs-2.nix13
-rw-r--r--tests/lang/parse-fail-dup-attrs-3.nix13
-rw-r--r--tests/lang/parse-fail-dup-attrs-4.nix4
-rw-r--r--tests/lang/parse-fail-dup-attrs-6.nix4
-rw-r--r--tests/lang/parse-fail-dup-attrs-7.nix9
-rw-r--r--tests/lang/parse-fail-dup-formals.nix1
-rw-r--r--tests/lang/parse-fail-patterns-1.nix1
-rw-r--r--tests/lang/parse-fail-regression-20060610.nix11
-rw-r--r--tests/lang/parse-fail-undef-var-2.nix7
-rw-r--r--tests/lang/parse-fail-undef-var.nix1
-rw-r--r--tests/lang/parse-okay-1.nix1
-rw-r--r--tests/lang/parse-okay-crlf.nix17
-rw-r--r--tests/lang/parse-okay-dup-attrs-5.nix4
-rw-r--r--tests/lang/parse-okay-regression-20041027.nix11
-rw-r--r--tests/lang/parse-okay-subversion.nix43
-rw-r--r--tests/lang/parse-okay-url.nix7
-rw-r--r--tests/local.mk21
-rw-r--r--tests/logging.sh24
-rw-r--r--tests/misc.sh16
-rw-r--r--tests/multiple-outputs.nix68
-rw-r--r--tests/multiple-outputs.sh63
-rw-r--r--tests/negative-caching.nix21
-rw-r--r--tests/negative-caching.sh22
-rw-r--r--tests/nix-build.sh19
-rw-r--r--tests/nix-channel.sh43
-rw-r--r--tests/nix-copy-closure.nix54
-rw-r--r--tests/nix-profile.sh10
-rw-r--r--tests/nix-pull.sh33
-rw-r--r--tests/nix-push.sh12
-rw-r--r--tests/optimise-store.sh26
-rw-r--r--tests/parallel.builder.sh29
-rw-r--r--tests/parallel.nix19
-rw-r--r--tests/parallel.sh56
-rw-r--r--tests/referrers.sh36
-rw-r--r--tests/remote-builds.nix98
-rw-r--r--tests/remote-store.sh7
-rw-r--r--tests/secure-drv-outputs.nix23
-rw-r--r--tests/secure-drv-outputs.sh37
-rw-r--r--tests/simple.builder.sh11
-rw-r--r--tests/simple.nix8
-rw-r--r--tests/simple.sh25
-rwxr-xr-xtests/substituter.sh37
-rwxr-xr-xtests/substituter2.sh33
-rw-r--r--tests/substitutes.sh22
-rw-r--r--tests/substitutes2.sh21
-rw-r--r--tests/timeout.builder.sh2
-rw-r--r--tests/timeout.nix6
-rw-r--r--tests/timeout.sh21
-rw-r--r--tests/user-envs.builder.sh5
-rw-r--r--tests/user-envs.nix29
-rw-r--r--tests/user-envs.sh131
-rw-r--r--tests/verify.sh3
237 files changed, 3869 insertions, 0 deletions
diff --git a/tests/add.sh b/tests/add.sh
new file mode 100644
index 000000000000..e26e05843d7f
--- /dev/null
+++ b/tests/add.sh
@@ -0,0 +1,28 @@
+source common.sh
+
+path1=$(nix-store --add ./dummy)
+echo $path1
+
+path2=$(nix-store --add-fixed sha256 --recursive ./dummy)
+echo $path2
+
+if test "$path1" != "$path2"; then
+    echo "nix-store --add and --add-fixed mismatch"
+    exit 1
+fi    
+
+path3=$(nix-store --add-fixed sha256 ./dummy)
+echo $path3
+test "$path1" != "$path3" || exit 1
+
+path4=$(nix-store --add-fixed sha1 --recursive ./dummy)
+echo $path4
+test "$path1" != "$path4" || exit 1
+
+hash1=$(nix-store -q --hash $path1)
+echo $hash1
+
+hash2=$(nix-hash --type sha256 --base32 ./dummy)
+echo $hash2
+
+test "$hash1" = "sha256:$hash2"
diff --git a/tests/binary-cache.sh b/tests/binary-cache.sh
new file mode 100644
index 000000000000..9bd4fecb6dec
--- /dev/null
+++ b/tests/binary-cache.sh
@@ -0,0 +1,89 @@
+source common.sh
+
+clearStore
+clearManifests
+
+# Create the binary cache.
+cacheDir=$TEST_ROOT/binary-cache
+rm -rf "$cacheDir"
+
+outPath=$(nix-build dependencies.nix --no-out-link)
+
+nix-push --dest $cacheDir $outPath
+
+
+# By default, a binary cache doesn't support "nix-env -qas", but does
+# support installation.
+clearStore
+rm -f $NIX_STATE_DIR/binary-cache*
+
+export _NIX_CACHE_FILE_URLS=1
+
+nix-env --option binary-caches "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---"
+
+nix-store --option binary-caches "file://$cacheDir" -r $outPath
+
+
+# But with the right configuration, "nix-env -qas" should also work.
+clearStore
+rm -f $NIX_STATE_DIR/binary-cache*
+echo "WantMassQuery: 1" >> $cacheDir/nix-cache-info
+
+nix-env --option binary-caches "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S"
+
+x=$(nix-env -f dependencies.nix -qas \* --prebuilt-only)
+[ -z "$x" ]
+
+nix-store --option binary-caches "file://$cacheDir" -r $outPath
+
+nix-store --check-validity $outPath
+nix-store -qR $outPath | grep input-2
+
+
+# Test whether Nix notices if the NAR doesn't match the hash in the NAR info.
+clearStore
+
+nar=$(ls $cacheDir/*.nar.xz | head -n1)
+mv $nar $nar.good
+mkdir -p $TEST_ROOT/empty
+nix-store --dump $TEST_ROOT/empty | xz > $nar
+
+nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
+grep -q "hash mismatch in downloaded path" $TEST_ROOT/log
+
+mv $nar.good $nar
+
+
+# Test whether this unsigned cache is rejected if the user requires signed caches.
+clearStore
+
+rm -f $NIX_STATE_DIR/binary-cache*
+
+if nix-store --option binary-caches "file://$cacheDir" --option signed-binary-caches '*' -r $outPath; then
+    echo "unsigned binary cache incorrectly accepted"
+    exit 1
+fi
+
+
+# Test whether fallback works if we have cached info but the
+# corresponding NAR has disappeared.
+clearStore
+
+nix-build --option binary-caches "file://$cacheDir" dependencies.nix --dry-run # get info
+
+mkdir $cacheDir/tmp
+mv $cacheDir/*.nar* $cacheDir/tmp/
+
+NIX_DEBUG_SUBST=1 nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result --fallback
+
+mv $cacheDir/tmp/* $cacheDir/
+
+
+# Test whether building works if the binary cache contains an
+# incomplete closure.
+clearStore
+
+rm $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo)
+
+nix-build --option binary-caches "file://$cacheDir" dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
+grep -q "Downloading" $TEST_ROOT/log
diff --git a/tests/binary-patching.nix b/tests/binary-patching.nix
new file mode 100644
index 000000000000..8ed474d1f27f
--- /dev/null
+++ b/tests/binary-patching.nix
@@ -0,0 +1,18 @@
+{ version }:
+
+with import ./config.nix;
+
+mkDerivation {
+  name = "foo-${toString version}";
+  builder = builtins.toFile "builder.sh"
+    ''
+      mkdir $out
+      (for ((n = 1; n < 100000; n++)); do echo $n; done) > $out/foo
+      ${if version != 1 then ''
+        (for ((n = 100000; n < 110000; n++)); do echo $n; done) >> $out/foo
+      '' else ""}
+      ${if version == 3 then ''
+        echo foobar >> $out/foo
+      '' else ""}
+    '';
+}
diff --git a/tests/binary-patching.sh b/tests/binary-patching.sh
new file mode 100644
index 000000000000..188be109a0b5
--- /dev/null
+++ b/tests/binary-patching.sh
@@ -0,0 +1,61 @@
+source common.sh
+
+clearManifests
+
+mkdir -p $TEST_ROOT/cache2 $TEST_ROOT/patches
+
+RESULT=$TEST_ROOT/result
+
+# Build version 1 and 2 of the "foo" package.
+nix-push --dest $TEST_ROOT/cache2 --manifest --bzip2 \
+    $(nix-build -o $RESULT binary-patching.nix --arg version 1)
+mv $TEST_ROOT/cache2/MANIFEST $TEST_ROOT/manifest1 
+
+out2=$(nix-build -o $RESULT binary-patching.nix --arg version 2)
+nix-push --dest $TEST_ROOT/cache2 --manifest --bzip2 $out2
+mv $TEST_ROOT/cache2/MANIFEST $TEST_ROOT/manifest2
+    
+out3=$(nix-build -o $RESULT binary-patching.nix --arg version 3)
+nix-push --dest $TEST_ROOT/cache2 --manifest --bzip2 $out3
+mv $TEST_ROOT/cache2/MANIFEST $TEST_ROOT/manifest3
+
+rm $RESULT
+
+# Generate binary patches.
+nix-generate-patches $TEST_ROOT/cache2 $TEST_ROOT/patches \
+    file://$TEST_ROOT/patches $TEST_ROOT/manifest1 $TEST_ROOT/manifest2
+
+nix-generate-patches $TEST_ROOT/cache2 $TEST_ROOT/patches \
+    file://$TEST_ROOT/patches $TEST_ROOT/manifest2 $TEST_ROOT/manifest3
+
+grep -q "patch {" $TEST_ROOT/manifest3
+
+# Get rid of versions 2 and 3.
+nix-store --delete $out2 $out3
+
+# Pull the manifest containing the patches.
+clearManifests
+nix-pull file://$TEST_ROOT/manifest3
+
+# Make sure that the download size prediction uses the patches rather
+# than the full download.
+nix-build -o $RESULT binary-patching.nix --arg version 3 --dry-run 2>&1 | grep -q "0.01 MiB"
+
+# Now rebuild it.  This should use the two patches generated above.
+rm -f $TEST_ROOT/var/log/nix/downloads
+nix-build -o $RESULT binary-patching.nix --arg version 3
+rm $RESULT
+[ "$(grep ' patch ' $TEST_ROOT/var/log/nix/downloads | wc -l)" -eq 2 ]
+
+# Add a patch from version 1 directly to version 3.
+nix-generate-patches $TEST_ROOT/cache2 $TEST_ROOT/patches \
+    file://$TEST_ROOT/patches $TEST_ROOT/manifest1 $TEST_ROOT/manifest3
+
+# Rebuild version 3.  This should use the direct patch rather than the
+# sequence of two patches.
+nix-store --delete $out2 $out3
+clearManifests
+rm $TEST_ROOT/var/log/nix/downloads
+nix-pull file://$TEST_ROOT/manifest3
+nix-build -o $RESULT binary-patching.nix --arg version 3
+[ "$(grep ' patch ' $TEST_ROOT/var/log/nix/downloads | wc -l)" -eq 1 ]
diff --git a/tests/build-hook.hook.sh b/tests/build-hook.hook.sh
new file mode 100755
index 000000000000..ff709985d3af
--- /dev/null
+++ b/tests/build-hook.hook.sh
@@ -0,0 +1,23 @@
+#! /bin/sh
+
+#set -x
+
+while read x y drv rest; do
+
+    echo "HOOK for $drv" >&2
+
+    outPath=`sed 's/Derive(\[("out",\"\([^\"]*\)\".*/\1/' $drv`
+
+    echo "output path is $outPath" >&2
+
+    if `echo $outPath | grep -q input-1`; then
+        echo "# accept" >&2
+        read inputs
+        read outputs
+        mkdir $outPath
+        echo "BAR" > $outPath/foo
+    else
+        echo "# decline" >&2
+    fi
+
+done
\ No newline at end of file
diff --git a/tests/build-hook.nix b/tests/build-hook.nix
new file mode 100644
index 000000000000..666cc6ef8041
--- /dev/null
+++ b/tests/build-hook.nix
@@ -0,0 +1,22 @@
+with import ./config.nix;
+
+let
+
+  input1 = mkDerivation {
+    name = "build-hook-input-1";
+    builder = ./dependencies.builder1.sh;
+  };
+
+  input2 = mkDerivation {
+    name = "build-hook-input-2";
+    builder = ./dependencies.builder2.sh;
+  };
+
+in
+
+  mkDerivation {
+    name = "build-hook";
+    builder = ./dependencies.builder0.sh;
+    input1 = " " + input1 + "/.";
+    input2 = " ${input2}/.";
+  }
diff --git a/tests/build-hook.sh b/tests/build-hook.sh
new file mode 100644
index 000000000000..681f65cc3b6c
--- /dev/null
+++ b/tests/build-hook.sh
@@ -0,0 +1,10 @@
+source common.sh
+
+export NIX_BUILD_HOOK="build-hook.hook.sh"
+
+outPath=$(nix-build build-hook.nix --no-out-link)
+
+echo "output path is $outPath"
+
+text=$(cat "$outPath"/foobar)
+if test "$text" != "BARBAR"; then exit 1; fi
diff --git a/tests/check-refs.nix b/tests/check-refs.nix
new file mode 100644
index 000000000000..63791fe16094
--- /dev/null
+++ b/tests/check-refs.nix
@@ -0,0 +1,58 @@
+with import ./config.nix;
+
+rec {
+
+  dep = import ./dependencies.nix;
+
+  makeTest = nr: args: mkDerivation ({
+    name = "check-refs-" + toString nr;
+  } // args);
+
+  src = builtins.toFile "aux-ref" "bla bla";
+
+  test1 = makeTest 1 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    inherit dep;
+  };
+
+  test2 = makeTest 2 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link";
+    inherit dep;
+  };
+
+  test3 = makeTest 3 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    allowedReferences = [];
+    inherit dep;
+  };
+
+  test4 = makeTest 4 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    allowedReferences = [dep];
+    inherit dep;
+  };
+
+  test5 = makeTest 5 {
+    builder = builtins.toFile "builder.sh" "mkdir $out";
+    allowedReferences = [];
+    inherit dep;
+  };
+
+  test6 = makeTest 6 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
+    allowedReferences = [];
+    inherit dep;
+  };
+
+  test7 = makeTest 7 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
+    allowedReferences = ["out"];
+    inherit dep;
+  };
+
+  test8 = makeTest 8 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link";
+    inherit dep;
+  };
+
+}
diff --git a/tests/check-refs.sh b/tests/check-refs.sh
new file mode 100644
index 000000000000..08fe01ec18b4
--- /dev/null
+++ b/tests/check-refs.sh
@@ -0,0 +1,36 @@
+source common.sh
+
+set -x
+
+RESULT=$TEST_ROOT/result
+
+dep=$(nix-build -o $RESULT check-refs.nix -A dep)
+
+# test1 references dep, not itself.
+test1=$(nix-build -o $RESULT check-refs.nix -A test1)
+! nix-store -q --references $test1 | grep -q $test1
+nix-store -q --references $test1 | grep -q $dep
+
+# test2 references src, not itself nor dep.
+test2=$(nix-build -o $RESULT check-refs.nix -A test2)
+! nix-store -q --references $test2 | grep -q $test2
+! nix-store -q --references $test2 | grep -q $dep
+nix-store -q --references $test2 | grep -q aux-ref
+
+# test3 should fail (unallowed ref).
+! nix-build -o $RESULT check-refs.nix -A test3
+
+# test4 should succeed.
+nix-build -o $RESULT check-refs.nix -A test4
+
+# test5 should succeed.
+nix-build -o $RESULT check-refs.nix -A test5
+
+# test6 should fail (unallowed self-ref).
+! nix-build -o $RESULT check-refs.nix -A test6
+
+# test7 should succeed (allowed self-ref).
+nix-build -o $RESULT check-refs.nix -A test7
+
+# test8 should fail (toFile depending on derivation output).
+! nix-build -o $RESULT check-refs.nix -A test8
diff --git a/tests/common.sh.in b/tests/common.sh.in
new file mode 100644
index 000000000000..2bbc39c28a84
--- /dev/null
+++ b/tests/common.sh.in
@@ -0,0 +1,84 @@
+set -e
+
+datadir="@datadir@"
+
+export TEST_ROOT=$(pwd)/test-tmp
+export NIX_STORE_DIR
+if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
+    # Maybe the build directory is symlinked.
+    export NIX_IGNORE_SYMLINK_STORE=1
+    NIX_STORE_DIR=$TEST_ROOT/store
+fi
+export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
+export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
+export NIX_STATE_DIR=$TEST_ROOT/var/nix
+export NIX_DB_DIR=$TEST_ROOT/db
+export NIX_CONF_DIR=$TEST_ROOT/etc
+export NIX_MANIFESTS_DIR=$TEST_ROOT/var/nix/manifests
+export _NIX_TEST_SHARED=$TEST_ROOT/shared
+export NIX_REMOTE=$NIX_REMOTE_
+
+export PATH=@bindir@:$PATH
+
+export NIX_BUILD_HOOK=
+export dot=@dot@
+export xmllint="@xmllint@"
+export xmlflags="@xmlflags@"
+export xsltproc="@xsltproc@"
+export SHELL="@bash@"
+
+export version=@PACKAGE_VERSION@
+export system=@system@
+
+readLink() {
+    ls -l "$1" | sed 's/.*->\ //'
+}
+
+clearProfiles() {
+    profiles="$NIX_STATE_DIR"/profiles
+    rm -rf $profiles
+}
+
+clearStore() {
+    echo "clearing store..."
+    chmod -R +w "$NIX_STORE_DIR"
+    rm -rf "$NIX_STORE_DIR"
+    mkdir "$NIX_STORE_DIR"
+    rm -rf "$NIX_DB_DIR"
+    mkdir "$NIX_DB_DIR"
+    nix-store --init
+    clearProfiles
+    rm -f "$NIX_STATE_DIR"/gcroots/auto/*
+    rm -f "$NIX_STATE_DIR"/gcroots/ref
+}
+
+clearManifests() {
+    rm -f $NIX_STATE_DIR/manifests/*
+}
+
+startDaemon() {
+    # Start the daemon, wait for the socket to appear.  !!!
+    # ‘nix-daemon’ should have an option to fork into the background.
+    rm -f $NIX_STATE_DIR/daemon-socket/socket
+    nix-daemon &
+    for ((i = 0; i < 30; i++)); do
+        if [ -e $NIX_STATE_DIR/daemon-socket/socket ]; then break; fi
+        sleep 1
+    done
+    pidDaemon=$!
+    trap "kill -9 $pidDaemon" EXIT
+    export NIX_REMOTE=daemon
+}
+
+killDaemon() {
+    kill -9 $pidDaemon
+    wait $pidDaemon || true
+    trap "" EXIT
+}
+
+fail() {
+    echo "$1"
+    exit 1
+}
+
+set -x
diff --git a/tests/config.nix b/tests/config.nix
new file mode 100644
index 000000000000..6244a15fa48a
--- /dev/null
+++ b/tests/config.nix
@@ -0,0 +1,20 @@
+with import <nix/config.nix>;
+
+rec {
+  inherit shell;
+
+  path = coreutils;
+
+  system = builtins.currentSystem;
+
+  shared = builtins.getEnv "_NIX_TEST_SHARED";
+
+  mkDerivation = args:
+    derivation ({
+      inherit system;
+      builder = shell;
+      args = ["-e" args.builder];
+      PATH = path;
+    } // removeAttrs args ["builder" "meta"])
+    // { meta = args.meta or {}; };
+}
diff --git a/tests/dependencies.builder0.sh b/tests/dependencies.builder0.sh
new file mode 100644
index 000000000000..b02108fe6b48
--- /dev/null
+++ b/tests/dependencies.builder0.sh
@@ -0,0 +1,12 @@
+[ "${input1: -2}" = /. ]
+[ "${input2: -2}" = /. ]
+
+mkdir $out
+echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
+
+ln -s $input2 $out/input-2
+
+# Self-reference.
+ln -s $out $out/self
+
+echo FOO
diff --git a/tests/dependencies.builder1.sh b/tests/dependencies.builder1.sh
new file mode 100644
index 000000000000..4b006a17d70f
--- /dev/null
+++ b/tests/dependencies.builder1.sh
@@ -0,0 +1,2 @@
+mkdir $out
+echo FOO > $out/foo
diff --git a/tests/dependencies.builder2.sh b/tests/dependencies.builder2.sh
new file mode 100644
index 000000000000..4f886fdb3a1a
--- /dev/null
+++ b/tests/dependencies.builder2.sh
@@ -0,0 +1,2 @@
+mkdir $out
+echo BAR > $out/bar
diff --git a/tests/dependencies.nix b/tests/dependencies.nix
new file mode 100644
index 000000000000..687237add820
--- /dev/null
+++ b/tests/dependencies.nix
@@ -0,0 +1,23 @@
+with import ./config.nix;
+
+let {
+
+  input1 = mkDerivation {
+    name = "dependencies-input-1";
+    builder = ./dependencies.builder1.sh;
+  };
+
+  input2 = mkDerivation {
+    name = "dependencies-input-2";
+    builder = "${./dependencies.builder2.sh}";
+  };
+
+  body = mkDerivation {
+    name = "dependencies";
+    builder = ./dependencies.builder0.sh + "/FOOBAR/../.";
+    input1 = input1 + "/.";
+    input2 = "${input2}/.";
+    meta.description = "Random test package";
+  };
+
+}
diff --git a/tests/dependencies.sh b/tests/dependencies.sh
new file mode 100644
index 000000000000..df204d185ddc
--- /dev/null
+++ b/tests/dependencies.sh
@@ -0,0 +1,52 @@
+source common.sh
+
+clearStore
+
+drvPath=$(nix-instantiate dependencies.nix)
+
+echo "derivation is $drvPath"
+
+nix-store -q --tree "$drvPath" | grep '   +---.*builder1.sh'
+
+# Test Graphviz graph generation.
+nix-store -q --graph "$drvPath" > $TEST_ROOT/graph
+if test -n "$dot"; then
+    # Does it parse?
+    $dot < $TEST_ROOT/graph
+fi
+
+outPath=$(nix-store -rvv "$drvPath") || fail "build failed"
+
+# Test Graphviz graph generation.
+nix-store -q --graph "$outPath" > $TEST_ROOT/graph
+if test -n "$dot"; then
+    # Does it parse?
+    $dot < $TEST_ROOT/graph
+fi    
+
+nix-store -q --tree "$outPath" | grep '+---.*dependencies-input-2'
+
+echo "output path is $outPath"
+
+text=$(cat "$outPath"/foobar)
+if test "$text" != "FOOBAR"; then exit 1; fi
+
+deps=$(nix-store -quR "$drvPath")
+
+echo "output closure contains $deps"
+
+# The output path should be in the closure.
+echo "$deps" | grep -q "$outPath"
+
+# Input-1 is not retained.
+if echo "$deps" | grep -q "dependencies-input-1"; then exit 1; fi
+
+# Input-2 is retained.
+input2OutPath=$(echo "$deps" | grep "dependencies-input-2")
+
+# The referrers closure of input-2 should include outPath.
+nix-store -q --referrers-closure "$input2OutPath" | grep "$outPath"
+
+# Check that the derivers are set properly.
+test $(nix-store -q --deriver "$outPath") = "$drvPath"
+nix-store -q --deriver "$input2OutPath" | grep -q -- "-input-2.drv" 
diff --git a/tests/export-graph.nix b/tests/export-graph.nix
new file mode 100644
index 000000000000..fdac9583db2c
--- /dev/null
+++ b/tests/export-graph.nix
@@ -0,0 +1,29 @@
+with import ./config.nix;
+
+rec {
+
+  printRefs =
+    ''
+      echo $exportReferencesGraph
+      while read path; do
+          read drv
+          read nrRefs
+          echo "$path has $nrRefs references"
+          echo "$path" >> $out
+          for ((n = 0; n < $nrRefs; n++)); do read ref; echo "ref $ref"; test -e "$ref"; done
+      done < refs
+    '';
+
+  foo."bar.runtimeGraph" = mkDerivation {
+    name = "dependencies";
+    builder = builtins.toFile "build-graph-builder" "${printRefs}";
+    exportReferencesGraph = ["refs" (import ./dependencies.nix)];
+  };
+
+  foo."bar.buildGraph" = mkDerivation {
+    name = "dependencies";
+    builder = builtins.toFile "build-graph-builder" "${printRefs}";
+    exportReferencesGraph = ["refs" (import ./dependencies.nix).drvPath];
+  };
+
+}
diff --git a/tests/export-graph.sh b/tests/export-graph.sh
new file mode 100644
index 000000000000..a6fd69054425
--- /dev/null
+++ b/tests/export-graph.sh
@@ -0,0 +1,30 @@
+source common.sh
+
+clearStore
+clearProfiles
+
+checkRef() {
+    nix-store -q --references $TEST_ROOT/result | grep -q "$1" || fail "missing reference $1"
+}
+
+# Test the export of the runtime dependency graph.
+
+outPath=$(nix-build ./export-graph.nix -A 'foo."bar.runtimeGraph"' -o $TEST_ROOT/result)
+
+test $(nix-store -q --references $TEST_ROOT/result | wc -l) = 2 || fail "bad nr of references"
+
+checkRef input-2
+for i in $(cat $outPath); do checkRef $i; done
+
+# Test the export of the build-time dependency graph.
+
+nix-store --gc # should force rebuild of input-1
+
+outPath=$(nix-build ./export-graph.nix -A 'foo."bar.buildGraph"' -o $TEST_ROOT/result)
+
+checkRef input-1
+checkRef input-1.drv
+checkRef input-2
+checkRef input-2.drv
+
+for i in $(cat $outPath); do checkRef $i; done
diff --git a/tests/export.sh b/tests/export.sh
new file mode 100644
index 000000000000..ec7560f19728
--- /dev/null
+++ b/tests/export.sh
@@ -0,0 +1,31 @@
+source common.sh
+
+clearStore
+
+outPath=$(nix-build dependencies.nix --no-out-link)
+
+nix-store --export $outPath > $TEST_ROOT/exp
+
+nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
+
+
+clearStore
+
+if nix-store --import < $TEST_ROOT/exp; then
+    echo "importing a non-closure should fail"
+    exit 1
+fi
+
+
+clearStore
+
+nix-store --import < $TEST_ROOT/exp_all
+
+nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all2
+
+
+clearStore
+
+# Regression test: the derivers in exp_all2 are empty, which shouldn't
+# cause a failure.
+nix-store --import < $TEST_ROOT/exp_all2
diff --git a/tests/fallback.sh b/tests/fallback.sh
new file mode 100644
index 000000000000..f3a6b50515bf
--- /dev/null
+++ b/tests/fallback.sh
@@ -0,0 +1,20 @@
+source common.sh
+
+clearStore
+
+drvPath=$(nix-instantiate simple.nix)
+echo "derivation is $drvPath"
+
+outPath=$(nix-store -q --fallback "$drvPath")
+echo "output path is $outPath"
+
+# Build with a substitute that fails.  This should fail.
+export NIX_SUBSTITUTERS=$(pwd)/substituter2.sh
+if nix-store -r "$drvPath"; then echo unexpected fallback; exit 1; fi
+
+# Build with a substitute that fails.  This should fall back to a source build.
+export NIX_SUBSTITUTERS=$(pwd)/substituter2.sh
+nix-store -r --fallback "$drvPath"
+
+text=$(cat "$outPath"/hello)
+if test "$text" != "Hello World!"; then exit 1; fi
diff --git a/tests/fetchurl.nix b/tests/fetchurl.nix
new file mode 100644
index 000000000000..2abcc039a832
--- /dev/null
+++ b/tests/fetchurl.nix
@@ -0,0 +1,6 @@
+{ filename, sha256 }:
+
+import <nix/fetchurl.nix> {
+  url = "file://${filename}";
+  inherit sha256;
+}
diff --git a/tests/fetchurl.sh b/tests/fetchurl.sh
new file mode 100644
index 000000000000..6acc87eafca8
--- /dev/null
+++ b/tests/fetchurl.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+clearStore
+
+hash=$(nix-hash --flat --type sha256 ./fetchurl.nix)
+
+outPath=$(nix-build ./fetchurl.nix --argstr filename $(pwd)/fetchurl.nix --argstr sha256 $hash --no-out-link)
+
+cmp $outPath fetchurl.nix
diff --git a/tests/filter-source.nix b/tests/filter-source.nix
new file mode 100644
index 000000000000..a620f0fda5c4
--- /dev/null
+++ b/tests/filter-source.nix
@@ -0,0 +1,12 @@
+with import ./config.nix;
+
+mkDerivation {
+  name = "filter";
+  builder = builtins.toFile "builder" "ln -s $input $out";
+  input =
+    let filter = path: type:
+      type != "symlink"
+      && baseNameOf path != "foo"
+      && !((import ./lang/lib.nix).hasSuffix ".bak" (baseNameOf path));
+    in builtins.filterSource filter ./test-tmp/filterin;
+}
diff --git a/tests/filter-source.sh b/tests/filter-source.sh
new file mode 100644
index 000000000000..f7a096ed605d
--- /dev/null
+++ b/tests/filter-source.sh
@@ -0,0 +1,20 @@
+source common.sh
+
+rm -rf $TEST_ROOT/filterin
+mkdir $TEST_ROOT/filterin
+mkdir $TEST_ROOT/filterin/foo
+touch $TEST_ROOT/filterin/foo/bar
+touch $TEST_ROOT/filterin/xyzzy
+touch $TEST_ROOT/filterin/b
+touch $TEST_ROOT/filterin/bak
+touch $TEST_ROOT/filterin/bla.c.bak
+ln -s xyzzy $TEST_ROOT/filterin/link
+
+nix-build ./filter-source.nix -o $TEST_ROOT/filterout
+
+set -x
+test ! -e $TEST_ROOT/filterout/foo/bar
+test -e $TEST_ROOT/filterout/xyzzy
+test -e $TEST_ROOT/filterout/bak
+test ! -e $TEST_ROOT/filterout/bla.c.bak
+test ! -L $TEST_ROOT/filterout/link
diff --git a/tests/fixed.builder1.sh b/tests/fixed.builder1.sh
new file mode 100644
index 000000000000..c41bb2b9a611
--- /dev/null
+++ b/tests/fixed.builder1.sh
@@ -0,0 +1,3 @@
+if test "$IMPURE_VAR1" != "foo"; then exit 1; fi
+if test "$IMPURE_VAR2" != "bar"; then exit 1; fi
+echo "Hello World!" > $out
diff --git a/tests/fixed.builder2.sh b/tests/fixed.builder2.sh
new file mode 100644
index 000000000000..31ea1579a514
--- /dev/null
+++ b/tests/fixed.builder2.sh
@@ -0,0 +1,6 @@
+echo dummy: $dummy
+if test -n "$dummy"; then sleep 2; fi
+mkdir $out
+mkdir $out/bla
+echo "Hello World!" > $out/foo
+ln -s foo $out/bar
diff --git a/tests/fixed.nix b/tests/fixed.nix
new file mode 100644
index 000000000000..76580ffa19e8
--- /dev/null
+++ b/tests/fixed.nix
@@ -0,0 +1,50 @@
+with import ./config.nix;
+
+rec {
+
+  f2 = dummy: builder: mode: algo: hash: mkDerivation {
+    name = "fixed";
+    inherit builder;
+    outputHashMode = mode;
+    outputHashAlgo = algo;
+    outputHash = hash;
+    inherit dummy;
+    impureEnvVars = ["IMPURE_VAR1" "IMPURE_VAR2"];
+  };
+
+  f = f2 "";
+
+  good = [
+    (f ./fixed.builder1.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858")
+    (f ./fixed.builder1.sh "flat" "sha1" "a0b65939670bc2c010f4d5d6a0b3e4e4590fb92b")
+    (f ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
+    (f ./fixed.builder2.sh "recursive" "sha1" "vw46m23bizj4n8afrc0fj19wrp7mj3c0")
+  ];
+
+  good2 = [
+    # Yes, this looks fscked up: builder2 doesn't have that result.
+    # But Nix sees that an output with the desired hash already
+    # exists, and will refrain from building it.
+    (f ./fixed.builder2.sh "flat" "md5" "8ddd8be4b179a529afa5f2ffae4b9858")
+  ];
+
+  sameAsAdd =
+    f ./fixed.builder2.sh "recursive" "sha256" "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik";
+
+  bad = [
+    (f ./fixed.builder1.sh "flat" "md5" "0ddd8be4b179a529afa5f2ffae4b9858")
+  ];
+
+  reallyBad = [
+    # Hash too short, and not base-32 either.
+    (f ./fixed.builder1.sh "flat" "md5" "ddd8be4b179a529afa5f2ffae4b9858")
+  ];
+
+  # Test for building two derivations in parallel that produce the
+  # same output path because they're fixed-output derivations.
+  parallelSame = [
+    (f2 "foo" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
+    (f2 "bar" ./fixed.builder2.sh "recursive" "md5" "3670af73070fa14077ad74e0f5ea4e42")
+  ];
+
+}
diff --git a/tests/fixed.sh b/tests/fixed.sh
new file mode 100644
index 000000000000..ed0d06dd29cb
--- /dev/null
+++ b/tests/fixed.sh
@@ -0,0 +1,52 @@
+source common.sh
+
+clearStore
+
+export IMPURE_VAR1=foo
+export IMPURE_VAR2=bar
+
+echo 'testing good...'
+nix-build fixed.nix -A good --no-out-link
+
+echo 'testing good2...'
+nix-build fixed.nix -A good2 --no-out-link
+
+echo 'testing bad...'
+nix-build fixed.nix -A bad --no-out-link && fail "should fail"
+
+echo 'testing reallyBad...'
+nix-instantiate fixed.nix -A reallyBad && fail "should fail"
+
+# While we're at it, check attribute selection a bit more.
+echo 'testing attribute selection...'
+test $(nix-instantiate fixed.nix -A good.1 | wc -l) = 1
+
+# Test parallel builds of derivations that produce the same output.
+# Only one should run at the same time.
+echo 'testing parallelSame...'
+clearStore
+nix-build fixed.nix -A parallelSame --no-out-link -j2
+
+# Fixed-output derivations with a recursive SHA-256 hash should
+# produce the same path as "nix-store --add".
+echo 'testing sameAsAdd...'
+out=$(nix-build fixed.nix -A sameAsAdd --no-out-link)
+
+# This is what fixed.builder2 produces...
+rm -rf $TEST_ROOT/fixed
+mkdir $TEST_ROOT/fixed
+mkdir $TEST_ROOT/fixed/bla
+echo "Hello World!" > $TEST_ROOT/fixed/foo
+ln -s foo $TEST_ROOT/fixed/bar
+
+out2=$(nix-store --add $TEST_ROOT/fixed)
+echo $out2
+test "$out" = "$out2" || exit 1
+
+out3=$(nix-store --add-fixed --recursive sha256 $TEST_ROOT/fixed)
+echo $out3
+test "$out" = "$out3" || exit 1
+
+out4=$(nix-store --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed)
+echo $out4
+test "$out" = "$out4" || exit 1
diff --git a/tests/gc-concurrent.builder.sh b/tests/gc-concurrent.builder.sh
new file mode 100644
index 000000000000..0cd67df3aeda
--- /dev/null
+++ b/tests/gc-concurrent.builder.sh
@@ -0,0 +1,13 @@
+mkdir $out
+echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
+
+sleep 10
+
+# $out should not have been GC'ed while we were sleeping, but just in
+# case...
+mkdir -p $out
+
+# Check that the GC hasn't deleted the lock on our output.
+test -e "$out.lock"
+
+ln -s $input2 $out/input-2
diff --git a/tests/gc-concurrent.nix b/tests/gc-concurrent.nix
new file mode 100644
index 000000000000..c0595cc471b9
--- /dev/null
+++ b/tests/gc-concurrent.nix
@@ -0,0 +1,27 @@
+with import ./config.nix;
+
+rec {
+
+  input1 = mkDerivation {
+    name = "dependencies-input-1";
+    builder = ./dependencies.builder1.sh;
+  };
+
+  input2 = mkDerivation {
+    name = "dependencies-input-2";
+    builder = ./dependencies.builder2.sh;
+  };
+
+  test1 = mkDerivation {
+    name = "gc-concurrent";
+    builder = ./gc-concurrent.builder.sh;
+    inherit input1 input2;
+  };
+
+  test2 = mkDerivation {
+    name = "gc-concurrent2";
+    builder = ./gc-concurrent2.builder.sh;
+    inherit input1 input2;
+  };
+  
+}
diff --git a/tests/gc-concurrent.sh b/tests/gc-concurrent.sh
new file mode 100644
index 000000000000..0bc5a12d318f
--- /dev/null
+++ b/tests/gc-concurrent.sh
@@ -0,0 +1,58 @@
+source common.sh
+
+clearStore
+
+drvPath1=$(nix-instantiate gc-concurrent.nix -A test1)
+outPath1=$(nix-store -q $drvPath1)
+
+drvPath2=$(nix-instantiate gc-concurrent.nix -A test2)
+outPath2=$(nix-store -q $drvPath2)
+
+drvPath3=$(nix-instantiate simple.nix)
+outPath3=$(nix-store -r $drvPath3)
+
+! test -e $outPath3.lock
+touch $outPath3.lock
+
+rm -f "$NIX_STATE_DIR"/gcroots/foo*
+ln -s $drvPath2 "$NIX_STATE_DIR"/gcroots/foo
+ln -s $outPath3 "$NIX_STATE_DIR"/gcroots/foo2
+
+# Start build #1 in the background.  It starts immediately.
+nix-store -rvv "$drvPath1" &
+pid1=$!
+
+# Start build #2 in the background after 10 seconds.
+(sleep 10 && nix-store -rvv "$drvPath2") &
+pid2=$!
+
+# Run the garbage collector while the build is running.
+sleep 6
+nix-collect-garbage
+
+# Wait for build #1/#2 to finish.
+echo waiting for pid $pid1 to finish...
+wait $pid1
+echo waiting for pid $pid2 to finish...
+wait $pid2
+
+# Check that the root of build #1 and its dependencies haven't been
+# deleted.  The should not be deleted by the GC because they were
+# being built during the GC.
+cat $outPath1/foobar
+cat $outPath1/input-2/bar
+
+# Check that build #2 has succeeded.  It should succeed because the
+# derivation is a GC root.
+cat $outPath2/foobar
+
+rm -f "$NIX_STATE_DIR"/gcroots/foo*
+
+# The collector should have deleted lock files for paths that have
+# been built previously.
+! test -e $outPath3.lock
+
+# If we run the collector now, it should delete outPath1/2.
+nix-collect-garbage
+! test -e $outPath1
+! test -e $outPath2
diff --git a/tests/gc-concurrent2.builder.sh b/tests/gc-concurrent2.builder.sh
new file mode 100644
index 000000000000..4bfb33103e73
--- /dev/null
+++ b/tests/gc-concurrent2.builder.sh
@@ -0,0 +1,7 @@
+mkdir $out
+echo $(cat $input1/foo)$(cat $input2/bar)xyzzy > $out/foobar
+
+# Check that the GC hasn't deleted the lock on our output.
+test -e "$out.lock"
+
+sleep 6
diff --git a/tests/gc-runtime.nix b/tests/gc-runtime.nix
new file mode 100644
index 000000000000..ee5980bdff98
--- /dev/null
+++ b/tests/gc-runtime.nix
@@ -0,0 +1,17 @@
+with import ./config.nix;
+
+mkDerivation {
+  name = "gc-runtime";
+  builder =
+    # Test inline source file definitions.
+    builtins.toFile "builder.sh" ''
+      mkdir $out
+
+      cat > $out/program <<EOF
+      #! ${shell}
+      sleep 10000
+      EOF
+
+      chmod +x $out/program
+    '';
+}
diff --git a/tests/gc-runtime.sh b/tests/gc-runtime.sh
new file mode 100644
index 000000000000..a44195756f52
--- /dev/null
+++ b/tests/gc-runtime.sh
@@ -0,0 +1,38 @@
+source common.sh
+
+case $system in
+    *linux*)
+        ;;
+    *)
+        exit 0;
+esac
+
+set -m # enable job control, needed for kill
+
+profiles="$NIX_STATE_DIR"/profiles
+rm -f $profiles/*
+
+nix-env -p $profiles/test -f ./gc-runtime.nix -i gc-runtime
+
+outPath=$(nix-env -p $profiles/test -q --no-name --out-path gc-runtime)
+echo $outPath
+
+echo "backgrounding program..."
+$profiles/test/program &
+sleep 2 # hack - wait for the program to get started
+child=$!
+echo PID=$child
+
+nix-env -p $profiles/test -e gc-runtime
+nix-env -p $profiles/test --delete-generations old
+
+nix-store --gc
+
+kill -- -$child
+
+if ! test -e $outPath; then
+    echo "running program was garbage collected!"
+    exit 1
+fi
+
+exit 0
diff --git a/tests/gc.sh b/tests/gc.sh
new file mode 100644
index 000000000000..a375a35c2eb6
--- /dev/null
+++ b/tests/gc.sh
@@ -0,0 +1,38 @@
+source common.sh
+
+drvPath=$(nix-instantiate dependencies.nix)
+outPath=$(nix-store -rvv "$drvPath")
+
+# Set a GC root.
+rm -f "$NIX_STATE_DIR"/gcroots/foo
+ln -sf $outPath "$NIX_STATE_DIR"/gcroots/foo
+
+nix-store --gc --print-roots | grep $outPath
+nix-store --gc --print-live | grep $outPath
+nix-store --gc --print-dead | grep $drvPath
+if nix-store --gc --print-dead | grep $outPath; then false; fi
+
+nix-store --gc --print-dead
+
+inUse=$(readLink $outPath/input-2)
+if nix-store --delete $inUse; then false; fi
+test -e $inUse
+
+if nix-store --delete $outPath; then false; fi
+test -e $outPath
+
+nix-collect-garbage
+
+# Check that the root and its dependencies haven't been deleted.
+cat $outPath/foobar
+cat $outPath/input-2/bar
+
+# Check that the derivation has been GC'd.
+if test -e $drvPath; then false; fi
+
+rm "$NIX_STATE_DIR"/gcroots/foo
+
+nix-collect-garbage
+
+# Check that the output has been GC'd.
+if test -e $outPath/foobar; then false; fi
diff --git a/tests/hash-check.nix b/tests/hash-check.nix
new file mode 100644
index 000000000000..4a8e9b8a8df9
--- /dev/null
+++ b/tests/hash-check.nix
@@ -0,0 +1,29 @@
+let {
+
+  input1 = derivation {
+    name = "dependencies-input-1";
+    system = "i086-msdos";
+    builder = "/bar/sh";
+    args = ["-e" "-x" ./dummy];
+  };
+
+  input2 = derivation {
+    name = "dependencies-input-2";
+    system = "i086-msdos";
+    builder = "/bar/sh";
+    args = ["-e" "-x" ./dummy];
+    outputHashMode = "recursive";
+    outputHashAlgo = "md5";
+    outputHash = "ffffffffffffffffffffffffffffffff";
+  };
+
+  body = derivation {
+    name = "dependencies";
+    system = "i086-msdos";
+    builder = "/bar/sh";
+    args = ["-e" "-x" (./dummy  + "/FOOBAR/../.")];
+    input1 = input1 + "/.";
+    inherit input2;
+  };
+
+}
\ No newline at end of file
diff --git a/tests/hash.sh b/tests/hash.sh
new file mode 100644
index 000000000000..d659bbe34e8f
--- /dev/null
+++ b/tests/hash.sh
@@ -0,0 +1,62 @@
+source common.sh
+
+try () {
+    printf "%s" "$2" > $TEST_ROOT/vector
+    hash=$(nix-hash $EXTRA --flat --type "$1" $TEST_ROOT/vector)
+    if test "$hash" != "$3"; then
+        echo "hash $1, expected $3, got $hash"
+        exit 1
+    fi
+}
+
+try md5 "" "d41d8cd98f00b204e9800998ecf8427e"
+try md5 "a" "0cc175b9c0f1b6a831c399e269772661"
+try md5 "abc" "900150983cd24fb0d6963f7d28e17f72"
+try md5 "message digest" "f96b697d7cb7938d525a2f31aaf161d0"
+try md5 "abcdefghijklmnopqrstuvwxyz" "c3fcd3d76192e4007dfb496cca67e13b"
+try md5 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" "d174ab98d277d9f5a5611c2c9f419d9f"
+try md5 "12345678901234567890123456789012345678901234567890123456789012345678901234567890" "57edf4a22be3c955ac49da2e2107b67a"
+
+try sha1 "abc" "a9993e364706816aba3e25717850c26c9cd0d89d"
+try sha1 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
+
+try sha256 "abc" "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
+try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
+
+EXTRA=--base32
+try sha256 "abc" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
+EXTRA=
+
+try2 () {
+    hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path)
+    if test "$hash" != "$2"; then
+        echo "hash $1, expected $2, got $hash"
+        exit 1
+    fi
+}
+
+rm -rf $TEST_ROOT/hash-path
+mkdir $TEST_ROOT/hash-path
+echo "Hello World" > $TEST_ROOT/hash-path/hello
+
+try2 md5 "ea9b55537dd4c7e104515b2ccfaf4100"
+
+# Execute bit matters.
+chmod +x $TEST_ROOT/hash-path/hello
+try2 md5 "20f3ffe011d4cfa7d72bfabef7882836"
+
+# Mtime and other bits don't.
+touch -r . $TEST_ROOT/hash-path/hello
+chmod 744 $TEST_ROOT/hash-path/hello
+try2 md5 "20f3ffe011d4cfa7d72bfabef7882836"
+
+# File type (e.g., symlink) does.
+rm $TEST_ROOT/hash-path/hello
+ln -s x $TEST_ROOT/hash-path/hello
+try2 md5 "f78b733a68f5edbdf9413899339eaa4a"
+
+# Conversion.
+test $(nix-hash --type sha256 --to-base32 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad") = "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
+test $(nix-hash --type sha256 --to-base16 "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s") = "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
+test $(nix-hash --type sha1 --to-base32 "800d59cfcd3c05e900cb4e214be48f6b886a08df") = "vw46m23bizj4n8afrc0fj19wrp7mj3c0"
+test $(nix-hash --type sha1 --to-base16 "vw46m23bizj4n8afrc0fj19wrp7mj3c0") = "800d59cfcd3c05e900cb4e214be48f6b886a08df"
diff --git a/tests/import-derivation.nix b/tests/import-derivation.nix
new file mode 100644
index 000000000000..91adcd288f6e
--- /dev/null
+++ b/tests/import-derivation.nix
@@ -0,0 +1,23 @@
+with import ./config.nix;
+
+let
+
+  bar = mkDerivation {
+    name = "bar";
+    builder = builtins.toFile "builder.sh"
+      ''
+        echo 'builtins.add 123 456' > $out
+      '';
+  };
+
+  value = import bar;
+
+in
+
+mkDerivation {
+  name = "foo";
+  builder = builtins.toFile "builder.sh"
+    ''
+      echo -n FOO${toString value} > $out
+    '';
+}
diff --git a/tests/import-derivation.sh b/tests/import-derivation.sh
new file mode 100644
index 000000000000..98d61ef49b9c
--- /dev/null
+++ b/tests/import-derivation.sh
@@ -0,0 +1,12 @@
+source common.sh
+
+clearStore
+
+if nix-instantiate --readonly-mode ./import-derivation.nix; then
+    echo "read-only evaluation of an imported derivation unexpectedly failed"
+    exit 1
+fi
+
+outPath=$(nix-build ./import-derivation.nix --no-out-link)
+
+[ "$(cat $outPath)" = FOO579 ]
diff --git a/tests/init.sh b/tests/init.sh
new file mode 100644
index 000000000000..37480f52bd5b
--- /dev/null
+++ b/tests/init.sh
@@ -0,0 +1,32 @@
+source common.sh
+
+echo "NIX_STORE_DIR=$NIX_STORE_DIR NIX_DB_DIR=$NIX_DB_DIR"
+
+test -n "$TEST_ROOT"
+if test -d "$TEST_ROOT"; then
+    chmod -R u+w "$TEST_ROOT"
+    rm -rf "$TEST_ROOT"
+fi
+mkdir "$TEST_ROOT"
+
+mkdir "$NIX_STORE_DIR"
+mkdir "$NIX_LOCALSTATE_DIR"
+mkdir -p "$NIX_LOG_DIR"/drvs
+mkdir "$NIX_STATE_DIR"
+mkdir "$NIX_DB_DIR"
+mkdir "$NIX_CONF_DIR"
+
+cat > "$NIX_CONF_DIR"/nix.conf <<EOF
+gc-keep-outputs = false
+gc-keep-derivations = false
+env-keep-derivations = false
+fsync-metadata = false
+EOF
+
+# Initialise the database.
+nix-store --init
+
+# Did anything happen?
+test -e "$NIX_DB_DIR"/db.sqlite
+
+echo 'Hello World' > ./dummy
diff --git a/tests/install-package.sh b/tests/install-package.sh
new file mode 100644
index 000000000000..653dfee4c8d1
--- /dev/null
+++ b/tests/install-package.sh
@@ -0,0 +1,21 @@
+source common.sh
+
+# Note: this test expects to be run *after* nix-push.sh.
+
+drvPath=$(nix-instantiate ./dependencies.nix)
+outPath=$(nix-store -q $drvPath)
+
+clearStore
+clearProfiles
+
+cat > $TEST_ROOT/foo.nixpkg <<EOF
+NIXPKG1 file://$TEST_ROOT/cache/MANIFEST simple $system $drvPath $outPath
+EOF
+
+nix-install-package --non-interactive -p $profiles/test $TEST_ROOT/foo.nixpkg
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 1
+
+clearProfiles
+
+nix-install-package --non-interactive -p $profiles/test --url file://$TEST_ROOT/foo.nixpkg
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 1
diff --git a/tests/lang.sh b/tests/lang.sh
new file mode 100644
index 000000000000..0d93b9215c68
--- /dev/null
+++ b/tests/lang.sh
@@ -0,0 +1,64 @@
+source common.sh
+
+export TEST_VAR=foo # for eval-okay-getenv.nix
+
+fail=0
+
+for i in lang/parse-fail-*.nix; do
+    echo "parsing $i (should fail)";
+    i=$(basename $i .nix)
+    if nix-instantiate --parse-only - < lang/$i.nix; then
+        echo "FAIL: $i shouldn't parse"
+        fail=1
+    fi
+done
+
+for i in lang/parse-okay-*.nix; do
+    echo "parsing $i (should succeed)";
+    i=$(basename $i .nix)
+    if ! nix-instantiate --parse-only - < lang/$i.nix > lang/$i.out; then
+        echo "FAIL: $i should parse"
+        fail=1
+    fi
+done
+
+for i in lang/eval-fail-*.nix; do
+    echo "evaluating $i (should fail)";
+    i=$(basename $i .nix)
+    if nix-instantiate --eval-only lang/$i.nix; then
+        echo "FAIL: $i shouldn't evaluate"
+        fail=1
+    fi
+done
+
+for i in lang/eval-okay-*.nix; do
+    echo "evaluating $i (should succeed)";
+    i=$(basename $i .nix)
+
+    if test -e lang/$i.exp; then
+        flags=
+        if test -e lang/$i.flags; then
+            flags=$(cat lang/$i.flags)
+        fi
+        if ! NIX_PATH=lang/dir3:lang/dir4_PATH nix-instantiate $flags --eval-only --strict lang/$i.nix > lang/$i.out; then
+            echo "FAIL: $i should evaluate"
+            fail=1
+        elif ! diff lang/$i.out lang/$i.exp; then
+            echo "FAIL: evaluation result of $i not as expected"
+            fail=1
+        fi
+    fi
+    
+    if test -e lang/$i.exp.xml; then
+        if ! nix-instantiate --eval-only --xml --no-location --strict \
+                lang/$i.nix > lang/$i.out.xml; then
+            echo "FAIL: $i should evaluate"
+            fail=1
+        elif ! cmp -s lang/$i.out.xml lang/$i.exp.xml; then
+            echo "FAIL: XML evaluation result of $i not as expected"
+            fail=1
+        fi
+    fi
+done
+
+exit $fail
diff --git a/tests/lang/dir1/a.nix b/tests/lang/dir1/a.nix
new file mode 100644
index 000000000000..231f150c579c
--- /dev/null
+++ b/tests/lang/dir1/a.nix
@@ -0,0 +1 @@
+"a"
diff --git a/tests/lang/dir2/a.nix b/tests/lang/dir2/a.nix
new file mode 100644
index 000000000000..170df520ab68
--- /dev/null
+++ b/tests/lang/dir2/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir2/b.nix b/tests/lang/dir2/b.nix
new file mode 100644
index 000000000000..19010cc35ca6
--- /dev/null
+++ b/tests/lang/dir2/b.nix
@@ -0,0 +1 @@
+"b"
diff --git a/tests/lang/dir3/a.nix b/tests/lang/dir3/a.nix
new file mode 100644
index 000000000000..170df520ab68
--- /dev/null
+++ b/tests/lang/dir3/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir3/b.nix b/tests/lang/dir3/b.nix
new file mode 100644
index 000000000000..170df520ab68
--- /dev/null
+++ b/tests/lang/dir3/b.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir3/c.nix b/tests/lang/dir3/c.nix
new file mode 100644
index 000000000000..cdf158597eef
--- /dev/null
+++ b/tests/lang/dir3/c.nix
@@ -0,0 +1 @@
+"c"
diff --git a/tests/lang/dir4/a.nix b/tests/lang/dir4/a.nix
new file mode 100644
index 000000000000..170df520ab68
--- /dev/null
+++ b/tests/lang/dir4/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/dir4/c.nix b/tests/lang/dir4/c.nix
new file mode 100644
index 000000000000..170df520ab68
--- /dev/null
+++ b/tests/lang/dir4/c.nix
@@ -0,0 +1 @@
+"X"
diff --git a/tests/lang/eval-fail-abort.nix b/tests/lang/eval-fail-abort.nix
new file mode 100644
index 000000000000..75c51bceb540
--- /dev/null
+++ b/tests/lang/eval-fail-abort.nix
@@ -0,0 +1 @@
+if true then abort "this should fail" else 1
diff --git a/tests/lang/eval-fail-antiquoted-path.nix b/tests/lang/eval-fail-antiquoted-path.nix
new file mode 100644
index 000000000000..f2f08107b516
--- /dev/null
+++ b/tests/lang/eval-fail-antiquoted-path.nix
@@ -0,0 +1,4 @@
+# This must fail to evaluate, since ./fnord doesn't exist.  If it did
+# exist, it would produce "/nix/store/<hash>-fnord/xyzzy" (with an
+# appropriate context).
+"${./fnord}/xyzzy"
diff --git a/tests/lang/eval-fail-assert.nix b/tests/lang/eval-fail-assert.nix
new file mode 100644
index 000000000000..3b7a1e8bf0c2
--- /dev/null
+++ b/tests/lang/eval-fail-assert.nix
@@ -0,0 +1,5 @@
+let {
+  x = arg: assert arg == "y"; 123;
+
+  body = x "x";
+}
\ No newline at end of file
diff --git a/tests/lang/eval-fail-bad-antiquote-1.nix b/tests/lang/eval-fail-bad-antiquote-1.nix
new file mode 100644
index 000000000000..ffe9c983c26b
--- /dev/null
+++ b/tests/lang/eval-fail-bad-antiquote-1.nix
@@ -0,0 +1 @@
+"${x: x}"
diff --git a/tests/lang/eval-fail-bad-antiquote-2.nix b/tests/lang/eval-fail-bad-antiquote-2.nix
new file mode 100644
index 000000000000..3745235ce95e
--- /dev/null
+++ b/tests/lang/eval-fail-bad-antiquote-2.nix
@@ -0,0 +1 @@
+"${./fnord}"
diff --git a/tests/lang/eval-fail-bad-antiquote-3.nix b/tests/lang/eval-fail-bad-antiquote-3.nix
new file mode 100644
index 000000000000..65b9d4f505b1
--- /dev/null
+++ b/tests/lang/eval-fail-bad-antiquote-3.nix
@@ -0,0 +1 @@
+''${x: x}''
diff --git a/tests/lang/eval-fail-blackhole.nix b/tests/lang/eval-fail-blackhole.nix
new file mode 100644
index 000000000000..81133b511c95
--- /dev/null
+++ b/tests/lang/eval-fail-blackhole.nix
@@ -0,0 +1,5 @@
+let {
+  body = x;
+  x = y;
+  y = x;
+}
diff --git a/tests/lang/eval-fail-missing-arg.nix b/tests/lang/eval-fail-missing-arg.nix
new file mode 100644
index 000000000000..c4be9797c534
--- /dev/null
+++ b/tests/lang/eval-fail-missing-arg.nix
@@ -0,0 +1 @@
+({x, y, z}: x + y + z) {x = "foo"; z = "bar";}
diff --git a/tests/lang/eval-fail-remove.nix b/tests/lang/eval-fail-remove.nix
new file mode 100644
index 000000000000..539e0eb0a6f6
--- /dev/null
+++ b/tests/lang/eval-fail-remove.nix
@@ -0,0 +1,5 @@
+let {
+  attrs = {x = 123; y = 456;};
+
+  body = (removeAttrs attrs ["x"]).x;
+}
\ No newline at end of file
diff --git a/tests/lang/eval-fail-scope-5.nix b/tests/lang/eval-fail-scope-5.nix
new file mode 100644
index 000000000000..f89a65a99be3
--- /dev/null
+++ b/tests/lang/eval-fail-scope-5.nix
@@ -0,0 +1,10 @@
+let {
+
+  x = "a";
+  y = "b";
+
+  f = {x ? y, y ? x}: x + y;
+
+  body = f {};
+
+}
diff --git a/tests/lang/eval-fail-substring.nix b/tests/lang/eval-fail-substring.nix
new file mode 100644
index 000000000000..f37c2bc0a160
--- /dev/null
+++ b/tests/lang/eval-fail-substring.nix
@@ -0,0 +1 @@
+builtins.substring (builtins.sub 0 1) 1 "x"
diff --git a/tests/lang/eval-fail-to-path.nix b/tests/lang/eval-fail-to-path.nix
new file mode 100644
index 000000000000..5e322bc31369
--- /dev/null
+++ b/tests/lang/eval-fail-to-path.nix
@@ -0,0 +1 @@
+builtins.toPath "foo/bar"
diff --git a/tests/lang/eval-fail-undeclared-arg.nix b/tests/lang/eval-fail-undeclared-arg.nix
new file mode 100644
index 000000000000..cafdf1636272
--- /dev/null
+++ b/tests/lang/eval-fail-undeclared-arg.nix
@@ -0,0 +1 @@
+({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";}
diff --git a/tests/lang/eval-okay-arithmetic.exp b/tests/lang/eval-okay-arithmetic.exp
new file mode 100644
index 000000000000..b195055b7a09
--- /dev/null
+++ b/tests/lang/eval-okay-arithmetic.exp
@@ -0,0 +1 @@
+2188
diff --git a/tests/lang/eval-okay-arithmetic.nix b/tests/lang/eval-okay-arithmetic.nix
new file mode 100644
index 000000000000..8f307b20ea95
--- /dev/null
+++ b/tests/lang/eval-okay-arithmetic.nix
@@ -0,0 +1,60 @@
+with import ./lib.nix;
+
+let {
+
+  range = first: last:
+    if builtins.lessThan last first
+    then []
+    else [first] ++ range (builtins.add first 1) last;
+
+  /* Supposedly tail recursive version:
+
+  range_ = accum: first: last:
+    if first == last then ([first] ++ accum)
+    else range_ ([first] ++ accum) (builtins.add first 1) last;
+
+  range = range_ [];
+  */
+
+  x = 12;
+
+  err = abort "urgh";
+
+  body = sum
+    [ (sum (range 1 50))
+      (123 + 456)
+      (0 + -10 + -(-11) + -x)
+      (10 - 7 - -2)
+      (10 - (6 - -1))
+      (10 - 1 + 2)
+      (3 * 4 * 5)
+      (56088 / 123 / 2)
+      (3 + 4 * const 5 0 - 6 / id 2)
+
+      (if 3 < 7 then 1 else err)
+      (if 7 < 3 then err else 1)
+      (if 3 < 3 then err else 1)
+
+      (if 3 <= 7 then 1 else err)
+      (if 7 <= 3 then err else 1)
+      (if 3 <= 3 then 1 else err)
+
+      (if 3 > 7 then err else 1)
+      (if 7 > 3 then 1 else err)
+      (if 3 > 3 then err else 1)
+
+      (if 3 >= 7 then err else 1)
+      (if 7 >= 3 then 1 else err)
+      (if 3 >= 3 then 1 else err)
+
+      (if 2 > 1 == 1 < 2 then 1 else err)
+      (if 1 + 2 * 3 >= 7 then 1 else err)
+      (if 1 + 2 * 3 < 7 then err else 1)
+
+      # Not integer, but so what.
+      (if "aa" < "ab" then 1 else err)
+      (if "aa" < "aa" then err else 1)
+      (if "foo" < "foobar" then 1 else err)
+    ];
+
+}
diff --git a/tests/lang/eval-okay-attrnames.exp b/tests/lang/eval-okay-attrnames.exp
new file mode 100644
index 000000000000..b4aa387e07b8
--- /dev/null
+++ b/tests/lang/eval-okay-attrnames.exp
@@ -0,0 +1 @@
+"newxfoonewxy"
diff --git a/tests/lang/eval-okay-attrnames.nix b/tests/lang/eval-okay-attrnames.nix
new file mode 100644
index 000000000000..978138f0c0d9
--- /dev/null
+++ b/tests/lang/eval-okay-attrnames.nix
@@ -0,0 +1,11 @@
+with import ./lib.nix;
+
+let
+
+  attrs = {y = "y"; x = "x"; foo = "foo";} // rec {x = "newx"; bar = x;};
+
+  names = builtins.attrNames attrs;
+
+  values = map (name: builtins.getAttr name attrs) names;
+
+in concat values
diff --git a/tests/lang/eval-okay-attrs.exp b/tests/lang/eval-okay-attrs.exp
new file mode 100644
index 000000000000..45b0f829eb33
--- /dev/null
+++ b/tests/lang/eval-okay-attrs.exp
@@ -0,0 +1 @@
+987
diff --git a/tests/lang/eval-okay-attrs.nix b/tests/lang/eval-okay-attrs.nix
new file mode 100644
index 000000000000..810b31a5da96
--- /dev/null
+++ b/tests/lang/eval-okay-attrs.nix
@@ -0,0 +1,5 @@
+let {
+  as = { x = 123; y = 456; } // { z = 789; } // { z = 987; };
+
+  body = if as ? a then as.a else assert as ? z; as.z;
+}
diff --git a/tests/lang/eval-okay-attrs2.exp b/tests/lang/eval-okay-attrs2.exp
new file mode 100644
index 000000000000..45b0f829eb33
--- /dev/null
+++ b/tests/lang/eval-okay-attrs2.exp
@@ -0,0 +1 @@
+987
diff --git a/tests/lang/eval-okay-attrs2.nix b/tests/lang/eval-okay-attrs2.nix
new file mode 100644
index 000000000000..9e06b83ac1fd
--- /dev/null
+++ b/tests/lang/eval-okay-attrs2.nix
@@ -0,0 +1,10 @@
+let {
+  as = { x = 123; y = 456; } // { z = 789; } // { z = 987; };
+
+  A = "a";
+  Z = "z";
+
+  body = if builtins.hasAttr A as
+         then builtins.getAttr A as
+         else assert builtins.hasAttr Z as; builtins.getAttr Z as;
+}
diff --git a/tests/lang/eval-okay-attrs3.exp b/tests/lang/eval-okay-attrs3.exp
new file mode 100644
index 000000000000..19de4fdf79f7
--- /dev/null
+++ b/tests/lang/eval-okay-attrs3.exp
@@ -0,0 +1 @@
+"foo 22 80 itchyxac"
diff --git a/tests/lang/eval-okay-attrs3.nix b/tests/lang/eval-okay-attrs3.nix
new file mode 100644
index 000000000000..f29de11fe660
--- /dev/null
+++ b/tests/lang/eval-okay-attrs3.nix
@@ -0,0 +1,22 @@
+let
+
+  config = 
+    {
+      services.sshd.enable = true;
+      services.sshd.port = 22;
+      services.httpd.port = 80;
+      hostName = "itchy";
+      a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z = "x";
+      foo = {
+        a = "a";
+        b.c = "c";
+      };
+    };
+
+in
+  if config.services.sshd.enable
+  then "foo ${toString config.services.sshd.port} ${toString config.services.httpd.port} ${config.hostName}"
+       + "${config.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z}"
+       + "${config.foo.a}"
+       + "${config.foo.b.c}"
+  else "bar"
diff --git a/tests/lang/eval-okay-attrs4.exp b/tests/lang/eval-okay-attrs4.exp
new file mode 100644
index 000000000000..1851731442d3
--- /dev/null
+++ b/tests/lang/eval-okay-attrs4.exp
@@ -0,0 +1 @@
+[ true false true false false true false false ]
diff --git a/tests/lang/eval-okay-attrs4.nix b/tests/lang/eval-okay-attrs4.nix
new file mode 100644
index 000000000000..43ec81210f38
--- /dev/null
+++ b/tests/lang/eval-okay-attrs4.nix
@@ -0,0 +1,7 @@
+let
+
+  as = { x.y.z = 123; a.b.c = 456; };
+
+  bs = null;
+
+in [ (as ? x) (as ? y) (as ? x.y.z) (as ? x.y.z.a) (as ? x.y.a) (as ? a.b.c) (bs ? x) (bs ? x.y.z) ]
diff --git a/tests/lang/eval-okay-attrs5.exp b/tests/lang/eval-okay-attrs5.exp
new file mode 100644
index 000000000000..ce0430d78081
--- /dev/null
+++ b/tests/lang/eval-okay-attrs5.exp
@@ -0,0 +1 @@
+[ 123 "foo" 456 456 "foo" "xyzzy" "xyzzy" true ]
diff --git a/tests/lang/eval-okay-attrs5.nix b/tests/lang/eval-okay-attrs5.nix
new file mode 100644
index 000000000000..0a98b8fdffa6
--- /dev/null
+++ b/tests/lang/eval-okay-attrs5.nix
@@ -0,0 +1,21 @@
+with import ./lib.nix;
+
+let
+
+  as = { x.y.z = 123; a.b.c = 456; };
+
+  bs = { f-o-o.bar = "foo"; };
+
+  or = x: y: x || y;
+  
+in
+  [ as.x.y.z
+    as.foo or "foo"
+    as.x.y.bla or as.a.b.c
+    as.a.b.c or as.x.y.z
+    as.x.y.bla or bs.f-o-o.bar or "xyzzy"
+    as.x.y.bla or bs.bar.foo or "xyzzy"
+    123.bla or null.foo or "xyzzy"
+    # Backwards compatibility test.
+    (fold or [] [true false false])
+  ]
diff --git a/tests/lang/eval-okay-autoargs.exp b/tests/lang/eval-okay-autoargs.exp
new file mode 100644
index 000000000000..7a8391786a09
--- /dev/null
+++ b/tests/lang/eval-okay-autoargs.exp
@@ -0,0 +1 @@
+"xyzzy!xyzzy!foobar"
diff --git a/tests/lang/eval-okay-autoargs.flags b/tests/lang/eval-okay-autoargs.flags
new file mode 100644
index 000000000000..ae3762254460
--- /dev/null
+++ b/tests/lang/eval-okay-autoargs.flags
@@ -0,0 +1 @@
+--arg lib import(lang/lib.nix) --argstr xyzzy xyzzy! -A result
diff --git a/tests/lang/eval-okay-autoargs.nix b/tests/lang/eval-okay-autoargs.nix
new file mode 100644
index 000000000000..815f51b1d67a
--- /dev/null
+++ b/tests/lang/eval-okay-autoargs.nix
@@ -0,0 +1,15 @@
+let
+
+  foobar = "foobar";
+
+in
+
+{ xyzzy2 ? xyzzy # mutually recursive args
+, xyzzy ? "blaat" # will be overridden by --argstr
+, fb ? foobar
+, lib # will be set by --arg
+}:
+
+{
+  result = lib.concat [xyzzy xyzzy2 fb];
+}
diff --git a/tests/lang/eval-okay-builtins.exp b/tests/lang/eval-okay-builtins.exp
new file mode 100644
index 000000000000..0661686d611d
--- /dev/null
+++ b/tests/lang/eval-okay-builtins.exp
@@ -0,0 +1 @@
+/foo
diff --git a/tests/lang/eval-okay-builtins.nix b/tests/lang/eval-okay-builtins.nix
new file mode 100644
index 000000000000..e9d65e88a817
--- /dev/null
+++ b/tests/lang/eval-okay-builtins.nix
@@ -0,0 +1,12 @@
+assert builtins ? currentSystem;
+assert !builtins ? __currentSystem;
+
+let {
+
+  x = if builtins ? dirOf then builtins.dirOf /foo/bar else "";
+
+  y = if builtins ? fnord then builtins.fnord "foo" else "";
+
+  body = x + y;
+  
+}
diff --git a/tests/lang/eval-okay-closure.exp.xml b/tests/lang/eval-okay-closure.exp.xml
new file mode 100644
index 000000000000..dffc03a99891
--- /dev/null
+++ b/tests/lang/eval-okay-closure.exp.xml
@@ -0,0 +1,343 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-13" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-12" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-11" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-9" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-8" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-7" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-5" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-4" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-3" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="-1" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="0" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="1" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="2" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="4" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="5" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="6" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="8" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="9" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="10" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="13" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="14" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="15" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="17" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="18" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="19" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="22" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="23" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="26" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="27" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="28" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="31" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="32" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="35" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="36" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="40" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="41" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="44" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="45" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="49" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="53" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="54" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="58" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="62" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="67" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="71" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="80" />
+      </attr>
+    </attrs>
+  </list>
+</expr>
diff --git a/tests/lang/eval-okay-closure.nix b/tests/lang/eval-okay-closure.nix
new file mode 100644
index 000000000000..cccd4dc35730
--- /dev/null
+++ b/tests/lang/eval-okay-closure.nix
@@ -0,0 +1,13 @@
+let
+
+  closure = builtins.genericClosure {
+    startSet = [{key = 80;}];
+    operator = {key, foo ? false}:
+      if builtins.lessThan key 0
+      then []
+      else [{key = builtins.sub key 9;} {key = builtins.sub key 13; foo = true;}];
+  };
+
+  sort = (import ./lib.nix).sortBy (a: b: builtins.lessThan a.key b.key);
+
+in sort closure
diff --git a/tests/lang/eval-okay-concat.exp b/tests/lang/eval-okay-concat.exp
new file mode 100644
index 000000000000..bb4bbd577410
--- /dev/null
+++ b/tests/lang/eval-okay-concat.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 5 6 7 8 9 ]
diff --git a/tests/lang/eval-okay-concat.nix b/tests/lang/eval-okay-concat.nix
new file mode 100644
index 000000000000..d158a9bf05b9
--- /dev/null
+++ b/tests/lang/eval-okay-concat.nix
@@ -0,0 +1 @@
+[1 2 3] ++ [4 5 6] ++ [7 8 9]
diff --git a/tests/lang/eval-okay-context.exp b/tests/lang/eval-okay-context.exp
new file mode 100644
index 000000000000..2f535bdbc454
--- /dev/null
+++ b/tests/lang/eval-okay-context.exp
@@ -0,0 +1 @@
+"foo eval-okay-context.nix bar"
diff --git a/tests/lang/eval-okay-context.nix b/tests/lang/eval-okay-context.nix
new file mode 100644
index 000000000000..b3f1748975f7
--- /dev/null
+++ b/tests/lang/eval-okay-context.nix
@@ -0,0 +1,6 @@
+let s = "foo ${builtins.substring 33 100 (baseNameOf ./eval-okay-context.nix)} bar";
+in
+  if s == "foo eval-okay-context.nix bar"
+  then abort "context not discarded"
+  else builtins.unsafeDiscardStringContext s
+
diff --git a/tests/lang/eval-okay-curpos.exp b/tests/lang/eval-okay-curpos.exp
new file mode 100644
index 000000000000..65fd65b4d01f
--- /dev/null
+++ b/tests/lang/eval-okay-curpos.exp
@@ -0,0 +1 @@
+[ 3 7 4 9 ]
diff --git a/tests/lang/eval-okay-curpos.nix b/tests/lang/eval-okay-curpos.nix
new file mode 100644
index 000000000000..b79553df0bd3
--- /dev/null
+++ b/tests/lang/eval-okay-curpos.nix
@@ -0,0 +1,5 @@
+# Bla
+let
+  x = __curPos;
+    y = __curPos;
+in [ x.line x.column y.line y.column ]
diff --git a/tests/lang/eval-okay-delayed-with-inherit.exp b/tests/lang/eval-okay-delayed-with-inherit.exp
new file mode 100644
index 000000000000..eaacb55c1aff
--- /dev/null
+++ b/tests/lang/eval-okay-delayed-with-inherit.exp
@@ -0,0 +1 @@
+"b-overridden"
diff --git a/tests/lang/eval-okay-delayed-with-inherit.nix b/tests/lang/eval-okay-delayed-with-inherit.nix
new file mode 100644
index 000000000000..84b388c27130
--- /dev/null
+++ b/tests/lang/eval-okay-delayed-with-inherit.nix
@@ -0,0 +1,24 @@
+let
+  pkgs_ = with pkgs; {
+    a = derivation {
+      name = "a";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+      inherit b;
+    };
+
+    inherit b;
+  };
+
+  packageOverrides = p: {
+    b = derivation {
+      name = "b-overridden";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+    };
+  };
+
+  pkgs = pkgs_ // (packageOverrides pkgs_);
+in pkgs.a.b.name
diff --git a/tests/lang/eval-okay-delayed-with.exp b/tests/lang/eval-okay-delayed-with.exp
new file mode 100644
index 000000000000..8e7c61ab8e77
--- /dev/null
+++ b/tests/lang/eval-okay-delayed-with.exp
@@ -0,0 +1 @@
+"b-overridden b-overridden a"
diff --git a/tests/lang/eval-okay-delayed-with.nix b/tests/lang/eval-okay-delayed-with.nix
new file mode 100644
index 000000000000..3fb023e1cd42
--- /dev/null
+++ b/tests/lang/eval-okay-delayed-with.nix
@@ -0,0 +1,29 @@
+let
+
+  pkgs_ = with pkgs; {
+    a = derivation {
+      name = "a";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+      inherit b;
+    };
+
+    b = derivation {
+      name = "b";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+      inherit a;
+    };
+
+    c = b;
+  };
+
+  packageOverrides = pkgs: with pkgs; {
+    b = derivation (b.drvAttrs // { name = "${b.name}-overridden"; });
+  };
+
+  pkgs = pkgs_ // (packageOverrides pkgs_);
+
+in "${pkgs.a.b.name} ${pkgs.c.name} ${pkgs.b.a.name}"
diff --git a/tests/lang/eval-okay-dynamic-attrs-2.exp b/tests/lang/eval-okay-dynamic-attrs-2.exp
new file mode 100644
index 000000000000..27ba77ddaf61
--- /dev/null
+++ b/tests/lang/eval-okay-dynamic-attrs-2.exp
@@ -0,0 +1 @@
+true
diff --git a/tests/lang/eval-okay-dynamic-attrs-2.nix b/tests/lang/eval-okay-dynamic-attrs-2.nix
new file mode 100644
index 000000000000..6d57bf854908
--- /dev/null
+++ b/tests/lang/eval-okay-dynamic-attrs-2.nix
@@ -0,0 +1 @@
+{ a."${"b"}" = true; a."${"c"}" = false; }.a.b
diff --git a/tests/lang/eval-okay-dynamic-attrs-bare.exp b/tests/lang/eval-okay-dynamic-attrs-bare.exp
new file mode 100644
index 000000000000..df8750afc036
--- /dev/null
+++ b/tests/lang/eval-okay-dynamic-attrs-bare.exp
@@ -0,0 +1 @@
+{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; }
diff --git a/tests/lang/eval-okay-dynamic-attrs-bare.nix b/tests/lang/eval-okay-dynamic-attrs-bare.nix
new file mode 100644
index 000000000000..0dbe15e6384c
--- /dev/null
+++ b/tests/lang/eval-okay-dynamic-attrs-bare.nix
@@ -0,0 +1,17 @@
+let
+  aString = "a";
+
+  bString = "b";
+in {
+  hasAttrs = { a.b = null; } ? ${aString}.b;
+
+  selectAttrs = { a.b = true; }.a.${bString};
+
+  selectOrAttrs = { }.${aString} or true;
+
+  binds = { ${aString}."${bString}c" = true; }.a.bc;
+
+  recBinds = rec { ${bString} = a; a = true; }.b;
+
+  multiAttrs = { ${aString} = true; ${bString} = false; }.a;
+}
diff --git a/tests/lang/eval-okay-dynamic-attrs.exp b/tests/lang/eval-okay-dynamic-attrs.exp
new file mode 100644
index 000000000000..df8750afc036
--- /dev/null
+++ b/tests/lang/eval-okay-dynamic-attrs.exp
@@ -0,0 +1 @@
+{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; }
diff --git a/tests/lang/eval-okay-dynamic-attrs.nix b/tests/lang/eval-okay-dynamic-attrs.nix
new file mode 100644
index 000000000000..ee02ac7e6579
--- /dev/null
+++ b/tests/lang/eval-okay-dynamic-attrs.nix
@@ -0,0 +1,17 @@
+let
+  aString = "a";
+
+  bString = "b";
+in {
+  hasAttrs = { a.b = null; } ? "${aString}".b;
+
+  selectAttrs = { a.b = true; }.a."${bString}";
+
+  selectOrAttrs = { }."${aString}" or true;
+
+  binds = { "${aString}"."${bString}c" = true; }.a.bc;
+
+  recBinds = rec { "${bString}" = a; a = true; }.b;
+
+  multiAttrs = { "${aString}" = true; "${bString}" = false; }.a;
+}
diff --git a/tests/lang/eval-okay-empty-args.exp b/tests/lang/eval-okay-empty-args.exp
new file mode 100644
index 000000000000..cb5537d5d7ce
--- /dev/null
+++ b/tests/lang/eval-okay-empty-args.exp
@@ -0,0 +1 @@
+"ab"
diff --git a/tests/lang/eval-okay-empty-args.nix b/tests/lang/eval-okay-empty-args.nix
new file mode 100644
index 000000000000..78c133afdd94
--- /dev/null
+++ b/tests/lang/eval-okay-empty-args.nix
@@ -0,0 +1 @@
+({}: {x,y,}: "${x}${y}") {} {x = "a"; y = "b";}
diff --git a/tests/lang/eval-okay-eq-derivations.exp b/tests/lang/eval-okay-eq-derivations.exp
new file mode 100644
index 000000000000..ec04aab6aeec
--- /dev/null
+++ b/tests/lang/eval-okay-eq-derivations.exp
@@ -0,0 +1 @@
+[ true true true false ]
diff --git a/tests/lang/eval-okay-eq-derivations.nix b/tests/lang/eval-okay-eq-derivations.nix
new file mode 100644
index 000000000000..d526cb4a2161
--- /dev/null
+++ b/tests/lang/eval-okay-eq-derivations.nix
@@ -0,0 +1,10 @@
+let
+
+  drvA1 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; };
+  drvA2 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; };
+  drvA3 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; } // { dummy = 1; };
+  
+  drvC1 = derivation { name = "c"; builder = "/foo"; system = "i686-linux"; };
+  drvC2 = derivation { name = "c"; builder = "/bar"; system = "i686-linux"; };
+
+in [ (drvA1 == drvA1) (drvA1 == drvA2) (drvA1 == drvA3) (drvC1 == drvC2) ]
diff --git a/tests/lang/eval-okay-eq.exp.disabled b/tests/lang/eval-okay-eq.exp.disabled
new file mode 100644
index 000000000000..2015847b65e7
--- /dev/null
+++ b/tests/lang/eval-okay-eq.exp.disabled
@@ -0,0 +1 @@
+Bool(True)
diff --git a/tests/lang/eval-okay-eq.nix b/tests/lang/eval-okay-eq.nix
new file mode 100644
index 000000000000..73d200b38141
--- /dev/null
+++ b/tests/lang/eval-okay-eq.nix
@@ -0,0 +1,3 @@
+["foobar" (rec {x = 1; y = x;})]
+==
+[("foo" + "bar") ({x = 1; y = 1;})]
diff --git a/tests/lang/eval-okay-flatten.exp b/tests/lang/eval-okay-flatten.exp
new file mode 100644
index 000000000000..b979b2b8b9bc
--- /dev/null
+++ b/tests/lang/eval-okay-flatten.exp
@@ -0,0 +1 @@
+"1234567"
diff --git a/tests/lang/eval-okay-flatten.nix b/tests/lang/eval-okay-flatten.nix
new file mode 100644
index 000000000000..fe911e9683e2
--- /dev/null
+++ b/tests/lang/eval-okay-flatten.nix
@@ -0,0 +1,8 @@
+with import ./lib.nix;
+
+let {
+
+  l = ["1" "2" ["3" ["4"] ["5" "6"]] "7"];
+
+  body = concat (flatten l);
+}
diff --git a/tests/lang/eval-okay-functionargs.exp.xml b/tests/lang/eval-okay-functionargs.exp.xml
new file mode 100644
index 000000000000..651f54c36341
--- /dev/null
+++ b/tests/lang/eval-okay-functionargs.exp.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <string value="stdenv" />
+    <string value="fetchurl" />
+    <string value="aterm-stdenv" />
+    <string value="aterm-stdenv2" />
+    <string value="libX11" />
+    <string value="libXv" />
+    <string value="mplayer-stdenv2.libXv-libX11" />
+    <string value="mplayer-stdenv2.libXv-libX11_2" />
+    <string value="nix-stdenv-aterm-stdenv" />
+    <string value="nix-stdenv2-aterm2-stdenv2" />
+  </list>
+</expr>
diff --git a/tests/lang/eval-okay-functionargs.nix b/tests/lang/eval-okay-functionargs.nix
new file mode 100644
index 000000000000..68dca62ee18d
--- /dev/null
+++ b/tests/lang/eval-okay-functionargs.nix
@@ -0,0 +1,80 @@
+let
+
+  stdenvFun = { }: { name = "stdenv"; };
+  stdenv2Fun = { }: { name = "stdenv2"; };
+  fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; };
+  atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; };
+  aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; };
+  nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; };
+  
+  mplayerFun =
+    { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null  }:
+    assert stdenv.name == "stdenv2";
+    assert enableX11 -> xorg.libXv.name == "libXv";
+    assert enableFoo -> foo != null;
+    { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; };
+
+  makeOverridable = f: origArgs: f origArgs //
+    { override = newArgs:
+        makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
+    };
+    
+  callPackage_ = pkgs: f: args:
+    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args);
+
+  allPackages =
+    { overrides ? (pkgs: pkgsPrev: { }) }:
+    let
+      callPackage = callPackage_ pkgs;
+      pkgs = pkgsStd // (overrides pkgs pkgsStd);
+      pkgsStd = {
+        inherit pkgs;
+        stdenv = callPackage stdenvFun { };
+        stdenv2 = callPackage stdenv2Fun { };
+        fetchurl = callPackage fetchurlFun { };
+        aterm = callPackage atermFun { };
+        xorg = callPackage xorgFun { };
+        mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; };
+        nix = callPackage nixFun { };
+      };
+    in pkgs;
+
+  libX11Fun = { stdenv, fetchurl }: { name = "libX11"; };
+  libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; };
+  libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; };
+  
+  xorgFun =
+    { pkgs }:
+    let callPackage = callPackage_ (pkgs // pkgs.xorg); in
+    {
+      libX11 = callPackage libX11Fun { };
+      libXv = callPackage libXvFun { };
+    };
+
+in
+
+let
+
+  pkgs = allPackages { };
+  
+  pkgs2 = allPackages {
+    overrides = pkgs: pkgsPrev: {
+      stdenv = pkgs.stdenv2;
+      nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; };
+      xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; };
+    };
+  };
+  
+in
+
+  [ pkgs.stdenv.name
+    pkgs.fetchurl.name
+    pkgs.aterm.name
+    pkgs2.aterm.name
+    pkgs.xorg.libX11.name
+    pkgs.xorg.libXv.name
+    pkgs.mplayer.name
+    pkgs2.mplayer.name
+    pkgs.nix.name
+    pkgs2.nix.name
+  ]
diff --git a/tests/lang/eval-okay-getattrpos.exp b/tests/lang/eval-okay-getattrpos.exp
new file mode 100644
index 000000000000..469249bbc646
--- /dev/null
+++ b/tests/lang/eval-okay-getattrpos.exp
@@ -0,0 +1 @@
+{ column = 5; file = "eval-okay-getattrpos.nix"; line = 3; }
diff --git a/tests/lang/eval-okay-getattrpos.nix b/tests/lang/eval-okay-getattrpos.nix
new file mode 100644
index 000000000000..ca6b07961547
--- /dev/null
+++ b/tests/lang/eval-okay-getattrpos.nix
@@ -0,0 +1,6 @@
+let
+  as = {
+    foo = "bar";
+  };
+  pos = builtins.unsafeGetAttrPos "foo" as;
+in { inherit (pos) column line; file = baseNameOf pos.file; }
diff --git a/tests/lang/eval-okay-getenv.exp b/tests/lang/eval-okay-getenv.exp
new file mode 100644
index 000000000000..14e24d419005
--- /dev/null
+++ b/tests/lang/eval-okay-getenv.exp
@@ -0,0 +1 @@
+"foobar"
diff --git a/tests/lang/eval-okay-getenv.nix b/tests/lang/eval-okay-getenv.nix
new file mode 100644
index 000000000000..4cfec5f553d9
--- /dev/null
+++ b/tests/lang/eval-okay-getenv.nix
@@ -0,0 +1 @@
+builtins.getEnv "TEST_VAR" + (if builtins.getEnv "NO_SUCH_VAR" == "" then "bar" else "bla")
diff --git a/tests/lang/eval-okay-hash.exp b/tests/lang/eval-okay-hash.exp
new file mode 100644
index 000000000000..7bbe452bcc01
--- /dev/null
+++ b/tests/lang/eval-okay-hash.exp
@@ -0,0 +1 @@
+[ "d41d8cd98f00b204e9800998ecf8427e" "6c69ee7f211c640419d5366cc076ae46" "bb3438fbabd460ea6dbd27d153e2233b" "da39a3ee5e6b4b0d3255bfef95601890afd80709" "cd54e8568c1b37cf1e5badb0779bcbf382212189" "6d12e10b1d331dad210e47fd25d4f260802b7e77" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "900a4469df00ccbfd0c145c6d1e4b7953dd0afafadd7534e3a4019e8d38fc663" "ad0387b3bd8652f730ca46d25f9c170af0fd589f42e7f23f5a9e6412d97d7e56" ]
diff --git a/tests/lang/eval-okay-hash.nix b/tests/lang/eval-okay-hash.nix
new file mode 100644
index 000000000000..2fff17f849bb
--- /dev/null
+++ b/tests/lang/eval-okay-hash.nix
@@ -0,0 +1,7 @@
+let
+  md5 = builtins.hashString "md5";
+  sha1 = builtins.hashString "sha1";
+  sha256 = builtins.hashString "sha256";
+  strings = [ "" "text 1" "text 2" ];
+in
+  (builtins.map md5 strings) ++ (builtins.map sha1 strings) ++ (builtins.map sha256 strings)
diff --git a/tests/lang/eval-okay-if.exp b/tests/lang/eval-okay-if.exp
new file mode 100644
index 000000000000..00750edc07d6
--- /dev/null
+++ b/tests/lang/eval-okay-if.exp
@@ -0,0 +1 @@
+3
diff --git a/tests/lang/eval-okay-if.nix b/tests/lang/eval-okay-if.nix
new file mode 100644
index 000000000000..23e4c74d5016
--- /dev/null
+++ b/tests/lang/eval-okay-if.nix
@@ -0,0 +1 @@
+if "foo" != "f" + "oo" then 1 else if false then 2 else 3
diff --git a/tests/lang/eval-okay-ind-string.exp b/tests/lang/eval-okay-ind-string.exp
new file mode 100644
index 000000000000..886219dcf652
--- /dev/null
+++ b/tests/lang/eval-okay-ind-string.exp
@@ -0,0 +1 @@
+"This is an indented multi-line string\nliteral.  An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed.  Thus,\nin this case four spaces will be\nstripped from each line, even though\n  THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n  followed by a newline, it's stripped, but\n  that's not the case here. Two spaces are\n  stripped because of the \"  \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n  The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n   Tabs are not interpreted as whitespace (since we can't guess\n   what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n   space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored.  But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n  Similarly you can force an indentation level,\n  in this case to 2 spaces.  This works because the anti-quote\n  is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n  rm -f /var/run/opengl-driver\n  ln -sf 123 /var/run/opengl-driver\n\n  rm -f /var/log/slim.log\n   \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf  \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin         \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/          # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\n"
diff --git a/tests/lang/eval-okay-ind-string.nix b/tests/lang/eval-okay-ind-string.nix
new file mode 100644
index 000000000000..1556aae9f54f
--- /dev/null
+++ b/tests/lang/eval-okay-ind-string.nix
@@ -0,0 +1,120 @@
+let
+
+  s1 = ''
+    This is an indented multi-line string
+    literal.  An amount of whitespace at
+    the start of each line matching the minimum
+    indentation of all lines in the string
+    literal together will be removed.  Thus,
+    in this case four spaces will be
+    stripped from each line, even though
+      THIS LINE is indented six spaces.
+
+    Also, empty lines don't count in the
+    determination of the indentation level (the
+    previous empty line has indentation 0, but
+    it doesn't matter).
+  '';
+
+  s2 = ''  If the string starts with whitespace
+    followed by a newline, it's stripped, but
+    that's not the case here. Two spaces are
+    stripped because of the "  " at the start. 
+  '';
+
+  s3 = ''
+      This line is indented
+      a bit further.
+        ''; # indentation of last line doesn't count if it's empty
+
+  s4 = ''
+    Anti-quotations, like ${if true then "so" else "not so"}, are
+    also allowed.
+  '';
+
+  s5 = ''
+      The \ is not special here.
+    ' can be followed by any character except another ', e.g. 'x'.
+    Likewise for $, e.g. $$ or $varName.
+    But ' followed by ' is special, as is $ followed by {.
+    If you want them, use anti-quotations: ${"''"}, ${"\${"}.
+  '';
+
+  s6 = ''  
+    Tabs are not interpreted as whitespace (since we can't guess
+    what tab settings are intended), so don't use them.
+ 	This line starts with a space and a tab, so only one
+    space will be stripped from each line.
+  '';
+
+  s7 = ''
+    Also note that if the last line (just before the closing ' ')
+    consists only of whitespace, it's ignored.  But here there is
+    some non-whitespace stuff, so the line isn't removed. '';
+
+  s8 = ''    ${""}
+    This shows a hacky way to preserve an empty line after the start.
+    But there's no reason to do so: you could just repeat the empty
+    line.
+  '';
+
+  s9 = ''
+  ${""}  Similarly you can force an indentation level,
+    in this case to 2 spaces.  This works because the anti-quote
+    is significant (not whitespace).
+  '';
+
+  s10 = ''
+  '';
+
+  s11 = '''';
+
+  s12 = ''   '';
+
+  s13 = ''
+    start on network-interfaces
+
+    start script
+    
+      rm -f /var/run/opengl-driver
+      ${if true
+        then "ln -sf 123 /var/run/opengl-driver"
+        else if true
+        then "ln -sf 456 /var/run/opengl-driver"
+        else ""
+      }
+
+      rm -f /var/log/slim.log
+       
+    end script
+
+    env SLIM_CFGFILE=${"abc"}
+    env SLIM_THEMESDIR=${"def"}
+    env FONTCONFIG_FILE=/etc/fonts/fonts.conf  				# !!! cleanup
+    env XKB_BINDIR=${"foo"}/bin         				# Needed for the Xkb extension.
+    env LD_LIBRARY_PATH=${"libX11"}/lib:${"libXext"}/lib:/usr/lib/          # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)
+
+    ${if true
+      then "env XORG_DRI_DRIVER_PATH=${"nvidiaDrivers"}/X11R6/lib/modules/drivers/"
+    else if true
+      then "env XORG_DRI_DRIVER_PATH=${"mesa"}/lib/modules/dri"
+      else ""
+    } 
+
+    exec ${"slim"}/bin/slim
+  '';
+
+  s14 = ''
+    Escaping of ' followed by ': '''
+    Escaping of $ followed by {: ''${
+    And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t.
+  '';
+
+  # Regression test: antiquotation in '${x}' should work, but didn't.
+  s15 = let x = "bla"; in ''
+    foo
+    '${x}'
+    bar
+  '';
+
+in s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15
diff --git a/tests/lang/eval-okay-let.exp b/tests/lang/eval-okay-let.exp
new file mode 100644
index 000000000000..14e24d419005
--- /dev/null
+++ b/tests/lang/eval-okay-let.exp
@@ -0,0 +1 @@
+"foobar"
diff --git a/tests/lang/eval-okay-let.nix b/tests/lang/eval-okay-let.nix
new file mode 100644
index 000000000000..fe118c5282e3
--- /dev/null
+++ b/tests/lang/eval-okay-let.nix
@@ -0,0 +1,5 @@
+let {
+  x = "foo";
+  y = "bar";
+  body = x + y;
+}
diff --git a/tests/lang/eval-okay-list.exp b/tests/lang/eval-okay-list.exp
new file mode 100644
index 000000000000..f784f26d83f4
--- /dev/null
+++ b/tests/lang/eval-okay-list.exp
@@ -0,0 +1 @@
+"foobarblatest"
diff --git a/tests/lang/eval-okay-list.nix b/tests/lang/eval-okay-list.nix
new file mode 100644
index 000000000000..d433bcf908ba
--- /dev/null
+++ b/tests/lang/eval-okay-list.nix
@@ -0,0 +1,7 @@
+with import ./lib.nix;
+
+let {
+
+  body = concat ["foo" "bar" "bla" "test"];
+    
+}
\ No newline at end of file
diff --git a/tests/lang/eval-okay-listtoattrs.exp b/tests/lang/eval-okay-listtoattrs.exp
new file mode 100644
index 000000000000..74abef7bc6ed
--- /dev/null
+++ b/tests/lang/eval-okay-listtoattrs.exp
@@ -0,0 +1 @@
+"AAbar"
diff --git a/tests/lang/eval-okay-listtoattrs.nix b/tests/lang/eval-okay-listtoattrs.nix
new file mode 100644
index 000000000000..4186e029b538
--- /dev/null
+++ b/tests/lang/eval-okay-listtoattrs.nix
@@ -0,0 +1,11 @@
+# this test shows how to use listToAttrs and that evaluation is still lazy (throw isn't called)
+with import ./lib.nix;
+
+let 
+  asi = name: value : { inherit name value; };
+  list = [ ( asi "a" "A" ) ( asi "b" "B" ) ];
+  a = builtins.listToAttrs list;
+  b = builtins.listToAttrs ( list ++ list );
+  r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ];
+  x = builtins.listToAttrs [ (asi "foo" "bar") (asi "foo" "bla") ];
+in concat (map (x: x.a) r.result) + x.foo
diff --git a/tests/lang/eval-okay-logic.exp b/tests/lang/eval-okay-logic.exp
new file mode 100644
index 000000000000..d00491fd7e5b
--- /dev/null
+++ b/tests/lang/eval-okay-logic.exp
@@ -0,0 +1 @@
+1
diff --git a/tests/lang/eval-okay-logic.nix b/tests/lang/eval-okay-logic.nix
new file mode 100644
index 000000000000..fbb12794401f
--- /dev/null
+++ b/tests/lang/eval-okay-logic.nix
@@ -0,0 +1 @@
+assert !false && (true || false) -> true; 1
diff --git a/tests/lang/eval-okay-map.exp b/tests/lang/eval-okay-map.exp
new file mode 100644
index 000000000000..dbb64f717b96
--- /dev/null
+++ b/tests/lang/eval-okay-map.exp
@@ -0,0 +1 @@
+"foobarblabarxyzzybar"
diff --git a/tests/lang/eval-okay-map.nix b/tests/lang/eval-okay-map.nix
new file mode 100644
index 000000000000..a76c1d811454
--- /dev/null
+++ b/tests/lang/eval-okay-map.nix
@@ -0,0 +1,3 @@
+with import ./lib.nix;
+
+concat (map (x: x + "bar") [ "foo" "bla" "xyzzy" ])
\ No newline at end of file
diff --git a/tests/lang/eval-okay-new-let.exp b/tests/lang/eval-okay-new-let.exp
new file mode 100644
index 000000000000..f98b388071c2
--- /dev/null
+++ b/tests/lang/eval-okay-new-let.exp
@@ -0,0 +1 @@
+"xyzzyfoobar"
diff --git a/tests/lang/eval-okay-new-let.nix b/tests/lang/eval-okay-new-let.nix
new file mode 100644
index 000000000000..738123141508
--- /dev/null
+++ b/tests/lang/eval-okay-new-let.nix
@@ -0,0 +1,14 @@
+let
+
+  f = z: 
+
+    let
+      x = "foo";
+      y = "bar";
+      body = 1; # compat test
+    in
+      z + x + y;
+
+  arg = "xyzzy";
+
+in f arg
diff --git a/tests/lang/eval-okay-overrides.exp b/tests/lang/eval-okay-overrides.exp
new file mode 100644
index 000000000000..0cfbf08886fc
--- /dev/null
+++ b/tests/lang/eval-okay-overrides.exp
@@ -0,0 +1 @@
+2
diff --git a/tests/lang/eval-okay-overrides.nix b/tests/lang/eval-okay-overrides.nix
new file mode 100644
index 000000000000..358742b36e22
--- /dev/null
+++ b/tests/lang/eval-okay-overrides.nix
@@ -0,0 +1,9 @@
+let
+
+  overrides = { a = 2; };
+
+in (rec {
+  __overrides = overrides;
+  x = a;
+  a = 1;
+}).x
diff --git a/tests/lang/eval-okay-pathexists.exp b/tests/lang/eval-okay-pathexists.exp
new file mode 100644
index 000000000000..27ba77ddaf61
--- /dev/null
+++ b/tests/lang/eval-okay-pathexists.exp
@@ -0,0 +1 @@
+true
diff --git a/tests/lang/eval-okay-pathexists.nix b/tests/lang/eval-okay-pathexists.nix
new file mode 100644
index 000000000000..50c28ee0cd30
--- /dev/null
+++ b/tests/lang/eval-okay-pathexists.nix
@@ -0,0 +1,5 @@
+builtins.pathExists (builtins.toPath ./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/lang/eval-okay-patterns.exp b/tests/lang/eval-okay-patterns.exp
new file mode 100644
index 000000000000..a4304010fe80
--- /dev/null
+++ b/tests/lang/eval-okay-patterns.exp
@@ -0,0 +1 @@
+"abcxyzDDDDEFijk"
diff --git a/tests/lang/eval-okay-patterns.nix b/tests/lang/eval-okay-patterns.nix
new file mode 100644
index 000000000000..96fd25a01517
--- /dev/null
+++ b/tests/lang/eval-okay-patterns.nix
@@ -0,0 +1,16 @@
+let
+
+  f = args@{x, y, z}: x + args.y + z;
+
+  g = {x, y, z}@args: f args;
+
+  h = {x ? "d", y ? x, z ? args.x}@args: x + y + z;
+
+  j = {x, y, z, ...}: x + y + z;
+
+in
+  f {x = "a"; y = "b"; z = "c";} +
+  g {x = "x"; y = "y"; z = "z";} +
+  h {x = "D";} +
+  h {x = "D"; y = "E"; z = "F";} +
+  j {x = "i"; y = "j"; z = "k"; bla = "bla"; foo = "bar";}
diff --git a/tests/lang/eval-okay-readfile.exp b/tests/lang/eval-okay-readfile.exp
new file mode 100644
index 000000000000..a2c87d0c439f
--- /dev/null
+++ b/tests/lang/eval-okay-readfile.exp
@@ -0,0 +1 @@
+"builtins.readFile ./eval-okay-readfile.nix\n"
diff --git a/tests/lang/eval-okay-readfile.nix b/tests/lang/eval-okay-readfile.nix
new file mode 100644
index 000000000000..82f7cb17435a
--- /dev/null
+++ b/tests/lang/eval-okay-readfile.nix
@@ -0,0 +1 @@
+builtins.readFile ./eval-okay-readfile.nix
diff --git a/tests/lang/eval-okay-redefine-builtin.exp b/tests/lang/eval-okay-redefine-builtin.exp
new file mode 100644
index 000000000000..c508d5366f70
--- /dev/null
+++ b/tests/lang/eval-okay-redefine-builtin.exp
@@ -0,0 +1 @@
+false
diff --git a/tests/lang/eval-okay-redefine-builtin.nix b/tests/lang/eval-okay-redefine-builtin.nix
new file mode 100644
index 000000000000..df9fc3f37d22
--- /dev/null
+++ b/tests/lang/eval-okay-redefine-builtin.nix
@@ -0,0 +1,3 @@
+let
+  throw = abort "Error!";
+in (builtins.tryEval <foobaz>).success
diff --git a/tests/lang/eval-okay-remove.exp b/tests/lang/eval-okay-remove.exp
new file mode 100644
index 000000000000..8d38505c1686
--- /dev/null
+++ b/tests/lang/eval-okay-remove.exp
@@ -0,0 +1 @@
+456
diff --git a/tests/lang/eval-okay-remove.nix b/tests/lang/eval-okay-remove.nix
new file mode 100644
index 000000000000..4ad5ba897fa7
--- /dev/null
+++ b/tests/lang/eval-okay-remove.nix
@@ -0,0 +1,5 @@
+let {
+  attrs = {x = 123; y = 456;};
+
+  body = (removeAttrs attrs ["x"]).y;
+}
\ No newline at end of file
diff --git a/tests/lang/eval-okay-scope-1.exp b/tests/lang/eval-okay-scope-1.exp
new file mode 100644
index 000000000000..00750edc07d6
--- /dev/null
+++ b/tests/lang/eval-okay-scope-1.exp
@@ -0,0 +1 @@
+3
diff --git a/tests/lang/eval-okay-scope-1.nix b/tests/lang/eval-okay-scope-1.nix
new file mode 100644
index 000000000000..fa38a7174e03
--- /dev/null
+++ b/tests/lang/eval-okay-scope-1.nix
@@ -0,0 +1,6 @@
+(({x}: x:
+
+  { x = 1;
+    y = x;
+  }
+) {x = 2;} 3).y
diff --git a/tests/lang/eval-okay-scope-2.exp b/tests/lang/eval-okay-scope-2.exp
new file mode 100644
index 000000000000..d00491fd7e5b
--- /dev/null
+++ b/tests/lang/eval-okay-scope-2.exp
@@ -0,0 +1 @@
+1
diff --git a/tests/lang/eval-okay-scope-2.nix b/tests/lang/eval-okay-scope-2.nix
new file mode 100644
index 000000000000..eb8b02bc4994
--- /dev/null
+++ b/tests/lang/eval-okay-scope-2.nix
@@ -0,0 +1,6 @@
+((x: {x}:
+  rec {
+    x = 1;
+    y = x;
+  }
+) 2 {x = 3;}).y
diff --git a/tests/lang/eval-okay-scope-3.exp b/tests/lang/eval-okay-scope-3.exp
new file mode 100644
index 000000000000..b8626c4cff28
--- /dev/null
+++ b/tests/lang/eval-okay-scope-3.exp
@@ -0,0 +1 @@
+4
diff --git a/tests/lang/eval-okay-scope-3.nix b/tests/lang/eval-okay-scope-3.nix
new file mode 100644
index 000000000000..10d6bc04d830
--- /dev/null
+++ b/tests/lang/eval-okay-scope-3.nix
@@ -0,0 +1,6 @@
+((x: as: {x}:
+  rec {
+    inherit (as) x;
+    y = x;
+  }
+) 2 {x = 4;} {x = 3;}).y
diff --git a/tests/lang/eval-okay-scope-4.exp b/tests/lang/eval-okay-scope-4.exp
new file mode 100644
index 000000000000..00ff03a46c9b
--- /dev/null
+++ b/tests/lang/eval-okay-scope-4.exp
@@ -0,0 +1 @@
+"ccdd"
diff --git a/tests/lang/eval-okay-scope-4.nix b/tests/lang/eval-okay-scope-4.nix
new file mode 100644
index 000000000000..dc8243bc8546
--- /dev/null
+++ b/tests/lang/eval-okay-scope-4.nix
@@ -0,0 +1,10 @@
+let {
+
+  x = "a";
+  y = "b";
+
+  f = {x ? y, y ? x}: x + y;
+
+  body = f {x = "c";} + f {y = "d";};
+
+}
diff --git a/tests/lang/eval-okay-scope-6.exp b/tests/lang/eval-okay-scope-6.exp
new file mode 100644
index 000000000000..00ff03a46c9b
--- /dev/null
+++ b/tests/lang/eval-okay-scope-6.exp
@@ -0,0 +1 @@
+"ccdd"
diff --git a/tests/lang/eval-okay-scope-6.nix b/tests/lang/eval-okay-scope-6.nix
new file mode 100644
index 000000000000..0995d4e7e7e0
--- /dev/null
+++ b/tests/lang/eval-okay-scope-6.nix
@@ -0,0 +1,7 @@
+let {
+
+  f = {x ? y, y ? x}: x + y;
+
+  body = f {x = "c";} + f {y = "d";};
+
+}
diff --git a/tests/lang/eval-okay-scope-7.exp b/tests/lang/eval-okay-scope-7.exp
new file mode 100644
index 000000000000..d00491fd7e5b
--- /dev/null
+++ b/tests/lang/eval-okay-scope-7.exp
@@ -0,0 +1 @@
+1
diff --git a/tests/lang/eval-okay-scope-7.nix b/tests/lang/eval-okay-scope-7.nix
new file mode 100644
index 000000000000..4da02968f6b7
--- /dev/null
+++ b/tests/lang/eval-okay-scope-7.nix
@@ -0,0 +1,6 @@
+rec {
+  inherit (x) y;
+  x = {
+    y = 1;
+  };
+}.y
diff --git a/tests/lang/eval-okay-search-path.exp b/tests/lang/eval-okay-search-path.exp
new file mode 100644
index 000000000000..d0bc8c5e86b2
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.exp
@@ -0,0 +1 @@
+"abcc"
diff --git a/tests/lang/eval-okay-search-path.flags b/tests/lang/eval-okay-search-path.flags
new file mode 100644
index 000000000000..a28e6821004a
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.flags
@@ -0,0 +1 @@
+-I lang/dir1 -I lang/dir2 -I dir5=lang/dir3
\ No newline at end of file
diff --git a/tests/lang/eval-okay-search-path.nix b/tests/lang/eval-okay-search-path.nix
new file mode 100644
index 000000000000..9b711502336b
--- /dev/null
+++ b/tests/lang/eval-okay-search-path.nix
@@ -0,0 +1,3 @@
+assert builtins.pathExists <nix/buildenv.nix>;
+
+import <a.nix> + import <b.nix> + import <c.nix> + import <dir5/c.nix>
diff --git a/tests/lang/eval-okay-string.exp b/tests/lang/eval-okay-string.exp
new file mode 100644
index 000000000000..6079202470e3
--- /dev/null
+++ b/tests/lang/eval-okay-string.exp
@@ -0,0 +1 @@
+"foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar"
diff --git a/tests/lang/eval-okay-string.nix b/tests/lang/eval-okay-string.nix
new file mode 100644
index 000000000000..839539e6c171
--- /dev/null
+++ b/tests/lang/eval-okay-string.nix
@@ -0,0 +1,10 @@
+"foo" + "bar"
+  + toString (/a/b + /c/d)
+  + toString (/foo/bar + "/../xyzzy/." + "/foo.txt")
+  + ("/../foo" + toString /x/y)
+  + "escape: \"quote\" \n \\"
+  + "end
+of
+line"
+  + "foo${if true then "b${"a" + "r"}" else "xyzzy"}blaat"
+  + "foo$bar"
diff --git a/tests/lang/eval-okay-strings-as-attrs-names.exp b/tests/lang/eval-okay-strings-as-attrs-names.exp
new file mode 100644
index 000000000000..27ba77ddaf61
--- /dev/null
+++ b/tests/lang/eval-okay-strings-as-attrs-names.exp
@@ -0,0 +1 @@
+true
diff --git a/tests/lang/eval-okay-strings-as-attrs-names.nix b/tests/lang/eval-okay-strings-as-attrs-names.nix
new file mode 100644
index 000000000000..5e40928dbe31
--- /dev/null
+++ b/tests/lang/eval-okay-strings-as-attrs-names.nix
@@ -0,0 +1,20 @@
+let
+
+  attr = {
+    "key 1" = "test";
+    "key 2" = "caseok";
+  };
+
+  t1 = builtins.getAttr "key 1" attr;
+  t2 = attr."key 2";
+  t3 = attr ? "key 1";
+  t4 = builtins.attrNames { inherit (attr) "key 1"; };
+
+  # This is permitted, but there is currently no way to reference this
+  # variable.
+  "foo bar" = 1;
+
+in t1 == "test"
+   && t2 == "caseok"
+   && t3 == true
+   && t4 == ["key 1"]
diff --git a/tests/lang/eval-okay-substring.exp b/tests/lang/eval-okay-substring.exp
new file mode 100644
index 000000000000..6aace04b0f57
--- /dev/null
+++ b/tests/lang/eval-okay-substring.exp
@@ -0,0 +1 @@
+"ooxfoobarybarzobaabbc"
diff --git a/tests/lang/eval-okay-substring.nix b/tests/lang/eval-okay-substring.nix
new file mode 100644
index 000000000000..424af00d9b3b
--- /dev/null
+++ b/tests/lang/eval-okay-substring.nix
@@ -0,0 +1,21 @@
+with builtins;
+
+let
+
+  s = "foobar";
+
+in
+
+substring 1 2 s
++ "x"
++ substring 0 (stringLength s) s
++ "y"
++ substring 3 100 s
++ "z"
++ substring 2 (sub (stringLength s) 3) s
++ "a"
++ substring 3 0 s
++ "b"
++ substring 3 1 s
++ "c"
++ substring 5 10 "perl"
diff --git a/tests/lang/eval-okay-tail-call-1.exp-disabled b/tests/lang/eval-okay-tail-call-1.exp-disabled
new file mode 100644
index 000000000000..f7393e847d34
--- /dev/null
+++ b/tests/lang/eval-okay-tail-call-1.exp-disabled
@@ -0,0 +1 @@
+100000
diff --git a/tests/lang/eval-okay-tail-call-1.nix b/tests/lang/eval-okay-tail-call-1.nix
new file mode 100644
index 000000000000..a3962ce3fdb5
--- /dev/null
+++ b/tests/lang/eval-okay-tail-call-1.nix
@@ -0,0 +1,3 @@
+let
+  f = n: if n == 100000 then n else f (n + 1);
+in f 0
diff --git a/tests/lang/eval-okay-tojson.exp b/tests/lang/eval-okay-tojson.exp
new file mode 100644
index 000000000000..e8164af2b66e
--- /dev/null
+++ b/tests/lang/eval-okay-tojson.exp
@@ -0,0 +1 @@
+"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3}"
diff --git a/tests/lang/eval-okay-tojson.nix b/tests/lang/eval-okay-tojson.nix
new file mode 100644
index 000000000000..0d4e55b3d367
--- /dev/null
+++ b/tests/lang/eval-okay-tojson.nix
@@ -0,0 +1,11 @@
+builtins.toJSON
+  { a = 123;
+    b = -456;
+    c = "foo";
+    d = "foo\n\"bar\"";
+    e = true;
+    f = false;
+    g = [ 1 2 3 ];
+    h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
+    i = 1 + 2;
+  }
diff --git a/tests/lang/eval-okay-toxml.exp b/tests/lang/eval-okay-toxml.exp
new file mode 100644
index 000000000000..828220890ecd
--- /dev/null
+++ b/tests/lang/eval-okay-toxml.exp
@@ -0,0 +1 @@
+"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n  <attrs>\n    <attr name=\"a\">\n      <string value=\"s\" />\n    </attr>\n  </attrs>\n</expr>\n"
diff --git a/tests/lang/eval-okay-toxml.nix b/tests/lang/eval-okay-toxml.nix
new file mode 100644
index 000000000000..068c97a6c1b3
--- /dev/null
+++ b/tests/lang/eval-okay-toxml.nix
@@ -0,0 +1,3 @@
+# Make sure the expected XML output is produced; in particular, make sure it
+# doesn't contain source location information.
+builtins.toXML { a = "s"; }
diff --git a/tests/lang/eval-okay-toxml2.exp b/tests/lang/eval-okay-toxml2.exp
new file mode 100644
index 000000000000..634a841eb190
--- /dev/null
+++ b/tests/lang/eval-okay-toxml2.exp
@@ -0,0 +1 @@
+"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n  <list>\n    <string value=\"ab\" />\n    <int value=\"10\" />\n    <attrs>\n      <attr name=\"x\">\n        <string value=\"x\" />\n      </attr>\n      <attr name=\"y\">\n        <string value=\"x\" />\n      </attr>\n    </attrs>\n  </list>\n</expr>\n"
diff --git a/tests/lang/eval-okay-toxml2.nix b/tests/lang/eval-okay-toxml2.nix
new file mode 100644
index 000000000000..ff1791b30eb5
--- /dev/null
+++ b/tests/lang/eval-okay-toxml2.nix
@@ -0,0 +1 @@
+builtins.toXML [("a" + "b") 10 (rec {x = "x"; y = x;})]
diff --git a/tests/lang/eval-okay-tryeval.exp b/tests/lang/eval-okay-tryeval.exp
new file mode 100644
index 000000000000..2b2e6fa711f4
--- /dev/null
+++ b/tests/lang/eval-okay-tryeval.exp
@@ -0,0 +1 @@
+{ x = { success = true; value = "x"; }; y = { success = false; value = false; }; z = { success = false; value = false; }; }
diff --git a/tests/lang/eval-okay-tryeval.nix b/tests/lang/eval-okay-tryeval.nix
new file mode 100644
index 000000000000..629bc440a85a
--- /dev/null
+++ b/tests/lang/eval-okay-tryeval.nix
@@ -0,0 +1,5 @@
+{
+  x = builtins.tryEval "x";
+  y = builtins.tryEval (assert false; "y");
+  z = builtins.tryEval (throw "bla");
+}
diff --git a/tests/lang/eval-okay-types.exp b/tests/lang/eval-okay-types.exp
new file mode 100644
index 000000000000..7a1f2cc17027
--- /dev/null
+++ b/tests/lang/eval-okay-types.exp
@@ -0,0 +1 @@
+[ true false true false true false true false true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
diff --git a/tests/lang/eval-okay-types.nix b/tests/lang/eval-okay-types.nix
new file mode 100644
index 000000000000..c3fe370ef770
--- /dev/null
+++ b/tests/lang/eval-okay-types.nix
@@ -0,0 +1,23 @@
+with builtins;
+
+[ (isNull null)
+  (isNull (x: x))
+  (isFunction (x: x))
+  (isFunction "fnord")
+  (isString ("foo" + "bar"))
+  (isString [ "x" ])
+  (isInt (1 + 2))
+  (isInt { x = 123; })
+  (isBool (true && false))
+  (isBool null)
+  (typeOf (3 * 4))
+  (typeOf true)
+  (typeOf "xyzzy")
+  (typeOf null)
+  (typeOf { x = 456; })
+  (typeOf [ 1 2 3 ])
+  (typeOf (x: x))
+  (typeOf ((x: y: x) 1))
+  (typeOf map)
+  (typeOf (map (x: x)))
+]
diff --git a/tests/lang/eval-okay-versions.exp b/tests/lang/eval-okay-versions.exp
new file mode 100644
index 000000000000..27ba77ddaf61
--- /dev/null
+++ b/tests/lang/eval-okay-versions.exp
@@ -0,0 +1 @@
+true
diff --git a/tests/lang/eval-okay-versions.nix b/tests/lang/eval-okay-versions.nix
new file mode 100644
index 000000000000..e63c36586bb6
--- /dev/null
+++ b/tests/lang/eval-okay-versions.nix
@@ -0,0 +1,40 @@
+let
+
+  name1 = "hello-1.0.2";
+  name2 = "hello";
+  name3 = "915resolution-0.5.2";
+  name4 = "xf86-video-i810-1.7.4";
+
+  eq = 0;
+  lt = builtins.sub 0 1;
+  gt = 1;
+
+  versionTest = v1: v2: expected:
+    let d1 = builtins.compareVersions v1 v2;
+        d2 = builtins.compareVersions v2 v1;
+    in d1 == builtins.sub 0 d2 && d1 == expected;
+
+  tests = [
+    ((builtins.parseDrvName name1).name == "hello")
+    ((builtins.parseDrvName name1).version == "1.0.2")
+    ((builtins.parseDrvName name2).name == "hello")
+    ((builtins.parseDrvName name2).version == "")
+    ((builtins.parseDrvName name3).name == "915resolution")
+    ((builtins.parseDrvName name3).version == "0.5.2")
+    ((builtins.parseDrvName name4).name == "xf86-video-i810")
+    ((builtins.parseDrvName name4).version == "1.7.4")
+    (versionTest "1.0" "2.3" lt)
+    (versionTest "2.1" "2.3" lt)
+    (versionTest "2.3" "2.3" eq)
+    (versionTest "2.5" "2.3" gt)
+    (versionTest "3.1" "2.3" gt)
+    (versionTest "2.3.1" "2.3" gt)
+    (versionTest "2.3.1" "2.3a" gt)
+    (versionTest "2.3pre1" "2.3" lt)
+    (versionTest "2.3pre3" "2.3pre12" lt)
+    (versionTest "2.3a" "2.3c" lt)
+    (versionTest "2.3pre1" "2.3c" lt)
+    (versionTest "2.3pre1" "2.3q" lt)
+  ];
+
+in (import ./lib.nix).and tests
diff --git a/tests/lang/eval-okay-with.exp b/tests/lang/eval-okay-with.exp
new file mode 100644
index 000000000000..378c8dc80403
--- /dev/null
+++ b/tests/lang/eval-okay-with.exp
@@ -0,0 +1 @@
+"xyzzybarxyzzybar"
diff --git a/tests/lang/eval-okay-with.nix b/tests/lang/eval-okay-with.nix
new file mode 100644
index 000000000000..033e8d3aba57
--- /dev/null
+++ b/tests/lang/eval-okay-with.nix
@@ -0,0 +1,19 @@
+let {
+
+  a = "xyzzy";
+
+  as = {
+    a = "foo";
+    b = "bar";
+  };
+
+  bs = {
+    a = "bar";
+  };
+
+  x = with as; a + b;
+
+  y = with as; with bs; a + b;
+
+  body = x + y;
+}
diff --git a/tests/lang/eval-okay-xml.exp.xml b/tests/lang/eval-okay-xml.exp.xml
new file mode 100644
index 000000000000..f124f939ed48
--- /dev/null
+++ b/tests/lang/eval-okay-xml.exp.xml
@@ -0,0 +1,49 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <attrs>
+    <attr name="a">
+      <string value="foo" />
+    </attr>
+    <attr name="at">
+      <function>
+        <attrspat name="args">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="b">
+      <string value="bar" />
+    </attr>
+    <attr name="c">
+      <string value="foobar" />
+    </attr>
+    <attr name="ellipsis">
+      <function>
+        <attrspat ellipsis="1">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="f">
+      <function>
+        <attrspat>
+          <attr name="z" />
+          <attr name="x" />
+          <attr name="y" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="id">
+      <function>
+        <varpat name="x" />
+      </function>
+    </attr>
+    <attr name="x">
+      <int value="123" />
+    </attr>
+  </attrs>
+</expr>
diff --git a/tests/lang/eval-okay-xml.nix b/tests/lang/eval-okay-xml.nix
new file mode 100644
index 000000000000..b9389bfae759
--- /dev/null
+++ b/tests/lang/eval-okay-xml.nix
@@ -0,0 +1,19 @@
+rec {
+
+  x = 123;
+
+  a = "foo";
+
+  b = "bar";
+
+  c = "foo" + "bar";
+
+  f = {z, x, y}: if y then x else z;
+
+  id = x: x;
+
+  at = args@{x, y, z}: x;
+
+  ellipsis = {x, y, z, ...}: x;
+
+}
diff --git a/tests/lang/lib.nix b/tests/lang/lib.nix
new file mode 100644
index 000000000000..cdba2b947793
--- /dev/null
+++ b/tests/lang/lib.nix
@@ -0,0 +1,56 @@
+with builtins;
+
+rec {
+
+  fold = op: nul: list:
+    if list == []
+    then nul
+    else op (head list) (fold op nul (tail list));
+
+  concat =
+    fold (x: y: x + y) "";
+
+  and = fold (x: y: x && y) true;
+
+  flatten = x:
+    if isList x
+    then fold (x: y: (flatten x) ++ y) [] x
+    else [x];
+
+  sum = fold (x: y: add x y) 0;
+
+  hasSuffix = ext: fileName:
+    let lenFileName = stringLength fileName;
+        lenExt = stringLength ext;
+    in !(lessThan lenFileName lenExt) &&
+       substring (sub lenFileName lenExt) lenFileName fileName == ext;
+
+  # Split a list at the given position.
+  splitAt = pos: list:
+    if pos == 0 then {first = []; second = list;} else
+    if list == [] then {first = []; second = [];} else
+    let res = splitAt (sub pos 1) (tail list);
+    in {first = [(head list)] ++ res.first; second = res.second;};
+
+  # Stable merge sort.
+  sortBy = comp: list:
+    if lessThan 1 (length list)
+    then
+      let
+        split = splitAt (div (length list) 2) list;
+        first = sortBy comp split.first;
+        second = sortBy comp split.second;
+      in mergeLists comp first second
+    else list;
+
+  mergeLists = comp: list1: list2:
+    if list1 == [] then list2 else
+    if list2 == [] then list1 else
+    if comp (head list2) (head list1) then [(head list2)] ++ mergeLists comp list1 (tail list2) else
+    [(head list1)] ++ mergeLists comp (tail list1) list2;
+
+  id = x: x;
+
+  const = x: y: x;
+
+}
diff --git a/tests/lang/parse-fail-dup-attrs-1.nix b/tests/lang/parse-fail-dup-attrs-1.nix
new file mode 100644
index 000000000000..2c02317d2a19
--- /dev/null
+++ b/tests/lang/parse-fail-dup-attrs-1.nix
@@ -0,0 +1,4 @@
+{ x = 123;
+  y = 456;
+  x = 789;
+}
diff --git a/tests/lang/parse-fail-dup-attrs-2.nix b/tests/lang/parse-fail-dup-attrs-2.nix
new file mode 100644
index 000000000000..864d9865e07d
--- /dev/null
+++ b/tests/lang/parse-fail-dup-attrs-2.nix
@@ -0,0 +1,13 @@
+let {
+
+  as = {
+    x = 123;
+    y = 456;
+  };
+
+  bs = {
+    x = 789;
+    inherit (as) x;
+  };
+  
+}
diff --git a/tests/lang/parse-fail-dup-attrs-3.nix b/tests/lang/parse-fail-dup-attrs-3.nix
new file mode 100644
index 000000000000..114d19779f86
--- /dev/null
+++ b/tests/lang/parse-fail-dup-attrs-3.nix
@@ -0,0 +1,13 @@
+let {
+
+  as = {
+    x = 123;
+    y = 456;
+  };
+
+  bs = rec {
+    x = 789;
+    inherit (as) x;
+  };
+  
+}
diff --git a/tests/lang/parse-fail-dup-attrs-4.nix b/tests/lang/parse-fail-dup-attrs-4.nix
new file mode 100644
index 000000000000..77417432b347
--- /dev/null
+++ b/tests/lang/parse-fail-dup-attrs-4.nix
@@ -0,0 +1,4 @@
+{
+  services.ssh.port = 22;
+  services.ssh.port = 23;
+}
diff --git a/tests/lang/parse-fail-dup-attrs-6.nix b/tests/lang/parse-fail-dup-attrs-6.nix
new file mode 100644
index 000000000000..ae6d7a769305
--- /dev/null
+++ b/tests/lang/parse-fail-dup-attrs-6.nix
@@ -0,0 +1,4 @@
+{
+  services.ssh.port = 23;
+  services.ssh = { enable = true; };
+}
diff --git a/tests/lang/parse-fail-dup-attrs-7.nix b/tests/lang/parse-fail-dup-attrs-7.nix
new file mode 100644
index 000000000000..bbc3eb08c0f6
--- /dev/null
+++ b/tests/lang/parse-fail-dup-attrs-7.nix
@@ -0,0 +1,9 @@
+rec {
+
+  x = 1;
+
+  as = {
+    inherit x;
+    inherit x;
+  };
+}
\ No newline at end of file
diff --git a/tests/lang/parse-fail-dup-formals.nix b/tests/lang/parse-fail-dup-formals.nix
new file mode 100644
index 000000000000..a0edd91a9666
--- /dev/null
+++ b/tests/lang/parse-fail-dup-formals.nix
@@ -0,0 +1 @@
+{x, y, x}: x
\ No newline at end of file
diff --git a/tests/lang/parse-fail-patterns-1.nix b/tests/lang/parse-fail-patterns-1.nix
new file mode 100644
index 000000000000..7b40616417b8
--- /dev/null
+++ b/tests/lang/parse-fail-patterns-1.nix
@@ -0,0 +1 @@
+args@{args, x, y, z}: x
diff --git a/tests/lang/parse-fail-regression-20060610.nix b/tests/lang/parse-fail-regression-20060610.nix
new file mode 100644
index 000000000000..b1934f7e1e82
--- /dev/null
+++ b/tests/lang/parse-fail-regression-20060610.nix
@@ -0,0 +1,11 @@
+let {
+  x =
+    {gcc}:
+    {
+      inherit gcc;
+    };
+
+  body = ({
+    inherit gcc;
+  }).gcc;
+}
diff --git a/tests/lang/parse-fail-undef-var-2.nix b/tests/lang/parse-fail-undef-var-2.nix
new file mode 100644
index 000000000000..c10a52b1ea42
--- /dev/null
+++ b/tests/lang/parse-fail-undef-var-2.nix
@@ -0,0 +1,7 @@
+let {
+
+  f = {x, y : ["baz" "bar" z "bat"]}: x + y;
+
+  body = f {x = "foo"; y = "bar";};
+
+}
diff --git a/tests/lang/parse-fail-undef-var.nix b/tests/lang/parse-fail-undef-var.nix
new file mode 100644
index 000000000000..7b63008110db
--- /dev/null
+++ b/tests/lang/parse-fail-undef-var.nix
@@ -0,0 +1 @@
+x: y
diff --git a/tests/lang/parse-okay-1.nix b/tests/lang/parse-okay-1.nix
new file mode 100644
index 000000000000..23a58ed109b1
--- /dev/null
+++ b/tests/lang/parse-okay-1.nix
@@ -0,0 +1 @@
+{x, y, z}: x + y + z
diff --git a/tests/lang/parse-okay-crlf.nix b/tests/lang/parse-okay-crlf.nix
new file mode 100644
index 000000000000..21518d4c6d80
--- /dev/null
+++ b/tests/lang/parse-okay-crlf.nix
@@ -0,0 +1,17 @@
+rec {
+
+  /* Dit is

+  een test. */
+
+  x = 
+  # Dit is een test.
y;
+  
+  y = 123;

+
+  # CR or CR/LF (but not explicit \r's) in strings should be
+  # translated to LF.
+  foo = "multi
line

+  string
+  test\r";
+
+  z = 456;
}
diff --git a/tests/lang/parse-okay-dup-attrs-5.nix b/tests/lang/parse-okay-dup-attrs-5.nix
new file mode 100644
index 000000000000..f4b9efd0c596
--- /dev/null
+++ b/tests/lang/parse-okay-dup-attrs-5.nix
@@ -0,0 +1,4 @@
+{
+  services.ssh = { enable = true; };
+  services.ssh.port = 23;
+}
diff --git a/tests/lang/parse-okay-regression-20041027.nix b/tests/lang/parse-okay-regression-20041027.nix
new file mode 100644
index 000000000000..ae2e256eeaaa
--- /dev/null
+++ b/tests/lang/parse-okay-regression-20041027.nix
@@ -0,0 +1,11 @@
+{stdenv, fetchurl /* pkgconfig, libX11 */ }:
+
+stdenv.mkDerivation {
+  name = "libXi-6.0.1";
+  src = fetchurl {
+    url = http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2;
+    md5 = "7e935a42428d63a387b3c048be0f2756";
+  };
+/*  buildInputs = [pkgconfig];
+  propagatedBuildInputs = [libX11]; */
+}
diff --git a/tests/lang/parse-okay-subversion.nix b/tests/lang/parse-okay-subversion.nix
new file mode 100644
index 000000000000..356272815d26
--- /dev/null
+++ b/tests/lang/parse-okay-subversion.nix
@@ -0,0 +1,43 @@
+{ localServer ? false
+, httpServer ? false
+, sslSupport ? false
+, pythonBindings ? false
+, javaSwigBindings ? false
+, javahlBindings ? false
+, stdenv, fetchurl
+, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
+}:
+
+assert expat != null;
+assert localServer -> db4 != null;
+assert httpServer -> httpd != null && httpd.expat == expat;
+assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl);
+assert pythonBindings -> swig != null && swig.pythonSupport;
+assert javaSwigBindings -> swig != null && swig.javaSupport;
+assert javahlBindings -> j2sdk != null;
+
+stdenv.mkDerivation {
+  name = "subversion-1.1.1";
+
+  builder = /foo/bar;
+  src = fetchurl {
+    url = http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2;
+    md5 = "a180c3fe91680389c210c99def54d9e0";
+  };
+
+  # This is a hopefully temporary fix for the problem that
+  # libsvnjavahl.so isn't linked against libstdc++, which causes
+  # loading the library into the JVM to fail.
+  patches = if javahlBindings then [/javahl.patch] else [];
+
+  openssl = if sslSupport then openssl else null;
+  httpd = if httpServer then httpd else null;
+  db4 = if localServer then db4 else null;
+  swig = if pythonBindings || javaSwigBindings then swig else null;
+  python = if pythonBindings then swig.python else null;
+  j2sdk = if javaSwigBindings then swig.j2sdk else
+          if javahlBindings then j2sdk else null;
+
+  inherit expat localServer httpServer sslSupport
+          pythonBindings javaSwigBindings javahlBindings;
+}
diff --git a/tests/lang/parse-okay-url.nix b/tests/lang/parse-okay-url.nix
new file mode 100644
index 000000000000..fce3b13ee64b
--- /dev/null
+++ b/tests/lang/parse-okay-url.nix
@@ -0,0 +1,7 @@
+[ x:x
+  https://svn.cs.uu.nl:12443/repos/trace/trunk
+  http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2
+  http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz
+  http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz
+  ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz
+]
diff --git a/tests/local.mk b/tests/local.mk
new file mode 100644
index 000000000000..87f4e25e6024
--- /dev/null
+++ b/tests/local.mk
@@ -0,0 +1,21 @@
+check:
+	@echo "Warning: Nix has no 'make check'. Please install Nix and run 'make installcheck' instead."
+
+nix_tests = \
+  init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
+  parallel.sh build-hook.sh substitutes.sh substitutes2.sh \
+  fallback.sh nix-push.sh gc.sh gc-concurrent.sh verify.sh nix-pull.sh \
+  referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
+  gc-runtime.sh install-package.sh check-refs.sh filter-source.sh \
+  remote-store.sh export.sh export-graph.sh negative-caching.sh \
+  binary-patching.sh timeout.sh secure-drv-outputs.sh nix-channel.sh \
+  multiple-outputs.sh import-derivation.sh fetchurl.sh optimise-store.sh \
+  binary-cache.sh nix-profile.sh
+
+install-tests += $(foreach x, $(nix_tests), tests/$(x))
+
+tests-environment = NIX_REMOTE= $(bash) -e
+
+clean-files += $(d)/common.sh
+
+installcheck: $(d)/common.sh
diff --git a/tests/logging.sh b/tests/logging.sh
new file mode 100644
index 000000000000..117d483716a6
--- /dev/null
+++ b/tests/logging.sh
@@ -0,0 +1,24 @@
+source common.sh
+
+clearStore
+
+# Produce an escaped log file.
+set -x
+nix-build --log-type escapes -vv dependencies.nix --no-out-link 2> $TEST_ROOT/log.esc
+
+# Convert it to an XML representation.
+nix-log2xml < $TEST_ROOT/log.esc > $TEST_ROOT/log.xml
+
+# Is this well-formed XML?
+if test "$xmllint" != "false"; then
+    $xmllint $xmlflags --noout $TEST_ROOT/log.xml || fail "malformed XML"
+fi
+
+# Convert to HTML.
+if test "$xsltproc" != "false"; then
+    (cd $datadir/nix/log2html && $xsltproc mark-errors.xsl - | $xsltproc log2html.xsl -) < $TEST_ROOT/log.xml > $TEST_ROOT/log.html
+    # Ideally we would check that the generated HTML is valid...
+
+    # A few checks...
+    grep "<code>.*FOO" $TEST_ROOT/log.html || fail "bad HTML output"
+fi
diff --git a/tests/misc.sh b/tests/misc.sh
new file mode 100644
index 000000000000..42aa82c8d457
--- /dev/null
+++ b/tests/misc.sh
@@ -0,0 +1,16 @@
+source common.sh
+
+# Tests miscellaneous commands.
+
+# Do all commands have help?
+#nix-env --help | grep -q install
+#nix-store --help | grep -q realise
+#nix-instantiate --help | grep -q eval-only
+#nix-hash --help | grep -q base32
+
+# Can we ask for the version number?
+nix-env --version | grep "$version"
+
+# Usage errors.
+nix-env --foo 2>&1 | grep "no operation"
+nix-env -q --foo 2>&1 | grep "unknown flag"
diff --git a/tests/multiple-outputs.nix b/tests/multiple-outputs.nix
new file mode 100644
index 000000000000..4a9010d1868e
--- /dev/null
+++ b/tests/multiple-outputs.nix
@@ -0,0 +1,68 @@
+with import ./config.nix;
+
+rec {
+
+  a = mkDerivation {
+    name = "multiple-outputs-a";
+    outputs = [ "first" "second" ];
+    builder = builtins.toFile "builder.sh"
+      ''
+        mkdir $first $second
+        test -z $all
+        echo "first" > $first/file
+        echo "second" > $second/file
+        ln -s $first $second/link
+      '';
+    helloString = "Hello, world!";
+  };
+
+  b = mkDerivation {
+    defaultOutput = assert a.second.helloString == "Hello, world!"; a;
+    firstOutput = assert a.outputName == "first"; a.first.first;
+    secondOutput = assert a.second.outputName == "second"; a.second.first.first.second.second.first.second;
+    allOutputs = a.all;
+    name = "multiple-outputs-b";
+    builder = builtins.toFile "builder.sh"
+      ''
+        mkdir $out
+        test "$firstOutput $secondOutput" = "$allOutputs"
+        test "$defaultOutput" = "$firstOutput"
+        test "$(cat $firstOutput/file)" = "first"
+        test "$(cat $secondOutput/file)" = "second"
+        echo "success" > $out/file
+      '';
+  };
+
+  c = mkDerivation {
+    name = "multiple-outputs-c";
+    drv = b.drvPath;
+    builder = builtins.toFile "builder.sh"
+      ''
+        mkdir $out
+        ln -s $drv $out/drv
+      '';
+  };
+
+  d = mkDerivation {
+    name = "multiple-outputs-d";
+    drv = builtins.unsafeDiscardOutputDependency b.drvPath;
+    builder = builtins.toFile "builder.sh"
+      ''
+        mkdir $out
+        echo $drv > $out/drv
+      '';
+  };
+
+  cyclic = (mkDerivation {
+    name = "cyclic-outputs";
+    outputs = [ "a" "b" "c" ];
+    builder = builtins.toFile "builder.sh"
+      ''
+        mkdir $a $b $c
+        echo $a > $b/foo
+        echo $b > $c/bar
+        echo $c > $a/baz
+      '';
+  }).a;
+
+}
diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh
new file mode 100644
index 000000000000..ac622a7b4eaf
--- /dev/null
+++ b/tests/multiple-outputs.sh
@@ -0,0 +1,63 @@
+source common.sh
+
+clearStore
+
+# Test whether read-only evaluation works when referring to the
+# ‘drvPath’ attribute.
+echo "evaluating c..."
+#drvPath=$(nix-instantiate multiple-outputs.nix -A c --readonly-mode)
+
+# And check whether the resulting derivation explicitly depends on all
+# outputs.
+drvPath=$(nix-instantiate multiple-outputs.nix -A c)
+#[ "$drvPath" = "$drvPath2" ]
+grep -q 'multiple-outputs-a.drv",\["first","second"\]' $drvPath
+grep -q 'multiple-outputs-b.drv",\["out"\]' $drvPath
+
+# While we're at it, test the ‘unsafeDiscardOutputDependency’ primop.
+outPath=$(nix-build multiple-outputs.nix -A d --no-out-link)
+drvPath=$(cat $outPath/drv)
+outPath=$(nix-store -q $drvPath)
+! [ -e "$outPath" ]
+
+# Do a build of something that depends on a derivation with multiple
+# outputs.
+echo "building b..."
+outPath=$(nix-build multiple-outputs.nix -A b --no-out-link)
+echo "output path is $outPath"
+[ "$(cat "$outPath"/file)" = "success" ]
+
+# Test nix-build on a derivation with multiple outputs.
+nix-build multiple-outputs.nix -A a -o $TEST_ROOT/result
+[ -e $TEST_ROOT/result-first ]
+! [ -e $TEST_ROOT/result-second ]
+nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result
+[ "$(cat $TEST_ROOT/result-first/file)" = "first" ]
+[ "$(cat $TEST_ROOT/result-second/file)" = "second" ]
+[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ]
+hash1=$(nix-store -q --hash $TEST_ROOT/result-second)
+
+# Delete one of the outputs and rebuild it.  This will cause a hash
+# rewrite.
+nix-store --delete $TEST_ROOT/result-second --ignore-liveness
+nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result
+[ "$(cat $TEST_ROOT/result-second/file)" = "second" ]
+[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ]
+hash2=$(nix-store -q --hash $TEST_ROOT/result-second)
+[ "$hash1" = "$hash2" ]
+
+# Make sure that nix-build works on derivations with multiple outputs.
+echo "building a.first..."
+nix-build multiple-outputs.nix -A a.first --no-out-link
+
+# Cyclic outputs should be rejected.
+echo "building cyclic..."
+if nix-build multiple-outputs.nix -A cyclic --no-out-link; then
+    echo "Cyclic outputs incorrectly accepted!"
+    exit 1
+fi
+
+echo "collecting garbage..."
+rm $TEST_ROOT/result*
+nix-store --gc --option gc-keep-derivations true --option gc-keep-outputs true
+nix-store --gc --print-roots
diff --git a/tests/negative-caching.nix b/tests/negative-caching.nix
new file mode 100644
index 000000000000..10df67a748fc
--- /dev/null
+++ b/tests/negative-caching.nix
@@ -0,0 +1,21 @@
+with import ./config.nix;
+
+rec {
+
+  fail = mkDerivation {
+    name = "fail";
+    builder = builtins.toFile "builder.sh" "echo FAIL; exit 1";
+  };
+
+  succeed = mkDerivation {
+    name = "succeed";
+    builder = builtins.toFile "builder.sh" "echo SUCCEED; mkdir $out";
+  };
+
+  depOnFail = mkDerivation {
+    name = "dep-on-fail";
+    builder = builtins.toFile "builder.sh" "echo URGH; mkdir $out";
+    inputs = [fail succeed];
+  };
+
+}
diff --git a/tests/negative-caching.sh b/tests/negative-caching.sh
new file mode 100644
index 000000000000..4217bc38e121
--- /dev/null
+++ b/tests/negative-caching.sh
@@ -0,0 +1,22 @@
+source common.sh
+
+clearStore
+
+set +e
+
+opts="--option build-cache-failure true --print-build-trace"
+
+# This build should fail, and the failure should be cached.
+log=$(nix-build $opts negative-caching.nix -A fail --no-out-link 2>&1) && fail "should fail"
+echo "$log" | grep -q "@ build-failed" || fail "no build-failed trace"
+
+# Do it again.  The build shouldn't be tried again.
+log=$(nix-build $opts negative-caching.nix -A fail --no-out-link 2>&1) && fail "should fail"
+echo "$log" | grep -q "FAIL" && fail "failed build not cached"
+echo "$log" | grep -q "@ build-failed .* cached" || fail "trace doesn't say cached"
+
+# Check that --keep-going works properly with cached failures.
+log=$(nix-build $opts --keep-going negative-caching.nix -A depOnFail --no-out-link 2>&1) && fail "should fail"
+echo "$log" | grep -q "FAIL" && fail "failed build not cached (2)"
+echo "$log" | grep -q "@ build-failed .* cached" || fail "trace doesn't say cached (2)"
+echo "$log" | grep -q "@ build-succeeded .*-succeed" || fail "didn't keep going"
diff --git a/tests/nix-build.sh b/tests/nix-build.sh
new file mode 100644
index 000000000000..dc0e99c73621
--- /dev/null
+++ b/tests/nix-build.sh
@@ -0,0 +1,19 @@
+source common.sh
+
+clearStore
+
+nix-build dependencies.nix -o $TEST_ROOT/result
+test "$(cat $TEST_ROOT/result/foobar)" = FOOBAR
+
+# The result should be retained by a GC.
+echo A
+target=$(readLink $TEST_ROOT/result)
+echo B
+echo target is $target
+nix-store --gc
+test -e $target/foobar
+
+# But now it should be gone.
+rm $TEST_ROOT/result
+nix-store --gc
+if test -e $target/foobar; then false; fi
diff --git a/tests/nix-channel.sh b/tests/nix-channel.sh
new file mode 100644
index 000000000000..a25d56bec11e
--- /dev/null
+++ b/tests/nix-channel.sh
@@ -0,0 +1,43 @@
+source common.sh
+
+clearProfiles
+clearManifests
+
+rm -f $TEST_ROOT/.nix-channels
+
+# Override location of ~/.nix-channels.
+export HOME=$TEST_ROOT
+
+# Test add/list/remove.
+nix-channel --add http://foo/bar xyzzy
+nix-channel --list | grep -q http://foo/bar
+nix-channel --remove xyzzy
+
+[ -e $TEST_ROOT/.nix-channels ]
+[ "$(cat $TEST_ROOT/.nix-channels)" = '' ]
+
+# Create a channel.
+rm -rf $TEST_ROOT/foo
+mkdir -p $TEST_ROOT/foo
+nix-push --dest $TEST_ROOT/foo --manifest --bzip2 $(nix-store -r $(nix-instantiate dependencies.nix))
+rm -rf $TEST_ROOT/nixexprs
+mkdir -p $TEST_ROOT/nixexprs
+cp config.nix dependencies.nix dependencies.builder*.sh $TEST_ROOT/nixexprs/
+ln -s dependencies.nix $TEST_ROOT/nixexprs/default.nix
+(cd $TEST_ROOT && tar cvf - nixexprs) | bzip2 > $TEST_ROOT/foo/nixexprs.tar.bz2
+
+# Test the update action.
+nix-channel --add file://$TEST_ROOT/foo
+nix-channel --update
+
+# Do a query.
+nix-env -qa \* --meta --xml --out-path > $TEST_ROOT/meta.xml
+if [ "$xmllint" != false ]; then
+    $xmllint --noout $TEST_ROOT/meta.xml || fail "malformed XML"
+fi
+grep -q 'meta.*description.*Random test package' $TEST_ROOT/meta.xml
+grep -q 'item.*attrPath="foo".*name="dependencies"' $TEST_ROOT/meta.xml
+
+# Do an install.
+nix-env -i dependencies
+[ -e $TEST_ROOT/var/nix/profiles/default/foobar ]
diff --git a/tests/nix-copy-closure.nix b/tests/nix-copy-closure.nix
new file mode 100644
index 000000000000..66c19a45d42d
--- /dev/null
+++ b/tests/nix-copy-closure.nix
@@ -0,0 +1,54 @@
+# Test ‘nix-copy-closure’.
+
+{ system, nix }:
+
+with import <nixpkgs/nixos/lib/testing.nix> { inherit system; };
+
+makeTest ({ pkgs, ... }: let pkgA = pkgs.aterm; pkgB = pkgs.wget; in {
+
+  nodes =
+    { client =
+        { config, pkgs, ... }:
+        { virtualisation.writableStore = true;
+          virtualisation.pathsInNixDB = [ pkgA ];
+          environment.nix = nix;
+        };
+
+      server =
+        { config, pkgs, ... }:
+        { services.openssh.enable = true;
+          virtualisation.writableStore = true;
+          virtualisation.pathsInNixDB = [ pkgB ];
+          environment.nix = nix;
+        };
+    };
+
+  testScript = { nodes }:
+    ''
+      startAll;
+
+      # Create an SSH key on the client.
+      my $key = `${pkgs.openssh}/bin/ssh-keygen -t dsa -f key -N ""`;
+      $client->succeed("mkdir -m 700 /root/.ssh");
+      $client->copyFileFromHost("key", "/root/.ssh/id_dsa");
+      $client->succeed("chmod 600 /root/.ssh/id_dsa");
+
+      # Install the SSH key on the server.
+      $server->succeed("mkdir -m 700 /root/.ssh");
+      $server->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
+      $server->waitForUnit("sshd");
+      $client->waitForUnit("network.target");
+      $client->succeed("ssh -o StrictHostKeyChecking=no " . $server->name() . " 'echo hello world'");
+
+      # Copy the closure of package A from the client to the server.
+      $server->fail("nix-store --check-validity ${pkgA}");
+      $client->succeed("nix-copy-closure --to server --gzip ${pkgA} >&2");
+      $server->succeed("nix-store --check-validity ${pkgA}");
+
+      # Copy the closure of package B from the server to the client.
+      $client->fail("nix-store --check-validity ${pkgB}");
+      $client->succeed("nix-copy-closure --from server --gzip ${pkgB} >&2");
+      $client->succeed("nix-store --check-validity ${pkgB}");
+    '';
+
+})
diff --git a/tests/nix-profile.sh b/tests/nix-profile.sh
new file mode 100644
index 000000000000..3586a7efc3c8
--- /dev/null
+++ b/tests/nix-profile.sh
@@ -0,0 +1,10 @@
+source common.sh
+
+home=$TEST_ROOT/home
+rm -rf $home
+mkdir -p $home
+HOME=$home $SHELL -e -c ". ../scripts/nix-profile.sh"
+HOME=$home $SHELL -e -c ". ../scripts/nix-profile.sh" # test idempotency
+
+[ -L $home/.nix-profile ]
+[ -e $home/.nix-channels ]
diff --git a/tests/nix-pull.sh b/tests/nix-pull.sh
new file mode 100644
index 000000000000..87239948c481
--- /dev/null
+++ b/tests/nix-pull.sh
@@ -0,0 +1,33 @@
+source common.sh
+
+pullCache () {
+    echo "pulling cache..."
+    nix-pull file://$TEST_ROOT/cache/MANIFEST
+}
+
+clearStore
+clearManifests
+pullCache
+
+drvPath=$(nix-instantiate dependencies.nix)
+outPath=$(nix-store -q $drvPath)
+
+echo "building $outPath using substitutes..."
+nix-store -r $outPath
+
+cat $outPath/input-2/bar
+
+clearStore
+clearManifests
+pullCache
+
+echo "building $drvPath using substitutes..."
+nix-store -r $drvPath
+
+cat $outPath/input-2/bar
+
+# Check that the derivers are set properly.
+test $(nix-store -q --deriver "$outPath") = "$drvPath"
+nix-store -q --deriver $(readLink $outPath/input-2) | grep -q -- "-input-2.drv"
+
+clearManifests
diff --git a/tests/nix-push.sh b/tests/nix-push.sh
new file mode 100644
index 000000000000..8ea59516c62c
--- /dev/null
+++ b/tests/nix-push.sh
@@ -0,0 +1,12 @@
+source common.sh
+
+clearStore
+
+drvPath=$(nix-instantiate dependencies.nix)
+outPath=$(nix-store -r $drvPath)
+
+echo "pushing $drvPath"
+
+mkdir -p $TEST_ROOT/cache
+
+nix-push --dest $TEST_ROOT/cache --manifest $drvPath --bzip2
diff --git a/tests/optimise-store.sh b/tests/optimise-store.sh
new file mode 100644
index 000000000000..6eb54ca50875
--- /dev/null
+++ b/tests/optimise-store.sh
@@ -0,0 +1,26 @@
+source common.sh
+
+clearStore
+
+outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --option auto-optimise-store true)
+outPath2=$(echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --option auto-optimise-store true)
+
+inode1="$(perl -e "print ((lstat('$outPath1/foo'))[1])")"
+inode2="$(perl -e "print ((lstat('$outPath2/foo'))[1])")"
+if [ "$inode1" != "$inode2" ]; then
+    echo "inodes do not match"
+    exit 1
+fi
+
+nlink="$(perl -e "print ((lstat('$outPath1/foo'))[3])")"
+if [ "$nlink" != 3 ]; then
+    echo "link count incorrect"
+    exit 1
+fi
+
+nix-store --gc
+
+if [ -n "$(ls $NIX_STORE_DIR/.links)" ]; then
+    echo ".links directory not empty after GC"
+    exit 1
+fi
diff --git a/tests/parallel.builder.sh b/tests/parallel.builder.sh
new file mode 100644
index 000000000000..280eb1de8c41
--- /dev/null
+++ b/tests/parallel.builder.sh
@@ -0,0 +1,29 @@
+echo "DOING $text"
+
+
+# increase counter
+while ! ln -s x $shared.lock 2> /dev/null; do
+    sleep 1
+done
+test -f $shared.cur || echo 0 > $shared.cur
+test -f $shared.max || echo 0 > $shared.max
+new=$(expr $(cat $shared.cur) + 1)
+if test $new -gt $(cat $shared.max); then
+    echo $new > $shared.max
+fi
+echo $new > $shared.cur
+rm $shared.lock
+
+
+echo -n $(cat $inputs)$text > $out
+
+sleep $sleepTime
+
+
+# decrease counter
+while ! ln -s x $shared.lock 2> /dev/null; do
+    sleep 1
+done
+test -f $shared.cur || echo 0 > $shared.cur
+echo $(expr $(cat $shared.cur) - 1) > $shared.cur
+rm $shared.lock
diff --git a/tests/parallel.nix b/tests/parallel.nix
new file mode 100644
index 000000000000..23f142059f58
--- /dev/null
+++ b/tests/parallel.nix
@@ -0,0 +1,19 @@
+{sleepTime ? 3}:
+
+with import ./config.nix;
+
+let
+
+  mkDrv = text: inputs: mkDerivation {
+    name = "parallel";
+    builder = ./parallel.builder.sh;
+    inherit text inputs shared sleepTime;
+  };
+
+  a = mkDrv "a" [];
+  b = mkDrv "b" [a];
+  c = mkDrv "c" [a];
+  d = mkDrv "d" [a];
+  e = mkDrv "e" [b c d];
+
+in e
diff --git a/tests/parallel.sh b/tests/parallel.sh
new file mode 100644
index 000000000000..3b7bbe5a2251
--- /dev/null
+++ b/tests/parallel.sh
@@ -0,0 +1,56 @@
+source common.sh
+
+
+# First, test that -jN performs builds in parallel.
+echo "testing nix-build -j..."
+
+clearStore
+
+rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max
+
+outPath=$(nix-build -j10000 parallel.nix --no-out-link)
+
+echo "output path is $outPath"
+
+text=$(cat "$outPath")
+if test "$text" != "abacade"; then exit 1; fi
+
+if test "$(cat $_NIX_TEST_SHARED.cur)" != 0; then fail "wrong current process count"; fi
+if test "$(cat $_NIX_TEST_SHARED.max)" != 3; then fail "not enough parallelism"; fi
+
+
+# Second, test that parallel invocations of nix-build perform builds
+# in parallel, and don't block waiting on locks held by the others.
+echo "testing multiple nix-build -j1..."
+
+clearStore
+
+rm -f $_NIX_TEST_SHARED.cur $_NIX_TEST_SHARED.max
+
+drvPath=$(nix-instantiate parallel.nix --argstr sleepTime 15)
+
+cmd="nix-store -j1 -r $drvPath"
+
+$cmd &
+pid1=$!
+echo "pid 1 is $pid1"
+
+$cmd &
+pid2=$!
+echo "pid 2 is $pid2"
+
+$cmd &
+pid3=$!
+echo "pid 3 is $pid3"
+
+$cmd &
+pid4=$!
+echo "pid 4 is $pid4"
+
+wait $pid1 || fail "instance 1 failed: $?"
+wait $pid2 || fail "instance 2 failed: $?"
+wait $pid3 || fail "instance 3 failed: $?"
+wait $pid4 || fail "instance 4 failed: $?"
+
+if test "$(cat $_NIX_TEST_SHARED.cur)" != 0; then fail "wrong current process count"; fi
+if test "$(cat $_NIX_TEST_SHARED.max)" != 3; then fail "not enough parallelism"; fi
diff --git a/tests/referrers.sh b/tests/referrers.sh
new file mode 100644
index 000000000000..e57b2d8e31ff
--- /dev/null
+++ b/tests/referrers.sh
@@ -0,0 +1,36 @@
+source common.sh
+
+clearStore
+
+max=500
+
+reference=$NIX_STORE_DIR/abcdef
+touch $reference
+(echo $reference && echo && echo 0) | nix-store --register-validity 
+
+echo "making registration..."
+
+set +x
+for ((n = 0; n < $max; n++)); do
+    storePath=$NIX_STORE_DIR/$n
+    echo -n > $storePath
+    ref2=$NIX_STORE_DIR/$((n+1))
+    if test $((n+1)) = $max; then
+        ref2=$reference
+    fi
+    echo $storePath; echo; echo 2; echo $reference; echo $ref2
+done > $TEST_ROOT/reg_info
+set -x
+
+echo "registering..."
+
+nix-store --register-validity < $TEST_ROOT/reg_info
+
+echo "collecting garbage..."
+ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref
+nix-store --gc
+
+if test "$(sqlite3 ./test-tmp/db/db.sqlite 'select count(*) from Refs')" -ne 0; then
+    echo "referrers not cleaned up"
+    exit 1
+fi
diff --git a/tests/remote-builds.nix b/tests/remote-builds.nix
new file mode 100644
index 000000000000..56514e17499c
--- /dev/null
+++ b/tests/remote-builds.nix
@@ -0,0 +1,98 @@
+# Test Nix's remote build feature.
+
+{ system, nix }:
+
+with import <nixpkgs/nixos/lib/testing.nix> { inherit system; };
+
+makeTest ({ pkgs, ... }:
+
+let
+
+  # The configuration of the build slaves.
+  slave =
+    { config, pkgs, ... }:
+    { services.openssh.enable = true;
+      virtualisation.writableStore = true;
+      environment.nix = nix;
+    };
+
+  # Trivial Nix expression to build remotely.
+  expr = config: nr: pkgs.writeText "expr.nix"
+    ''
+      let utils = builtins.storePath ${config.system.build.extraUtils}; in
+      derivation {
+        name = "hello-${toString nr}";
+        system = "i686-linux";
+        PATH = "''${utils}/bin";
+        builder = "''${utils}/bin/sh";
+        args = [ "-c" "echo Hello; mkdir $out; cat /proc/sys/kernel/hostname > $out/host; sleep 3" ];
+      }
+    '';
+
+in
+
+{
+
+  nodes =
+    { slave1 = slave;
+      slave2 = slave;
+
+      client =
+        { config, pkgs, ... }:
+        { nix.maxJobs = 0; # force remote building
+          nix.distributedBuilds = true;
+          nix.buildMachines =
+            [ { hostName = "slave1";
+                sshUser = "root";
+                sshKey = "/root/.ssh/id_dsa";
+                system = "i686-linux";
+                maxJobs = 1;
+              }
+              { hostName = "slave2";
+                sshUser = "root";
+                sshKey = "/root/.ssh/id_dsa";
+                system = "i686-linux";
+                maxJobs = 1;
+              }
+            ];
+          virtualisation.writableStore = true;
+          virtualisation.pathsInNixDB = [ config.system.build.extraUtils ];
+          environment.nix = nix;
+        };
+    };
+
+  testScript = { nodes }:
+    ''
+      startAll;
+
+      # Create an SSH key on the client.
+      my $key = `${pkgs.openssh}/bin/ssh-keygen -t dsa -f key -N ""`;
+      $client->succeed("mkdir -m 700 /root/.ssh");
+      $client->copyFileFromHost("key", "/root/.ssh/id_dsa");
+      $client->succeed("chmod 600 /root/.ssh/id_dsa");
+
+      # Install the SSH key on the slaves.
+      $client->waitForUnit("network.target");
+      foreach my $slave ($slave1, $slave2) {
+          $slave->succeed("mkdir -m 700 /root/.ssh");
+          $slave->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
+          $slave->waitForUnit("sshd");
+          $client->succeed("ssh -o StrictHostKeyChecking=no " . $slave->name() . " 'echo hello world'");
+      }
+
+      # Perform a build and check that it was performed on the slave.
+      my $out = $client->succeed("nix-build ${expr nodes.client.config 1}");
+      $slave1->succeed("test -e $out");
+
+      # And a parallel build.
+      my ($out1, $out2) = split /\s/,
+          $client->succeed("nix-store -r \$(nix-instantiate ${expr nodes.client.config 2} ${expr nodes.client.config 3})");
+      $slave1->succeed("test -e $out1 -o -e $out2");
+      $slave2->succeed("test -e $out1 -o -e $out2");
+
+      # Test whether the build hook automatically skips unavailable slaves.
+      $slave1->block;
+      $client->succeed("nix-build ${expr nodes.client.config 4}");
+    '';
+
+})
diff --git a/tests/remote-store.sh b/tests/remote-store.sh
new file mode 100644
index 000000000000..6a9585cacfa8
--- /dev/null
+++ b/tests/remote-store.sh
@@ -0,0 +1,7 @@
+source common.sh
+
+clearStore
+clearManifests
+startDaemon
+$SHELL ./user-envs.sh
+killDaemon
diff --git a/tests/secure-drv-outputs.nix b/tests/secure-drv-outputs.nix
new file mode 100644
index 000000000000..b4ac8ff531f8
--- /dev/null
+++ b/tests/secure-drv-outputs.nix
@@ -0,0 +1,23 @@
+with import ./config.nix;
+
+{
+
+  good = mkDerivation {
+    name = "good";
+    builder = builtins.toFile "builder"
+      ''
+        mkdir $out
+        echo > $out/good
+      '';
+  };
+
+  bad = mkDerivation {
+    name = "good";
+    builder = builtins.toFile "builder"
+      ''
+        mkdir $out
+        echo > $out/bad
+      '';
+  };
+
+}
diff --git a/tests/secure-drv-outputs.sh b/tests/secure-drv-outputs.sh
new file mode 100644
index 000000000000..4888123da910
--- /dev/null
+++ b/tests/secure-drv-outputs.sh
@@ -0,0 +1,37 @@
+# Test that users cannot register specially-crafted derivations that
+# produce output paths belonging to other derivations.  This could be
+# used to inject malware into the store.
+
+source common.sh
+
+clearStore
+clearManifests
+
+startDaemon
+
+# Determine the output path of the "good" derivation.
+goodOut=$(nix-store -q $(nix-instantiate ./secure-drv-outputs.nix -A good))
+
+# Instantiate the "bad" derivation.
+badDrv=$(nix-instantiate ./secure-drv-outputs.nix -A bad)
+badOut=$(nix-store -q $badDrv)
+
+# Rewrite the bad derivation to produce the output path of the good
+# derivation.
+rm -f $TEST_ROOT/bad.drv
+sed -e "s|$badOut|$goodOut|g" < $badDrv > $TEST_ROOT/bad.drv
+
+# Add the manipulated derivation to the store and build it.  This
+# should fail.
+if badDrv2=$(nix-store --add $TEST_ROOT/bad.drv); then
+    nix-store -r "$badDrv2"
+fi
+
+# Now build the good derivation.
+goodOut2=$(nix-build ./secure-drv-outputs.nix -A good --no-out-link)
+test "$goodOut" = "$goodOut2"
+
+if ! test -e "$goodOut"/good; then
+    echo "Bad derivation stole the output path of the good derivation!"
+    exit 1
+fi
diff --git a/tests/simple.builder.sh b/tests/simple.builder.sh
new file mode 100644
index 000000000000..569e8ca88c1e
--- /dev/null
+++ b/tests/simple.builder.sh
@@ -0,0 +1,11 @@
+echo "PATH=$PATH"
+
+# Verify that the PATH is empty.
+if mkdir foo 2> /dev/null; then exit 1; fi
+
+# Set a PATH (!!! impure).
+export PATH=$goodPath
+
+mkdir $out
+
+echo "Hello World!" > $out/hello
\ No newline at end of file
diff --git a/tests/simple.nix b/tests/simple.nix
new file mode 100644
index 000000000000..4223c0f23a5b
--- /dev/null
+++ b/tests/simple.nix
@@ -0,0 +1,8 @@
+with import ./config.nix;
+
+mkDerivation {
+  name = "simple";
+  builder = ./simple.builder.sh;
+  PATH = "";
+  goodPath = path;
+}
diff --git a/tests/simple.sh b/tests/simple.sh
new file mode 100644
index 000000000000..af8bccc2b457
--- /dev/null
+++ b/tests/simple.sh
@@ -0,0 +1,25 @@
+source common.sh
+
+drvPath=$(nix-instantiate simple.nix)
+
+test "$(nix-store -q --binding system "$drvPath")" = "$system"
+
+echo "derivation is $drvPath"
+
+outPath=$(nix-store -rvv "$drvPath")
+
+echo "output path is $outPath"
+
+text=$(cat "$outPath"/hello)
+if test "$text" != "Hello World!"; then exit 1; fi
+
+# Directed delete: $outPath is not reachable from a root, so it should
+# be deleteable.
+nix-store --delete $outPath
+if test -e $outPath/hello; then false; fi
+
+outPath="$(NIX_STORE_DIR=/foo nix-instantiate --readonly-mode hash-check.nix)"
+if test "$outPath" != "/foo/lfy1s6ca46rm5r6w4gg9hc0axiakjcnm-dependencies.drv"; then
+    echo "hashDerivationModulo appears broken, got $outPath"
+    exit 1
+fi
diff --git a/tests/substituter.sh b/tests/substituter.sh
new file mode 100755
index 000000000000..9aab295de87b
--- /dev/null
+++ b/tests/substituter.sh
@@ -0,0 +1,37 @@
+#! /bin/sh -e
+echo
+echo substituter args: $* >&2
+
+if test $1 = "--query"; then
+    while read cmd args; do
+        echo "CMD = $cmd, ARGS = $args" >&2
+        if test "$cmd" = "have"; then
+            for path in $args; do 
+                read path
+                if grep -q "$path" $TEST_ROOT/sub-paths; then
+                    echo $path
+                fi
+            done
+            echo
+        elif test "$cmd" = "info"; then
+            for path in $args; do
+                echo $path
+                echo "" # deriver
+                echo 0 # nr of refs
+                echo $((1 * 1024 * 1024)) # download size
+                echo $((2 * 1024 * 1024)) # nar size
+            done
+            echo
+        else
+            echo "bad command $cmd"
+            exit 1
+        fi
+    done
+elif test $1 = "--substitute"; then
+    mkdir $2
+    echo "Hallo Wereld" > $2/hello
+    echo # no expected hash
+else
+    echo "unknown substituter operation"
+    exit 1
+fi
diff --git a/tests/substituter2.sh b/tests/substituter2.sh
new file mode 100755
index 000000000000..5d1763599c25
--- /dev/null
+++ b/tests/substituter2.sh
@@ -0,0 +1,33 @@
+#! /bin/sh -e
+echo
+echo substituter2 args: $* >&2
+
+if test $1 = "--query"; then
+    while read cmd args; do
+        if test "$cmd" = have; then
+            for path in $args; do
+                if grep -q "$path" $TEST_ROOT/sub-paths; then
+                    echo $path
+                fi
+            done
+            echo
+        elif test "$cmd" = info; then
+            for path in $args; do
+                echo $path
+                echo "" # deriver
+                echo 0 # nr of refs
+                echo 0 # download size
+                echo 0 # nar size
+            done
+            echo
+        else
+            echo "bad command $cmd"
+            exit 1
+        fi
+    done
+elif test $1 = "--substitute"; then
+    exit 1
+else
+    echo "unknown substituter operation"
+    exit 1
+fi
diff --git a/tests/substitutes.sh b/tests/substitutes.sh
new file mode 100644
index 000000000000..0c6adf2601fa
--- /dev/null
+++ b/tests/substitutes.sh
@@ -0,0 +1,22 @@
+source common.sh
+
+clearStore
+
+# Instantiate.
+drvPath=$(nix-instantiate simple.nix)
+echo "derivation is $drvPath"
+
+# Find the output path.
+outPath=$(nix-store -qvv "$drvPath")
+echo "output path is $outPath"
+
+echo $outPath > $TEST_ROOT/sub-paths
+
+export NIX_SUBSTITUTERS=$(pwd)/substituter.sh
+
+nix-store -r "$drvPath" --dry-run 2>&1 | grep -q "1.00 MiB.*2.00 MiB"
+
+nix-store -rvv "$drvPath"
+
+text=$(cat "$outPath"/hello)
+if test "$text" != "Hallo Wereld"; then echo "wrong substitute output: $text"; exit 1; fi
diff --git a/tests/substitutes2.sh b/tests/substitutes2.sh
new file mode 100644
index 000000000000..bd914575cca8
--- /dev/null
+++ b/tests/substitutes2.sh
@@ -0,0 +1,21 @@
+source common.sh
+
+clearStore
+
+# Instantiate.
+drvPath=$(nix-instantiate simple.nix)
+echo "derivation is $drvPath"
+
+# Find the output path.
+outPath=$(nix-store -qvvvvv "$drvPath")
+echo "output path is $outPath"
+
+echo $outPath > $TEST_ROOT/sub-paths
+
+# First try a substituter that fails, then one that succeeds
+export NIX_SUBSTITUTERS=$(pwd)/substituter2.sh:$(pwd)/substituter.sh
+
+nix-store -j0 -rvv "$drvPath"
+
+text=$(cat "$outPath"/hello)
+if test "$text" != "Hallo Wereld"; then echo "wrong substitute output: $text"; exit 1; fi
diff --git a/tests/timeout.builder.sh b/tests/timeout.builder.sh
new file mode 100644
index 000000000000..1ddb44e99c7f
--- /dev/null
+++ b/tests/timeout.builder.sh
@@ -0,0 +1,2 @@
+echo "\`timeout' builder entering an infinite loop"
+while true ; do echo -n .; done
diff --git a/tests/timeout.nix b/tests/timeout.nix
new file mode 100644
index 000000000000..b41368bb38e2
--- /dev/null
+++ b/tests/timeout.nix
@@ -0,0 +1,6 @@
+with import ./config.nix;
+
+mkDerivation {
+  name = "timeout";
+  builder = ./timeout.builder.sh;
+}
diff --git a/tests/timeout.sh b/tests/timeout.sh
new file mode 100644
index 000000000000..4a4769a165e8
--- /dev/null
+++ b/tests/timeout.sh
@@ -0,0 +1,21 @@
+# Test the `--timeout' option.
+
+source common.sh
+
+failed=0
+messages="`nix-build timeout.nix --timeout 2 2>&1 || failed=1`"
+if [ $failed -ne 0 ]; then
+    echo "error: \`nix-store' succeeded; should have timed out"
+    exit 1
+fi
+
+if ! echo "$messages" | grep -q "timed out"; then
+    echo "error: build may have failed for reasons other than timeout; output:"
+    echo "$messages" >&2
+    exit 1
+fi
+
+if nix-build timeout.nix --option build-max-log-size 100; then
+    echo "build should have failed"
+    exit 1
+fi
diff --git a/tests/user-envs.builder.sh b/tests/user-envs.builder.sh
new file mode 100644
index 000000000000..5fafa797f11e
--- /dev/null
+++ b/tests/user-envs.builder.sh
@@ -0,0 +1,5 @@
+mkdir $out
+mkdir $out/bin
+echo "#! $shell" > $out/bin/$progName
+echo "echo $name" >> $out/bin/$progName
+chmod +x $out/bin/$progName
diff --git a/tests/user-envs.nix b/tests/user-envs.nix
new file mode 100644
index 000000000000..1aa410cc9680
--- /dev/null
+++ b/tests/user-envs.nix
@@ -0,0 +1,29 @@
+# Some dummy arguments...
+{ foo ? "foo"
+}:
+
+with import ./config.nix;
+
+assert foo == "foo";
+
+let
+
+  makeDrv = name: progName: (mkDerivation {
+    inherit name progName system;
+    builder = ./user-envs.builder.sh;
+  } // {
+    meta = {
+      description = "A silly test package";
+    };
+  });
+
+in
+
+  [
+    (makeDrv "foo-1.0" "foo")
+    (makeDrv "foo-2.0pre1" "foo")
+    (makeDrv "bar-0.1" "bar")
+    (makeDrv "foo-2.0" "foo")
+    (makeDrv "bar-0.1.1" "bar")
+    (makeDrv "foo-0.1" "foo" // { meta.priority = 10; })
+  ]
diff --git a/tests/user-envs.sh b/tests/user-envs.sh
new file mode 100644
index 000000000000..fa24d49df9c0
--- /dev/null
+++ b/tests/user-envs.sh
@@ -0,0 +1,131 @@
+source common.sh
+
+clearProfiles
+
+set -x
+
+# Query installed: should be empty.
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0
+
+# Query available: should contain several.
+test "$(nix-env -p $profiles/test -f ./user-envs.nix -qa '*' | wc -l)" -eq 6
+
+# Query descriptions.
+nix-env -p $profiles/test -f ./user-envs.nix -qa '*' --description | grep silly
+
+# Install "foo-1.0".
+nix-env -p $profiles/test -f ./user-envs.nix -i foo-1.0
+
+# Query installed: should contain foo-1.0 now (which should be
+# executable).
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 1
+nix-env -p $profiles/test -q '*' | grep -q foo-1.0
+test "$($profiles/test/bin/foo)" = "foo-1.0"
+
+# Disable foo.
+nix-env -p $profiles/test --set-flag active false foo
+! [ -e "$profiles/test/bin/foo" ]
+
+# Enable foo.
+nix-env -p $profiles/test --set-flag active true foo
+[ -e "$profiles/test/bin/foo" ]
+
+# Store the path of foo-1.0.
+outPath10=$(nix-env -p $profiles/test -q --out-path --no-name '*' | grep foo-1.0)
+echo "foo-1.0 = $outPath10"
+test -n "$outPath10"
+
+# Install "foo-2.0pre1": should remove foo-1.0.
+nix-env -p $profiles/test -f ./user-envs.nix -i foo-2.0pre1
+
+# Query installed: should contain foo-2.0pre1 now.
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 1
+nix-env -p $profiles/test -q '*' | grep -q foo-2.0pre1
+test "$($profiles/test/bin/foo)" = "foo-2.0pre1"
+
+# Upgrade "foo": should install foo-2.0.
+NIX_PATH=nixpkgs=./user-envs.nix:$NIX_PATH nix-env -p $profiles/test -f '<nixpkgs>' -u foo
+
+# Query installed: should contain foo-2.0 now.
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 1
+nix-env -p $profiles/test -q '*' | grep -q foo-2.0
+test "$($profiles/test/bin/foo)" = "foo-2.0"
+
+# Store the path of foo-2.0.
+outPath20=$(nix-env -p $profiles/test -q --out-path --no-name '*' | grep foo-2.0)
+test -n "$outPath20"
+
+# Install bar-0.1, uninstall foo.
+nix-env -p $profiles/test -f ./user-envs.nix -i bar-0.1
+nix-env -p $profiles/test -f ./user-envs.nix -e foo
+
+# Query installed: should only contain bar-0.1 now.
+if nix-env -p $profiles/test -q '*' | grep -q foo; then false; fi
+nix-env -p $profiles/test -q '*' | grep -q bar
+
+# Rollback: should bring "foo" back.
+nix-env -p $profiles/test --rollback
+nix-env -p $profiles/test -q '*' | grep -q foo-2.0
+nix-env -p $profiles/test -q '*' | grep -q bar
+
+# Rollback again: should remove "bar".
+nix-env -p $profiles/test --rollback
+nix-env -p $profiles/test -q '*' | grep -q foo-2.0
+if nix-env -p $profiles/test -q '*' | grep -q bar; then false; fi
+
+# Count generations.
+nix-env -p $profiles/test --list-generations
+test "$(nix-env -p $profiles/test --list-generations | wc -l)" -eq 7
+
+# Install foo-1.0, now using its store path.
+echo $outPath10
+nix-env -p $profiles/test -i "$outPath10"
+nix-env -p $profiles/test -q '*' | grep -q foo-1.0
+
+# Uninstall foo-1.0, using a symlink to its store path.
+ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink
+nix-env -p $profiles/test -e $TEST_ROOT/symlink
+if nix-env -p $profiles/test -q '*' | grep -q foo; then false; fi
+
+# Install foo-1.0, now using a symlink to its store path.
+nix-env -p $profiles/test -i $TEST_ROOT/symlink
+nix-env -p $profiles/test -q '*' | grep -q foo
+
+# Delete all old generations.
+nix-env -p $profiles/test --delete-generations old
+
+# Run the garbage collector.  This should get rid of foo-2.0 but not
+# foo-1.0.
+nix-collect-garbage
+test -e "$outPath10"
+! [ -e "$outPath20" ]
+
+# Uninstall everything
+nix-env -p $profiles/test -f ./user-envs.nix -e '*'
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0
+
+# Installing "foo" should only install the newest foo.
+nix-env -p $profiles/test -f ./user-envs.nix -i foo
+test "$(nix-env -p $profiles/test -q '*' | grep foo- | wc -l)" -eq 1
+nix-env -p $profiles/test -q '*' | grep -q foo-2.0
+
+# On the other hand, this should install both (and should fail due to
+# a collision).
+nix-env -p $profiles/test -f ./user-envs.nix -e '*'
+! nix-env -p $profiles/test -f ./user-envs.nix -i foo-1.0 foo-2.0
+
+# Installing "*" should install one foo and one bar.
+nix-env -p $profiles/test -f ./user-envs.nix -e '*'
+nix-env -p $profiles/test -f ./user-envs.nix -i '*'
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 2
+nix-env -p $profiles/test -q '*' | grep -q foo-2.0
+nix-env -p $profiles/test -q '*' | grep -q bar-0.1.1
+
+# Test priorities: foo-0.1 has a lower priority than foo-1.0, so it
+# should be possible to install both without a collision.  Also test
+# ‘--set-flag priority’ to manually override the declared priorities.
+nix-env -p $profiles/test -f ./user-envs.nix -e '*'
+nix-env -p $profiles/test -f ./user-envs.nix -i foo-0.1 foo-1.0
+[ "$($profiles/test/bin/foo)" = "foo-1.0" ]
+nix-env -p $profiles/test -f ./user-envs.nix --set-flag priority 1 foo-0.1
+[ "$($profiles/test/bin/foo)" = "foo-0.1" ]
diff --git a/tests/verify.sh b/tests/verify.sh
new file mode 100644
index 000000000000..e0d68e849d09
--- /dev/null
+++ b/tests/verify.sh
@@ -0,0 +1,3 @@
+source common.sh
+
+nix-store --verify