about summary refs log tree commit diff
path: root/third_party/nix/tests
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/nix/tests')
-rw-r--r--third_party/nix/tests/add.sh28
-rw-r--r--third_party/nix/tests/binary-cache.sh170
-rw-r--r--third_party/nix/tests/brotli.sh21
-rw-r--r--third_party/nix/tests/build-dry.sh52
-rw-r--r--third_party/nix/tests/build-hook.nix23
-rw-r--r--third_party/nix/tests/build-remote.sh24
-rw-r--r--third_party/nix/tests/case-hack.sh19
-rw-r--r--third_party/nix/tests/case.narbin0 -> 2416 bytes
-rw-r--r--third_party/nix/tests/check-refs.nix70
-rw-r--r--third_party/nix/tests/check-refs.sh42
-rw-r--r--third_party/nix/tests/check-reqs.nix57
-rw-r--r--third_party/nix/tests/check-reqs.sh16
-rw-r--r--third_party/nix/tests/check.nix22
-rw-r--r--third_party/nix/tests/check.sh47
-rw-r--r--third_party/nix/tests/common.sh.in118
-rw-r--r--third_party/nix/tests/config.nix20
-rw-r--r--third_party/nix/tests/dependencies.builder0.sh16
-rw-r--r--third_party/nix/tests/dependencies.builder1.sh2
-rw-r--r--third_party/nix/tests/dependencies.builder2.sh2
-rw-r--r--third_party/nix/tests/dependencies.nix24
-rw-r--r--third_party/nix/tests/dependencies.sh52
-rw-r--r--third_party/nix/tests/dump-db.sh20
-rw-r--r--third_party/nix/tests/export-graph.nix29
-rw-r--r--third_party/nix/tests/export-graph.sh30
-rw-r--r--third_party/nix/tests/export.sh36
-rw-r--r--third_party/nix/tests/fetchGit.sh141
-rw-r--r--third_party/nix/tests/fetchMercurial.sh93
-rw-r--r--third_party/nix/tests/fetchurl.sh78
-rw-r--r--third_party/nix/tests/filter-source.nix12
-rw-r--r--third_party/nix/tests/filter-source.sh19
-rw-r--r--third_party/nix/tests/fixed.builder1.sh3
-rw-r--r--third_party/nix/tests/fixed.builder2.sh6
-rw-r--r--third_party/nix/tests/fixed.nix50
-rw-r--r--third_party/nix/tests/fixed.sh56
-rwxr-xr-xthird_party/nix/tests/function-trace.sh85
-rw-r--r--third_party/nix/tests/gc-auto.sh70
-rw-r--r--third_party/nix/tests/gc-concurrent.builder.sh13
-rw-r--r--third_party/nix/tests/gc-concurrent.nix27
-rw-r--r--third_party/nix/tests/gc-concurrent.sh58
-rw-r--r--third_party/nix/tests/gc-concurrent2.builder.sh7
-rw-r--r--third_party/nix/tests/gc-runtime.nix17
-rw-r--r--third_party/nix/tests/gc-runtime.sh38
-rw-r--r--third_party/nix/tests/gc.sh40
-rw-r--r--third_party/nix/tests/hash-check.nix29
-rw-r--r--third_party/nix/tests/hash.sh87
-rw-r--r--third_party/nix/tests/import-derivation.nix26
-rw-r--r--third_party/nix/tests/import-derivation.sh12
-rw-r--r--third_party/nix/tests/init.sh34
-rwxr-xr-xthird_party/nix/tests/install-darwin.sh96
-rw-r--r--third_party/nix/tests/lang.sh68
-rw-r--r--third_party/nix/tests/linux-sandbox.sh30
-rw-r--r--third_party/nix/tests/logging.sh15
-rw-r--r--third_party/nix/tests/misc.sh19
-rw-r--r--third_party/nix/tests/multiple-outputs.nix68
-rw-r--r--third_party/nix/tests/multiple-outputs.sh76
-rw-r--r--third_party/nix/tests/nar-access.nix23
-rw-r--r--third_party/nix/tests/nar-access.sh44
-rw-r--r--third_party/nix/tests/nix-build.sh25
-rw-r--r--third_party/nix/tests/nix-channel.sh59
-rw-r--r--third_party/nix/tests/nix-copy-closure.nix64
-rw-r--r--third_party/nix/tests/nix-copy-ssh.sh20
-rw-r--r--third_party/nix/tests/nix-profile.sh9
-rw-r--r--third_party/nix/tests/nix-shell.sh57
-rw-r--r--third_party/nix/tests/optimise-store.sh43
-rw-r--r--third_party/nix/tests/parallel.builder.sh29
-rw-r--r--third_party/nix/tests/parallel.nix19
-rw-r--r--third_party/nix/tests/parallel.sh56
-rw-r--r--third_party/nix/tests/pass-as-file.sh18
-rw-r--r--third_party/nix/tests/placeholders.sh20
-rw-r--r--third_party/nix/tests/post-hook.sh15
-rw-r--r--third_party/nix/tests/pure-eval.nix3
-rw-r--r--third_party/nix/tests/pure-eval.sh18
-rwxr-xr-xthird_party/nix/tests/push-to-store.sh4
-rw-r--r--third_party/nix/tests/referrers.sh36
-rw-r--r--third_party/nix/tests/remote-builds.nix108
-rw-r--r--third_party/nix/tests/remote-store.sh19
-rw-r--r--third_party/nix/tests/repair.sh77
-rw-r--r--third_party/nix/tests/restricted.nix1
-rw-r--r--third_party/nix/tests/restricted.sh51
-rw-r--r--third_party/nix/tests/run.nix17
-rw-r--r--third_party/nix/tests/run.sh28
-rw-r--r--third_party/nix/tests/search.nix25
-rw-r--r--third_party/nix/tests/search.sh43
-rw-r--r--third_party/nix/tests/secure-drv-outputs.nix23
-rw-r--r--third_party/nix/tests/secure-drv-outputs.sh36
-rw-r--r--third_party/nix/tests/setuid.nix108
-rw-r--r--third_party/nix/tests/shell.nix56
-rw-r--r--third_party/nix/tests/shell.shebang.rb7
-rwxr-xr-xthird_party/nix/tests/shell.shebang.sh4
-rw-r--r--third_party/nix/tests/signing.sh105
-rw-r--r--third_party/nix/tests/simple.builder.sh11
-rw-r--r--third_party/nix/tests/simple.nix8
-rw-r--r--third_party/nix/tests/simple.sh25
-rw-r--r--third_party/nix/tests/structured-attrs.nix66
-rw-r--r--third_party/nix/tests/structured-attrs.sh7
-rw-r--r--third_party/nix/tests/tarball.sh28
-rw-r--r--third_party/nix/tests/timeout.nix31
-rw-r--r--third_party/nix/tests/timeout.sh40
-rw-r--r--third_party/nix/tests/user-envs.builder.sh5
-rw-r--r--third_party/nix/tests/user-envs.nix29
-rw-r--r--third_party/nix/tests/user-envs.sh181
101 files changed, 4014 insertions, 0 deletions
diff --git a/third_party/nix/tests/add.sh b/third_party/nix/tests/add.sh
new file mode 100644
index 000000000000..e26e05843d7f
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/binary-cache.sh b/third_party/nix/tests/binary-cache.sh
new file mode 100644
index 000000000000..eb58ae7c12a8
--- /dev/null
+++ b/third_party/nix/tests/binary-cache.sh
@@ -0,0 +1,170 @@
+source common.sh
+
+clearStore
+clearCache
+
+# Create the binary cache.
+outPath=$(nix-build dependencies.nix --no-out-link)
+
+nix copy --to file://$cacheDir $outPath
+
+
+basicTests() {
+
+    # By default, a binary cache doesn't support "nix-env -qas", but does
+    # support installation.
+    clearStore
+    clearCacheCache
+
+    nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "---"
+
+    nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath
+
+    [ -x $outPath/program ]
+
+
+    # But with the right configuration, "nix-env -qas" should also work.
+    clearStore
+    clearCacheCache
+    echo "WantMassQuery: 1" >> $cacheDir/nix-cache-info
+
+    nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S"
+    nix-env --substituters "file://$cacheDir" -f dependencies.nix -qas \* | grep -- "--S"
+
+    x=$(nix-env -f dependencies.nix -qas \* --prebuilt-only)
+    [ -z "$x" ]
+
+    nix-store --substituters "file://$cacheDir" --no-require-sigs -r $outPath
+
+    nix-store --check-validity $outPath
+    nix-store -qR $outPath | grep input-2
+
+    echo "WantMassQuery: 0" >> $cacheDir/nix-cache-info
+}
+
+
+# Test LocalBinaryCacheStore.
+basicTests
+
+
+# Test HttpBinaryCacheStore.
+export _NIX_FORCE_HTTP_BINARY_CACHE_STORE=1
+basicTests
+
+
+# Test whether Nix notices if the NAR doesn't match the hash in the NAR info.
+clearStore
+
+nar=$(ls $cacheDir/nar/*.nar.xz | head -n1)
+mv $nar $nar.good
+mkdir -p $TEST_ROOT/empty
+nix-store --dump $TEST_ROOT/empty | xz > $nar
+
+nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
+grep -q "hash mismatch" $TEST_ROOT/log
+
+mv $nar.good $nar
+
+
+# Test whether this unsigned cache is rejected if the user requires signed caches.
+clearStore
+clearCacheCache
+
+if nix-store --substituters "file://$cacheDir" -r $outPath; then
+    echo "unsigned binary cache incorrectly accepted"
+    exit 1
+fi
+
+
+# Test whether fallback works if a NAR has disappeared. This does not require --fallback.
+clearStore
+
+mv $cacheDir/nar $cacheDir/nar2
+
+nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result
+
+mv $cacheDir/nar2 $cacheDir/nar
+
+
+# Test whether fallback works if a NAR is corrupted. This does require --fallback.
+clearStore
+
+mv $cacheDir/nar $cacheDir/nar2
+mkdir $cacheDir/nar
+for i in $(cd $cacheDir/nar2 && echo *); do touch $cacheDir/nar/$i; done
+
+(! nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result)
+
+nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result --fallback
+
+rm -rf $cacheDir/nar
+mv $cacheDir/nar2 $cacheDir/nar
+
+
+# Test whether building works if the binary cache contains an
+# incomplete closure.
+clearStore
+
+rm $(grep -l "StorePath:.*dependencies-input-2" $cacheDir/*.narinfo)
+
+nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log
+grep -q "copying path" $TEST_ROOT/log
+
+
+if [ -n "$HAVE_SODIUM" ]; then
+
+# Create a signed binary cache.
+clearCache
+clearCacheCache
+
+declare -a res=($(nix-store --generate-binary-cache-key test.nixos.org-1 $TEST_ROOT/sk1 $TEST_ROOT/pk1 ))
+publicKey="$(cat $TEST_ROOT/pk1)"
+
+res=($(nix-store --generate-binary-cache-key test.nixos.org-1 $TEST_ROOT/sk2 $TEST_ROOT/pk2))
+badKey="$(cat $TEST_ROOT/pk2)"
+
+res=($(nix-store --generate-binary-cache-key foo.nixos.org-1 $TEST_ROOT/sk3 $TEST_ROOT/pk3))
+otherKey="$(cat $TEST_ROOT/pk3)"
+
+_NIX_FORCE_HTTP_BINARY_CACHE_STORE= nix copy --to file://$cacheDir?secret-key=$TEST_ROOT/sk1 $outPath
+
+
+# Downloading should fail if we don't provide a key.
+clearStore
+clearCacheCache
+
+(! nix-store -r $outPath --substituters "file://$cacheDir")
+
+
+# And it should fail if we provide an incorrect key.
+clearStore
+clearCacheCache
+
+(! nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$badKey")
+
+
+# It should succeed if we provide the correct key.
+nix-store -r $outPath --substituters "file://$cacheDir" --trusted-public-keys "$otherKey $publicKey"
+
+
+# It should fail if we corrupt the .narinfo.
+clearStore
+
+cacheDir2=$TEST_ROOT/binary-cache-2
+rm -rf $cacheDir2
+cp -r $cacheDir $cacheDir2
+
+for i in $cacheDir2/*.narinfo; do
+    grep -v References $i > $i.tmp
+    mv $i.tmp $i
+done
+
+clearCacheCache
+
+(! nix-store -r $outPath --substituters "file://$cacheDir2" --trusted-public-keys "$publicKey")
+
+# If we provide a bad and a good binary cache, it should succeed.
+
+nix-store -r $outPath --substituters "file://$cacheDir2 file://$cacheDir" --trusted-public-keys "$publicKey"
+
+fi # HAVE_LIBSODIUM
diff --git a/third_party/nix/tests/brotli.sh b/third_party/nix/tests/brotli.sh
new file mode 100644
index 000000000000..a3c6e55a8fad
--- /dev/null
+++ b/third_party/nix/tests/brotli.sh
@@ -0,0 +1,21 @@
+source common.sh
+
+clearStore
+clearCache
+
+cacheURI="file://$cacheDir?compression=br"
+
+outPath=$(nix-build dependencies.nix --no-out-link)
+
+nix copy --to $cacheURI $outPath
+
+HASH=$(nix hash-path $outPath)
+
+clearStore
+clearCacheCache
+
+nix copy --from $cacheURI $outPath --no-check-sigs
+
+HASH2=$(nix hash-path $outPath)
+
+[[ $HASH = $HASH2 ]]
diff --git a/third_party/nix/tests/build-dry.sh b/third_party/nix/tests/build-dry.sh
new file mode 100644
index 000000000000..e72533e70614
--- /dev/null
+++ b/third_party/nix/tests/build-dry.sh
@@ -0,0 +1,52 @@
+source common.sh
+
+###################################################
+# Check that --dry-run isn't confused with read-only mode
+# https://github.com/NixOS/nix/issues/1795
+
+clearStore
+clearCache
+
+# Ensure this builds successfully first
+nix build --no-link -f dependencies.nix
+
+clearStore
+clearCache
+
+# Try --dry-run using old command first
+nix-build --no-out-link dependencies.nix --dry-run 2>&1 | grep "will be built"
+# Now new command:
+nix build -f dependencies.nix --dry-run 2>&1 | grep "will be built"
+
+# TODO: XXX: FIXME: #1793
+# Disable this part of the test until the problem is resolved:
+if [ -n "$ISSUE_1795_IS_FIXED" ]; then
+clearStore
+clearCache
+
+# Try --dry-run using new command first
+nix build -f dependencies.nix --dry-run 2>&1 | grep "will be built"
+# Now old command:
+nix-build --no-out-link dependencies.nix --dry-run 2>&1 | grep "will be built"
+fi
+
+###################################################
+# Check --dry-run doesn't create links with --dry-run
+# https://github.com/NixOS/nix/issues/1849
+clearStore
+clearCache
+
+RESULT=$TEST_ROOT/result-link
+rm -f $RESULT
+
+nix-build dependencies.nix -o $RESULT --dry-run
+
+[[ ! -h $RESULT ]] || fail "nix-build --dry-run created output link"
+
+nix build -f dependencies.nix -o $RESULT --dry-run
+
+[[ ! -h $RESULT ]] || fail "nix build --dry-run created output link"
+
+nix build -f dependencies.nix -o $RESULT
+
+[[ -h $RESULT ]]
diff --git a/third_party/nix/tests/build-hook.nix b/third_party/nix/tests/build-hook.nix
new file mode 100644
index 000000000000..8bff0fe79032
--- /dev/null
+++ b/third_party/nix/tests/build-hook.nix
@@ -0,0 +1,23 @@
+with import ./config.nix;
+
+let
+
+  input1 = mkDerivation {
+    name = "build-hook-input-1";
+    builder = ./dependencies.builder1.sh;
+    requiredSystemFeatures = ["foo"];
+  };
+
+  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/third_party/nix/tests/build-remote.sh b/third_party/nix/tests/build-remote.sh
new file mode 100644
index 000000000000..ddd68f327a15
--- /dev/null
+++ b/third_party/nix/tests/build-remote.sh
@@ -0,0 +1,24 @@
+source common.sh
+
+clearStore
+
+if ! canUseSandbox; then exit; fi
+if [[ ! $SHELL =~ /nix/store ]]; then exit; fi
+
+chmod -R u+w $TEST_ROOT/store0 || true
+chmod -R u+w $TEST_ROOT/store1 || true
+rm -rf $TEST_ROOT/store0 $TEST_ROOT/store1
+
+nix build -f build-hook.nix -o $TEST_ROOT/result --max-jobs 0 \
+  --sandbox-paths /nix/store --sandbox-build-dir /build-tmp \
+  --builders "$TEST_ROOT/store0; $TEST_ROOT/store1 - - 1 1 foo" \
+  --system-features foo
+
+outPath=$TEST_ROOT/result
+
+cat $outPath/foobar | grep FOOBAR
+
+# Ensure that input1 was built on store1 due to the required feature.
+p=$(readlink -f $outPath/input-2)
+(! nix path-info --store $TEST_ROOT/store0 --all | grep dependencies.builder1.sh)
+nix path-info --store $TEST_ROOT/store1 --all | grep dependencies.builder1.sh
diff --git a/third_party/nix/tests/case-hack.sh b/third_party/nix/tests/case-hack.sh
new file mode 100644
index 000000000000..61bf9b94bf5c
--- /dev/null
+++ b/third_party/nix/tests/case-hack.sh
@@ -0,0 +1,19 @@
+source common.sh
+
+clearStore
+
+rm -rf $TEST_ROOT/case
+
+opts="--option use-case-hack true"
+
+# Check whether restoring and dumping a NAR that contains case
+# collisions is round-tripping, even on a case-insensitive system.
+nix-store $opts  --restore $TEST_ROOT/case < case.nar
+nix-store $opts --dump $TEST_ROOT/case > $TEST_ROOT/case.nar
+cmp case.nar $TEST_ROOT/case.nar
+[ "$(nix-hash $opts --type sha256 $TEST_ROOT/case)" = "$(nix-hash --flat --type sha256 case.nar)" ]
+
+# Check whether we detect true collisions (e.g. those remaining after
+# removal of the suffix).
+touch "$TEST_ROOT/case/xt_CONNMARK.h~nix~case~hack~3"
+(! nix-store $opts --dump $TEST_ROOT/case > /dev/null)
diff --git a/third_party/nix/tests/case.nar b/third_party/nix/tests/case.nar
new file mode 100644
index 000000000000..22ff26db5afd
--- /dev/null
+++ b/third_party/nix/tests/case.nar
Binary files differdiff --git a/third_party/nix/tests/check-refs.nix b/third_party/nix/tests/check-refs.nix
new file mode 100644
index 000000000000..9d90b0920542
--- /dev/null
+++ b/third_party/nix/tests/check-refs.nix
@@ -0,0 +1,70 @@
+with import ./config.nix;
+
+rec {
+
+  dep = import ./dependencies.nix;
+
+  makeTest = nr: args: mkDerivation ({
+    name = "check-refs-" + toString nr;
+  } // args);
+
+  src = builtins.toFile "aux-ref" "bla bla";
+
+  test1 = makeTest 1 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    inherit dep;
+  };
+
+  test2 = makeTest 2 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${src} $out/link";
+    inherit dep;
+  };
+
+  test3 = makeTest 3 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    allowedReferences = [];
+    inherit dep;
+  };
+
+  test4 = makeTest 4 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    allowedReferences = [dep];
+    inherit dep;
+  };
+
+  test5 = makeTest 5 {
+    builder = builtins.toFile "builder.sh" "mkdir $out";
+    allowedReferences = [];
+    inherit dep;
+  };
+
+  test6 = makeTest 6 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
+    allowedReferences = [];
+    inherit dep;
+  };
+
+  test7 = makeTest 7 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $out $out/link";
+    allowedReferences = ["out"];
+    inherit dep;
+  };
+
+  test8 = makeTest 8 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s ${test1} $out/link";
+    inherit dep;
+  };
+
+  test9 = makeTest 9 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $dep $out/link";
+    inherit dep;
+    disallowedReferences = [dep];
+  };
+
+  test10 = makeTest 10 {
+    builder = builtins.toFile "builder.sh" "mkdir $out; echo $test5; ln -s $dep $out/link";
+    inherit dep test5;
+    disallowedReferences = [test5];
+  };
+
+}
diff --git a/third_party/nix/tests/check-refs.sh b/third_party/nix/tests/check-refs.sh
new file mode 100644
index 000000000000..16bbabc40985
--- /dev/null
+++ b/third_party/nix/tests/check-refs.sh
@@ -0,0 +1,42 @@
+source common.sh
+
+clearStore
+
+RESULT=$TEST_ROOT/result
+
+dep=$(nix-build -o $RESULT check-refs.nix -A dep)
+
+# test1 references dep, not itself.
+test1=$(nix-build -o $RESULT check-refs.nix -A test1)
+(! nix-store -q --references $test1 | 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)
+
+# test9 should fail (disallowed reference).
+(! nix-build -o $RESULT check-refs.nix -A test9)
+
+# test10 should succeed (no disallowed references).
+nix-build -o $RESULT check-refs.nix -A test10
diff --git a/third_party/nix/tests/check-reqs.nix b/third_party/nix/tests/check-reqs.nix
new file mode 100644
index 000000000000..41436cb48e08
--- /dev/null
+++ b/third_party/nix/tests/check-reqs.nix
@@ -0,0 +1,57 @@
+with import ./config.nix;
+
+rec {
+  dep1 = mkDerivation {
+    name = "check-reqs-dep1";
+    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file1";
+  };
+
+  dep2 = mkDerivation {
+    name = "check-reqs-dep2";
+    builder = builtins.toFile "builder.sh" "mkdir $out; touch $out/file2";
+  };
+
+  deps = mkDerivation {
+    name = "check-reqs-deps";
+    dep1 = dep1;
+    dep2 = dep2;
+    builder = builtins.toFile "builder.sh" ''
+      mkdir $out
+      ln -s $dep1/file1 $out/file1
+      ln -s $dep2/file2 $out/file2
+    '';
+  };
+
+  makeTest = nr: allowreqs: mkDerivation {
+    name = "check-reqs-" + toString nr;
+    inherit deps;
+    builder = builtins.toFile "builder.sh" ''
+      mkdir $out
+      ln -s $deps $out/depdir1
+    '';
+    allowedRequisites = allowreqs;
+  };
+
+  # When specifying all the requisites, the build succeeds.
+  test1 = makeTest 1 [ dep1 dep2 deps ];
+
+  # But missing anything it fails.
+  test2 = makeTest 2 [ dep2 deps ];
+  test3 = makeTest 3 [ dep1 deps ];
+  test4 = makeTest 4 [ deps ];
+  test5 = makeTest 5 [];
+
+  test6 = mkDerivation {
+    name = "check-reqs";
+    inherit deps;
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1";
+    disallowedRequisites = [dep1];
+  };
+
+  test7 = mkDerivation {
+    name = "check-reqs";
+    inherit deps;
+    builder = builtins.toFile "builder.sh" "mkdir $out; ln -s $deps $out/depdir1";
+    disallowedRequisites = [test1];
+  };
+}
diff --git a/third_party/nix/tests/check-reqs.sh b/third_party/nix/tests/check-reqs.sh
new file mode 100644
index 000000000000..e9f65fc2a6d3
--- /dev/null
+++ b/third_party/nix/tests/check-reqs.sh
@@ -0,0 +1,16 @@
+source common.sh
+
+clearStore
+
+RESULT=$TEST_ROOT/result
+
+nix-build -o $RESULT check-reqs.nix -A test1
+
+(! nix-build -o $RESULT check-reqs.nix -A test2)
+(! nix-build -o $RESULT check-reqs.nix -A test3)
+(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grep -q 'check-reqs-dep1'
+(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grep -q 'check-reqs-dep2'
+(! nix-build -o $RESULT check-reqs.nix -A test5)
+(! nix-build -o $RESULT check-reqs.nix -A test6)
+
+nix-build -o $RESULT check-reqs.nix -A test7
diff --git a/third_party/nix/tests/check.nix b/third_party/nix/tests/check.nix
new file mode 100644
index 000000000000..56c82e565a8f
--- /dev/null
+++ b/third_party/nix/tests/check.nix
@@ -0,0 +1,22 @@
+with import ./config.nix;
+
+{
+  nondeterministic = mkDerivation {
+    name = "nondeterministic";
+    buildCommand =
+      ''
+        mkdir $out
+        date +%s.%N > $out/date
+      '';
+  };
+
+  hashmismatch = import <nix/fetchurl.nix> {
+    url = "file://" + toString ./dummy;
+    sha256 = "0mdqa9w1p6cmli6976v4wi0sw9r4p5prkj7lzfd1877wk11c9c73";
+  };
+
+  fetchurl = import <nix/fetchurl.nix> {
+    url = "file://" + toString ./lang/eval-okay-xml.exp.xml;
+    sha256 = "0kg4sla7ihm8ijr8cb3117fhl99zrc2bwy1jrngsfmkh8bav4m0v";
+  };
+}
diff --git a/third_party/nix/tests/check.sh b/third_party/nix/tests/check.sh
new file mode 100644
index 000000000000..bc23a6634ca0
--- /dev/null
+++ b/third_party/nix/tests/check.sh
@@ -0,0 +1,47 @@
+source common.sh
+
+clearStore
+
+nix-build dependencies.nix --no-out-link
+nix-build dependencies.nix --no-out-link --check
+
+nix-build check.nix -A nondeterministic --no-out-link
+nix-build check.nix -A nondeterministic --no-out-link --check 2> $TEST_ROOT/log || status=$?
+grep 'may not be deterministic' $TEST_ROOT/log
+[ "$status" = "104" ]
+
+clearStore
+
+nix-build dependencies.nix --no-out-link --repeat 3
+
+nix-build check.nix -A nondeterministic --no-out-link --repeat 1 2> $TEST_ROOT/log || status=$?
+[ "$status" = "1" ]
+grep 'differs from previous round' $TEST_ROOT/log
+
+path=$(nix-build check.nix -A fetchurl --no-out-link --hashed-mirrors '')
+
+chmod +w $path
+echo foo > $path
+chmod -w $path
+
+nix-build check.nix -A fetchurl --no-out-link --check --hashed-mirrors ''
+# Note: "check" doesn't repair anything, it just compares to the hash stored in the database.
+[[ $(cat $path) = foo ]]
+
+nix-build check.nix -A fetchurl --no-out-link --repair --hashed-mirrors ''
+[[ $(cat $path) != foo ]]
+
+nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors '' || status=$?
+[ "$status" = "102" ]
+
+echo -n > ./dummy
+nix-build check.nix -A hashmismatch --no-out-link --hashed-mirrors ''
+echo 'Hello World' > ./dummy
+
+nix-build check.nix -A hashmismatch --no-out-link --check --hashed-mirrors '' || status=$?
+[ "$status" = "102" ]
+
+# Multiple failures with --keep-going
+nix-build check.nix -A nondeterministic --no-out-link
+nix-build check.nix -A nondeterministic -A hashmismatch --no-out-link --check --keep-going --hashed-mirrors '' || status=$?
+[ "$status" = "110" ]
diff --git a/third_party/nix/tests/common.sh.in b/third_party/nix/tests/common.sh.in
new file mode 100644
index 000000000000..15d7b1ef9119
--- /dev/null
+++ b/third_party/nix/tests/common.sh.in
@@ -0,0 +1,118 @@
+set -e
+
+export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)
+export NIX_STORE_DIR
+if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
+    # Maybe the build directory is symlinked.
+    export NIX_IGNORE_SYMLINK_STORE=1
+    NIX_STORE_DIR=$TEST_ROOT/store
+fi
+export NIX_LOCALSTATE_DIR=$TEST_ROOT/var
+export NIX_LOG_DIR=$TEST_ROOT/var/log/nix
+export NIX_STATE_DIR=$TEST_ROOT/var/nix
+export NIX_CONF_DIR=$TEST_ROOT/etc
+export _NIX_TEST_SHARED=$TEST_ROOT/shared
+if [[ -n $NIX_STORE ]]; then
+    export _NIX_TEST_NO_SANDBOX=1
+fi
+export _NIX_IN_TEST=$TEST_ROOT/shared
+export _NIX_TEST_NO_LSOF=1
+export NIX_REMOTE=$NIX_REMOTE_
+unset NIX_PATH
+export TEST_HOME=$TEST_ROOT/test-home
+export HOME=$TEST_HOME
+unset XDG_CACHE_HOME
+mkdir -p $TEST_HOME
+
+export PATH=@bindir@:$PATH
+coreutils=@coreutils@
+
+export dot=@dot@
+export xmllint="@xmllint@"
+export SHELL="@bash@"
+export PAGER=cat
+export HAVE_SODIUM="@HAVE_SODIUM@"
+
+export version=@PACKAGE_VERSION@
+export system=@system@
+
+cacheDir=$TEST_ROOT/binary-cache
+
+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_STATE_DIR"
+    mkdir "$NIX_STATE_DIR"
+    nix-store --init
+    clearProfiles
+}
+
+clearCache() {
+    rm -rf "$cacheDir"
+}
+
+clearCacheCache() {
+    rm -f $TEST_HOME/.cache/nix/binary-cache*
+}
+
+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
+}
+
+if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then
+    _canUseSandbox=1
+fi
+
+canUseSandbox() {
+    if [[ ! $_canUseSandbox ]]; then
+        echo "Sandboxing not supported, skipping this test..."
+        return 1
+    fi
+
+    return 0
+}
+
+fail() {
+    echo "$1"
+    exit 1
+}
+
+expect() {
+    local expected res
+    expected="$1"
+    shift
+    set +e
+    "$@"
+    res="$?"
+    set -e
+    [[ $res -eq $expected ]]
+}
+
+set -x
diff --git a/third_party/nix/tests/config.nix b/third_party/nix/tests/config.nix
new file mode 100644
index 000000000000..6ba91065b83d
--- /dev/null
+++ b/third_party/nix/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 or (builtins.toFile "builder.sh" "if [ -e .attrs.sh ]; then source .attrs.sh; fi; eval \"$buildCommand\"")];
+      PATH = path;
+    } // removeAttrs args ["builder" "meta"])
+    // { meta = args.meta or {}; };
+}
diff --git a/third_party/nix/tests/dependencies.builder0.sh b/third_party/nix/tests/dependencies.builder0.sh
new file mode 100644
index 000000000000..c37bf909a5f9
--- /dev/null
+++ b/third_party/nix/tests/dependencies.builder0.sh
@@ -0,0 +1,16 @@
+[ "${input1: -2}" = /. ]
+[ "${input2: -2}" = /. ]
+
+mkdir $out
+echo $(cat $input1/foo)$(cat $input2/bar) > $out/foobar
+
+ln -s $input2 $out/input-2
+
+# Self-reference.
+ln -s $out $out/self
+
+# Executable.
+echo program > $out/program
+chmod +x $out/program
+
+echo FOO
diff --git a/third_party/nix/tests/dependencies.builder1.sh b/third_party/nix/tests/dependencies.builder1.sh
new file mode 100644
index 000000000000..4b006a17d70f
--- /dev/null
+++ b/third_party/nix/tests/dependencies.builder1.sh
@@ -0,0 +1,2 @@
+mkdir $out
+echo FOO > $out/foo
diff --git a/third_party/nix/tests/dependencies.builder2.sh b/third_party/nix/tests/dependencies.builder2.sh
new file mode 100644
index 000000000000..4f886fdb3a1a
--- /dev/null
+++ b/third_party/nix/tests/dependencies.builder2.sh
@@ -0,0 +1,2 @@
+mkdir $out
+echo BAR > $out/bar
diff --git a/third_party/nix/tests/dependencies.nix b/third_party/nix/tests/dependencies.nix
new file mode 100644
index 000000000000..eca4b2964cfb
--- /dev/null
+++ b/third_party/nix/tests/dependencies.nix
@@ -0,0 +1,24 @@
+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}/.";
+    input1_drv = input1;
+    meta.description = "Random test package";
+  };
+
+}
diff --git a/third_party/nix/tests/dependencies.sh b/third_party/nix/tests/dependencies.sh
new file mode 100644
index 000000000000..df204d185ddc
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/dump-db.sh b/third_party/nix/tests/dump-db.sh
new file mode 100644
index 000000000000..d6eea42aa04e
--- /dev/null
+++ b/third_party/nix/tests/dump-db.sh
@@ -0,0 +1,20 @@
+source common.sh
+
+clearStore
+
+path=$(nix-build dependencies.nix -o $TEST_ROOT/result)
+
+deps="$(nix-store -qR $TEST_ROOT/result)"
+
+nix-store --dump-db > $TEST_ROOT/dump
+
+rm -rf $NIX_STATE_DIR/db
+
+nix-store --load-db < $TEST_ROOT/dump
+
+deps2="$(nix-store -qR $TEST_ROOT/result)"
+
+[ "$deps" = "$deps2" ];
+
+nix-store --dump-db > $TEST_ROOT/dump2
+cmp $TEST_ROOT/dump $TEST_ROOT/dump2
diff --git a/third_party/nix/tests/export-graph.nix b/third_party/nix/tests/export-graph.nix
new file mode 100644
index 000000000000..fdac9583db2c
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/export-graph.sh b/third_party/nix/tests/export-graph.sh
new file mode 100644
index 000000000000..a6fd69054425
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/export.sh b/third_party/nix/tests/export.sh
new file mode 100644
index 000000000000..2238539bcca9
--- /dev/null
+++ b/third_party/nix/tests/export.sh
@@ -0,0 +1,36 @@
+source common.sh
+
+clearStore
+
+outPath=$(nix-build dependencies.nix --no-out-link)
+
+nix-store --export $outPath > $TEST_ROOT/exp
+
+nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all
+
+if nix-store --export $outPath >/dev/full ; then
+    echo "exporting to a bad file descriptor should fail"
+    exit 1
+fi
+
+
+clearStore
+
+if nix-store --import < $TEST_ROOT/exp; then
+    echo "importing a non-closure should fail"
+    exit 1
+fi
+
+
+clearStore
+
+nix-store --import < $TEST_ROOT/exp_all
+
+nix-store --export $(nix-store -qR $outPath) > $TEST_ROOT/exp_all2
+
+
+clearStore
+
+# Regression test: the derivers in exp_all2 are empty, which shouldn't
+# cause a failure.
+nix-store --import < $TEST_ROOT/exp_all2
diff --git a/third_party/nix/tests/fetchGit.sh b/third_party/nix/tests/fetchGit.sh
new file mode 100644
index 000000000000..4c46bdf0465b
--- /dev/null
+++ b/third_party/nix/tests/fetchGit.sh
@@ -0,0 +1,141 @@
+source common.sh
+
+if [[ -z $(type -p git) ]]; then
+    echo "Git not installed; skipping Git tests"
+    exit 99
+fi
+
+clearStore
+
+repo=$TEST_ROOT/git
+
+rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/gitv2
+
+git init $repo
+git -C $repo config user.email "foobar@example.com"
+git -C $repo config user.name "Foobar"
+
+echo utrecht > $repo/hello
+touch $repo/.gitignore
+git -C $repo add hello .gitignore
+git -C $repo commit -m 'Bla1'
+rev1=$(git -C $repo rev-parse HEAD)
+
+echo world > $repo/hello
+git -C $repo commit -m 'Bla2' -a
+rev2=$(git -C $repo rev-parse HEAD)
+
+# Fetch the default branch.
+path=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
+[[ $(cat $path/hello) = world ]]
+
+# In pure eval mode, fetchGit without a revision should fail.
+[[ $(nix eval --raw "(builtins.readFile (fetchGit file://$repo + \"/hello\"))") = world ]]
+(! nix eval --pure-eval --raw "(builtins.readFile (fetchGit file://$repo + \"/hello\"))")
+
+# Fetch using an explicit revision hash.
+path2=$(nix eval --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
+[[ $path = $path2 ]]
+
+# In pure eval mode, fetchGit with a revision should succeed.
+[[ $(nix eval --pure-eval --raw "(builtins.readFile (fetchGit { url = file://$repo; rev = \"$rev2\"; } + \"/hello\"))") = world ]]
+
+# Fetch again. This should be cached.
+mv $repo ${repo}-tmp
+path2=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
+[[ $path = $path2 ]]
+
+[[ $(nix eval "(builtins.fetchGit file://$repo).revCount") = 2 ]]
+[[ $(nix eval --raw "(builtins.fetchGit file://$repo).rev") = $rev2 ]]
+
+# But with TTL 0, it should fail.
+(! nix eval --tarball-ttl 0 "(builtins.fetchGit file://$repo)" -vvvvv)
+
+# Fetching with a explicit hash should succeed.
+path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev2\"; }).outPath")
+[[ $path = $path2 ]]
+
+path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchGit { url = file://$repo; rev = \"$rev1\"; }).outPath")
+[[ $(cat $path2/hello) = utrecht ]]
+
+mv ${repo}-tmp $repo
+
+# Using a clean working tree should produce the same result.
+path2=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
+[[ $path = $path2 ]]
+
+# Using an unclean tree should yield the tracked but uncommitted changes.
+mkdir $repo/dir1 $repo/dir2
+echo foo > $repo/dir1/foo
+echo bar > $repo/bar
+echo bar > $repo/dir2/bar
+git -C $repo add dir1/foo
+git -C $repo rm hello
+
+path2=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
+[ ! -e $path2/hello ]
+[ ! -e $path2/bar ]
+[ ! -e $path2/dir2/bar ]
+[ ! -e $path2/.git ]
+[[ $(cat $path2/dir1/foo) = foo ]]
+
+[[ $(nix eval --raw "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]]
+
+# ... unless we're using an explicit ref or rev.
+path3=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"master\"; }).outPath")
+[[ $path = $path3 ]]
+
+path3=$(nix eval --raw "(builtins.fetchGit { url = $repo; rev = \"$rev2\"; }).outPath")
+[[ $path = $path3 ]]
+
+# Committing should not affect the store path.
+git -C $repo commit -m 'Bla3' -a
+
+path4=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchGit file://$repo).outPath")
+[[ $path2 = $path4 ]]
+
+# tarball-ttl should be ignored if we specify a rev
+echo delft > $repo/hello
+git -C $repo add hello
+git -C $repo commit -m 'Bla4'
+rev3=$(git -C $repo rev-parse HEAD)
+nix eval --tarball-ttl 3600 "(builtins.fetchGit { url = $repo; rev = \"$rev3\"; })" >/dev/null
+
+# Update 'path' to reflect latest master
+path=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
+
+# Check behavior when non-master branch is used
+git -C $repo checkout $rev2 -b dev
+echo dev > $repo/hello
+
+# File URI uses 'master' unless specified otherwise
+path2=$(nix eval --raw "(builtins.fetchGit file://$repo).outPath")
+[[ $path = $path2 ]]
+
+# Using local path with branch other than 'master' should work when clean or dirty
+path3=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
+# (check dirty-tree handling was used)
+[[ $(nix eval --raw "(builtins.fetchGit $repo).rev") = 0000000000000000000000000000000000000000 ]]
+
+# Committing shouldn't change store path, or switch to using 'master'
+git -C $repo commit -m 'Bla5' -a
+path4=$(nix eval --raw "(builtins.fetchGit $repo).outPath")
+[[ $(cat $path4/hello) = dev ]]
+[[ $path3 = $path4 ]]
+
+# Confirm same as 'dev' branch
+path5=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath")
+[[ $path3 = $path5 ]]
+
+
+# Nuke the cache
+rm -rf $TEST_HOME/.cache/nix/gitv2
+
+# Try again, but without 'git' on PATH
+NIX=$(command -v nix)
+# This should fail
+(! PATH= $NIX eval --raw "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath" )
+
+# Try again, with 'git' available.  This should work.
+path5=$(nix eval --raw "(builtins.fetchGit { url = $repo; ref = \"dev\"; }).outPath")
+[[ $path3 = $path5 ]]
diff --git a/third_party/nix/tests/fetchMercurial.sh b/third_party/nix/tests/fetchMercurial.sh
new file mode 100644
index 000000000000..4088dbd39796
--- /dev/null
+++ b/third_party/nix/tests/fetchMercurial.sh
@@ -0,0 +1,93 @@
+source common.sh
+
+if [[ -z $(type -p hg) ]]; then
+    echo "Mercurial not installed; skipping Mercurial tests"
+    exit 99
+fi
+
+clearStore
+
+repo=$TEST_ROOT/hg
+
+rm -rf $repo ${repo}-tmp $TEST_HOME/.cache/nix/hg
+
+hg init $repo
+echo '[ui]' >> $repo/.hg/hgrc
+echo 'username = Foobar <foobar@example.org>' >> $repo/.hg/hgrc
+
+echo utrecht > $repo/hello
+touch $repo/.hgignore
+hg add --cwd $repo hello .hgignore
+hg commit --cwd $repo -m 'Bla1'
+rev1=$(hg log --cwd $repo -r tip --template '{node}')
+
+echo world > $repo/hello
+hg commit --cwd $repo -m 'Bla2'
+rev2=$(hg log --cwd $repo -r tip --template '{node}')
+
+# Fetch the default branch.
+path=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
+[[ $(cat $path/hello) = world ]]
+
+# In pure eval mode, fetchGit without a revision should fail.
+[[ $(nix eval --raw "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))") = world ]]
+(! nix eval --pure-eval --raw "(builtins.readFile (fetchMercurial file://$repo + \"/hello\"))")
+
+# Fetch using an explicit revision hash.
+path2=$(nix eval --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath")
+[[ $path = $path2 ]]
+
+# In pure eval mode, fetchGit with a revision should succeed.
+[[ $(nix eval --pure-eval --raw "(builtins.readFile (fetchMercurial { url = file://$repo; rev = \"$rev2\"; } + \"/hello\"))") = world ]]
+
+# Fetch again. This should be cached.
+mv $repo ${repo}-tmp
+path2=$(nix eval --raw "(builtins.fetchMercurial file://$repo).outPath")
+[[ $path = $path2 ]]
+
+[[ $(nix eval --raw "(builtins.fetchMercurial file://$repo).branch") = default ]]
+[[ $(nix eval "(builtins.fetchMercurial file://$repo).revCount") = 1 ]]
+[[ $(nix eval --raw "(builtins.fetchMercurial file://$repo).rev") = $rev2 ]]
+
+# But with TTL 0, it should fail.
+(! nix eval --tarball-ttl 0 "(builtins.fetchMercurial file://$repo)")
+
+# Fetching with a explicit hash should succeed.
+path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev2\"; }).outPath")
+[[ $path = $path2 ]]
+
+path2=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial { url = file://$repo; rev = \"$rev1\"; }).outPath")
+[[ $(cat $path2/hello) = utrecht ]]
+
+mv ${repo}-tmp $repo
+
+# Using a clean working tree should produce the same result.
+path2=$(nix eval --raw "(builtins.fetchMercurial $repo).outPath")
+[[ $path = $path2 ]]
+
+# Using an unclean tree should yield the tracked but uncommitted changes.
+mkdir $repo/dir1 $repo/dir2
+echo foo > $repo/dir1/foo
+echo bar > $repo/bar
+echo bar > $repo/dir2/bar
+hg add --cwd $repo dir1/foo
+hg rm --cwd $repo hello
+
+path2=$(nix eval --raw "(builtins.fetchMercurial $repo).outPath")
+[ ! -e $path2/hello ]
+[ ! -e $path2/bar ]
+[ ! -e $path2/dir2/bar ]
+[ ! -e $path2/.hg ]
+[[ $(cat $path2/dir1/foo) = foo ]]
+
+[[ $(nix eval --raw "(builtins.fetchMercurial $repo).rev") = 0000000000000000000000000000000000000000 ]]
+
+# ... unless we're using an explicit rev.
+path3=$(nix eval --raw "(builtins.fetchMercurial { url = $repo; rev = \"default\"; }).outPath")
+[[ $path = $path3 ]]
+
+# Committing should not affect the store path.
+hg commit --cwd $repo -m 'Bla3'
+
+path4=$(nix eval --tarball-ttl 0 --raw "(builtins.fetchMercurial file://$repo).outPath")
+[[ $path2 = $path4 ]]
diff --git a/third_party/nix/tests/fetchurl.sh b/third_party/nix/tests/fetchurl.sh
new file mode 100644
index 000000000000..7319ced2b599
--- /dev/null
+++ b/third_party/nix/tests/fetchurl.sh
@@ -0,0 +1,78 @@
+source common.sh
+
+clearStore
+
+# Test fetching a flat file.
+hash=$(nix-hash --flat --type sha256 ./fetchurl.sh)
+
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha256 $hash --no-out-link --hashed-mirrors '')
+
+cmp $outPath fetchurl.sh
+
+# Now using a base-64 hash.
+clearStore
+
+hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh)
+
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr sha512 $hash --no-out-link --hashed-mirrors '')
+
+cmp $outPath fetchurl.sh
+
+# Now using an SRI hash.
+clearStore
+
+hash=$(nix hash-file ./fetchurl.sh)
+
+[[ $hash =~ ^sha256- ]]
+
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$(pwd)/fetchurl.sh --argstr hash $hash --no-out-link --hashed-mirrors '')
+
+cmp $outPath fetchurl.sh
+
+# Test the hashed mirror feature.
+clearStore
+
+hash=$(nix hash-file --type sha512 --base64 ./fetchurl.sh)
+hash32=$(nix hash-file --type sha512 --base16 ./fetchurl.sh)
+
+mirror=$TMPDIR/hashed-mirror
+rm -rf $mirror
+mkdir -p $mirror/sha512
+ln -s $(pwd)/fetchurl.sh $mirror/sha512/$hash32
+
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr sha512 $hash --no-out-link --hashed-mirrors "file://$mirror")
+
+# Test hashed mirrors with an SRI hash.
+nix-build '<nix/fetchurl.nix>' --argstr url file:///no-such-dir/fetchurl.sh --argstr hash $(nix to-sri --type sha512 $hash) \
+          --argstr name bla --no-out-link --hashed-mirrors "file://$mirror"
+
+# Test unpacking a NAR.
+rm -rf $TEST_ROOT/archive
+mkdir -p $TEST_ROOT/archive
+cp ./fetchurl.sh $TEST_ROOT/archive
+chmod +x $TEST_ROOT/archive/fetchurl.sh
+ln -s foo $TEST_ROOT/archive/symlink
+nar=$TEST_ROOT/archive.nar
+nix-store --dump $TEST_ROOT/archive > $nar
+
+hash=$(nix-hash --flat --type sha256 $nar)
+
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$nar --argstr sha256 $hash \
+          --arg unpack true --argstr name xyzzy --no-out-link)
+
+echo $outPath | grep -q 'xyzzy'
+
+test -x $outPath/fetchurl.sh
+test -L $outPath/symlink
+
+nix-store --delete $outPath
+
+# Test unpacking a compressed NAR.
+narxz=$TEST_ROOT/archive.nar.xz
+rm -f $narxz
+xz --keep $nar
+outPath=$(nix-build '<nix/fetchurl.nix>' --argstr url file://$narxz --argstr sha256 $hash \
+          --arg unpack true --argstr name xyzzy --no-out-link)
+
+test -x $outPath/fetchurl.sh
+test -L $outPath/symlink
diff --git a/third_party/nix/tests/filter-source.nix b/third_party/nix/tests/filter-source.nix
new file mode 100644
index 000000000000..9071636394af
--- /dev/null
+++ b/third_party/nix/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 ((builtins.getEnv "TEST_ROOT") + "/filterin");
+}
diff --git a/third_party/nix/tests/filter-source.sh b/third_party/nix/tests/filter-source.sh
new file mode 100644
index 000000000000..1f8dceee5786
--- /dev/null
+++ b/third_party/nix/tests/filter-source.sh
@@ -0,0 +1,19 @@
+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
+
+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/third_party/nix/tests/fixed.builder1.sh b/third_party/nix/tests/fixed.builder1.sh
new file mode 100644
index 000000000000..c41bb2b9a611
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/fixed.builder2.sh b/third_party/nix/tests/fixed.builder2.sh
new file mode 100644
index 000000000000..31ea1579a514
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/fixed.nix b/third_party/nix/tests/fixed.nix
new file mode 100644
index 000000000000..76580ffa19e8
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/fixed.sh b/third_party/nix/tests/fixed.sh
new file mode 100644
index 000000000000..8f51403a7071
--- /dev/null
+++ b/third_party/nix/tests/fixed.sh
@@ -0,0 +1,56 @@
+source common.sh
+
+clearStore
+
+export IMPURE_VAR1=foo
+export IMPURE_VAR2=bar
+
+path=$(nix-store -q $(nix-instantiate fixed.nix -A good.0))
+
+echo 'testing bad...'
+nix-build fixed.nix -A bad --no-out-link && fail "should fail"
+
+# Building with the bad hash should produce the "good" output path as
+# a side-effect.
+[[ -e $path ]]
+nix path-info --json $path | grep fixed:md5:2qk15sxzzjlnpjk9brn7j8ppcd
+
+echo 'testing good...'
+nix-build fixed.nix -A good --no-out-link
+
+echo 'testing good2...'
+nix-build fixed.nix -A good2 --no-out-link
+
+echo 'testing reallyBad...'
+nix-instantiate fixed.nix -A reallyBad && fail "should fail"
+
+# While we're at it, check attribute selection a bit more.
+echo 'testing attribute selection...'
+test $(nix-instantiate fixed.nix -A good.1 | wc -l) = 1
+
+# Test parallel builds of derivations that produce the same output.
+# Only one should run at the same time.
+echo 'testing parallelSame...'
+clearStore
+nix-build fixed.nix -A parallelSame --no-out-link -j2
+
+# Fixed-output derivations with a recursive SHA-256 hash should
+# produce the same path as "nix-store --add".
+echo 'testing sameAsAdd...'
+out=$(nix-build fixed.nix -A sameAsAdd --no-out-link)
+
+# This is what fixed.builder2 produces...
+rm -rf $TEST_ROOT/fixed
+mkdir $TEST_ROOT/fixed
+mkdir $TEST_ROOT/fixed/bla
+echo "Hello World!" > $TEST_ROOT/fixed/foo
+ln -s foo $TEST_ROOT/fixed/bar
+
+out2=$(nix-store --add $TEST_ROOT/fixed)
+[ "$out" = "$out2" ]
+
+out3=$(nix-store --add-fixed --recursive sha256 $TEST_ROOT/fixed)
+[ "$out" = "$out3" ]
+
+out4=$(nix-store --print-fixed-path --recursive sha256 "1ixr6yd3297ciyp9im522dfxpqbkhcw0pylkb2aab915278fqaik" fixed)
+[ "$out" = "$out4" ]
diff --git a/third_party/nix/tests/function-trace.sh b/third_party/nix/tests/function-trace.sh
new file mode 100755
index 000000000000..182a4d5c287a
--- /dev/null
+++ b/third_party/nix/tests/function-trace.sh
@@ -0,0 +1,85 @@
+source common.sh
+
+set +x
+
+expect_trace() {
+    expr="$1"
+    expect="$2"
+    actual=$(
+        nix-instantiate \
+            --trace-function-calls \
+            --expr "$expr" 2>&1 \
+            | grep "function-trace" \
+            | sed -e 's/ [0-9]*$//'
+    );
+
+    echo -n "Tracing expression '$expr'"
+    set +e
+    msg=$(diff -swB \
+               <(echo "$expect") \
+               <(echo "$actual")
+    );
+    result=$?
+    set -e
+    if [ $result -eq 0 ]; then
+        echo " ok."
+    else
+        echo " failed. difference:"
+        echo "$msg"
+        return $result
+    fi
+}
+
+# failure inside a tryEval
+expect_trace 'builtins.tryEval (throw "example")' "
+function-trace entered undefined position at
+function-trace exited undefined position at
+function-trace entered (string):1:1 at
+function-trace entered (string):1:19 at
+function-trace exited (string):1:19 at
+function-trace exited (string):1:1 at
+"
+
+# Missing argument to a formal function
+expect_trace '({ x }: x) { }' "
+function-trace entered undefined position at
+function-trace exited undefined position at
+function-trace entered (string):1:1 at
+function-trace exited (string):1:1 at
+"
+
+# Too many arguments to a formal function
+expect_trace '({ x }: x) { x = "x"; y = "y"; }' "
+function-trace entered undefined position at
+function-trace exited undefined position at
+function-trace entered (string):1:1 at
+function-trace exited (string):1:1 at
+"
+
+# Not enough arguments to a lambda
+expect_trace '(x: y: x + y) 1' "
+function-trace entered undefined position at
+function-trace exited undefined position at
+function-trace entered (string):1:1 at
+function-trace exited (string):1:1 at
+"
+
+# Too many arguments to a lambda
+expect_trace '(x: x) 1 2' "
+function-trace entered undefined position at
+function-trace exited undefined position at
+function-trace entered (string):1:1 at
+function-trace exited (string):1:1 at
+function-trace entered (string):1:1 at
+function-trace exited (string):1:1 at
+"
+
+# Not a function
+expect_trace '1 2' "
+function-trace entered undefined position at
+function-trace exited undefined position at
+function-trace entered (string):1:1 at
+function-trace exited (string):1:1 at
+"
+
+set -e
diff --git a/third_party/nix/tests/gc-auto.sh b/third_party/nix/tests/gc-auto.sh
new file mode 100644
index 000000000000..de1e2cfe4059
--- /dev/null
+++ b/third_party/nix/tests/gc-auto.sh
@@ -0,0 +1,70 @@
+source common.sh
+
+clearStore
+
+garbage1=$(nix add-to-store --name garbage1 ./nar-access.sh)
+garbage2=$(nix add-to-store --name garbage2 ./nar-access.sh)
+garbage3=$(nix add-to-store --name garbage3 ./nar-access.sh)
+
+ls -l $garbage3
+POSIXLY_CORRECT=1 du $garbage3
+
+fake_free=$TEST_ROOT/fake-free
+export _NIX_TEST_FREE_SPACE_FILE=$fake_free
+echo 1100 > $fake_free
+
+expr=$(cat <<EOF
+with import ./config.nix; mkDerivation {
+  name = "gc-A";
+  buildCommand = ''
+    set -x
+    [[ \$(ls \$NIX_STORE/*-garbage? | wc -l) = 3 ]]
+    mkdir \$out
+    echo foo > \$out/bar
+    echo 1...
+    sleep 2
+    echo 200 > ${fake_free}.tmp1
+    mv ${fake_free}.tmp1 $fake_free
+    echo 2...
+    sleep 2
+    echo 3...
+    sleep 2
+    echo 4...
+    [[ \$(ls \$NIX_STORE/*-garbage? | wc -l) = 1 ]]
+  '';
+}
+EOF
+)
+
+expr2=$(cat <<EOF
+with import ./config.nix; mkDerivation {
+  name = "gc-B";
+  buildCommand = ''
+    set -x
+    mkdir \$out
+    echo foo > \$out/bar
+    echo 1...
+    sleep 2
+    echo 200 > ${fake_free}.tmp2
+    mv ${fake_free}.tmp2 $fake_free
+    echo 2...
+    sleep 2
+    echo 3...
+    sleep 2
+    echo 4...
+  '';
+}
+EOF
+)
+
+nix build -v -o $TEST_ROOT/result-A -L "($expr)" \
+    --min-free 1000 --max-free 2000 --min-free-check-interval 1 &
+pid=$!
+
+nix build -v -o $TEST_ROOT/result-B -L "($expr2)" \
+    --min-free 1000 --max-free 2000 --min-free-check-interval 1
+
+wait "$pid"
+
+[[ foo = $(cat $TEST_ROOT/result-A/bar) ]]
+[[ foo = $(cat $TEST_ROOT/result-B/bar) ]]
diff --git a/third_party/nix/tests/gc-concurrent.builder.sh b/third_party/nix/tests/gc-concurrent.builder.sh
new file mode 100644
index 000000000000..0cd67df3aeda
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/gc-concurrent.nix b/third_party/nix/tests/gc-concurrent.nix
new file mode 100644
index 000000000000..c0595cc471b9
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/gc-concurrent.sh b/third_party/nix/tests/gc-concurrent.sh
new file mode 100644
index 000000000000..d395930ca0dc
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/gc-concurrent2.builder.sh b/third_party/nix/tests/gc-concurrent2.builder.sh
new file mode 100644
index 000000000000..4bfb33103e73
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/gc-runtime.nix b/third_party/nix/tests/gc-runtime.nix
new file mode 100644
index 000000000000..ee5980bdff98
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/gc-runtime.sh b/third_party/nix/tests/gc-runtime.sh
new file mode 100644
index 000000000000..4c5028005c57
--- /dev/null
+++ b/third_party/nix/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 -rf $profiles
+
+nix-env -p $profiles/test -f ./gc-runtime.nix -i gc-runtime
+
+outPath=$(nix-env -p $profiles/test -q --no-name --out-path gc-runtime)
+echo $outPath
+
+echo "backgrounding program..."
+$profiles/test/program &
+sleep 2 # hack - wait for the program to get started
+child=$!
+echo PID=$child
+
+nix-env -p $profiles/test -e gc-runtime
+nix-env -p $profiles/test --delete-generations old
+
+nix-store --gc
+
+kill -- -$child
+
+if ! test -e $outPath; then
+    echo "running program was garbage collected!"
+    exit 1
+fi
+
+exit 0
diff --git a/third_party/nix/tests/gc.sh b/third_party/nix/tests/gc.sh
new file mode 100644
index 000000000000..8b4f8d282184
--- /dev/null
+++ b/third_party/nix/tests/gc.sh
@@ -0,0 +1,40 @@
+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 -q --roots $outPath)" = "$NIX_STATE_DIR/gcroots/foo -> $outPath" ]
+
+nix-store --gc --print-roots | grep $outPath
+nix-store --gc --print-live | grep $outPath
+nix-store --gc --print-dead | grep $drvPath
+if nix-store --gc --print-dead | grep $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/third_party/nix/tests/hash-check.nix b/third_party/nix/tests/hash-check.nix
new file mode 100644
index 000000000000..4a8e9b8a8df9
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/hash.sh b/third_party/nix/tests/hash.sh
new file mode 100644
index 000000000000..4cfc97901012
--- /dev/null
+++ b/third_party/nix/tests/hash.sh
@@ -0,0 +1,87 @@
+source common.sh
+
+try () {
+    printf "%s" "$2" > $TEST_ROOT/vector
+    hash=$(nix hash-file --base16 $EXTRA --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 "" "da39a3ee5e6b4b0d3255bfef95601890afd80709"
+try sha1 "abc" "a9993e364706816aba3e25717850c26c9cd0d89d"
+try sha1 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "84983e441c3bd26ebaae4aa1f95129e5e54670f1"
+
+try sha256 "" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
+try sha256 "abc" "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
+try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
+
+try sha512 "" "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
+try sha512 "abc" "ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
+try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445"
+
+EXTRA=--base32
+try sha256 "abc" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s"
+EXTRA=
+
+EXTRA=--sri
+try sha512 "" "sha512-z4PhNX7vuL3xVChQ1m2AB9Yg5AULVxXcg/SpIdNs6c5H0NE8XYXysP+DGNKHfuwvY7kxvUdBeoGlODJ6+SfaPg=="
+try sha512 "abc" "sha512-3a81oZNherrMQXNJriBBMRLm+k6JqX6iCp7u5ktV05ohkpkqJ0/BqDa6PCOj/uu9RU1EI2Q86A4qmslPpUyknw=="
+try sha512 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha512-IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
+try sha256 "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" "sha256-JI1qYdIGOLjlwCaTDD5gOaM85Flk/yFn9uzt1BnbBsE="
+
+try2 () {
+    hash=$(nix-hash --type "$1" $TEST_ROOT/hash-path)
+    if test "$hash" != "$2"; then
+        echo "hash $1, expected $2, got $hash"
+        exit 1
+    fi
+}
+
+rm -rf $TEST_ROOT/hash-path
+mkdir $TEST_ROOT/hash-path
+echo "Hello World" > $TEST_ROOT/hash-path/hello
+
+try2 md5 "ea9b55537dd4c7e104515b2ccfaf4100"
+
+# Execute bit matters.
+chmod +x $TEST_ROOT/hash-path/hello
+try2 md5 "20f3ffe011d4cfa7d72bfabef7882836"
+
+# Mtime and other bits don't.
+touch -r . $TEST_ROOT/hash-path/hello
+chmod 744 $TEST_ROOT/hash-path/hello
+try2 md5 "20f3ffe011d4cfa7d72bfabef7882836"
+
+# File type (e.g., symlink) does.
+rm $TEST_ROOT/hash-path/hello
+ln -s x $TEST_ROOT/hash-path/hello
+try2 md5 "f78b733a68f5edbdf9413899339eaa4a"
+
+# Conversion.
+try3() {
+    h64=$(nix to-base64 --type "$1" "$2")
+    [ "$h64" = "$4" ]
+    sri=$(nix to-sri --type "$1" "$2")
+    [ "$sri" = "$1-$4" ]
+    h32=$(nix-hash --type "$1" --to-base32 "$2")
+    [ "$h32" = "$3" ]
+    h16=$(nix-hash --type "$1" --to-base16 "$h32")
+    [ "$h16" = "$2" ]
+    h16=$(nix to-base16 --type "$1" "$h64")
+    [ "$h16" = "$2" ]
+    h16=$(nix to-base16 "$sri")
+    [ "$h16" = "$2" ]
+}
+try3 sha1 "800d59cfcd3c05e900cb4e214be48f6b886a08df" "vw46m23bizj4n8afrc0fj19wrp7mj3c0" "gA1Zz808BekAy04hS+SPa4hqCN8="
+try3 sha256 "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad" "1b8m03r63zqhnjf7l5wnldhh7c134ap5vpj0850ymkq1iyzicy5s" "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0="
+try3 sha512 "204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445" "12k9jiq29iyqm03swfsgiw5mlqs173qazm3n7daz43infy12pyrcdf30fkk3qwv4yl2ick8yipc2mqnlh48xsvvxl60lbx8vp38yji0" "IEqPxt2oLwoM7XvrjgikFlfBbvRosiioJ5vjMacDwzWW/RXBOxsH+aodO+pXeJygMa2Fx6cd1wNU7GMSOMo0RQ=="
diff --git a/third_party/nix/tests/import-derivation.nix b/third_party/nix/tests/import-derivation.nix
new file mode 100644
index 000000000000..44fa9a45d7e1
--- /dev/null
+++ b/third_party/nix/tests/import-derivation.nix
@@ -0,0 +1,26 @@
+with import ./config.nix;
+
+let
+
+  bar = mkDerivation {
+    name = "bar";
+    builder = builtins.toFile "builder.sh"
+      ''
+        echo 'builtins.add 123 456' > $out
+      '';
+  };
+
+  value =
+    # Test that pathExists can check the existence of /nix/store paths
+    assert builtins.pathExists bar;
+    import bar;
+
+in
+
+mkDerivation {
+  name = "foo";
+  builder = builtins.toFile "builder.sh"
+    ''
+      echo -n FOO${toString value} > $out
+    '';
+}
diff --git a/third_party/nix/tests/import-derivation.sh b/third_party/nix/tests/import-derivation.sh
new file mode 100644
index 000000000000..98d61ef49b9c
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/init.sh b/third_party/nix/tests/init.sh
new file mode 100644
index 000000000000..19a12c1e2d9e
--- /dev/null
+++ b/third_party/nix/tests/init.sh
@@ -0,0 +1,34 @@
+source common.sh
+
+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_CONF_DIR"
+
+cat > "$NIX_CONF_DIR"/nix.conf <<EOF
+build-users-group =
+keep-derivations = false
+sandbox = false
+include nix.conf.extra
+EOF
+
+cat > "$NIX_CONF_DIR"/nix.conf.extra <<EOF
+fsync-metadata = false
+!include nix.conf.extra.not-there
+EOF
+
+# Initialise the database.
+nix-store --init
+
+# Did anything happen?
+test -e "$NIX_STATE_DIR"/db/db.sqlite
+
+echo 'Hello World' > ./dummy
diff --git a/third_party/nix/tests/install-darwin.sh b/third_party/nix/tests/install-darwin.sh
new file mode 100755
index 000000000000..9933eba94431
--- /dev/null
+++ b/third_party/nix/tests/install-darwin.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+set -eux
+
+cleanup() {
+    PLIST="/Library/LaunchDaemons/org.nixos.nix-daemon.plist"
+    if sudo launchctl list | grep -q nix-daemon; then
+        sudo launchctl unload "$PLIST"
+    fi
+
+    if [ -f "$PLIST" ]; then
+        sudo rm /Library/LaunchDaemons/org.nixos.nix-daemon.plist
+    fi
+
+    profiles=(/etc/profile /etc/bashrc /etc/zshrc)
+    for profile in "${profiles[@]}"; do
+        if [ -f "${profile}.backup-before-nix" ]; then
+            sudo mv "${profile}.backup-before-nix" "${profile}"
+        fi
+    done
+
+    for file in ~/.bash_profile ~/.bash_login ~/.profile ~/.zshenv ~/.zprofile ~/.zshrc ~/.zlogin; do
+        if [ -e "$file" ]; then
+            cat "$file" | grep -v nix-profile > "$file.next"
+            mv "$file.next" "$file"
+        fi
+    done
+
+    for i in $(seq 1 $(sysctl -n hw.ncpu)); do
+        sudo /usr/bin/dscl . -delete "/Users/nixbld$i" || true
+    done
+    sudo /usr/bin/dscl . -delete "/Groups/nixbld" || true
+
+    sudo rm -rf /etc/nix \
+         /nix \
+         /var/root/.nix-profile /var/root/.nix-defexpr /var/root/.nix-channels \
+         "$HOME/.nix-profile" "$HOME/.nix-defexpr" "$HOME/.nix-channels"
+}
+
+verify() {
+    set +e
+    output=$(echo "nix-shell -p bash --run 'echo toow | rev'" | bash -l)
+    set -e
+
+    test "$output" = "woot"
+}
+
+scratch=$(mktemp -d -t tmp.XXXXXXXXXX)
+function finish {
+    rm -rf "$scratch"
+}
+trap finish EXIT
+
+# First setup Nix
+cleanup
+curl -o install https://nixos.org/nix/install
+yes | bash ./install
+verify
+
+
+(
+    set +e
+    (
+        echo "cd $(pwd)"
+        echo nix-build ./release.nix -A binaryTarball.x86_64-darwin
+    ) | bash -l
+    set -e
+    cp ./result/nix-*.tar.bz2 $scratch/nix.tar.bz2
+)
+
+(
+    cd $scratch
+    tar -xf ./nix.tar.bz2
+
+    cd nix-*
+
+    set -eux
+
+    cleanup
+
+    yes | ./install
+    verify
+    cleanup
+
+    echo -n "" | ./install
+    verify
+    cleanup
+
+    sudo mkdir -p /nix/store
+    sudo touch /nix/store/.silly-hint
+    echo -n "" | ALLOW_PREEXISTING_INSTALLATION=true ./install
+    verify
+    test -e /nix/store/.silly-hint
+
+    cleanup
+)
diff --git a/third_party/nix/tests/lang.sh b/third_party/nix/tests/lang.sh
new file mode 100644
index 000000000000..151a71316683
--- /dev/null
+++ b/third_party/nix/tests/lang.sh
@@ -0,0 +1,68 @@
+export TEST_VAR=foo # for eval-okay-getenv.nix
+
+nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>&1 | grep -q Hello
+(! nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grep -q Hello)
+nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' 2>&1 | grep -q Hello
+
+set +x
+
+fail=0
+
+for i in lang/parse-fail-*.nix; do
+    echo "parsing $i (should fail)";
+    i=$(basename $i .nix)
+    if nix-instantiate --parse - < 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 - < 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 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 nix-instantiate $flags --eval --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 --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/third_party/nix/tests/linux-sandbox.sh b/third_party/nix/tests/linux-sandbox.sh
new file mode 100644
index 000000000000..52967d07dda2
--- /dev/null
+++ b/third_party/nix/tests/linux-sandbox.sh
@@ -0,0 +1,30 @@
+source common.sh
+
+clearStore
+
+if ! canUseSandbox; then exit; fi
+
+# Note: we need to bind-mount $SHELL into the chroot. Currently we
+# only support the case where $SHELL is in the Nix store, because
+# otherwise things get complicated (e.g. if it's in /bin, do we need
+# /lib as well?).
+if [[ ! $SHELL =~ /nix/store ]]; then exit; fi
+
+chmod -R u+w $TEST_ROOT/store0 || true
+rm -rf $TEST_ROOT/store0
+
+export NIX_STORE_DIR=/my/store
+export NIX_REMOTE=$TEST_ROOT/store0
+
+outPath=$(nix-build dependencies.nix --no-out-link --sandbox-paths /nix/store)
+
+[[ $outPath =~ /my/store/.*-dependencies ]]
+
+nix path-info -r $outPath | grep input-2
+
+nix ls-store -R -l $outPath | grep foobar
+
+nix cat-store $outPath/foobar | grep FOOBAR
+
+# Test --check without hash rewriting.
+nix-build dependencies.nix --no-out-link --check --sandbox-paths /nix/store
diff --git a/third_party/nix/tests/logging.sh b/third_party/nix/tests/logging.sh
new file mode 100644
index 000000000000..c894ad3ff079
--- /dev/null
+++ b/third_party/nix/tests/logging.sh
@@ -0,0 +1,15 @@
+source common.sh
+
+clearStore
+
+path=$(nix-build dependencies.nix --no-out-link)
+
+# Test nix-store -l.
+[ "$(nix-store -l $path)" = FOO ]
+
+# Test compressed logs.
+clearStore
+rm -rf $NIX_LOG_DIR
+(! nix-store -l $path)
+nix-build dependencies.nix --no-out-link --compress-build-log
+[ "$(nix-store -l $path)" = FOO ]
diff --git a/third_party/nix/tests/misc.sh b/third_party/nix/tests/misc.sh
new file mode 100644
index 000000000000..eda0164167f2
--- /dev/null
+++ b/third_party/nix/tests/misc.sh
@@ -0,0 +1,19 @@
+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
+#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"
+
+# Eval Errors.
+nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 | grep "infinite recursion encountered, at .*(string).*:1:15$"
diff --git a/third_party/nix/tests/multiple-outputs.nix b/third_party/nix/tests/multiple-outputs.nix
new file mode 100644
index 000000000000..4a9010d1868e
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/multiple-outputs.sh b/third_party/nix/tests/multiple-outputs.sh
new file mode 100644
index 000000000000..bedbc39a4ebf
--- /dev/null
+++ b/third_party/nix/tests/multiple-outputs.sh
@@ -0,0 +1,76 @@
+source common.sh
+
+clearStore
+
+rm -f $TEST_ROOT/result*
+
+# 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.
+outPath1=$(nix-build multiple-outputs.nix -A a -o $TEST_ROOT/result)
+[ -e $TEST_ROOT/result-first ]
+(! [ -e $TEST_ROOT/result-second ])
+nix-build multiple-outputs.nix -A a.all -o $TEST_ROOT/result
+[ "$(cat $TEST_ROOT/result-first/file)" = "first" ]
+[ "$(cat $TEST_ROOT/result-second/file)" = "second" ]
+[ "$(cat $TEST_ROOT/result-second/link/file)" = "first" ]
+hash1=$(nix-store -q --hash $TEST_ROOT/result-second)
+
+outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a) --no-out-link)
+[[ $outPath1 = $outPath2 ]]
+
+outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a.first) --no-out-link)
+[[ $outPath1 = $outPath2 ]]
+
+outPath2=$(nix-build $(nix-instantiate multiple-outputs.nix -A a.second) --no-out-link)
+[[ $(cat $outPath2/file) = second ]]
+
+[[ $(nix-build $(nix-instantiate multiple-outputs.nix -A a.all) --no-out-link | wc -l) -eq 2 ]]
+
+# Delete one of the outputs and rebuild it.  This will cause a hash
+# rewrite.
+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 --keep-derivations --keep-outputs
+nix-store --gc --print-roots
diff --git a/third_party/nix/tests/nar-access.nix b/third_party/nix/tests/nar-access.nix
new file mode 100644
index 000000000000..0e2a7f721135
--- /dev/null
+++ b/third_party/nix/tests/nar-access.nix
@@ -0,0 +1,23 @@
+with import ./config.nix;
+
+rec {
+    a = mkDerivation {
+        name = "nar-index-a";
+        builder = builtins.toFile "builder.sh"
+      ''
+        mkdir $out
+        mkdir $out/foo
+        touch $out/foo-x
+        touch $out/foo/bar
+        touch $out/foo/baz
+        touch $out/qux
+        mkdir $out/zyx
+
+        cat >$out/foo/data <<EOF
+        lasjdöaxnasd
+asdom 12398
+ä"§Æẞ¢«»”alsd
+EOF
+      '';
+    };
+}
\ No newline at end of file
diff --git a/third_party/nix/tests/nar-access.sh b/third_party/nix/tests/nar-access.sh
new file mode 100644
index 000000000000..553d6ca89d7d
--- /dev/null
+++ b/third_party/nix/tests/nar-access.sh
@@ -0,0 +1,44 @@
+source common.sh
+
+echo "building test path"
+storePath="$(nix-build nar-access.nix -A a --no-out-link)"
+
+cd "$TEST_ROOT"
+
+# Dump path to nar.
+narFile="$TEST_ROOT/path.nar"
+nix-store --dump $storePath > $narFile
+
+# Check that find and ls-nar match.
+( cd $storePath; find . | sort ) > files.find
+nix ls-nar -R -d $narFile "" | sort > files.ls-nar
+diff -u files.find files.ls-nar
+
+# Check that file contents of data match.
+nix cat-nar $narFile /foo/data > data.cat-nar
+diff -u data.cat-nar $storePath/foo/data
+
+# Check that file contents of baz match.
+nix cat-nar $narFile /foo/baz > baz.cat-nar
+diff -u baz.cat-nar $storePath/foo/baz
+
+nix cat-store $storePath/foo/baz > baz.cat-nar
+diff -u baz.cat-nar $storePath/foo/baz
+
+# Test --json.
+[[ $(nix ls-nar --json $narFile /) = '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' ]]
+[[ $(nix ls-nar --json -R $narFile /foo) = '{"type":"directory","entries":{"bar":{"type":"regular","size":0,"narOffset":368},"baz":{"type":"regular","size":0,"narOffset":552},"data":{"type":"regular","size":58,"narOffset":736}}}' ]]
+[[ $(nix ls-nar --json -R $narFile /foo/bar) = '{"type":"regular","size":0,"narOffset":368}' ]]
+[[ $(nix ls-store --json $storePath) = '{"type":"directory","entries":{"foo":{},"foo-x":{},"qux":{},"zyx":{}}}' ]]
+[[ $(nix ls-store --json -R $storePath/foo) = '{"type":"directory","entries":{"bar":{"type":"regular","size":0},"baz":{"type":"regular","size":0},"data":{"type":"regular","size":58}}}' ]]
+[[ $(nix ls-store --json -R $storePath/foo/bar) = '{"type":"regular","size":0}' ]]
+
+# Test missing files.
+nix ls-store --json -R $storePath/xyzzy 2>&1 | grep 'does not exist in NAR'
+nix ls-store $storePath/xyzzy 2>&1 | grep 'does not exist'
+
+# Test failure to dump.
+if nix-store --dump $storePath >/dev/full ; then
+    echo "dumping to /dev/full should fail"
+    exit -1
+fi
diff --git a/third_party/nix/tests/nix-build.sh b/third_party/nix/tests/nix-build.sh
new file mode 100644
index 000000000000..395264863196
--- /dev/null
+++ b/third_party/nix/tests/nix-build.sh
@@ -0,0 +1,25 @@
+source common.sh
+
+clearStore
+
+outPath=$(nix-build dependencies.nix -o $TEST_ROOT/result)
+test "$(cat $TEST_ROOT/result/foobar)" = FOOBAR
+
+# The result should be retained by a GC.
+echo A
+target=$(readLink $TEST_ROOT/result)
+echo B
+echo target is $target
+nix-store --gc
+test -e $target/foobar
+
+# But now it should be gone.
+rm $TEST_ROOT/result
+nix-store --gc
+if test -e $target/foobar; then false; fi
+
+outPath2=$(nix-build $(nix-instantiate dependencies.nix) --no-out-link)
+[[ $outPath = $outPath2 ]]
+
+outPath2=$(nix-build $(nix-instantiate dependencies.nix)!out --no-out-link)
+[[ $outPath = $outPath2 ]]
diff --git a/third_party/nix/tests/nix-channel.sh b/third_party/nix/tests/nix-channel.sh
new file mode 100644
index 000000000000..93f837befcec
--- /dev/null
+++ b/third_party/nix/tests/nix-channel.sh
@@ -0,0 +1,59 @@
+source common.sh
+
+clearProfiles
+
+rm -f $TEST_HOME/.nix-channels $TEST_HOME/.nix-profile
+
+# Test add/list/remove.
+nix-channel --add http://foo/bar xyzzy
+nix-channel --list | grep -q http://foo/bar
+nix-channel --remove xyzzy
+
+[ -e $TEST_HOME/.nix-channels ]
+[ "$(cat $TEST_HOME/.nix-channels)" = '' ]
+
+# Create a channel.
+rm -rf $TEST_ROOT/foo
+mkdir -p $TEST_ROOT/foo
+nix copy --to file://$TEST_ROOT/foo?compression="bzip2" $(nix-store -r $(nix-instantiate dependencies.nix))
+rm -rf $TEST_ROOT/nixexprs
+mkdir -p $TEST_ROOT/nixexprs
+cp config.nix dependencies.nix dependencies.builder*.sh $TEST_ROOT/nixexprs/
+ln -s dependencies.nix $TEST_ROOT/nixexprs/default.nix
+(cd $TEST_ROOT && tar cvf - nixexprs) | bzip2 > $TEST_ROOT/foo/nixexprs.tar.bz2
+
+# Test the update action.
+nix-channel --add file://$TEST_ROOT/foo
+nix-channel --update
+
+# 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_HOME/.nix-profile/foobar ]
+
+clearProfiles
+rm -f $TEST_HOME/.nix-channels
+
+# Test updating from a tarball
+nix-channel --add file://$TEST_ROOT/foo/nixexprs.tar.bz2 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_HOME/.nix-profile/foobar ]
+
diff --git a/third_party/nix/tests/nix-copy-closure.nix b/third_party/nix/tests/nix-copy-closure.nix
new file mode 100644
index 000000000000..0dc147fb34e9
--- /dev/null
+++ b/third_party/nix/tests/nix-copy-closure.nix
@@ -0,0 +1,64 @@
+# Test ‘nix-copy-closure’.
+
+{ nixpkgs, system, nix }:
+
+with import (nixpkgs + "/nixos/lib/testing.nix") { inherit system; };
+
+makeTest (let pkgA = pkgs.cowsay; pkgB = pkgs.wget; pkgC = pkgs.hello; in {
+
+  nodes =
+    { client =
+        { config, pkgs, ... }:
+        { virtualisation.writableStore = true;
+          virtualisation.pathsInNixDB = [ pkgA ];
+          nix.package = nix;
+          nix.binaryCaches = [ ];
+        };
+
+      server =
+        { config, pkgs, ... }:
+        { services.openssh.enable = true;
+          virtualisation.writableStore = true;
+          virtualisation.pathsInNixDB = [ pkgB pkgC ];
+          nix.package = nix;
+        };
+    };
+
+  testScript = { nodes }:
+    ''
+      startAll;
+
+      # Create an SSH key on the client.
+      my $key = `${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f key -N ""`;
+      $client->succeed("mkdir -m 700 /root/.ssh");
+      $client->copyFileFromHost("key", "/root/.ssh/id_ed25519");
+      $client->succeed("chmod 600 /root/.ssh/id_ed25519");
+
+      # 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}");
+
+      # Copy the closure of package C via the SSH substituter.
+      $client->fail("nix-store -r ${pkgC}");
+      # FIXME
+      #$client->succeed(
+      #  "nix-store --option use-ssh-substituter true"
+      #  . " --option ssh-substituter-hosts root\@server"
+      #  . " -r ${pkgC} >&2");
+      #$client->succeed("nix-store --check-validity ${pkgC}");
+    '';
+
+})
diff --git a/third_party/nix/tests/nix-copy-ssh.sh b/third_party/nix/tests/nix-copy-ssh.sh
new file mode 100644
index 000000000000..eb801548d2f1
--- /dev/null
+++ b/third_party/nix/tests/nix-copy-ssh.sh
@@ -0,0 +1,20 @@
+source common.sh
+
+clearStore
+clearCache
+
+remoteRoot=$TEST_ROOT/store2
+chmod -R u+w "$remoteRoot" || true
+rm -rf "$remoteRoot"
+
+outPath=$(nix-build --no-out-link dependencies.nix)
+
+nix copy --to "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath
+
+[ -f $remoteRoot$outPath/foobar ]
+
+clearStore
+
+nix copy --no-check-sigs --from "ssh://localhost?store=$NIX_STORE_DIR&remote-store=$remoteRoot%3fstore=$NIX_STORE_DIR%26real=$remoteRoot$NIX_STORE_DIR" $outPath
+
+[ -f $outPath/foobar ]
diff --git a/third_party/nix/tests/nix-profile.sh b/third_party/nix/tests/nix-profile.sh
new file mode 100644
index 000000000000..e2e0d1090804
--- /dev/null
+++ b/third_party/nix/tests/nix-profile.sh
@@ -0,0 +1,9 @@
+source common.sh
+
+sed -e "s|@localstatedir@|$TEST_ROOT/profile-var|g" -e "s|@coreutils@|$coreutils|g" < ../scripts/nix-profile.sh.in > $TEST_ROOT/nix-profile.sh
+
+user=$(whoami)
+rm -rf $TEST_HOME $TEST_ROOT/profile-var
+mkdir -p $TEST_HOME
+USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh; set"
+USER=$user $SHELL -e -c ". $TEST_ROOT/nix-profile.sh" # test idempotency
diff --git a/third_party/nix/tests/nix-shell.sh b/third_party/nix/tests/nix-shell.sh
new file mode 100644
index 000000000000..ee502dddb955
--- /dev/null
+++ b/third_party/nix/tests/nix-shell.sh
@@ -0,0 +1,57 @@
+source common.sh
+
+clearStore
+
+# Test nix-shell -A
+export IMPURE_VAR=foo
+export SELECTED_IMPURE_VAR=baz
+export NIX_BUILD_SHELL=$SHELL
+output=$(nix-shell --pure shell.nix -A shellDrv --run \
+    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"')
+
+[ "$output" = " - foo - bar" ]
+
+# Test --keep
+output=$(nix-shell --pure --keep SELECTED_IMPURE_VAR shell.nix -A shellDrv --run \
+    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX - $SELECTED_IMPURE_VAR"')
+
+[ "$output" = " - foo - bar - baz" ]
+
+# Test nix-shell on a .drv
+[[ $(nix-shell --pure $(nix-instantiate shell.nix -A shellDrv) --run \
+    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
+
+[[ $(nix-shell --pure $(nix-instantiate shell.nix -A shellDrv) --run \
+    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
+
+# Test nix-shell on a .drv symlink
+
+# Legacy: absolute path and .drv extension required
+nix-instantiate shell.nix -A shellDrv --indirect --add-root $TEST_ROOT/shell.drv
+[[ $(nix-shell --pure $TEST_ROOT/shell.drv --run \
+    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
+
+# New behaviour: just needs to resolve to a derivation in the store
+nix-instantiate shell.nix -A shellDrv --indirect --add-root $TEST_ROOT/shell
+[[ $(nix-shell --pure $TEST_ROOT/shell --run \
+    'echo "$IMPURE_VAR - $VAR_FROM_STDENV_SETUP - $VAR_FROM_NIX"') = " - foo - bar" ]]
+
+# Test nix-shell -p
+output=$(NIX_PATH=nixpkgs=shell.nix nix-shell --pure -p foo bar --run 'echo "$(foo) $(bar)"')
+[ "$output" = "foo bar" ]
+
+# Test nix-shell shebang mode
+sed -e "s|@ENV_PROG@|$(type -p env)|" shell.shebang.sh > $TEST_ROOT/shell.shebang.sh
+chmod a+rx $TEST_ROOT/shell.shebang.sh
+
+output=$($TEST_ROOT/shell.shebang.sh abc def)
+[ "$output" = "foo bar abc def" ]
+
+# Test nix-shell shebang mode for ruby
+# This uses a fake interpreter that returns the arguments passed
+# This, in turn, verifies the `rc` script is valid and the `load()` script (given using `-e`) is as expected.
+sed -e "s|@SHELL_PROG@|$(type -p nix-shell)|" shell.shebang.rb > $TEST_ROOT/shell.shebang.rb
+chmod a+rx $TEST_ROOT/shell.shebang.rb
+
+output=$($TEST_ROOT/shell.shebang.rb abc ruby)
+[ "$output" = '-e load("'"$TEST_ROOT"'/shell.shebang.rb") -- abc ruby' ]
diff --git a/third_party/nix/tests/optimise-store.sh b/third_party/nix/tests/optimise-store.sh
new file mode 100644
index 000000000000..61e3df2f9f7e
--- /dev/null
+++ b/third_party/nix/tests/optimise-store.sh
@@ -0,0 +1,43 @@
+source common.sh
+
+clearStore
+
+outPath1=$(echo 'with import ./config.nix; mkDerivation { name = "foo1"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store)
+outPath2=$(echo 'with import ./config.nix; mkDerivation { name = "foo2"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link --auto-optimise-store)
+
+inode1="$(stat --format=%i $outPath1/foo)"
+inode2="$(stat --format=%i $outPath2/foo)"
+if [ "$inode1" != "$inode2" ]; then
+    echo "inodes do not match"
+    exit 1
+fi
+
+nlink="$(stat --format=%h $outPath1/foo)"
+if [ "$nlink" != 3 ]; then
+    echo "link count incorrect"
+    exit 1
+fi
+
+outPath3=$(echo 'with import ./config.nix; mkDerivation { name = "foo3"; builder = builtins.toFile "builder" "mkdir $out; echo hello > $out/foo"; }' | nix-build - --no-out-link)
+
+inode3="$(stat --format=%i $outPath3/foo)"
+if [ "$inode1" = "$inode3" ]; then
+    echo "inodes match unexpectedly"
+    exit 1
+fi
+
+nix-store --optimise
+
+inode1="$(stat --format=%i $outPath1/foo)"
+inode3="$(stat --format=%i $outPath3/foo)"
+if [ "$inode1" != "$inode3" ]; then
+    echo "inodes do not match"
+    exit 1
+fi
+
+nix-store --gc
+
+if [ -n "$(ls $NIX_STORE_DIR/.links)" ]; then
+    echo ".links directory not empty after GC"
+    exit 1
+fi
diff --git a/third_party/nix/tests/parallel.builder.sh b/third_party/nix/tests/parallel.builder.sh
new file mode 100644
index 000000000000..d092bc5a6bd4
--- /dev/null
+++ b/third_party/nix/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=$(($(cat $shared.cur) + 1))
+if test $new -gt $(cat $shared.max); then
+    echo $new > $shared.max
+fi
+echo $new > $shared.cur
+rm $shared.lock
+
+
+echo -n $(cat $inputs)$text > $out
+
+sleep $sleepTime
+
+
+# decrease counter
+while ! ln -s x $shared.lock 2> /dev/null; do
+    sleep 1
+done
+test -f $shared.cur || echo 0 > $shared.cur
+echo $(($(cat $shared.cur) - 1)) > $shared.cur
+rm $shared.lock
diff --git a/third_party/nix/tests/parallel.nix b/third_party/nix/tests/parallel.nix
new file mode 100644
index 000000000000..23f142059f58
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/parallel.sh b/third_party/nix/tests/parallel.sh
new file mode 100644
index 000000000000..3b7bbe5a2251
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/pass-as-file.sh b/third_party/nix/tests/pass-as-file.sh
new file mode 100644
index 000000000000..2c0bc5031ad7
--- /dev/null
+++ b/third_party/nix/tests/pass-as-file.sh
@@ -0,0 +1,18 @@
+source common.sh
+
+clearStore
+
+outPath=$(nix-build --no-out-link -E "
+with import ./config.nix;
+
+mkDerivation {
+  name = \"pass-as-file\";
+  passAsFile = [ \"foo\" ];
+  foo = [ \"xyzzy\" ];
+  builder = builtins.toFile \"builder.sh\" ''
+    [ \"\$(basename \$fooPath)\" = .attr-1bp7cri8hplaz6hbz0v4f0nl44rl84q1sg25kgwqzipzd1mv89ic ]
+    [ \"\$(cat \$fooPath)\" = xyzzy ]
+    touch \$out
+  '';
+}
+")
diff --git a/third_party/nix/tests/placeholders.sh b/third_party/nix/tests/placeholders.sh
new file mode 100644
index 000000000000..cd1bb7bc2aac
--- /dev/null
+++ b/third_party/nix/tests/placeholders.sh
@@ -0,0 +1,20 @@
+source common.sh
+
+clearStore
+
+nix-build --no-out-link -E '
+  with import ./config.nix;
+
+  mkDerivation {
+    name = "placeholders";
+    outputs = [ "out" "bin" "dev" ];
+    buildCommand = "
+      echo foo1 > $out
+      echo foo2 > $bin
+      echo foo3 > $dev
+      [[ $(cat ${placeholder "out"}) = foo1 ]]
+      [[ $(cat ${placeholder "bin"}) = foo2 ]]
+      [[ $(cat ${placeholder "dev"}) = foo3 ]]
+    ";
+  }
+'
diff --git a/third_party/nix/tests/post-hook.sh b/third_party/nix/tests/post-hook.sh
new file mode 100644
index 000000000000..a026572154db
--- /dev/null
+++ b/third_party/nix/tests/post-hook.sh
@@ -0,0 +1,15 @@
+source common.sh
+
+clearStore
+
+export REMOTE_STORE=$TEST_ROOT/remote_store
+
+# Build the dependencies and push them to the remote store
+nix-build -o $TEST_ROOT/result dependencies.nix --post-build-hook $PWD/push-to-store.sh
+
+clearStore
+
+# Ensure that we the remote store contains both the runtime and buildtime
+# closure of what we've just built
+nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix
+nix copy --from "$REMOTE_STORE" --no-require-sigs -f dependencies.nix input1_drv
diff --git a/third_party/nix/tests/pure-eval.nix b/third_party/nix/tests/pure-eval.nix
new file mode 100644
index 000000000000..ed25b3d45637
--- /dev/null
+++ b/third_party/nix/tests/pure-eval.nix
@@ -0,0 +1,3 @@
+{
+  x = 123;
+}
diff --git a/third_party/nix/tests/pure-eval.sh b/third_party/nix/tests/pure-eval.sh
new file mode 100644
index 000000000000..49c8564487c3
--- /dev/null
+++ b/third_party/nix/tests/pure-eval.sh
@@ -0,0 +1,18 @@
+source common.sh
+
+clearStore
+
+nix eval --pure-eval '(assert 1 + 2 == 3; true)'
+
+[[ $(nix eval '(builtins.readFile ./pure-eval.sh)') =~ clearStore ]]
+
+(! nix eval --pure-eval '(builtins.readFile ./pure-eval.sh)')
+
+(! nix eval --pure-eval '(builtins.currentTime)')
+(! nix eval --pure-eval '(builtins.currentSystem)')
+
+(! nix-instantiate --pure-eval ./simple.nix)
+
+[[ $(nix eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x)") == 123 ]]
+(! nix eval --pure-eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; })).x)")
+nix eval --pure-eval "((import (builtins.fetchurl { url = file://$(pwd)/pure-eval.nix; sha256 = \"$(nix hash-file pure-eval.nix --type sha256)\"; })).x)"
diff --git a/third_party/nix/tests/push-to-store.sh b/third_party/nix/tests/push-to-store.sh
new file mode 100755
index 000000000000..6aadb916ba0b
--- /dev/null
+++ b/third_party/nix/tests/push-to-store.sh
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+echo Pushing "$@" to "$REMOTE_STORE"
+printf "%s" "$OUT_PATHS" | xargs -d: nix copy --to "$REMOTE_STORE" --no-require-sigs
diff --git a/third_party/nix/tests/referrers.sh b/third_party/nix/tests/referrers.sh
new file mode 100644
index 000000000000..8ab8e5ddfe87
--- /dev/null
+++ b/third_party/nix/tests/referrers.sh
@@ -0,0 +1,36 @@
+source common.sh
+
+clearStore
+
+max=500
+
+reference=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
+touch $reference
+(echo $reference && echo && echo 0) | nix-store --register-validity 
+
+echo "making registration..."
+
+set +x
+for ((n = 0; n < $max; n++)); do
+    storePath=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-$n
+    echo -n > $storePath
+    ref2=$NIX_STORE_DIR/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa-$((n+1))
+    if test $((n+1)) = $max; then
+        ref2=$reference
+    fi
+    echo $storePath; echo; echo 2; echo $reference; echo $ref2
+done > $TEST_ROOT/reg_info
+set -x
+
+echo "registering..."
+
+nix-store --register-validity < $TEST_ROOT/reg_info
+
+echo "collecting garbage..."
+ln -sfn $reference "$NIX_STATE_DIR"/gcroots/ref
+nix-store --gc
+
+if [ -n "$(type -p sqlite3)" -a "$(sqlite3 $NIX_STATE_DIR/db/db.sqlite 'select count(*) from Refs')" -ne 0 ]; then
+    echo "referrers not cleaned up"
+    exit 1
+fi
diff --git a/third_party/nix/tests/remote-builds.nix b/third_party/nix/tests/remote-builds.nix
new file mode 100644
index 000000000000..b867f13b4995
--- /dev/null
+++ b/third_party/nix/tests/remote-builds.nix
@@ -0,0 +1,108 @@
+# Test Nix's remote build feature.
+
+{ nixpkgs, system, nix }:
+
+with import (nixpkgs + "/nixos/lib/testing.nix") { inherit system; };
+
+makeTest (
+
+let
+
+  # The configuration of the remote builders.
+  builder =
+    { config, pkgs, ... }:
+    { services.openssh.enable = true;
+      virtualisation.writableStore = true;
+      nix.package = nix;
+      nix.useSandbox = true;
+    };
+
+  # 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" "if [ ${toString nr} = 5 ]; then echo FAIL; exit 1; fi; echo Hello; mkdir $out $foo; cat /proc/sys/kernel/hostname > $out/host; ln -s $out $foo/bar; sleep 10" ];
+        outputs = [ "out" "foo" ];
+      }
+    '';
+
+in
+
+{
+
+  nodes =
+    { builder1 = builder;
+      builder2 = builder;
+
+      client =
+        { config, pkgs, ... }:
+        { nix.maxJobs = 0; # force remote building
+          nix.distributedBuilds = true;
+          nix.buildMachines =
+            [ { hostName = "builder1";
+                sshUser = "root";
+                sshKey = "/root/.ssh/id_ed25519";
+                system = "i686-linux";
+                maxJobs = 1;
+              }
+              { hostName = "builder2";
+                sshUser = "root";
+                sshKey = "/root/.ssh/id_ed25519";
+                system = "i686-linux";
+                maxJobs = 1;
+              }
+            ];
+          virtualisation.writableStore = true;
+          virtualisation.pathsInNixDB = [ config.system.build.extraUtils ];
+          nix.package = nix;
+          nix.binaryCaches = [ ];
+          programs.ssh.extraConfig = "ConnectTimeout 30";
+        };
+    };
+
+  testScript = { nodes }:
+    ''
+      startAll;
+
+      # Create an SSH key on the client.
+      my $key = `${pkgs.openssh}/bin/ssh-keygen -t ed25519 -f key -N ""`;
+      $client->succeed("mkdir -p -m 700 /root/.ssh");
+      $client->copyFileFromHost("key", "/root/.ssh/id_ed25519");
+      $client->succeed("chmod 600 /root/.ssh/id_ed25519");
+
+      # Install the SSH key on the builders.
+      $client->waitForUnit("network.target");
+      foreach my $builder ($builder1, $builder2) {
+          $builder->succeed("mkdir -p -m 700 /root/.ssh");
+          $builder->copyFileFromHost("key.pub", "/root/.ssh/authorized_keys");
+          $builder->waitForUnit("sshd");
+          $client->succeed("ssh -o StrictHostKeyChecking=no " . $builder->name() . " 'echo hello world'");
+      }
+
+      # Perform a build and check that it was performed on the builder.
+      my $out = $client->succeed(
+        "nix-build ${expr nodes.client.config 1} 2> build-output",
+        "grep -q Hello build-output"
+      );
+      $builder1->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})\!out $(nix-instantiate ${expr nodes.client.config 3})\!out');
+      $builder1->succeed("test -e $out1 -o -e $out2");
+      $builder2->succeed("test -e $out1 -o -e $out2");
+
+      # And a failing build.
+      $client->fail("nix-build ${expr nodes.client.config 5}");
+
+      # Test whether the build hook automatically skips unavailable builders.
+      $builder1->block;
+      $client->succeed("nix-build ${expr nodes.client.config 4}");
+    '';
+
+})
diff --git a/third_party/nix/tests/remote-store.sh b/third_party/nix/tests/remote-store.sh
new file mode 100644
index 000000000000..77437658ead6
--- /dev/null
+++ b/third_party/nix/tests/remote-store.sh
@@ -0,0 +1,19 @@
+source common.sh
+
+clearStore
+
+startDaemon
+
+storeCleared=1 $SHELL ./user-envs.sh
+
+nix-store --dump-db > $TEST_ROOT/d1
+NIX_REMOTE= nix-store --dump-db > $TEST_ROOT/d2
+cmp $TEST_ROOT/d1 $TEST_ROOT/d2
+
+nix-store --gc --max-freed 1K
+
+killDaemon
+
+user=$(whoami)
+[ -e $NIX_STATE_DIR/gcroots/per-user/$user ]
+[ -e $NIX_STATE_DIR/profiles/per-user/$user ]
diff --git a/third_party/nix/tests/repair.sh b/third_party/nix/tests/repair.sh
new file mode 100644
index 000000000000..ec7ad5dcaff4
--- /dev/null
+++ b/third_party/nix/tests/repair.sh
@@ -0,0 +1,77 @@
+source common.sh
+
+clearStore
+
+path=$(nix-build dependencies.nix -o $TEST_ROOT/result)
+path2=$(nix-store -qR $path | grep input-2)
+
+nix-store --verify --check-contents -v
+
+hash=$(nix-hash $path2)
+
+# Corrupt a path and check whether nix-build --repair can fix it.
+chmod u+w $path2
+touch $path2/bad
+
+if nix-store --verify --check-contents -v; then
+    echo "nix-store --verify succeeded unexpectedly" >&2
+    exit 1
+fi
+
+# The path can be repaired by rebuilding the derivation.
+nix-store --verify --check-contents --repair
+
+nix-store --verify-path $path2
+
+# Re-corrupt and delete the deriver. Now --verify --repair should
+# not work.
+chmod u+w $path2
+touch $path2/bad
+
+nix-store --delete $(nix-store -qd $path2)
+
+if nix-store --verify --check-contents --repair; then
+    echo "nix-store --verify --repair succeeded unexpectedly" >&2
+    exit 1
+fi
+
+nix-build dependencies.nix -o $TEST_ROOT/result --repair
+
+if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then
+    echo "path not repaired properly" >&2
+    exit 1
+fi
+
+# Corrupt a path that has a substitute and check whether nix-store
+# --verify can fix it.
+clearCache
+
+nix copy --to file://$cacheDir $path
+
+chmod u+w $path2
+rm -rf $path2
+
+nix-store --verify --check-contents --repair --substituters "file://$cacheDir" --no-require-sigs
+
+if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then
+    echo "path not repaired properly" >&2
+    exit 1
+fi
+
+# Check --verify-path and --repair-path.
+nix-store --verify-path $path2
+
+chmod u+w $path2
+rm -rf $path2
+
+if nix-store --verify-path $path2; then
+    echo "nix-store --verify-path succeeded unexpectedly" >&2
+    exit 1
+fi
+
+nix-store --repair-path $path2 --substituters "file://$cacheDir" --no-require-sigs
+
+if [ "$(nix-hash $path2)" != "$hash" -o -e $path2/bad ]; then
+    echo "path not repaired properly" >&2
+    exit 1
+fi
diff --git a/third_party/nix/tests/restricted.nix b/third_party/nix/tests/restricted.nix
new file mode 100644
index 000000000000..e0ef5840209c
--- /dev/null
+++ b/third_party/nix/tests/restricted.nix
@@ -0,0 +1 @@
+1 + 2
diff --git a/third_party/nix/tests/restricted.sh b/third_party/nix/tests/restricted.sh
new file mode 100644
index 000000000000..e02becc60e38
--- /dev/null
+++ b/third_party/nix/tests/restricted.sh
@@ -0,0 +1,51 @@
+source common.sh
+
+clearStore
+
+nix-instantiate --restrict-eval --eval -E '1 + 2'
+(! nix-instantiate --restrict-eval ./restricted.nix)
+(! nix-instantiate --eval --restrict-eval <(echo '1 + 2'))
+nix-instantiate --restrict-eval ./simple.nix -I src=.
+nix-instantiate --restrict-eval ./simple.nix -I src1=simple.nix -I src2=config.nix -I src3=./simple.builder.sh
+
+(! nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix')
+nix-instantiate --restrict-eval --eval -E 'builtins.readFile ./simple.nix' -I src=..
+
+(! nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel')
+nix-instantiate --restrict-eval --eval -E 'builtins.readDir ../src/nix-channel' -I src=../src
+
+(! nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>')
+nix-instantiate --restrict-eval --eval -E 'let __nixPath = [ { prefix = "foo"; path = ./.; } ]; in <foo>' -I src=.
+
+p=$(nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)")
+cmp $p restricted.sh
+
+(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval)
+
+(! nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh/")
+
+nix eval --raw "(builtins.fetchurl file://$(pwd)/restricted.sh)" --restrict-eval --allowed-uris "file://$(pwd)/restricted.sh"
+
+(! nix eval --raw "(builtins.fetchurl https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
+(! nix eval --raw "(builtins.fetchTarball https://github.com/NixOS/patchelf/archive/master.tar.gz)" --restrict-eval)
+(! nix eval --raw "(fetchGit git://github.com/NixOS/patchelf.git)" --restrict-eval)
+
+ln -sfn $(pwd)/restricted.nix $TEST_ROOT/restricted.nix
+[[ $(nix-instantiate --eval $TEST_ROOT/restricted.nix) == 3 ]]
+(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix)
+(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT)
+(! nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I .)
+nix-instantiate --eval --restrict-eval $TEST_ROOT/restricted.nix -I $TEST_ROOT -I .
+
+[[ $(nix eval --raw --restrict-eval -I . '(builtins.readFile "${import ./simple.nix}/hello")') == 'Hello World!' ]]
+
+# Check whether we can leak symlink information through directory traversal.
+traverseDir="$(pwd)/restricted-traverse-me"
+ln -sfn "$(pwd)/restricted-secret" "$(pwd)/restricted-innocent"
+mkdir -p "$traverseDir"
+goUp="..$(echo "$traverseDir" | sed -e 's,[^/]\+,..,g')"
+output="$(nix eval --raw --restrict-eval -I "$traverseDir" \
+    "(builtins.readFile \"$traverseDir/$goUp$(pwd)/restricted-innocent\")" \
+    2>&1 || :)"
+echo "$output" | grep "is forbidden"
+! echo "$output" | grep -F restricted-secret
diff --git a/third_party/nix/tests/run.nix b/third_party/nix/tests/run.nix
new file mode 100644
index 000000000000..77dcbd2a9df0
--- /dev/null
+++ b/third_party/nix/tests/run.nix
@@ -0,0 +1,17 @@
+with import ./config.nix;
+
+{
+  hello = mkDerivation {
+    name = "hello";
+    buildCommand =
+      ''
+        mkdir -p $out/bin
+        cat > $out/bin/hello <<EOF
+        #! ${shell}
+        who=\$1
+        echo "Hello \''${who:-World} from $out/bin/hello"
+        EOF
+        chmod +x $out/bin/hello
+      '';
+  };
+}
diff --git a/third_party/nix/tests/run.sh b/third_party/nix/tests/run.sh
new file mode 100644
index 000000000000..d1dbfd6bd4a6
--- /dev/null
+++ b/third_party/nix/tests/run.sh
@@ -0,0 +1,28 @@
+source common.sh
+
+clearStore
+clearCache
+
+nix run -f run.nix hello -c hello | grep 'Hello World'
+nix run -f run.nix hello -c hello NixOS | grep 'Hello NixOS'
+
+if ! canUseSandbox; then exit; fi
+
+chmod -R u+w $TEST_ROOT/store0 || true
+rm -rf $TEST_ROOT/store0
+
+clearStore
+
+path=$(nix eval --raw -f run.nix hello)
+
+# Note: we need the sandbox paths to ensure that the shell is
+# visible in the sandbox.
+nix run --sandbox-build-dir /build-tmp \
+    --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' \
+    --store $TEST_ROOT/store0 -f run.nix hello -c hello | grep 'Hello World'
+
+path2=$(nix run --sandbox-paths '/nix? /bin? /lib? /lib64? /usr?' --store $TEST_ROOT/store0 -f run.nix hello -c $SHELL -c 'type -p hello')
+
+[[ $path/bin/hello = $path2 ]]
+
+[[ -e $TEST_ROOT/store0/nix/store/$(basename $path)/bin/hello ]]
diff --git a/third_party/nix/tests/search.nix b/third_party/nix/tests/search.nix
new file mode 100644
index 000000000000..fea6e7a7a647
--- /dev/null
+++ b/third_party/nix/tests/search.nix
@@ -0,0 +1,25 @@
+with import ./config.nix;
+
+{
+  hello = mkDerivation rec {
+    name = "hello-${version}";
+    version = "0.1";
+    buildCommand = "touch $out";
+    meta.description = "Empty file";
+  };
+  foo = mkDerivation rec {
+    name = "foo-5";
+    buildCommand = ''
+      mkdir -p $out
+      echo ${name} > $out/${name}
+    '';
+  };
+  bar = mkDerivation rec {
+    name = "bar-3";
+    buildCommand = ''
+      echo "Does not build successfully"
+      exit 1
+    '';
+    meta.description = "broken bar";
+  };
+}
diff --git a/third_party/nix/tests/search.sh b/third_party/nix/tests/search.sh
new file mode 100644
index 000000000000..14da3127b0d5
--- /dev/null
+++ b/third_party/nix/tests/search.sh
@@ -0,0 +1,43 @@
+source common.sh
+
+clearStore
+clearCache
+
+# No packages
+(( $(NIX_PATH= nix search -u|wc -l) == 0 ))
+
+# Haven't updated cache, still nothing
+(( $(nix search -f search.nix hello|wc -l) == 0 ))
+(( $(nix search -f search.nix |wc -l) == 0 ))
+
+# Update cache, search should work
+(( $(nix search -f search.nix -u hello|wc -l) > 0 ))
+
+# Use cache
+(( $(nix search -f search.nix foo|wc -l) > 0 ))
+(( $(nix search foo|wc -l) > 0 ))
+
+# Test --no-cache works
+# No results from cache
+(( $(nix search --no-cache foo |wc -l) == 0 ))
+# Does find results from file pointed at
+(( $(nix search -f search.nix --no-cache foo |wc -l) > 0 ))
+
+# Check descriptions are searched
+(( $(nix search broken | wc -l) > 0 ))
+
+# Check search that matches nothing
+(( $(nix search nosuchpackageexists | wc -l) == 0 ))
+
+# Search for multiple arguments
+(( $(nix search hello empty | wc -l) == 3 ))
+
+# Multiple arguments will not exist
+(( $(nix search hello broken | wc -l) == 0 ))
+
+## Search expressions
+
+# Check that empty search string matches all
+nix search|grep -q foo
+nix search|grep -q bar
+nix search|grep -q hello
diff --git a/third_party/nix/tests/secure-drv-outputs.nix b/third_party/nix/tests/secure-drv-outputs.nix
new file mode 100644
index 000000000000..b4ac8ff531f8
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/secure-drv-outputs.sh b/third_party/nix/tests/secure-drv-outputs.sh
new file mode 100644
index 000000000000..50a9c4428d30
--- /dev/null
+++ b/third_party/nix/tests/secure-drv-outputs.sh
@@ -0,0 +1,36 @@
+# Test that users cannot register specially-crafted derivations that
+# produce output paths belonging to other derivations.  This could be
+# used to inject malware into the store.
+
+source common.sh
+
+clearStore
+
+startDaemon
+
+# Determine the output path of the "good" derivation.
+goodOut=$(nix-store -q $(nix-instantiate ./secure-drv-outputs.nix -A good))
+
+# Instantiate the "bad" derivation.
+badDrv=$(nix-instantiate ./secure-drv-outputs.nix -A bad)
+badOut=$(nix-store -q $badDrv)
+
+# Rewrite the bad derivation to produce the output path of the good
+# derivation.
+rm -f $TEST_ROOT/bad.drv
+sed -e "s|$badOut|$goodOut|g" < $badDrv > $TEST_ROOT/bad.drv
+
+# Add the manipulated derivation to the store and build it.  This
+# should fail.
+if badDrv2=$(nix-store --add $TEST_ROOT/bad.drv); then
+    nix-store -r "$badDrv2"
+fi
+
+# Now build the good derivation.
+goodOut2=$(nix-build ./secure-drv-outputs.nix -A good --no-out-link)
+test "$goodOut" = "$goodOut2"
+
+if ! test -e "$goodOut"/good; then
+    echo "Bad derivation stole the output path of the good derivation!"
+    exit 1
+fi
diff --git a/third_party/nix/tests/setuid.nix b/third_party/nix/tests/setuid.nix
new file mode 100644
index 000000000000..77e83c8d6c2c
--- /dev/null
+++ b/third_party/nix/tests/setuid.nix
@@ -0,0 +1,108 @@
+# Verify that Linux builds cannot create setuid or setgid binaries.
+
+{ nixpkgs, system, nix }:
+
+with import (nixpkgs + "/nixos/lib/testing.nix") { inherit system; };
+
+makeTest {
+
+  machine =
+    { config, lib, pkgs, ... }:
+    { virtualisation.writableStore = true;
+      nix.package = nix;
+      nix.binaryCaches = [ ];
+      nix.nixPath = [ "nixpkgs=${lib.cleanSource pkgs.path}" ];
+      virtualisation.pathsInNixDB = [ pkgs.stdenv pkgs.pkgsi686Linux.stdenv ];
+    };
+
+  testScript = { nodes }:
+    ''
+      startAll;
+
+      # Copying to /tmp should succeed.
+      $machine->succeed('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" {} "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      # Creating a setuid binary should fail.
+      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" {} "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        chmod 4755 /tmp/id
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      # Creating a setgid binary should fail.
+      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" {} "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        chmod 2755 /tmp/id
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      # The checks should also work on 32-bit binaries.
+      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> { system = "i686-linux"; }; runCommand "foo" {} "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        chmod 2755 /tmp/id
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      # The tests above use fchmodat(). Test chmod() as well.
+      $machine->succeed('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        perl -e \"chmod 0666, qw(/tmp/id) or die\"
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 666 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        perl -e \"chmod 04755, qw(/tmp/id) or die\"
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      # And test fchmod().
+      $machine->succeed('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 01750, \\\$x or die\"
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 1750 ]]');
+
+      $machine->succeed("rm /tmp/id");
+
+      $machine->fail('nix-build --no-sandbox -E \'(with import <nixpkgs> {}; runCommand "foo" { buildInputs = [ perl ]; } "
+        mkdir -p $out
+        cp ${pkgs.coreutils}/bin/id /tmp/id
+        perl -e \"my \\\$x; open \\\$x, qw(/tmp/id); chmod 04777, \\\$x or die\"
+      ")\' ');
+
+      $machine->succeed('[[ $(stat -c %a /tmp/id) = 555 ]]');
+
+      $machine->succeed("rm /tmp/id");
+    '';
+
+}
diff --git a/third_party/nix/tests/shell.nix b/third_party/nix/tests/shell.nix
new file mode 100644
index 000000000000..eb39f9039a88
--- /dev/null
+++ b/third_party/nix/tests/shell.nix
@@ -0,0 +1,56 @@
+{ }:
+
+with import ./config.nix;
+
+let pkgs = rec {
+  setupSh = builtins.toFile "setup" ''
+    export VAR_FROM_STDENV_SETUP=foo
+    for pkg in $buildInputs; do
+      export PATH=$PATH:$pkg/bin
+    done
+  '';
+
+  stdenv = mkDerivation {
+    name = "stdenv";
+    buildCommand = ''
+      mkdir -p $out
+      ln -s ${setupSh} $out/setup
+    '';
+  };
+
+  shellDrv = mkDerivation {
+    name = "shellDrv";
+    builder = "/does/not/exist";
+    VAR_FROM_NIX = "bar";
+    inherit stdenv;
+  };
+
+  # Used by nix-shell -p
+  runCommand = name: args: buildCommand: mkDerivation (args // {
+    inherit name buildCommand stdenv;
+  });
+
+  foo = runCommand "foo" {} ''
+    mkdir -p $out/bin
+    echo 'echo foo' > $out/bin/foo
+    chmod a+rx $out/bin/foo
+    ln -s ${shell} $out/bin/bash
+  '';
+
+  bar = runCommand "bar" {} ''
+    mkdir -p $out/bin
+    echo 'echo bar' > $out/bin/bar
+    chmod a+rx $out/bin/bar
+  '';
+
+  bash = shell;
+
+  # ruby "interpreter" that outputs "$@"
+  ruby = runCommand "ruby" {} ''
+    mkdir -p $out/bin
+    echo 'printf -- "$*"' > $out/bin/ruby
+    chmod a+rx $out/bin/ruby
+  '';
+
+  inherit pkgs;
+}; in pkgs
diff --git a/third_party/nix/tests/shell.shebang.rb b/third_party/nix/tests/shell.shebang.rb
new file mode 100644
index 000000000000..ea67eb09c1c6
--- /dev/null
+++ b/third_party/nix/tests/shell.shebang.rb
@@ -0,0 +1,7 @@
+#! @SHELL_PROG@
+#! ruby
+#! nix-shell -I nixpkgs=shell.nix --no-substitute
+#! nix-shell --pure -p ruby -i ruby
+
+# Contents doesn't matter.
+abort("This shouldn't be executed.")
diff --git a/third_party/nix/tests/shell.shebang.sh b/third_party/nix/tests/shell.shebang.sh
new file mode 100755
index 000000000000..f7132043de44
--- /dev/null
+++ b/third_party/nix/tests/shell.shebang.sh
@@ -0,0 +1,4 @@
+#! @ENV_PROG@ nix-shell
+#! nix-shell -I nixpkgs=shell.nix --no-substitute
+#! nix-shell --pure -i bash -p foo bar
+echo "$(foo) $(bar) $@"
diff --git a/third_party/nix/tests/signing.sh b/third_party/nix/tests/signing.sh
new file mode 100644
index 000000000000..9e29e3fbf063
--- /dev/null
+++ b/third_party/nix/tests/signing.sh
@@ -0,0 +1,105 @@
+source common.sh
+
+clearStore
+clearCache
+
+nix-store --generate-binary-cache-key cache1.example.org $TEST_ROOT/sk1 $TEST_ROOT/pk1
+pk1=$(cat $TEST_ROOT/pk1)
+nix-store --generate-binary-cache-key cache2.example.org $TEST_ROOT/sk2 $TEST_ROOT/pk2
+pk2=$(cat $TEST_ROOT/pk2)
+
+# Build a path.
+outPath=$(nix-build dependencies.nix --no-out-link --secret-key-files "$TEST_ROOT/sk1 $TEST_ROOT/sk2")
+
+# Verify that the path got signed.
+info=$(nix path-info --json $outPath)
+[[ $info =~ '"ultimate":true' ]]
+[[ $info =~ 'cache1.example.org' ]]
+[[ $info =~ 'cache2.example.org' ]]
+
+# Test "nix verify".
+nix verify -r $outPath
+
+expect 2 nix verify -r $outPath --sigs-needed 1
+
+nix verify -r $outPath --sigs-needed 1 --trusted-public-keys $pk1
+
+expect 2 nix verify -r $outPath --sigs-needed 2 --trusted-public-keys $pk1
+
+nix verify -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2"
+
+nix verify --all --sigs-needed 2 --trusted-public-keys "$pk1 $pk2"
+
+# Build something unsigned.
+outPath2=$(nix-build simple.nix --no-out-link)
+
+nix verify -r $outPath
+
+# Verify that the path did not get signed but does have the ultimate bit.
+info=$(nix path-info --json $outPath2)
+[[ $info =~ '"ultimate":true' ]]
+(! [[ $info =~ 'signatures' ]])
+
+# Test "nix verify".
+nix verify -r $outPath2
+
+expect 2 nix verify -r $outPath2 --sigs-needed 1
+
+expect 2 nix verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1
+
+# Test "nix sign-paths".
+nix sign-paths --key-file $TEST_ROOT/sk1 $outPath2
+
+nix verify -r $outPath2 --sigs-needed 1 --trusted-public-keys $pk1
+
+# Build something content-addressed.
+outPathCA=$(IMPURE_VAR1=foo IMPURE_VAR2=bar nix-build ./fixed.nix -A good.0 --no-out-link)
+
+[[ $(nix path-info --json $outPathCA) =~ '"ca":"fixed:md5:' ]]
+
+# Content-addressed paths don't need signatures, so they verify
+# regardless of --sigs-needed.
+nix verify $outPathCA
+nix verify $outPathCA --sigs-needed 1000
+
+# Check that signing a content-addressed path doesn't overflow validSigs
+nix sign-paths --key-file $TEST_ROOT/sk1 $outPathCA
+nix verify -r $outPathCA --sigs-needed 1000 --trusted-public-keys $pk1
+
+# Copy to a binary cache.
+nix copy --to file://$cacheDir $outPath2
+
+# Verify that signatures got copied.
+info=$(nix path-info --store file://$cacheDir --json $outPath2)
+(! [[ $info =~ '"ultimate":true' ]])
+[[ $info =~ 'cache1.example.org' ]]
+(! [[ $info =~ 'cache2.example.org' ]])
+
+# Verify that adding a signature to a path in a binary cache works.
+nix sign-paths --store file://$cacheDir --key-file $TEST_ROOT/sk2 $outPath2
+info=$(nix path-info --store file://$cacheDir --json $outPath2)
+[[ $info =~ 'cache1.example.org' ]]
+[[ $info =~ 'cache2.example.org' ]]
+
+# Copying to a diverted store should fail due to a lack of valid signatures.
+chmod -R u+w $TEST_ROOT/store0 || true
+rm -rf $TEST_ROOT/store0
+(! nix copy --to $TEST_ROOT/store0 $outPath)
+
+# But succeed if we supply the public keys.
+nix copy --to $TEST_ROOT/store0 $outPath --trusted-public-keys $pk1
+
+expect 2 nix verify --store $TEST_ROOT/store0 -r $outPath
+
+nix verify --store $TEST_ROOT/store0 -r $outPath --trusted-public-keys $pk1
+nix verify --store $TEST_ROOT/store0 -r $outPath --sigs-needed 2 --trusted-public-keys "$pk1 $pk2"
+
+# It should also succeed if we disable signature checking.
+(! nix copy --to $TEST_ROOT/store0 $outPath2)
+nix copy --to $TEST_ROOT/store0?require-sigs=false $outPath2
+
+# But signatures should still get copied.
+nix verify --store $TEST_ROOT/store0 -r $outPath2 --trusted-public-keys $pk1
+
+# Content-addressed stuff can be copied without signatures.
+nix copy --to $TEST_ROOT/store0 $outPathCA
diff --git a/third_party/nix/tests/simple.builder.sh b/third_party/nix/tests/simple.builder.sh
new file mode 100644
index 000000000000..569e8ca88c1e
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/simple.nix b/third_party/nix/tests/simple.nix
new file mode 100644
index 000000000000..4223c0f23a5b
--- /dev/null
+++ b/third_party/nix/tests/simple.nix
@@ -0,0 +1,8 @@
+with import ./config.nix;
+
+mkDerivation {
+  name = "simple";
+  builder = ./simple.builder.sh;
+  PATH = "";
+  goodPath = path;
+}
diff --git a/third_party/nix/tests/simple.sh b/third_party/nix/tests/simple.sh
new file mode 100644
index 000000000000..37631b648c67
--- /dev/null
+++ b/third_party/nix/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_REMOTE=local?store=/foo\&real=$TEST_ROOT/real-store nix-instantiate --readonly-mode hash-check.nix)"
+if test "$outPath" != "/foo/lfy1s6ca46rm5r6w4gg9hc0axiakjcnm-dependencies.drv"; then
+    echo "hashDerivationModulo appears broken, got $outPath"
+    exit 1
+fi
diff --git a/third_party/nix/tests/structured-attrs.nix b/third_party/nix/tests/structured-attrs.nix
new file mode 100644
index 000000000000..6c77a43913a7
--- /dev/null
+++ b/third_party/nix/tests/structured-attrs.nix
@@ -0,0 +1,66 @@
+with import ./config.nix;
+
+let
+
+  dep = mkDerivation {
+    name = "dep";
+    buildCommand = ''
+      mkdir $out; echo bla > $out/bla
+    '';
+  };
+
+in
+
+mkDerivation {
+  name = "structured";
+
+  __structuredAttrs = true;
+
+  buildCommand = ''
+    set -x
+
+    [[ $int = 123456789 ]]
+    [[ -z $float ]]
+    [[ -n $boolTrue ]]
+    [[ -z $boolFalse ]]
+    [[ -n ''${hardening[format]} ]]
+    [[ -z ''${hardening[fortify]} ]]
+    [[ ''${#buildInputs[@]} = 7 ]]
+    [[ ''${buildInputs[2]} = c ]]
+    [[ -v nothing ]]
+    [[ -z $nothing ]]
+
+    mkdir ''${outputs[out]}
+    echo bar > $dest
+
+    json=$(cat .attrs.json)
+    [[ $json =~ '"narHash":"sha256:1r7yc43zqnzl5b0als5vnyp649gk17i37s7mj00xr8kc47rjcybk"' ]]
+    [[ $json =~ '"narSize":288' ]]
+    [[ $json =~ '"closureSize":288' ]]
+    [[ $json =~ '"references":[]' ]]
+  '';
+
+  buildInputs = [ "a" "b" "c" 123 "'" "\"" null ];
+
+  hardening.format = true;
+  hardening.fortify = false;
+
+  outer.inner = [ 1 2 3 ];
+
+  int = 123456789;
+
+  float = 123.456;
+
+  boolTrue = true;
+  boolFalse = false;
+
+  nothing = null;
+
+  dest = "${placeholder "out"}/foo";
+
+  "foo bar" = "BAD";
+  "1foobar" = "BAD";
+  "foo$" = "BAD";
+
+  exportReferencesGraph.refs = [ dep ];
+}
diff --git a/third_party/nix/tests/structured-attrs.sh b/third_party/nix/tests/structured-attrs.sh
new file mode 100644
index 000000000000..9ba2672b6833
--- /dev/null
+++ b/third_party/nix/tests/structured-attrs.sh
@@ -0,0 +1,7 @@
+source common.sh
+
+clearStore
+
+outPath=$(nix-build structured-attrs.nix --no-out-link)
+
+[[ $(cat $outPath/foo) = bar ]]
diff --git a/third_party/nix/tests/tarball.sh b/third_party/nix/tests/tarball.sh
new file mode 100644
index 000000000000..ba534c6261ad
--- /dev/null
+++ b/third_party/nix/tests/tarball.sh
@@ -0,0 +1,28 @@
+source common.sh
+
+clearStore
+
+rm -rf $TEST_HOME
+
+tarroot=$TEST_ROOT/tarball
+rm -rf $tarroot
+mkdir -p $tarroot
+cp dependencies.nix $tarroot/default.nix
+cp config.nix dependencies.builder*.sh $tarroot/
+
+tarball=$TEST_ROOT/tarball.tar.xz
+(cd $TEST_ROOT && tar c tarball) | xz > $tarball
+
+nix-env -f file://$tarball -qa --out-path | grep -q dependencies
+
+nix-build -o $TEST_ROOT/result file://$tarball
+
+nix-build -o $TEST_ROOT/result '<foo>' -I foo=file://$tarball
+
+nix-build -o $TEST_ROOT/result -E "import (fetchTarball file://$tarball)"
+
+nix-instantiate --eval -E '1 + 2' -I fnord=file://no-such-tarball.tar.xz
+nix-instantiate --eval -E 'with <fnord/xyzzy>; 1 + 2' -I fnord=file://no-such-tarball.tar.xz
+(! nix-instantiate --eval -E '<fnord/xyzzy> 1' -I fnord=file://no-such-tarball.tar.xz)
+
+nix-instantiate --eval -E '<fnord/config.nix>' -I fnord=file://no-such-tarball.tar.xz -I fnord=.
diff --git a/third_party/nix/tests/timeout.nix b/third_party/nix/tests/timeout.nix
new file mode 100644
index 000000000000..d0e949e31498
--- /dev/null
+++ b/third_party/nix/tests/timeout.nix
@@ -0,0 +1,31 @@
+with import ./config.nix;
+
+{
+
+  infiniteLoop = mkDerivation {
+    name = "timeout";
+    buildCommand = ''
+      touch $out
+      echo "'timeout' builder entering an infinite loop"
+      while true ; do echo -n .; done
+    '';
+  };
+
+  silent = mkDerivation {
+    name = "silent";
+    buildCommand = ''
+      touch $out
+      sleep 60
+    '';
+  };
+
+  closeLog = mkDerivation {
+    name = "silent";
+    buildCommand = ''
+      touch $out
+      exec > /dev/null 2>&1
+      sleep 1000000000
+    '';
+  };
+
+}
diff --git a/third_party/nix/tests/timeout.sh b/third_party/nix/tests/timeout.sh
new file mode 100644
index 000000000000..eea9b5731da0
--- /dev/null
+++ b/third_party/nix/tests/timeout.sh
@@ -0,0 +1,40 @@
+# Test the `--timeout' option.
+
+source common.sh
+
+
+set +e
+messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1)
+status=$?
+set -e
+
+if [ $status -ne 101 ]; then
+    echo "error: 'nix-store' exited with '$status'; should have exited 101"
+    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 -Q timeout.nix -A infiniteLoop --max-build-log-size 100; then
+    echo "build should have failed"
+    exit 1
+fi
+
+if nix-build timeout.nix -A silent --max-silent-time 2; then
+    echo "build should have failed"
+    exit 1
+fi
+
+if nix-build timeout.nix -A closeLog; then
+    echo "build should have failed"
+    exit 1
+fi
+
+if nix build -f timeout.nix silent --max-silent-time 2; then
+    echo "build should have failed"
+    exit 1
+fi
diff --git a/third_party/nix/tests/user-envs.builder.sh b/third_party/nix/tests/user-envs.builder.sh
new file mode 100644
index 000000000000..5fafa797f11e
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/user-envs.nix b/third_party/nix/tests/user-envs.nix
new file mode 100644
index 000000000000..1aa410cc9680
--- /dev/null
+++ b/third_party/nix/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/third_party/nix/tests/user-envs.sh b/third_party/nix/tests/user-envs.sh
new file mode 100644
index 000000000000..aebf6a2a2b87
--- /dev/null
+++ b/third_party/nix/tests/user-envs.sh
@@ -0,0 +1,181 @@
+source common.sh
+
+if [ -z "$storeCleared" ]; then
+    clearStore
+fi
+
+clearProfiles
+
+# Query installed: should be empty.
+test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0
+
+mkdir -p $TEST_HOME
+nix-env --switch-profile $profiles/test
+
+# Query available: should contain several.
+test "$(nix-env -f ./user-envs.nix -qa '*' | wc -l)" -eq 6
+outPath10=$(nix-env -f ./user-envs.nix -qa --out-path --no-name '*' | grep foo-1.0)
+drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1.0)
+[ -n "$outPath10" -a -n "$drvPath10" ]
+
+# Query descriptions.
+nix-env -f ./user-envs.nix -qa '*' --description | grep -q silly
+rm -rf $HOME/.nix-defexpr
+ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr
+nix-env -qa '*' --description | grep -q silly
+
+# Query the system.
+nix-env -qa '*' --system | grep -q $system
+
+# Install "foo-1.0".
+nix-env -i foo-1.0
+
+# Query installed: should contain foo-1.0 now (which should be
+# executable).
+test "$(nix-env -q '*' | wc -l)" -eq 1
+nix-env -q '*' | grep -q foo-1.0
+test "$($profiles/test/bin/foo)" = "foo-1.0"
+
+# Test nix-env -qc to compare installed against available packages, and vice versa.
+nix-env -qc '*' | grep -q '< 2.0'
+nix-env -qac '*' | grep -q '> 1.0'
+
+# Test the -b flag to filter out source-only packages.
+[ "$(nix-env -qab | wc -l)" -eq 1 ]
+
+# Test the -s flag to get package status.
+nix-env -qas | grep -q 'IP-  foo-1.0'
+nix-env -qas | grep -q -- '---  bar-0.1'
+
+# Disable foo.
+nix-env --set-flag active false foo
+(! [ -e "$profiles/test/bin/foo" ])
+
+# Enable foo.
+nix-env --set-flag active true foo
+[ -e "$profiles/test/bin/foo" ]
+
+# Store the path of foo-1.0.
+outPath10_=$(nix-env -q --out-path --no-name '*' | grep foo-1.0)
+echo "foo-1.0 = $outPath10"
+[ "$outPath10" = "$outPath10_" ]
+
+# Install "foo-2.0pre1": should remove foo-1.0.
+nix-env -i foo-2.0pre1
+
+# Query installed: should contain foo-2.0pre1 now.
+test "$(nix-env -q '*' | wc -l)" -eq 1
+nix-env -q '*' | 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 -f '<nixpkgs>' -u foo
+
+# Query installed: should contain foo-2.0 now.
+test "$(nix-env -q '*' | wc -l)" -eq 1
+nix-env -q '*' | grep -q foo-2.0
+test "$($profiles/test/bin/foo)" = "foo-2.0"
+
+# Store the path of foo-2.0.
+outPath20=$(nix-env -q --out-path --no-name '*' | grep foo-2.0)
+test -n "$outPath20"
+
+# Install bar-0.1, uninstall foo.
+nix-env -i bar-0.1
+nix-env -e foo
+
+# Query installed: should only contain bar-0.1 now.
+if nix-env -q '*' | grep -q foo; then false; fi
+nix-env -q '*' | grep -q bar
+
+# Rollback: should bring "foo" back.
+oldGen="$(nix-store -q --resolve $profiles/test)"
+nix-env --rollback
+[ "$(nix-store -q --resolve $profiles/test)" != "$oldGen" ]
+nix-env -q '*' | grep -q foo-2.0
+nix-env -q '*' | grep -q bar
+
+# Rollback again: should remove "bar".
+nix-env --rollback
+nix-env -q '*' | grep -q foo-2.0
+if nix-env -q '*' | grep -q bar; then false; fi
+
+# Count generations.
+nix-env --list-generations
+test "$(nix-env --list-generations | wc -l)" -eq 7
+
+# Doing the same operation twice results in the same generation, which triggers
+# "lazy" behaviour and does not create a new symlink.
+
+nix-env -i foo
+nix-env -i foo
+
+# Count generations.
+nix-env --list-generations
+test "$(nix-env --list-generations | wc -l)" -eq 8
+
+# Switch to a specified generation.
+nix-env --switch-generation 7
+[ "$(nix-store -q --resolve $profiles/test)" = "$oldGen" ]
+
+# Install foo-1.0, now using its store path.
+nix-env -i "$outPath10"
+nix-env -q '*' | grep -q foo-1.0
+nix-store -qR $profiles/test | grep "$outPath10"
+nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve $profiles/test)"
+[ "$(nix-store -q --deriver "$outPath10")" = $drvPath10 ]
+
+# Uninstall foo-1.0, using a symlink to its store path.
+ln -sfn $outPath10/bin/foo $TEST_ROOT/symlink
+nix-env -e $TEST_ROOT/symlink
+if nix-env -q '*' | grep -q foo; then false; fi
+(! nix-store -qR $profiles/test | grep "$outPath10")
+
+# Install foo-1.0, now using a symlink to its store path.
+nix-env -i $TEST_ROOT/symlink
+nix-env -q '*' | grep -q foo
+
+# Delete all old generations.
+nix-env --delete-generations old
+
+# Run the garbage collector.  This should get rid of foo-2.0 but not
+# foo-1.0.
+nix-collect-garbage
+test -e "$outPath10"
+(! [ -e "$outPath20" ])
+
+# Uninstall everything
+nix-env -e '*'
+test "$(nix-env -q '*' | wc -l)" -eq 0
+
+# Installing "foo" should only install the newest foo.
+nix-env -i foo
+test "$(nix-env -q '*' | grep foo- | wc -l)" -eq 1
+nix-env -q '*' | grep -q foo-2.0
+
+# On the other hand, this should install both (and should fail due to
+# a collision).
+nix-env -e '*'
+(! nix-env -i foo-1.0 foo-2.0)
+
+# Installing "*" should install one foo and one bar.
+nix-env -e '*'
+nix-env -i '*'
+test "$(nix-env -q '*' | wc -l)" -eq 2
+nix-env -q '*' | grep -q foo-2.0
+nix-env -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 -e '*'
+nix-env -i foo-0.1 foo-1.0
+[ "$($profiles/test/bin/foo)" = "foo-1.0" ]
+nix-env --set-flag priority 1 foo-0.1
+[ "$($profiles/test/bin/foo)" = "foo-0.1" ]
+
+# Test nix-env --set.
+nix-env --set $outPath10
+[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ]
+nix-env --set $drvPath10
+[ "$(nix-store -q --resolve $profiles/test)" = $outPath10 ]