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.sh70
-rw-r--r--third_party/nix/tests/lang/binary-databin0 -> 1024 bytes
-rw-r--r--third_party/nix/tests/lang/data1
-rw-r--r--third_party/nix/tests/lang/dir1/a.nix1
-rw-r--r--third_party/nix/tests/lang/dir2/a.nix1
-rw-r--r--third_party/nix/tests/lang/dir2/b.nix1
-rw-r--r--third_party/nix/tests/lang/dir3/a.nix1
-rw-r--r--third_party/nix/tests/lang/dir3/b.nix1
-rw-r--r--third_party/nix/tests/lang/dir3/c.nix1
-rw-r--r--third_party/nix/tests/lang/dir4/a.nix1
-rw-r--r--third_party/nix/tests/lang/dir4/c.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-abort.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-antiquoted-path.nix4
-rw-r--r--third_party/nix/tests/lang/eval-fail-assert.nix5
-rw-r--r--third_party/nix/tests/lang/eval-fail-bad-antiquote-1.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-bad-antiquote-2.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-bad-antiquote-3.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-blackhole.nix5
-rw-r--r--third_party/nix/tests/lang/eval-fail-deepseq.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-hashfile-missing.nix5
-rw-r--r--third_party/nix/tests/lang/eval-fail-missing-arg.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-path-slash.nix6
-rw-r--r--third_party/nix/tests/lang/eval-fail-remove.nix5
-rw-r--r--third_party/nix/tests/lang/eval-fail-scope-5.nix10
-rw-r--r--third_party/nix/tests/lang/eval-fail-seq.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-substring.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-to-path.nix1
-rw-r--r--third_party/nix/tests/lang/eval-fail-undeclared-arg.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-any-all.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-any-all.nix11
-rw-r--r--third_party/nix/tests/lang/eval-okay-arithmetic.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-arithmetic.nix59
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrnames.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrnames.nix11
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs2.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs2.nix10
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs3.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs3.nix22
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs4.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs4.nix7
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs5.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs5.nix21
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs6.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-attrs6.nix4
-rw-r--r--third_party/nix/tests/lang/eval-okay-autoargs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-autoargs.flags1
-rw-r--r--third_party/nix/tests/lang/eval-okay-autoargs.nix15
-rw-r--r--third_party/nix/tests/lang/eval-okay-backslash-newline-1.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-backslash-newline-1.nix2
-rw-r--r--third_party/nix/tests/lang/eval-okay-backslash-newline-2.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-backslash-newline-2.nix2
-rw-r--r--third_party/nix/tests/lang/eval-okay-builtins-add.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-builtins-add.nix8
-rw-r--r--third_party/nix/tests/lang/eval-okay-builtins.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-builtins.nix12
-rw-r--r--third_party/nix/tests/lang/eval-okay-callable-attrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-callable-attrs.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-catattrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-catattrs.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-closure.exp.xml343
-rw-r--r--third_party/nix/tests/lang/eval-okay-closure.nix13
-rw-r--r--third_party/nix/tests/lang/eval-okay-comments.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-comments.nix59
-rw-r--r--third_party/nix/tests/lang/eval-okay-concat.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-concat.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-concatmap.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-concatmap.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-concatstringssep.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-concatstringssep.nix8
-rw-r--r--third_party/nix/tests/lang/eval-okay-context-introspection.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-context-introspection.nix24
-rw-r--r--third_party/nix/tests/lang/eval-okay-context.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-context.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-curpos.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-curpos.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-deepseq.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-deepseq.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-delayed-with-inherit.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-delayed-with-inherit.nix24
-rw-r--r--third_party/nix/tests/lang/eval-okay-delayed-with.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-delayed-with.nix29
-rw-r--r--third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.nix17
-rw-r--r--third_party/nix/tests/lang/eval-okay-dynamic-attrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-dynamic-attrs.nix17
-rw-r--r--third_party/nix/tests/lang/eval-okay-elem.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-elem.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-empty-args.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-empty-args.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-eq-derivations.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-eq-derivations.nix10
-rw-r--r--third_party/nix/tests/lang/eval-okay-eq.exp.disabled1
-rw-r--r--third_party/nix/tests/lang/eval-okay-eq.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-filter.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-filter.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-flatten.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-flatten.nix8
-rw-r--r--third_party/nix/tests/lang/eval-okay-float.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-float.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-fromTOML.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-fromTOML.nix208
-rw-r--r--third_party/nix/tests/lang/eval-okay-fromjson.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-fromjson.nix36
-rw-r--r--third_party/nix/tests/lang/eval-okay-functionargs.exp.xml15
-rw-r--r--third_party/nix/tests/lang/eval-okay-functionargs.nix80
-rw-r--r--third_party/nix/tests/lang/eval-okay-getattrpos-undefined.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-getattrpos-undefined.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-getattrpos.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-getattrpos.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-getenv.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-getenv.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-hash.exp0
-rw-r--r--third_party/nix/tests/lang/eval-okay-hashfile.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-hashfile.nix4
-rw-r--r--third_party/nix/tests/lang/eval-okay-hashstring.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-hashstring.nix4
-rw-r--r--third_party/nix/tests/lang/eval-okay-if.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-if.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-import.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-import.nix11
-rw-r--r--third_party/nix/tests/lang/eval-okay-ind-string.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-ind-string.nix128
-rw-r--r--third_party/nix/tests/lang/eval-okay-let.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-let.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-list.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-list.nix7
-rw-r--r--third_party/nix/tests/lang/eval-okay-listtoattrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-listtoattrs.nix11
-rw-r--r--third_party/nix/tests/lang/eval-okay-logic.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-logic.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-map.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-map.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-mapattrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-mapattrs.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-nested-with.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-nested-with.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-new-let.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-new-let.nix14
-rw-r--r--third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-overrides.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-overrides.nix9
-rw-r--r--third_party/nix/tests/lang/eval-okay-partition.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-partition.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-path.nix7
-rw-r--r--third_party/nix/tests/lang/eval-okay-pathexists.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-pathexists.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-patterns.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-patterns.nix16
-rw-r--r--third_party/nix/tests/lang/eval-okay-readDir.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-readDir.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-readfile.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-readfile.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-redefine-builtin.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-redefine-builtin.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-regex-match.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-regex-match.nix29
-rw-r--r--third_party/nix/tests/lang/eval-okay-regex-split.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-regex-split.nix48
-rw-r--r--third_party/nix/tests/lang/eval-okay-remove.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-remove.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-replacestrings.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-replacestrings.nix11
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-1.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-1.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-2.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-2.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-3.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-3.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-4.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-4.nix10
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-6.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-6.nix7
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-7.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-scope-7.nix6
-rw-r--r--third_party/nix/tests/lang/eval-okay-search-path.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-search-path.flags1
-rw-r--r--third_party/nix/tests/lang/eval-okay-search-path.nix11
-rw-r--r--third_party/nix/tests/lang/eval-okay-seq.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-seq.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-sort.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-sort.nix8
-rw-r--r--third_party/nix/tests/lang/eval-okay-splitversion.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-splitversion.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-string.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-string.nix12
-rw-r--r--third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.nix20
-rw-r--r--third_party/nix/tests/lang/eval-okay-substring.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-substring.nix21
-rw-r--r--third_party/nix/tests/lang/eval-okay-tail-call-1.exp-disabled1
-rw-r--r--third_party/nix/tests/lang/eval-okay-tail-call-1.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-tojson.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-tojson.nix13
-rw-r--r--third_party/nix/tests/lang/eval-okay-toxml.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-toxml.nix3
-rw-r--r--third_party/nix/tests/lang/eval-okay-toxml2.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-toxml2.nix1
-rw-r--r--third_party/nix/tests/lang/eval-okay-tryeval.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-tryeval.nix5
-rw-r--r--third_party/nix/tests/lang/eval-okay-types.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-types.nix37
-rw-r--r--third_party/nix/tests/lang/eval-okay-versions.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-versions.nix40
-rw-r--r--third_party/nix/tests/lang/eval-okay-with.exp1
-rw-r--r--third_party/nix/tests/lang/eval-okay-with.nix19
-rw-r--r--third_party/nix/tests/lang/eval-okay-xml.exp.xml52
-rw-r--r--third_party/nix/tests/lang/eval-okay-xml.nix21
-rw-r--r--third_party/nix/tests/lang/imported.nix3
-rw-r--r--third_party/nix/tests/lang/imported2.nix1
-rw-r--r--third_party/nix/tests/lang/lib.nix61
-rw-r--r--third_party/nix/tests/lang/parse-fail-dup-attrs-1.nix4
-rw-r--r--third_party/nix/tests/lang/parse-fail-dup-attrs-2.nix13
-rw-r--r--third_party/nix/tests/lang/parse-fail-dup-attrs-3.nix13
-rw-r--r--third_party/nix/tests/lang/parse-fail-dup-attrs-4.nix4
-rw-r--r--third_party/nix/tests/lang/parse-fail-dup-attrs-7.nix9
-rw-r--r--third_party/nix/tests/lang/parse-fail-dup-formals.nix1
-rw-r--r--third_party/nix/tests/lang/parse-fail-mixed-nested-attrs1.nix4
-rw-r--r--third_party/nix/tests/lang/parse-fail-mixed-nested-attrs2.nix4
-rw-r--r--third_party/nix/tests/lang/parse-fail-patterns-1.nix1
-rw-r--r--third_party/nix/tests/lang/parse-fail-regression-20060610.nix11
-rw-r--r--third_party/nix/tests/lang/parse-fail-uft8.nix1
-rw-r--r--third_party/nix/tests/lang/parse-fail-undef-var-2.nix7
-rw-r--r--third_party/nix/tests/lang/parse-fail-undef-var.nix1
-rw-r--r--third_party/nix/tests/lang/parse-okay-1.nix1
-rw-r--r--third_party/nix/tests/lang/parse-okay-crlf.nix17
-rw-r--r--third_party/nix/tests/lang/parse-okay-dup-attrs-5.nix4
-rw-r--r--third_party/nix/tests/lang/parse-okay-dup-attrs-6.nix4
-rw-r--r--third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-1.nix4
-rw-r--r--third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-2.nix4
-rw-r--r--third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-3.nix7
-rw-r--r--third_party/nix/tests/lang/parse-okay-regression-20041027.nix11
-rw-r--r--third_party/nix/tests/lang/parse-okay-regression-751.nix2
-rw-r--r--third_party/nix/tests/lang/parse-okay-subversion.nix43
-rw-r--r--third_party/nix/tests/lang/parse-okay-url.nix7
-rw-r--r--third_party/nix/tests/lang/readDir/bar0
-rw-r--r--third_party/nix/tests/lang/readDir/foo/git-hates-directories0
-rw-r--r--third_party/nix/tests/linux-sandbox.sh30
-rw-r--r--third_party/nix/tests/local.mk42
-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.sh17
-rw-r--r--third_party/nix/tests/placeholders.sh20
-rw-r--r--third_party/nix/tests/plugins.sh7
-rw-r--r--third_party/nix/tests/plugins/local.mk9
-rw-r--r--third_party/nix/tests/plugins/plugintest.cc24
-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
345 files changed, 6262 insertions, 0 deletions
diff --git a/third_party/nix/tests/add.sh b/third_party/nix/tests/add.sh
new file mode 100644
index 0000000000..e26e05843d
--- /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 0000000000..eb58ae7c12
--- /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 0000000000..a3c6e55a8f
--- /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 0000000000..e72533e706
--- /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 0000000000..8bff0fe790
--- /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 0000000000..ddd68f327a
--- /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 0000000000..61bf9b94bf
--- /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 0000000000..22ff26db5a
--- /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 0000000000..9d90b09205
--- /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 0000000000..16bbabc409
--- /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 0000000000..41436cb48e
--- /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 0000000000..e9f65fc2a6
--- /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 0000000000..56c82e565a
--- /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 0000000000..bc23a6634c
--- /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 0000000000..15d7b1ef91
--- /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 0000000000..6ba91065b8
--- /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 0000000000..c37bf909a5
--- /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 0000000000..4b006a17d7
--- /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 0000000000..4f886fdb3a
--- /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 0000000000..eca4b2964c
--- /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 0000000000..df204d185d
--- /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 0000000000..d6eea42aa0
--- /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 0000000000..fdac9583db
--- /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 0000000000..a6fd690544
--- /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 0000000000..2238539bcc
--- /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 0000000000..4c46bdf046
--- /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 0000000000..4088dbd397
--- /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 0000000000..7319ced2b5
--- /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 0000000000..9071636394
--- /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 0000000000..1f8dceee57
--- /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 0000000000..c41bb2b9a6
--- /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 0000000000..31ea1579a5
--- /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 0000000000..76580ffa19
--- /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 0000000000..8f51403a70
--- /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 0000000000..182a4d5c28
--- /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 0000000000..de1e2cfe40
--- /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 0000000000..0cd67df3ae
--- /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 0000000000..c0595cc471
--- /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 0000000000..d395930ca0
--- /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 0000000000..4bfb33103e
--- /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 0000000000..ee5980bdff
--- /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 0000000000..4c5028005c
--- /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 0000000000..8b4f8d2821
--- /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 0000000000..4a8e9b8a8d
--- /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 0000000000..4cfc979010
--- /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 0000000000..44fa9a45d7
--- /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 0000000000..98d61ef49b
--- /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 0000000000..19a12c1e2d
--- /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 0000000000..9933eba944
--- /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 0000000000..c797a2a74e
--- /dev/null
+++ b/third_party/nix/tests/lang.sh
@@ -0,0 +1,70 @@
+source common.sh
+
+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/lang/binary-data b/third_party/nix/tests/lang/binary-data
new file mode 100644
index 0000000000..06d7405020
--- /dev/null
+++ b/third_party/nix/tests/lang/binary-data
Binary files differdiff --git a/third_party/nix/tests/lang/data b/third_party/nix/tests/lang/data
new file mode 100644
index 0000000000..257cc5642c
--- /dev/null
+++ b/third_party/nix/tests/lang/data
@@ -0,0 +1 @@
+foo
diff --git a/third_party/nix/tests/lang/dir1/a.nix b/third_party/nix/tests/lang/dir1/a.nix
new file mode 100644
index 0000000000..231f150c57
--- /dev/null
+++ b/third_party/nix/tests/lang/dir1/a.nix
@@ -0,0 +1 @@
+"a"
diff --git a/third_party/nix/tests/lang/dir2/a.nix b/third_party/nix/tests/lang/dir2/a.nix
new file mode 100644
index 0000000000..170df520ab
--- /dev/null
+++ b/third_party/nix/tests/lang/dir2/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/third_party/nix/tests/lang/dir2/b.nix b/third_party/nix/tests/lang/dir2/b.nix
new file mode 100644
index 0000000000..19010cc35c
--- /dev/null
+++ b/third_party/nix/tests/lang/dir2/b.nix
@@ -0,0 +1 @@
+"b"
diff --git a/third_party/nix/tests/lang/dir3/a.nix b/third_party/nix/tests/lang/dir3/a.nix
new file mode 100644
index 0000000000..170df520ab
--- /dev/null
+++ b/third_party/nix/tests/lang/dir3/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/third_party/nix/tests/lang/dir3/b.nix b/third_party/nix/tests/lang/dir3/b.nix
new file mode 100644
index 0000000000..170df520ab
--- /dev/null
+++ b/third_party/nix/tests/lang/dir3/b.nix
@@ -0,0 +1 @@
+"X"
diff --git a/third_party/nix/tests/lang/dir3/c.nix b/third_party/nix/tests/lang/dir3/c.nix
new file mode 100644
index 0000000000..cdf158597e
--- /dev/null
+++ b/third_party/nix/tests/lang/dir3/c.nix
@@ -0,0 +1 @@
+"c"
diff --git a/third_party/nix/tests/lang/dir4/a.nix b/third_party/nix/tests/lang/dir4/a.nix
new file mode 100644
index 0000000000..170df520ab
--- /dev/null
+++ b/third_party/nix/tests/lang/dir4/a.nix
@@ -0,0 +1 @@
+"X"
diff --git a/third_party/nix/tests/lang/dir4/c.nix b/third_party/nix/tests/lang/dir4/c.nix
new file mode 100644
index 0000000000..170df520ab
--- /dev/null
+++ b/third_party/nix/tests/lang/dir4/c.nix
@@ -0,0 +1 @@
+"X"
diff --git a/third_party/nix/tests/lang/eval-fail-abort.nix b/third_party/nix/tests/lang/eval-fail-abort.nix
new file mode 100644
index 0000000000..75c51bceb5
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-abort.nix
@@ -0,0 +1 @@
+if true then abort "this should fail" else 1
diff --git a/third_party/nix/tests/lang/eval-fail-antiquoted-path.nix b/third_party/nix/tests/lang/eval-fail-antiquoted-path.nix
new file mode 100644
index 0000000000..f2f08107b5
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-antiquoted-path.nix
@@ -0,0 +1,4 @@
+# This must fail to evaluate, since ./fnord doesn't exist.  If it did
+# exist, it would produce "/nix/store/<hash>-fnord/xyzzy" (with an
+# appropriate context).
+"${./fnord}/xyzzy"
diff --git a/third_party/nix/tests/lang/eval-fail-assert.nix b/third_party/nix/tests/lang/eval-fail-assert.nix
new file mode 100644
index 0000000000..3b7a1e8bf0
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-assert.nix
@@ -0,0 +1,5 @@
+let {
+  x = arg: assert arg == "y"; 123;
+
+  body = x "x";
+}
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/eval-fail-bad-antiquote-1.nix b/third_party/nix/tests/lang/eval-fail-bad-antiquote-1.nix
new file mode 100644
index 0000000000..ffe9c983c2
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-bad-antiquote-1.nix
@@ -0,0 +1 @@
+"${x: x}"
diff --git a/third_party/nix/tests/lang/eval-fail-bad-antiquote-2.nix b/third_party/nix/tests/lang/eval-fail-bad-antiquote-2.nix
new file mode 100644
index 0000000000..3745235ce9
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-bad-antiquote-2.nix
@@ -0,0 +1 @@
+"${./fnord}"
diff --git a/third_party/nix/tests/lang/eval-fail-bad-antiquote-3.nix b/third_party/nix/tests/lang/eval-fail-bad-antiquote-3.nix
new file mode 100644
index 0000000000..65b9d4f505
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-bad-antiquote-3.nix
@@ -0,0 +1 @@
+''${x: x}''
diff --git a/third_party/nix/tests/lang/eval-fail-blackhole.nix b/third_party/nix/tests/lang/eval-fail-blackhole.nix
new file mode 100644
index 0000000000..81133b511c
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-blackhole.nix
@@ -0,0 +1,5 @@
+let {
+  body = x;
+  x = y;
+  y = x;
+}
diff --git a/third_party/nix/tests/lang/eval-fail-deepseq.nix b/third_party/nix/tests/lang/eval-fail-deepseq.nix
new file mode 100644
index 0000000000..9baa49b063
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-deepseq.nix
@@ -0,0 +1 @@
+builtins.deepSeq { x = abort "foo"; } 456
diff --git a/third_party/nix/tests/lang/eval-fail-hashfile-missing.nix b/third_party/nix/tests/lang/eval-fail-hashfile-missing.nix
new file mode 100644
index 0000000000..ce098b8238
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-hashfile-missing.nix
@@ -0,0 +1,5 @@
+let
+  paths = [ ./this-file-is-definitely-not-there-7392097 "/and/neither/is/this/37293620" ];
+in
+  toString (builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"]))
+
diff --git a/third_party/nix/tests/lang/eval-fail-missing-arg.nix b/third_party/nix/tests/lang/eval-fail-missing-arg.nix
new file mode 100644
index 0000000000..c4be9797c5
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-missing-arg.nix
@@ -0,0 +1 @@
+({x, y, z}: x + y + z) {x = "foo"; z = "bar";}
diff --git a/third_party/nix/tests/lang/eval-fail-path-slash.nix b/third_party/nix/tests/lang/eval-fail-path-slash.nix
new file mode 100644
index 0000000000..8c2e104c78
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-path-slash.nix
@@ -0,0 +1,6 @@
+# Trailing slashes in paths are not allowed.
+# This restriction could be lifted sometime,
+# for example if we make '/' a path concatenation operator.
+# See https://github.com/NixOS/nix/issues/1138
+# and https://nixos.org/nix-dev/2016-June/020829.html
+/nix/store/
diff --git a/third_party/nix/tests/lang/eval-fail-remove.nix b/third_party/nix/tests/lang/eval-fail-remove.nix
new file mode 100644
index 0000000000..539e0eb0a6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-remove.nix
@@ -0,0 +1,5 @@
+let {
+  attrs = {x = 123; y = 456;};
+
+  body = (removeAttrs attrs ["x"]).x;
+}
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/eval-fail-scope-5.nix b/third_party/nix/tests/lang/eval-fail-scope-5.nix
new file mode 100644
index 0000000000..f89a65a99b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-scope-5.nix
@@ -0,0 +1,10 @@
+let {
+
+  x = "a";
+  y = "b";
+
+  f = {x ? y, y ? x}: x + y;
+
+  body = f {};
+
+}
diff --git a/third_party/nix/tests/lang/eval-fail-seq.nix b/third_party/nix/tests/lang/eval-fail-seq.nix
new file mode 100644
index 0000000000..cddbbfd326
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-seq.nix
@@ -0,0 +1 @@
+builtins.seq (abort "foo") 2
diff --git a/third_party/nix/tests/lang/eval-fail-substring.nix b/third_party/nix/tests/lang/eval-fail-substring.nix
new file mode 100644
index 0000000000..f37c2bc0a1
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-substring.nix
@@ -0,0 +1 @@
+builtins.substring (builtins.sub 0 1) 1 "x"
diff --git a/third_party/nix/tests/lang/eval-fail-to-path.nix b/third_party/nix/tests/lang/eval-fail-to-path.nix
new file mode 100644
index 0000000000..5e322bc313
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-to-path.nix
@@ -0,0 +1 @@
+builtins.toPath "foo/bar"
diff --git a/third_party/nix/tests/lang/eval-fail-undeclared-arg.nix b/third_party/nix/tests/lang/eval-fail-undeclared-arg.nix
new file mode 100644
index 0000000000..cafdf16362
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-fail-undeclared-arg.nix
@@ -0,0 +1 @@
+({x, z}: x + z) {x = "foo"; y = "bla"; z = "bar";}
diff --git a/third_party/nix/tests/lang/eval-okay-any-all.exp b/third_party/nix/tests/lang/eval-okay-any-all.exp
new file mode 100644
index 0000000000..eb273f45b2
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-any-all.exp
@@ -0,0 +1 @@
+[ false false true true true true false true ]
diff --git a/third_party/nix/tests/lang/eval-okay-any-all.nix b/third_party/nix/tests/lang/eval-okay-any-all.nix
new file mode 100644
index 0000000000..a3f26ea2aa
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-any-all.nix
@@ -0,0 +1,11 @@
+with builtins;
+
+[ (any (x: x == 1) [])
+  (any (x: x == 1) [2 3 4])
+  (any (x: x == 1) [1 2 3 4])
+  (any (x: x == 1) [4 3 2 1])
+  (all (x: x == 1) [])
+  (all (x: x == 1) [1])
+  (all (x: x == 1) [1 2 3])
+  (all (x: x == 1) [1 1 1])
+]
diff --git a/third_party/nix/tests/lang/eval-okay-arithmetic.exp b/third_party/nix/tests/lang/eval-okay-arithmetic.exp
new file mode 100644
index 0000000000..5c54d10b7b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-arithmetic.exp
@@ -0,0 +1 @@
+2216
diff --git a/third_party/nix/tests/lang/eval-okay-arithmetic.nix b/third_party/nix/tests/lang/eval-okay-arithmetic.nix
new file mode 100644
index 0000000000..7e9e6a0b66
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-arithmetic.nix
@@ -0,0 +1,59 @@
+with import ./lib.nix;
+
+let {
+
+  /* Supposedly tail recursive version:
+
+  range_ = accum: first: last:
+    if first == last then ([first] ++ accum)
+    else range_ ([first] ++ accum) (builtins.add first 1) last;
+
+  range = range_ [];
+  */
+
+  x = 12;
+
+  err = abort "urgh";
+
+  body = sum
+    [ (sum (range 1 50))
+      (123 + 456)
+      (0 + -10 + -(-11) + -x)
+      (10 - 7 - -2)
+      (10 - (6 - -1))
+      (10 - 1 + 2)
+      (3 * 4 * 5)
+      (56088 / 123 / 2)
+      (3 + 4 * const 5 0 - 6 / id 2)
+
+      (builtins.bitAnd 12 10) # 0b1100 & 0b1010 =  8
+      (builtins.bitOr  12 10) # 0b1100 | 0b1010 = 14
+      (builtins.bitXor 12 10) # 0b1100 ^ 0b1010 =  6
+
+      (if 3 < 7 then 1 else err)
+      (if 7 < 3 then err else 1)
+      (if 3 < 3 then err else 1)
+
+      (if 3 <= 7 then 1 else err)
+      (if 7 <= 3 then err else 1)
+      (if 3 <= 3 then 1 else err)
+
+      (if 3 > 7 then err else 1)
+      (if 7 > 3 then 1 else err)
+      (if 3 > 3 then err else 1)
+
+      (if 3 >= 7 then err else 1)
+      (if 7 >= 3 then 1 else err)
+      (if 3 >= 3 then 1 else err)
+
+      (if 2 > 1 == 1 < 2 then 1 else err)
+      (if 1 + 2 * 3 >= 7 then 1 else err)
+      (if 1 + 2 * 3 < 7 then err else 1)
+
+      # Not integer, but so what.
+      (if "aa" < "ab" then 1 else err)
+      (if "aa" < "aa" then err else 1)
+      (if "foo" < "foobar" then 1 else err)
+    ];
+
+}
diff --git a/third_party/nix/tests/lang/eval-okay-attrnames.exp b/third_party/nix/tests/lang/eval-okay-attrnames.exp
new file mode 100644
index 0000000000..b4aa387e07
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrnames.exp
@@ -0,0 +1 @@
+"newxfoonewxy"
diff --git a/third_party/nix/tests/lang/eval-okay-attrnames.nix b/third_party/nix/tests/lang/eval-okay-attrnames.nix
new file mode 100644
index 0000000000..e5b26e9f2e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrnames.nix
@@ -0,0 +1,11 @@
+with import ./lib.nix;
+
+let
+
+  attrs = {y = "y"; x = "x"; foo = "foo";} // rec {x = "newx"; bar = x;};
+
+  names = builtins.attrNames attrs;
+
+  values = map (name: builtins.getAttr name attrs) names;
+
+in assert values == builtins.attrValues attrs; concat values
diff --git a/third_party/nix/tests/lang/eval-okay-attrs.exp b/third_party/nix/tests/lang/eval-okay-attrs.exp
new file mode 100644
index 0000000000..45b0f829eb
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs.exp
@@ -0,0 +1 @@
+987
diff --git a/third_party/nix/tests/lang/eval-okay-attrs.nix b/third_party/nix/tests/lang/eval-okay-attrs.nix
new file mode 100644
index 0000000000..810b31a5da
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs.nix
@@ -0,0 +1,5 @@
+let {
+  as = { x = 123; y = 456; } // { z = 789; } // { z = 987; };
+
+  body = if as ? a then as.a else assert as ? z; as.z;
+}
diff --git a/third_party/nix/tests/lang/eval-okay-attrs2.exp b/third_party/nix/tests/lang/eval-okay-attrs2.exp
new file mode 100644
index 0000000000..45b0f829eb
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs2.exp
@@ -0,0 +1 @@
+987
diff --git a/third_party/nix/tests/lang/eval-okay-attrs2.nix b/third_party/nix/tests/lang/eval-okay-attrs2.nix
new file mode 100644
index 0000000000..9e06b83ac1
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs2.nix
@@ -0,0 +1,10 @@
+let {
+  as = { x = 123; y = 456; } // { z = 789; } // { z = 987; };
+
+  A = "a";
+  Z = "z";
+
+  body = if builtins.hasAttr A as
+         then builtins.getAttr A as
+         else assert builtins.hasAttr Z as; builtins.getAttr Z as;
+}
diff --git a/third_party/nix/tests/lang/eval-okay-attrs3.exp b/third_party/nix/tests/lang/eval-okay-attrs3.exp
new file mode 100644
index 0000000000..19de4fdf79
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs3.exp
@@ -0,0 +1 @@
+"foo 22 80 itchyxac"
diff --git a/third_party/nix/tests/lang/eval-okay-attrs3.nix b/third_party/nix/tests/lang/eval-okay-attrs3.nix
new file mode 100644
index 0000000000..f29de11fe6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs3.nix
@@ -0,0 +1,22 @@
+let
+
+  config = 
+    {
+      services.sshd.enable = true;
+      services.sshd.port = 22;
+      services.httpd.port = 80;
+      hostName = "itchy";
+      a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z = "x";
+      foo = {
+        a = "a";
+        b.c = "c";
+      };
+    };
+
+in
+  if config.services.sshd.enable
+  then "foo ${toString config.services.sshd.port} ${toString config.services.httpd.port} ${config.hostName}"
+       + "${config.a.b.c.d.e.f.g.h.i.j.k.l.m.n.o.p.q.r.s.t.u.v.w.x.y.z}"
+       + "${config.foo.a}"
+       + "${config.foo.b.c}"
+  else "bar"
diff --git a/third_party/nix/tests/lang/eval-okay-attrs4.exp b/third_party/nix/tests/lang/eval-okay-attrs4.exp
new file mode 100644
index 0000000000..1851731442
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs4.exp
@@ -0,0 +1 @@
+[ true false true false false true false false ]
diff --git a/third_party/nix/tests/lang/eval-okay-attrs4.nix b/third_party/nix/tests/lang/eval-okay-attrs4.nix
new file mode 100644
index 0000000000..43ec81210f
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs4.nix
@@ -0,0 +1,7 @@
+let
+
+  as = { x.y.z = 123; a.b.c = 456; };
+
+  bs = null;
+
+in [ (as ? x) (as ? y) (as ? x.y.z) (as ? x.y.z.a) (as ? x.y.a) (as ? a.b.c) (bs ? x) (bs ? x.y.z) ]
diff --git a/third_party/nix/tests/lang/eval-okay-attrs5.exp b/third_party/nix/tests/lang/eval-okay-attrs5.exp
new file mode 100644
index 0000000000..ce0430d780
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs5.exp
@@ -0,0 +1 @@
+[ 123 "foo" 456 456 "foo" "xyzzy" "xyzzy" true ]
diff --git a/third_party/nix/tests/lang/eval-okay-attrs5.nix b/third_party/nix/tests/lang/eval-okay-attrs5.nix
new file mode 100644
index 0000000000..a4584cd3b3
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs5.nix
@@ -0,0 +1,21 @@
+with import ./lib.nix;
+
+let
+
+  as = { x.y.z = 123; a.b.c = 456; };
+
+  bs = { f-o-o.bar = "foo"; };
+
+  or = x: y: x || y;
+  
+in
+  [ as.x.y.z
+    as.foo or "foo"
+    as.x.y.bla or as.a.b.c
+    as.a.b.c or as.x.y.z
+    as.x.y.bla or bs.f-o-o.bar or "xyzzy"
+    as.x.y.bla or bs.bar.foo or "xyzzy"
+    (123).bla or null.foo or "xyzzy"
+    # Backwards compatibility test.
+    (fold or [] [true false false])
+  ]
diff --git a/third_party/nix/tests/lang/eval-okay-attrs6.exp b/third_party/nix/tests/lang/eval-okay-attrs6.exp
new file mode 100644
index 0000000000..b46938032e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs6.exp
@@ -0,0 +1 @@
+{ __overrides = { bar = "qux"; }; bar = "qux"; foo = "bar"; }
diff --git a/third_party/nix/tests/lang/eval-okay-attrs6.nix b/third_party/nix/tests/lang/eval-okay-attrs6.nix
new file mode 100644
index 0000000000..2e5c85483b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-attrs6.nix
@@ -0,0 +1,4 @@
+rec {
+  "${"foo"}" = "bar";
+   __overrides = { bar = "qux"; };
+}
diff --git a/third_party/nix/tests/lang/eval-okay-autoargs.exp b/third_party/nix/tests/lang/eval-okay-autoargs.exp
new file mode 100644
index 0000000000..7a8391786a
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-autoargs.exp
@@ -0,0 +1 @@
+"xyzzy!xyzzy!foobar"
diff --git a/third_party/nix/tests/lang/eval-okay-autoargs.flags b/third_party/nix/tests/lang/eval-okay-autoargs.flags
new file mode 100644
index 0000000000..ae37622544
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-autoargs.flags
@@ -0,0 +1 @@
+--arg lib import(lang/lib.nix) --argstr xyzzy xyzzy! -A result
diff --git a/third_party/nix/tests/lang/eval-okay-autoargs.nix b/third_party/nix/tests/lang/eval-okay-autoargs.nix
new file mode 100644
index 0000000000..815f51b1d6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-autoargs.nix
@@ -0,0 +1,15 @@
+let
+
+  foobar = "foobar";
+
+in
+
+{ xyzzy2 ? xyzzy # mutually recursive args
+, xyzzy ? "blaat" # will be overridden by --argstr
+, fb ? foobar
+, lib # will be set by --arg
+}:
+
+{
+  result = lib.concat [xyzzy xyzzy2 fb];
+}
diff --git a/third_party/nix/tests/lang/eval-okay-backslash-newline-1.exp b/third_party/nix/tests/lang/eval-okay-backslash-newline-1.exp
new file mode 100644
index 0000000000..3e754364cc
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-backslash-newline-1.exp
@@ -0,0 +1 @@
+"a\nb"
diff --git a/third_party/nix/tests/lang/eval-okay-backslash-newline-1.nix b/third_party/nix/tests/lang/eval-okay-backslash-newline-1.nix
new file mode 100644
index 0000000000..7fef3dddd4
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-backslash-newline-1.nix
@@ -0,0 +1,2 @@
+"a\
+b"
diff --git a/third_party/nix/tests/lang/eval-okay-backslash-newline-2.exp b/third_party/nix/tests/lang/eval-okay-backslash-newline-2.exp
new file mode 100644
index 0000000000..3e754364cc
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-backslash-newline-2.exp
@@ -0,0 +1 @@
+"a\nb"
diff --git a/third_party/nix/tests/lang/eval-okay-backslash-newline-2.nix b/third_party/nix/tests/lang/eval-okay-backslash-newline-2.nix
new file mode 100644
index 0000000000..35ddf495c6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-backslash-newline-2.nix
@@ -0,0 +1,2 @@
+''a''\
+b''
diff --git a/third_party/nix/tests/lang/eval-okay-builtins-add.exp b/third_party/nix/tests/lang/eval-okay-builtins-add.exp
new file mode 100644
index 0000000000..0350b518a7
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-builtins-add.exp
@@ -0,0 +1 @@
+[ 5 4 "int" "tt" "float" 4 ]
diff --git a/third_party/nix/tests/lang/eval-okay-builtins-add.nix b/third_party/nix/tests/lang/eval-okay-builtins-add.nix
new file mode 100644
index 0000000000..c841816222
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-builtins-add.nix
@@ -0,0 +1,8 @@
+[
+(builtins.add 2 3)
+(builtins.add 2 2)
+(builtins.typeOf (builtins.add 2  2))
+("t" + "t")
+(builtins.typeOf (builtins.add 2.0 2))
+(builtins.add 2.0 2)
+]
diff --git a/third_party/nix/tests/lang/eval-okay-builtins.exp b/third_party/nix/tests/lang/eval-okay-builtins.exp
new file mode 100644
index 0000000000..0661686d61
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-builtins.exp
@@ -0,0 +1 @@
+/foo
diff --git a/third_party/nix/tests/lang/eval-okay-builtins.nix b/third_party/nix/tests/lang/eval-okay-builtins.nix
new file mode 100644
index 0000000000..e9d65e88a8
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-builtins.nix
@@ -0,0 +1,12 @@
+assert builtins ? currentSystem;
+assert !builtins ? __currentSystem;
+
+let {
+
+  x = if builtins ? dirOf then builtins.dirOf /foo/bar else "";
+
+  y = if builtins ? fnord then builtins.fnord "foo" else "";
+
+  body = x + y;
+  
+}
diff --git a/third_party/nix/tests/lang/eval-okay-callable-attrs.exp b/third_party/nix/tests/lang/eval-okay-callable-attrs.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-callable-attrs.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-callable-attrs.nix b/third_party/nix/tests/lang/eval-okay-callable-attrs.nix
new file mode 100644
index 0000000000..310a030df0
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-callable-attrs.nix
@@ -0,0 +1 @@
+({ __functor = self: x: self.foo && x; foo = false; } // { foo = true; }) true
diff --git a/third_party/nix/tests/lang/eval-okay-catattrs.exp b/third_party/nix/tests/lang/eval-okay-catattrs.exp
new file mode 100644
index 0000000000..b4a1e66d6b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-catattrs.exp
@@ -0,0 +1 @@
+[ 1 2 ]
diff --git a/third_party/nix/tests/lang/eval-okay-catattrs.nix b/third_party/nix/tests/lang/eval-okay-catattrs.nix
new file mode 100644
index 0000000000..2c3dc10da5
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-catattrs.nix
@@ -0,0 +1 @@
+builtins.catAttrs "a" [ { a = 1; } { b = 0; } { a = 2; } ]
diff --git a/third_party/nix/tests/lang/eval-okay-closure.exp.xml b/third_party/nix/tests/lang/eval-okay-closure.exp.xml
new file mode 100644
index 0000000000..dffc03a998
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-closure.exp.xml
@@ -0,0 +1,343 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-13" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-12" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-11" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-9" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-8" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-7" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-5" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-4" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="-3" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="-1" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="0" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="1" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="2" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="4" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="5" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="6" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="8" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="9" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="10" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="13" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="14" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="15" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="17" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="18" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="19" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="22" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="23" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="26" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="27" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="28" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="31" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="32" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="35" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="36" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="40" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="41" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="44" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="45" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="49" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="53" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="54" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="58" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="62" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="foo">
+        <bool value="true" />
+      </attr>
+      <attr name="key">
+        <int value="67" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="71" />
+      </attr>
+    </attrs>
+    <attrs>
+      <attr name="key">
+        <int value="80" />
+      </attr>
+    </attrs>
+  </list>
+</expr>
diff --git a/third_party/nix/tests/lang/eval-okay-closure.nix b/third_party/nix/tests/lang/eval-okay-closure.nix
new file mode 100644
index 0000000000..cccd4dc357
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-closure.nix
@@ -0,0 +1,13 @@
+let
+
+  closure = builtins.genericClosure {
+    startSet = [{key = 80;}];
+    operator = {key, foo ? false}:
+      if builtins.lessThan key 0
+      then []
+      else [{key = builtins.sub key 9;} {key = builtins.sub key 13; foo = true;}];
+  };
+
+  sort = (import ./lib.nix).sortBy (a: b: builtins.lessThan a.key b.key);
+
+in sort closure
diff --git a/third_party/nix/tests/lang/eval-okay-comments.exp b/third_party/nix/tests/lang/eval-okay-comments.exp
new file mode 100644
index 0000000000..7182dc2f9b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-comments.exp
@@ -0,0 +1 @@
+"abcdefghijklmnopqrstuvwxyz"
diff --git a/third_party/nix/tests/lang/eval-okay-comments.nix b/third_party/nix/tests/lang/eval-okay-comments.nix
new file mode 100644
index 0000000000..cb2cce2180
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-comments.nix
@@ -0,0 +1,59 @@
+# A simple comment
+"a"+ # And another
+## A double comment
+"b"+  ## And another
+# Nested # comments #
+"c"+   # and # some # other #
+# An empty line, following here:
+
+"d"+      # and a comment not starting the line !
+
+"e"+
+/* multiline comments */
+"f" +
+/* multiline
+   comments,
+   on
+   multiple
+   lines
+*/
+"g" +
+# Small, tricky comments
+/**/ "h"+ /*/*/ "i"+ /***/ "j"+ /* /*/ "k"+ /*/* /*/ "l"+
+# Comments with an even number of ending '*' used to fail:
+"m"+
+/* */ /* **/ /* ***/ /* ****/ "n"+
+/* */ /** */ /*** */ /**** */ "o"+
+/** **/ /*** ***/ /**** ****/ "p"+
+/* * ** *** **** ***** */     "q"+
+# Random comments
+/* ***** ////// * / * / /* */ "r"+
+# Mixed comments
+/* # */
+"s"+
+# /* #
+"t"+
+# /* # */
+"u"+
+# /*********/
+"v"+
+## */*
+"w"+
+/*
+ * Multiline, decorated comments
+ * # This ain't a nest'd comm'nt
+ */
+"x"+
+''${/** with **/"y"
+  # real
+  /* comments
+     inside ! # */
+
+  # (and empty lines)
+
+}''+          /* And a multiline comment,
+                 on the same line,
+                 after some spaces
+*/             # followed by a one-line comment
+"z"
+/* EOF */
diff --git a/third_party/nix/tests/lang/eval-okay-concat.exp b/third_party/nix/tests/lang/eval-okay-concat.exp
new file mode 100644
index 0000000000..bb4bbd5774
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-concat.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 5 6 7 8 9 ]
diff --git a/third_party/nix/tests/lang/eval-okay-concat.nix b/third_party/nix/tests/lang/eval-okay-concat.nix
new file mode 100644
index 0000000000..d158a9bf05
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-concat.nix
@@ -0,0 +1 @@
+[1 2 3] ++ [4 5 6] ++ [7 8 9]
diff --git a/third_party/nix/tests/lang/eval-okay-concatmap.exp b/third_party/nix/tests/lang/eval-okay-concatmap.exp
new file mode 100644
index 0000000000..3b8be7739d
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-concatmap.exp
@@ -0,0 +1 @@
+[ [ 1 3 5 7 9 ] [ "a" "z" "b" "z" ] ]
diff --git a/third_party/nix/tests/lang/eval-okay-concatmap.nix b/third_party/nix/tests/lang/eval-okay-concatmap.nix
new file mode 100644
index 0000000000..97da5d37a4
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-concatmap.nix
@@ -0,0 +1,5 @@
+with import ./lib.nix;
+
+[ (builtins.concatMap (x: if x / 2 * 2 == x then [] else [ x ]) (range 0 10))
+  (builtins.concatMap (x: [x] ++ ["z"]) ["a" "b"])
+]
diff --git a/third_party/nix/tests/lang/eval-okay-concatstringssep.exp b/third_party/nix/tests/lang/eval-okay-concatstringssep.exp
new file mode 100644
index 0000000000..93987647ff
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-concatstringssep.exp
@@ -0,0 +1 @@
+[ "" "foobarxyzzy" "foo, bar, xyzzy" "foo" "" ]
diff --git a/third_party/nix/tests/lang/eval-okay-concatstringssep.nix b/third_party/nix/tests/lang/eval-okay-concatstringssep.nix
new file mode 100644
index 0000000000..adc4c41bd5
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-concatstringssep.nix
@@ -0,0 +1,8 @@
+with builtins;
+
+[ (concatStringsSep "" [])
+  (concatStringsSep "" ["foo" "bar" "xyzzy"])
+  (concatStringsSep ", " ["foo" "bar" "xyzzy"])
+  (concatStringsSep ", " ["foo"])
+  (concatStringsSep ", " [])
+]
diff --git a/third_party/nix/tests/lang/eval-okay-context-introspection.exp b/third_party/nix/tests/lang/eval-okay-context-introspection.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-context-introspection.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-context-introspection.nix b/third_party/nix/tests/lang/eval-okay-context-introspection.nix
new file mode 100644
index 0000000000..43178bd2ee
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-context-introspection.nix
@@ -0,0 +1,24 @@
+let
+  drv = derivation {
+    name = "fail";
+    builder = "/bin/false";
+    system = "x86_64-linux";
+    outputs = [ "out" "foo" ];
+  };
+
+  path = "${./eval-okay-context-introspection.nix}";
+
+  desired-context = {
+    "${builtins.unsafeDiscardStringContext path}" = {
+      path = true;
+    };
+    "${builtins.unsafeDiscardStringContext drv.drvPath}" = {
+      outputs = [ "foo" "out" ];
+      allOutputs = true;
+    };
+  };
+
+  legit-context = builtins.getContext "${path}${drv.outPath}${drv.foo.outPath}${drv.drvPath}";
+
+  constructed-context = builtins.getContext (builtins.appendContext "" desired-context);
+in legit-context == constructed-context
diff --git a/third_party/nix/tests/lang/eval-okay-context.exp b/third_party/nix/tests/lang/eval-okay-context.exp
new file mode 100644
index 0000000000..2f535bdbc4
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-context.exp
@@ -0,0 +1 @@
+"foo eval-okay-context.nix bar"
diff --git a/third_party/nix/tests/lang/eval-okay-context.nix b/third_party/nix/tests/lang/eval-okay-context.nix
new file mode 100644
index 0000000000..7b9531cfe9
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-context.nix
@@ -0,0 +1,6 @@
+let s = "foo ${builtins.substring 33 100 (baseNameOf "${./eval-okay-context.nix}")} bar";
+in
+  if s != "foo eval-okay-context.nix bar"
+  then abort "context not discarded"
+  else builtins.unsafeDiscardStringContext s
+
diff --git a/third_party/nix/tests/lang/eval-okay-curpos.exp b/third_party/nix/tests/lang/eval-okay-curpos.exp
new file mode 100644
index 0000000000..65fd65b4d0
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-curpos.exp
@@ -0,0 +1 @@
+[ 3 7 4 9 ]
diff --git a/third_party/nix/tests/lang/eval-okay-curpos.nix b/third_party/nix/tests/lang/eval-okay-curpos.nix
new file mode 100644
index 0000000000..b79553df0b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-curpos.nix
@@ -0,0 +1,5 @@
+# Bla
+let
+  x = __curPos;
+    y = __curPos;
+in [ x.line x.column y.line y.column ]
diff --git a/third_party/nix/tests/lang/eval-okay-deepseq.exp b/third_party/nix/tests/lang/eval-okay-deepseq.exp
new file mode 100644
index 0000000000..8d38505c16
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-deepseq.exp
@@ -0,0 +1 @@
+456
diff --git a/third_party/nix/tests/lang/eval-okay-deepseq.nix b/third_party/nix/tests/lang/eval-okay-deepseq.nix
new file mode 100644
index 0000000000..53aa4b1dc2
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-deepseq.nix
@@ -0,0 +1 @@
+builtins.deepSeq (let as = { x = 123; y = as; }; in as) 456
diff --git a/third_party/nix/tests/lang/eval-okay-delayed-with-inherit.exp b/third_party/nix/tests/lang/eval-okay-delayed-with-inherit.exp
new file mode 100644
index 0000000000..eaacb55c1a
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-delayed-with-inherit.exp
@@ -0,0 +1 @@
+"b-overridden"
diff --git a/third_party/nix/tests/lang/eval-okay-delayed-with-inherit.nix b/third_party/nix/tests/lang/eval-okay-delayed-with-inherit.nix
new file mode 100644
index 0000000000..84b388c271
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-delayed-with-inherit.nix
@@ -0,0 +1,24 @@
+let
+  pkgs_ = with pkgs; {
+    a = derivation {
+      name = "a";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+      inherit b;
+    };
+
+    inherit b;
+  };
+
+  packageOverrides = p: {
+    b = derivation {
+      name = "b-overridden";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+    };
+  };
+
+  pkgs = pkgs_ // (packageOverrides pkgs_);
+in pkgs.a.b.name
diff --git a/third_party/nix/tests/lang/eval-okay-delayed-with.exp b/third_party/nix/tests/lang/eval-okay-delayed-with.exp
new file mode 100644
index 0000000000..8e7c61ab8e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-delayed-with.exp
@@ -0,0 +1 @@
+"b-overridden b-overridden a"
diff --git a/third_party/nix/tests/lang/eval-okay-delayed-with.nix b/third_party/nix/tests/lang/eval-okay-delayed-with.nix
new file mode 100644
index 0000000000..3fb023e1cd
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-delayed-with.nix
@@ -0,0 +1,29 @@
+let
+
+  pkgs_ = with pkgs; {
+    a = derivation {
+      name = "a";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+      inherit b;
+    };
+
+    b = derivation {
+      name = "b";
+      system = builtins.currentSystem;
+      builder = "/bin/sh";
+      args = [ "-c" "touch $out" ];
+      inherit a;
+    };
+
+    c = b;
+  };
+
+  packageOverrides = pkgs: with pkgs; {
+    b = derivation (b.drvAttrs // { name = "${b.name}-overridden"; });
+  };
+
+  pkgs = pkgs_ // (packageOverrides pkgs_);
+
+in "${pkgs.a.b.name} ${pkgs.c.name} ${pkgs.b.a.name}"
diff --git a/third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.exp b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.nix b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.nix
new file mode 100644
index 0000000000..6d57bf8549
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-2.nix
@@ -0,0 +1 @@
+{ a."${"b"}" = true; a."${"c"}" = false; }.a.b
diff --git a/third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.exp b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.exp
new file mode 100644
index 0000000000..df8750afc0
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.exp
@@ -0,0 +1 @@
+{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; }
diff --git a/third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.nix b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.nix
new file mode 100644
index 0000000000..0dbe15e638
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-dynamic-attrs-bare.nix
@@ -0,0 +1,17 @@
+let
+  aString = "a";
+
+  bString = "b";
+in {
+  hasAttrs = { a.b = null; } ? ${aString}.b;
+
+  selectAttrs = { a.b = true; }.a.${bString};
+
+  selectOrAttrs = { }.${aString} or true;
+
+  binds = { ${aString}."${bString}c" = true; }.a.bc;
+
+  recBinds = rec { ${bString} = a; a = true; }.b;
+
+  multiAttrs = { ${aString} = true; ${bString} = false; }.a;
+}
diff --git a/third_party/nix/tests/lang/eval-okay-dynamic-attrs.exp b/third_party/nix/tests/lang/eval-okay-dynamic-attrs.exp
new file mode 100644
index 0000000000..df8750afc0
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-dynamic-attrs.exp
@@ -0,0 +1 @@
+{ binds = true; hasAttrs = true; multiAttrs = true; recBinds = true; selectAttrs = true; selectOrAttrs = true; }
diff --git a/third_party/nix/tests/lang/eval-okay-dynamic-attrs.nix b/third_party/nix/tests/lang/eval-okay-dynamic-attrs.nix
new file mode 100644
index 0000000000..ee02ac7e65
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-dynamic-attrs.nix
@@ -0,0 +1,17 @@
+let
+  aString = "a";
+
+  bString = "b";
+in {
+  hasAttrs = { a.b = null; } ? "${aString}".b;
+
+  selectAttrs = { a.b = true; }.a."${bString}";
+
+  selectOrAttrs = { }."${aString}" or true;
+
+  binds = { "${aString}"."${bString}c" = true; }.a.bc;
+
+  recBinds = rec { "${bString}" = a; a = true; }.b;
+
+  multiAttrs = { "${aString}" = true; "${bString}" = false; }.a;
+}
diff --git a/third_party/nix/tests/lang/eval-okay-elem.exp b/third_party/nix/tests/lang/eval-okay-elem.exp
new file mode 100644
index 0000000000..3cf6c0e962
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-elem.exp
@@ -0,0 +1 @@
+[ true false 30 ]
diff --git a/third_party/nix/tests/lang/eval-okay-elem.nix b/third_party/nix/tests/lang/eval-okay-elem.nix
new file mode 100644
index 0000000000..71ea7a4ed0
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-elem.nix
@@ -0,0 +1,6 @@
+with import ./lib.nix;
+
+let xs = range 10 40; in
+
+[ (builtins.elem 23 xs) (builtins.elem 42 xs) (builtins.elemAt xs 20) ]
+
diff --git a/third_party/nix/tests/lang/eval-okay-empty-args.exp b/third_party/nix/tests/lang/eval-okay-empty-args.exp
new file mode 100644
index 0000000000..cb5537d5d7
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-empty-args.exp
@@ -0,0 +1 @@
+"ab"
diff --git a/third_party/nix/tests/lang/eval-okay-empty-args.nix b/third_party/nix/tests/lang/eval-okay-empty-args.nix
new file mode 100644
index 0000000000..78c133afdd
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-empty-args.nix
@@ -0,0 +1 @@
+({}: {x,y,}: "${x}${y}") {} {x = "a"; y = "b";}
diff --git a/third_party/nix/tests/lang/eval-okay-eq-derivations.exp b/third_party/nix/tests/lang/eval-okay-eq-derivations.exp
new file mode 100644
index 0000000000..ec04aab6ae
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-eq-derivations.exp
@@ -0,0 +1 @@
+[ true true true false ]
diff --git a/third_party/nix/tests/lang/eval-okay-eq-derivations.nix b/third_party/nix/tests/lang/eval-okay-eq-derivations.nix
new file mode 100644
index 0000000000..d526cb4a21
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-eq-derivations.nix
@@ -0,0 +1,10 @@
+let
+
+  drvA1 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; };
+  drvA2 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; };
+  drvA3 = derivation { name = "a"; builder = "/foo"; system = "i686-linux"; } // { dummy = 1; };
+  
+  drvC1 = derivation { name = "c"; builder = "/foo"; system = "i686-linux"; };
+  drvC2 = derivation { name = "c"; builder = "/bar"; system = "i686-linux"; };
+
+in [ (drvA1 == drvA1) (drvA1 == drvA2) (drvA1 == drvA3) (drvC1 == drvC2) ]
diff --git a/third_party/nix/tests/lang/eval-okay-eq.exp.disabled b/third_party/nix/tests/lang/eval-okay-eq.exp.disabled
new file mode 100644
index 0000000000..2015847b65
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-eq.exp.disabled
@@ -0,0 +1 @@
+Bool(True)
diff --git a/third_party/nix/tests/lang/eval-okay-eq.nix b/third_party/nix/tests/lang/eval-okay-eq.nix
new file mode 100644
index 0000000000..73d200b381
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-eq.nix
@@ -0,0 +1,3 @@
+["foobar" (rec {x = 1; y = x;})]
+==
+[("foo" + "bar") ({x = 1; y = 1;})]
diff --git a/third_party/nix/tests/lang/eval-okay-filter.exp b/third_party/nix/tests/lang/eval-okay-filter.exp
new file mode 100644
index 0000000000..355d51c27d
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-filter.exp
@@ -0,0 +1 @@
+[ 0 2 4 6 8 10 100 102 104 106 108 110 ]
diff --git a/third_party/nix/tests/lang/eval-okay-filter.nix b/third_party/nix/tests/lang/eval-okay-filter.nix
new file mode 100644
index 0000000000..85109b0d0e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-filter.nix
@@ -0,0 +1,5 @@
+with import ./lib.nix;
+
+builtins.filter
+  (x: x / 2 * 2 == x)
+  (builtins.concatLists [ (range 0 10) (range 100 110) ])
diff --git a/third_party/nix/tests/lang/eval-okay-flatten.exp b/third_party/nix/tests/lang/eval-okay-flatten.exp
new file mode 100644
index 0000000000..b979b2b8b9
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-flatten.exp
@@ -0,0 +1 @@
+"1234567"
diff --git a/third_party/nix/tests/lang/eval-okay-flatten.nix b/third_party/nix/tests/lang/eval-okay-flatten.nix
new file mode 100644
index 0000000000..fe911e9683
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-flatten.nix
@@ -0,0 +1,8 @@
+with import ./lib.nix;
+
+let {
+
+  l = ["1" "2" ["3" ["4"] ["5" "6"]] "7"];
+
+  body = concat (flatten l);
+}
diff --git a/third_party/nix/tests/lang/eval-okay-float.exp b/third_party/nix/tests/lang/eval-okay-float.exp
new file mode 100644
index 0000000000..3c50a8adce
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-float.exp
@@ -0,0 +1 @@
+[ 3.4 3.5 2.5 1.5 ]
diff --git a/third_party/nix/tests/lang/eval-okay-float.nix b/third_party/nix/tests/lang/eval-okay-float.nix
new file mode 100644
index 0000000000..b2702c7b16
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-float.nix
@@ -0,0 +1,6 @@
+[
+  (1.1 + 2.3)
+  (builtins.add (0.5 + 0.5) (2.0 + 0.5))
+  ((0.5 + 0.5) * (2.0 + 0.5))
+  ((1.5 + 1.5) / (0.5 * 4.0))
+]
diff --git a/third_party/nix/tests/lang/eval-okay-fromTOML.exp b/third_party/nix/tests/lang/eval-okay-fromTOML.exp
new file mode 100644
index 0000000000..d0dd3af2c8
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-fromTOML.exp
@@ -0,0 +1 @@
+[ { clients = { data = [ [ "gamma" "delta" ] [ 1 2 ] ]; hosts = [ "alpha" "omega" ]; }; database = { connection_max = 5000; enabled = true; ports = [ 8001 8001 8002 ]; server = "192.168.1.1"; }; owner = { name = "Tom Preston-Werner"; }; servers = { alpha = { dc = "eqdc10"; ip = "10.0.0.1"; }; beta = { dc = "eqdc10"; ip = "10.0.0.2"; }; }; title = "TOML Example"; } { "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; name = "Orange"; oct1 = 342391; oct2 = 493; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; } { metadata = { "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"; "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"; "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"; "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"; }; package = [ { dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" ]; name = "aho-corasick"; source = "registry+https://github.com/rust-lang/crates.io-index"; version = "0.6.4"; } { name = "ansi_term"; source = "registry+https://github.com/rust-lang/crates.io-index"; version = "0.9.0"; } { dependencies = [ "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)" "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" ]; name = "atty"; source = "registry+https://github.com/rust-lang/crates.io-index"; version = "0.2.10"; } ]; } { a = [ [ { b = true; } ] ]; c = [ [ { d = true; } ] ]; e = [ [ 123 ] ]; } ]
diff --git a/third_party/nix/tests/lang/eval-okay-fromTOML.nix b/third_party/nix/tests/lang/eval-okay-fromTOML.nix
new file mode 100644
index 0000000000..9639326899
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-fromTOML.nix
@@ -0,0 +1,208 @@
+[
+
+  (builtins.fromTOML ''
+    # This is a TOML document.
+
+    title = "TOML Example"
+
+    [owner]
+    name = "Tom Preston-Werner"
+    #dob = 1979-05-27T07:32:00-08:00 # First class dates
+
+    [database]
+    server = "192.168.1.1"
+    ports = [ 8001, 8001, 8002 ]
+    connection_max = 5000
+    enabled = true
+
+    [servers]
+
+      # Indentation (tabs and/or spaces) is allowed but not required
+      [servers.alpha]
+      ip = "10.0.0.1"
+      dc = "eqdc10"
+
+      [servers.beta]
+      ip = "10.0.0.2"
+      dc = "eqdc10"
+
+    [clients]
+    data = [ ["gamma", "delta"], [1, 2] ]
+
+    # Line breaks are OK when inside arrays
+    hosts = [
+      "alpha",
+      "omega"
+    ]
+  '')
+
+  (builtins.fromTOML ''
+    key = "value"
+    bare_key = "value"
+    bare-key = "value"
+    1234 = "value"
+
+    "127.0.0.1" = "value"
+    "character encoding" = "value"
+    "ʎǝʞ" = "value"
+    'key2' = "value"
+    'quoted "value"' = "value"
+
+    name = "Orange"
+
+    physical.color = "orange"
+    physical.shape = "round"
+    site."google.com" = true
+
+    # This is legal according to the spec, but cpptoml doesn't handle it.
+    #a.b.c = 1
+    #a.d = 2
+
+    str = "I'm a string. \"You can quote me\". Name\tJos\u00E9\nLocation\tSF."
+
+    int1 = +99
+    int2 = 42
+    int3 = 0
+    int4 = -17
+    int5 = 1_000
+    int6 = 5_349_221
+    int7 = 1_2_3_4_5
+
+    hex1 = 0xDEADBEEF
+    hex2 = 0xdeadbeef
+    hex3 = 0xdead_beef
+
+    oct1 = 0o01234567
+    oct2 = 0o755
+
+    bin1 = 0b11010110
+
+    flt1 = +1.0
+    flt2 = 3.1415
+    flt3 = -0.01
+    flt4 = 5e+22
+    flt5 = 1e6
+    flt6 = -2E-2
+    flt7 = 6.626e-34
+    flt8 = 9_224_617.445_991_228_313
+
+    bool1 = true
+    bool2 = false
+
+    # FIXME: not supported because Nix doesn't have a date/time type.
+    #odt1 = 1979-05-27T07:32:00Z
+    #odt2 = 1979-05-27T00:32:00-07:00
+    #odt3 = 1979-05-27T00:32:00.999999-07:00
+    #odt4 = 1979-05-27 07:32:00Z
+    #ldt1 = 1979-05-27T07:32:00
+    #ldt2 = 1979-05-27T00:32:00.999999
+    #ld1 = 1979-05-27
+    #lt1 = 07:32:00
+    #lt2 = 00:32:00.999999
+
+    arr1 = [ 1, 2, 3 ]
+    arr2 = [ "red", "yellow", "green" ]
+    arr3 = [ [ 1, 2 ], [3, 4, 5] ]
+    arr4 = [ "all", 'strings', """are the same""", ''''type'''']
+    arr5 = [ [ 1, 2 ], ["a", "b", "c"] ]
+
+    arr7 = [
+      1, 2, 3
+    ]
+
+    arr8 = [
+      1,
+      2, # this is ok
+    ]
+
+    [table-1]
+    key1 = "some string"
+    key2 = 123
+
+
+    [table-2]
+    key1 = "another string"
+    key2 = 456
+
+    [dog."tater.man"]
+    type.name = "pug"
+
+    [a.b.c]
+    [ d.e.f ]
+    [ g .  h  . i ]
+    [ j . "ʞ" . 'l' ]
+    [x.y.z.w]
+
+    name = { first = "Tom", last = "Preston-Werner" }
+    point = { x = 1, y = 2 }
+    animal = { type.name = "pug" }
+
+    [[products]]
+    name = "Hammer"
+    sku = 738594937
+
+    [[products]]
+
+    [[products]]
+    name = "Nail"
+    sku = 284758393
+    color = "gray"
+
+    [[fruit]]
+      name = "apple"
+
+      [fruit.physical]
+        color = "red"
+        shape = "round"
+
+      [[fruit.variety]]
+        name = "red delicious"
+
+      [[fruit.variety]]
+        name = "granny smith"
+
+    [[fruit]]
+      name = "banana"
+
+      [[fruit.variety]]
+        name = "plantain"
+  '')
+
+  (builtins.fromTOML ''
+    [[package]]
+    name = "aho-corasick"
+    version = "0.6.4"
+    source = "registry+https://github.com/rust-lang/crates.io-index"
+    dependencies = [
+     "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
+    ]
+
+    [[package]]
+    name = "ansi_term"
+    version = "0.9.0"
+    source = "registry+https://github.com/rust-lang/crates.io-index"
+
+    [[package]]
+    name = "atty"
+    version = "0.2.10"
+    source = "registry+https://github.com/rust-lang/crates.io-index"
+    dependencies = [
+     "libc 0.2.42 (registry+https://github.com/rust-lang/crates.io-index)",
+     "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
+     "winapi 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
+    ]
+
+    [metadata]
+    "checksum aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "d6531d44de723825aa81398a6415283229725a00fa30713812ab9323faa82fc4"
+    "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+    "checksum ansi_term 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "23ac7c30002a5accbf7e8987d0632fa6de155b7c3d39d0067317a391e00a2ef6"
+    "checksum arrayvec 0.4.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a1e964f9e24d588183fcb43503abda40d288c8657dfc27311516ce2f05675aef"
+  '')
+
+  (builtins.fromTOML ''
+    a = [[{ b = true }]]
+    c = [ [ { d = true } ] ]
+    e = [[123]]
+  '')
+
+]
diff --git a/third_party/nix/tests/lang/eval-okay-fromjson.exp b/third_party/nix/tests/lang/eval-okay-fromjson.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-fromjson.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-fromjson.nix b/third_party/nix/tests/lang/eval-okay-fromjson.nix
new file mode 100644
index 0000000000..102ee82b5e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-fromjson.nix
@@ -0,0 +1,36 @@
+# RFC 7159, section 13.
+builtins.fromJSON
+  ''
+    {
+      "Image": {
+          "Width":  800,
+          "Height": 600,
+          "Title":  "View from 15th Floor",
+          "Thumbnail": {
+              "Url":    "http://www.example.com/image/481989943",
+              "Height": 125,
+              "Width":  100
+          },
+          "Animated" : false,
+          "IDs": [116, 943, 234, 38793, true  ,false,null, -100],
+          "Latitude":  37.7668,
+          "Longitude": -122.3959
+        }
+    }
+  ''
+==
+  { Image =
+    { Width = 800;
+      Height = 600;
+      Title = "View from 15th Floor";
+      Thumbnail =
+        { Url = http://www.example.com/image/481989943;
+          Height = 125;
+          Width = 100;
+        };
+      Animated = false;
+      IDs = [ 116 943 234 38793 true false null (0-100) ];
+      Latitude = 37.7668;
+      Longitude = -122.3959;
+    };
+  }
diff --git a/third_party/nix/tests/lang/eval-okay-functionargs.exp.xml b/third_party/nix/tests/lang/eval-okay-functionargs.exp.xml
new file mode 100644
index 0000000000..651f54c363
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-functionargs.exp.xml
@@ -0,0 +1,15 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <list>
+    <string value="stdenv" />
+    <string value="fetchurl" />
+    <string value="aterm-stdenv" />
+    <string value="aterm-stdenv2" />
+    <string value="libX11" />
+    <string value="libXv" />
+    <string value="mplayer-stdenv2.libXv-libX11" />
+    <string value="mplayer-stdenv2.libXv-libX11_2" />
+    <string value="nix-stdenv-aterm-stdenv" />
+    <string value="nix-stdenv2-aterm2-stdenv2" />
+  </list>
+</expr>
diff --git a/third_party/nix/tests/lang/eval-okay-functionargs.nix b/third_party/nix/tests/lang/eval-okay-functionargs.nix
new file mode 100644
index 0000000000..68dca62ee1
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-functionargs.nix
@@ -0,0 +1,80 @@
+let
+
+  stdenvFun = { }: { name = "stdenv"; };
+  stdenv2Fun = { }: { name = "stdenv2"; };
+  fetchurlFun = { stdenv }: assert stdenv.name == "stdenv"; { name = "fetchurl"; };
+  atermFun = { stdenv, fetchurl }: { name = "aterm-${stdenv.name}"; };
+  aterm2Fun = { stdenv, fetchurl }: { name = "aterm2-${stdenv.name}"; };
+  nixFun = { stdenv, fetchurl, aterm }: { name = "nix-${stdenv.name}-${aterm.name}"; };
+  
+  mplayerFun =
+    { stdenv, fetchurl, enableX11 ? false, xorg ? null, enableFoo ? true, foo ? null  }:
+    assert stdenv.name == "stdenv2";
+    assert enableX11 -> xorg.libXv.name == "libXv";
+    assert enableFoo -> foo != null;
+    { name = "mplayer-${stdenv.name}.${xorg.libXv.name}-${xorg.libX11.name}"; };
+
+  makeOverridable = f: origArgs: f origArgs //
+    { override = newArgs:
+        makeOverridable f (origArgs // (if builtins.isFunction newArgs then newArgs origArgs else newArgs));
+    };
+    
+  callPackage_ = pkgs: f: args:
+    makeOverridable f ((builtins.intersectAttrs (builtins.functionArgs f) pkgs) // args);
+
+  allPackages =
+    { overrides ? (pkgs: pkgsPrev: { }) }:
+    let
+      callPackage = callPackage_ pkgs;
+      pkgs = pkgsStd // (overrides pkgs pkgsStd);
+      pkgsStd = {
+        inherit pkgs;
+        stdenv = callPackage stdenvFun { };
+        stdenv2 = callPackage stdenv2Fun { };
+        fetchurl = callPackage fetchurlFun { };
+        aterm = callPackage atermFun { };
+        xorg = callPackage xorgFun { };
+        mplayer = callPackage mplayerFun { stdenv = pkgs.stdenv2; enableFoo = false; };
+        nix = callPackage nixFun { };
+      };
+    in pkgs;
+
+  libX11Fun = { stdenv, fetchurl }: { name = "libX11"; };
+  libX11_2Fun = { stdenv, fetchurl }: { name = "libX11_2"; };
+  libXvFun = { stdenv, fetchurl, libX11 }: { name = "libXv"; };
+  
+  xorgFun =
+    { pkgs }:
+    let callPackage = callPackage_ (pkgs // pkgs.xorg); in
+    {
+      libX11 = callPackage libX11Fun { };
+      libXv = callPackage libXvFun { };
+    };
+
+in
+
+let
+
+  pkgs = allPackages { };
+  
+  pkgs2 = allPackages {
+    overrides = pkgs: pkgsPrev: {
+      stdenv = pkgs.stdenv2;
+      nix = pkgsPrev.nix.override { aterm = aterm2Fun { inherit (pkgs) stdenv fetchurl; }; };
+      xorg = pkgsPrev.xorg // { libX11 = libX11_2Fun { inherit (pkgs) stdenv fetchurl; }; };
+    };
+  };
+  
+in
+
+  [ pkgs.stdenv.name
+    pkgs.fetchurl.name
+    pkgs.aterm.name
+    pkgs2.aterm.name
+    pkgs.xorg.libX11.name
+    pkgs.xorg.libXv.name
+    pkgs.mplayer.name
+    pkgs2.mplayer.name
+    pkgs.nix.name
+    pkgs2.nix.name
+  ]
diff --git a/third_party/nix/tests/lang/eval-okay-getattrpos-undefined.exp b/third_party/nix/tests/lang/eval-okay-getattrpos-undefined.exp
new file mode 100644
index 0000000000..19765bd501
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-getattrpos-undefined.exp
@@ -0,0 +1 @@
+null
diff --git a/third_party/nix/tests/lang/eval-okay-getattrpos-undefined.nix b/third_party/nix/tests/lang/eval-okay-getattrpos-undefined.nix
new file mode 100644
index 0000000000..14dd38f773
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-getattrpos-undefined.nix
@@ -0,0 +1 @@
+builtins.unsafeGetAttrPos "abort" builtins
diff --git a/third_party/nix/tests/lang/eval-okay-getattrpos.exp b/third_party/nix/tests/lang/eval-okay-getattrpos.exp
new file mode 100644
index 0000000000..469249bbc6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-getattrpos.exp
@@ -0,0 +1 @@
+{ column = 5; file = "eval-okay-getattrpos.nix"; line = 3; }
diff --git a/third_party/nix/tests/lang/eval-okay-getattrpos.nix b/third_party/nix/tests/lang/eval-okay-getattrpos.nix
new file mode 100644
index 0000000000..ca6b079615
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-getattrpos.nix
@@ -0,0 +1,6 @@
+let
+  as = {
+    foo = "bar";
+  };
+  pos = builtins.unsafeGetAttrPos "foo" as;
+in { inherit (pos) column line; file = baseNameOf pos.file; }
diff --git a/third_party/nix/tests/lang/eval-okay-getenv.exp b/third_party/nix/tests/lang/eval-okay-getenv.exp
new file mode 100644
index 0000000000..14e24d4190
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-getenv.exp
@@ -0,0 +1 @@
+"foobar"
diff --git a/third_party/nix/tests/lang/eval-okay-getenv.nix b/third_party/nix/tests/lang/eval-okay-getenv.nix
new file mode 100644
index 0000000000..4cfec5f553
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-getenv.nix
@@ -0,0 +1 @@
+builtins.getEnv "TEST_VAR" + (if builtins.getEnv "NO_SUCH_VAR" == "" then "bar" else "bla")
diff --git a/third_party/nix/tests/lang/eval-okay-hash.exp b/third_party/nix/tests/lang/eval-okay-hash.exp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-hash.exp
diff --git a/third_party/nix/tests/lang/eval-okay-hashfile.exp b/third_party/nix/tests/lang/eval-okay-hashfile.exp
new file mode 100644
index 0000000000..ff1e8293ef
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-hashfile.exp
@@ -0,0 +1 @@
+[ "d3b07384d113edec49eaa6238ad5ff00" "0f343b0931126a20f133d67c2b018a3b" "f1d2d2f924e986ac86fdf7b36c94bcdf32beec15" "60cacbf3d72e1e7834203da608037b1bf83b40e8" "b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b878ae4944c" "5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef" "0cf9180a764aba863a67b6d72f0918bc131c6772642cb2dce5a34f0a702f9470ddc2bf125c12198b1995c233c34b4afd346c54a2334c350a948a51b6e8b4e6b6" "8efb4f73c5655351c444eb109230c556d39e2c7624e9c11abc9e3fb4b9b9254218cc5085b454a9698d085cfa92198491f07a723be4574adc70617b73eb0b6461" ]
diff --git a/third_party/nix/tests/lang/eval-okay-hashfile.nix b/third_party/nix/tests/lang/eval-okay-hashfile.nix
new file mode 100644
index 0000000000..aff5a18568
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-hashfile.nix
@@ -0,0 +1,4 @@
+let
+  paths = [ ./data ./binary-data ];
+in
+  builtins.concatLists (map (hash: map (builtins.hashFile hash) paths) ["md5" "sha1" "sha256" "sha512"])
diff --git a/third_party/nix/tests/lang/eval-okay-hashstring.exp b/third_party/nix/tests/lang/eval-okay-hashstring.exp
new file mode 100644
index 0000000000..d720a082dd
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-hashstring.exp
@@ -0,0 +1 @@
+[ "d41d8cd98f00b204e9800998ecf8427e" "6c69ee7f211c640419d5366cc076ae46" "bb3438fbabd460ea6dbd27d153e2233b" "da39a3ee5e6b4b0d3255bfef95601890afd80709" "cd54e8568c1b37cf1e5badb0779bcbf382212189" "6d12e10b1d331dad210e47fd25d4f260802b7e77" "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" "900a4469df00ccbfd0c145c6d1e4b7953dd0afafadd7534e3a4019e8d38fc663" "ad0387b3bd8652f730ca46d25f9c170af0fd589f42e7f23f5a9e6412d97d7e56" "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e" "9d0886f8c6b389398a16257bc79780fab9831c7fc11c8ab07fa732cb7b348feade382f92617c9c5305fefba0af02ab5fd39a587d330997ff5bd0db19f7666653" "21644b72aa259e5a588cd3afbafb1d4310f4889680f6c83b9d531596a5a284f34dbebff409d23bcc86aee6bad10c891606f075c6f4755cb536da27db5693f3a7" ]
diff --git a/third_party/nix/tests/lang/eval-okay-hashstring.nix b/third_party/nix/tests/lang/eval-okay-hashstring.nix
new file mode 100644
index 0000000000..b0f62b245c
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-hashstring.nix
@@ -0,0 +1,4 @@
+let
+  strings = [ "" "text 1" "text 2" ];
+in
+  builtins.concatLists (map (hash: map (builtins.hashString hash) strings) ["md5" "sha1" "sha256" "sha512"])
diff --git a/third_party/nix/tests/lang/eval-okay-if.exp b/third_party/nix/tests/lang/eval-okay-if.exp
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-if.exp
@@ -0,0 +1 @@
+3
diff --git a/third_party/nix/tests/lang/eval-okay-if.nix b/third_party/nix/tests/lang/eval-okay-if.nix
new file mode 100644
index 0000000000..23e4c74d50
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-if.nix
@@ -0,0 +1 @@
+if "foo" != "f" + "oo" then 1 else if false then 2 else 3
diff --git a/third_party/nix/tests/lang/eval-okay-import.exp b/third_party/nix/tests/lang/eval-okay-import.exp
new file mode 100644
index 0000000000..c508125b55
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-import.exp
@@ -0,0 +1 @@
+[ 1 2 3 4 5 6 7 8 9 10 ]
diff --git a/third_party/nix/tests/lang/eval-okay-import.nix b/third_party/nix/tests/lang/eval-okay-import.nix
new file mode 100644
index 0000000000..0b18d94131
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-import.nix
@@ -0,0 +1,11 @@
+let
+
+  overrides = {
+    import = fn: scopedImport overrides fn;
+
+    scopedImport = attrs: fn: scopedImport (overrides // attrs) fn;
+
+    builtins = builtins // overrides;
+  } // import ./lib.nix;
+
+in scopedImport overrides ./imported.nix
diff --git a/third_party/nix/tests/lang/eval-okay-ind-string.exp b/third_party/nix/tests/lang/eval-okay-ind-string.exp
new file mode 100644
index 0000000000..9cf4bd2ee7
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-ind-string.exp
@@ -0,0 +1 @@
+"This is an indented multi-line string\nliteral.  An amount of whitespace at\nthe start of each line matching the minimum\nindentation of all lines in the string\nliteral together will be removed.  Thus,\nin this case four spaces will be\nstripped from each line, even though\n  THIS LINE is indented six spaces.\n\nAlso, empty lines don't count in the\ndetermination of the indentation level (the\nprevious empty line has indentation 0, but\nit doesn't matter).\nIf the string starts with whitespace\n  followed by a newline, it's stripped, but\n  that's not the case here. Two spaces are\n  stripped because of the \"  \" at the start. \nThis line is indented\na bit further.\nAnti-quotations, like so, are\nalso allowed.\n  The \\ is not special here.\n' can be followed by any character except another ', e.g. 'x'.\nLikewise for $, e.g. $$ or $varName.\nBut ' followed by ' is special, as is $ followed by {.\nIf you want them, use anti-quotations: '', ${.\n   Tabs are not interpreted as whitespace (since we can't guess\n   what tab settings are intended), so don't use them.\n\tThis line starts with a space and a tab, so only one\n   space will be stripped from each line.\nAlso note that if the last line (just before the closing ' ')\nconsists only of whitespace, it's ignored.  But here there is\nsome non-whitespace stuff, so the line isn't removed. \nThis shows a hacky way to preserve an empty line after the start.\nBut there's no reason to do so: you could just repeat the empty\nline.\n  Similarly you can force an indentation level,\n  in this case to 2 spaces.  This works because the anti-quote\n  is significant (not whitespace).\nstart on network-interfaces\n\nstart script\n\n  rm -f /var/run/opengl-driver\n  ln -sf 123 /var/run/opengl-driver\n\n  rm -f /var/log/slim.log\n   \nend script\n\nenv SLIM_CFGFILE=abc\nenv SLIM_THEMESDIR=def\nenv FONTCONFIG_FILE=/etc/fonts/fonts.conf  \t\t\t\t# !!! cleanup\nenv XKB_BINDIR=foo/bin         \t\t\t\t# Needed for the Xkb extension.\nenv LD_LIBRARY_PATH=libX11/lib:libXext/lib:/usr/lib/          # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)\n\nenv XORG_DRI_DRIVER_PATH=nvidiaDrivers/X11R6/lib/modules/drivers/ \n\nexec slim/bin/slim\nEscaping of ' followed by ': ''\nEscaping of $ followed by {: ${\nAnd finally to interpret \\n etc. as in a string: \n, \r, \t.\nfoo\n'bla'\nbar\ncut -d $'\\t' -f 1\nending dollar $$\n"
diff --git a/third_party/nix/tests/lang/eval-okay-ind-string.nix b/third_party/nix/tests/lang/eval-okay-ind-string.nix
new file mode 100644
index 0000000000..1669dc0648
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-ind-string.nix
@@ -0,0 +1,128 @@
+let
+
+  s1 = ''
+    This is an indented multi-line string
+    literal.  An amount of whitespace at
+    the start of each line matching the minimum
+    indentation of all lines in the string
+    literal together will be removed.  Thus,
+    in this case four spaces will be
+    stripped from each line, even though
+      THIS LINE is indented six spaces.
+
+    Also, empty lines don't count in the
+    determination of the indentation level (the
+    previous empty line has indentation 0, but
+    it doesn't matter).
+  '';
+
+  s2 = ''  If the string starts with whitespace
+    followed by a newline, it's stripped, but
+    that's not the case here. Two spaces are
+    stripped because of the "  " at the start. 
+  '';
+
+  s3 = ''
+      This line is indented
+      a bit further.
+        ''; # indentation of last line doesn't count if it's empty
+
+  s4 = ''
+    Anti-quotations, like ${if true then "so" else "not so"}, are
+    also allowed.
+  '';
+
+  s5 = ''
+      The \ is not special here.
+    ' can be followed by any character except another ', e.g. 'x'.
+    Likewise for $, e.g. $$ or $varName.
+    But ' followed by ' is special, as is $ followed by {.
+    If you want them, use anti-quotations: ${"''"}, ${"\${"}.
+  '';
+
+  s6 = ''  
+    Tabs are not interpreted as whitespace (since we can't guess
+    what tab settings are intended), so don't use them.
+ 	This line starts with a space and a tab, so only one
+    space will be stripped from each line.
+  '';
+
+  s7 = ''
+    Also note that if the last line (just before the closing ' ')
+    consists only of whitespace, it's ignored.  But here there is
+    some non-whitespace stuff, so the line isn't removed. '';
+
+  s8 = ''    ${""}
+    This shows a hacky way to preserve an empty line after the start.
+    But there's no reason to do so: you could just repeat the empty
+    line.
+  '';
+
+  s9 = ''
+  ${""}  Similarly you can force an indentation level,
+    in this case to 2 spaces.  This works because the anti-quote
+    is significant (not whitespace).
+  '';
+
+  s10 = ''
+  '';
+
+  s11 = '''';
+
+  s12 = ''   '';
+
+  s13 = ''
+    start on network-interfaces
+
+    start script
+    
+      rm -f /var/run/opengl-driver
+      ${if true
+        then "ln -sf 123 /var/run/opengl-driver"
+        else if true
+        then "ln -sf 456 /var/run/opengl-driver"
+        else ""
+      }
+
+      rm -f /var/log/slim.log
+       
+    end script
+
+    env SLIM_CFGFILE=${"abc"}
+    env SLIM_THEMESDIR=${"def"}
+    env FONTCONFIG_FILE=/etc/fonts/fonts.conf  				# !!! cleanup
+    env XKB_BINDIR=${"foo"}/bin         				# Needed for the Xkb extension.
+    env LD_LIBRARY_PATH=${"libX11"}/lib:${"libXext"}/lib:/usr/lib/          # related to xorg-sys-opengl - needed to load libglx for (AI)GLX support (for compiz)
+
+    ${if true
+      then "env XORG_DRI_DRIVER_PATH=${"nvidiaDrivers"}/X11R6/lib/modules/drivers/"
+    else if true
+      then "env XORG_DRI_DRIVER_PATH=${"mesa"}/lib/modules/dri"
+      else ""
+    } 
+
+    exec ${"slim"}/bin/slim
+  '';
+
+  s14 = ''
+    Escaping of ' followed by ': '''
+    Escaping of $ followed by {: ''${
+    And finally to interpret \n etc. as in a string: ''\n, ''\r, ''\t.
+  '';
+
+  # Regression test: antiquotation in '${x}' should work, but didn't.
+  s15 = let x = "bla"; in ''
+    foo
+    '${x}'
+    bar
+  '';
+
+  # Regression test: accept $'.
+  s16 = ''
+    cut -d $'\t' -f 1
+  '';
+
+  # Accept dollars at end of strings 
+  s17 = ''ending dollar $'' + ''$'' + "\n";
+
+in s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8 + s9 + s10 + s11 + s12 + s13 + s14 + s15 + s16 + s17
diff --git a/third_party/nix/tests/lang/eval-okay-let.exp b/third_party/nix/tests/lang/eval-okay-let.exp
new file mode 100644
index 0000000000..14e24d4190
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-let.exp
@@ -0,0 +1 @@
+"foobar"
diff --git a/third_party/nix/tests/lang/eval-okay-let.nix b/third_party/nix/tests/lang/eval-okay-let.nix
new file mode 100644
index 0000000000..fe118c5282
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-let.nix
@@ -0,0 +1,5 @@
+let {
+  x = "foo";
+  y = "bar";
+  body = x + y;
+}
diff --git a/third_party/nix/tests/lang/eval-okay-list.exp b/third_party/nix/tests/lang/eval-okay-list.exp
new file mode 100644
index 0000000000..f784f26d83
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-list.exp
@@ -0,0 +1 @@
+"foobarblatest"
diff --git a/third_party/nix/tests/lang/eval-okay-list.nix b/third_party/nix/tests/lang/eval-okay-list.nix
new file mode 100644
index 0000000000..d433bcf908
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-list.nix
@@ -0,0 +1,7 @@
+with import ./lib.nix;
+
+let {
+
+  body = concat ["foo" "bar" "bla" "test"];
+    
+}
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/eval-okay-listtoattrs.exp b/third_party/nix/tests/lang/eval-okay-listtoattrs.exp
new file mode 100644
index 0000000000..74abef7bc6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-listtoattrs.exp
@@ -0,0 +1 @@
+"AAbar"
diff --git a/third_party/nix/tests/lang/eval-okay-listtoattrs.nix b/third_party/nix/tests/lang/eval-okay-listtoattrs.nix
new file mode 100644
index 0000000000..4186e029b5
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-listtoattrs.nix
@@ -0,0 +1,11 @@
+# this test shows how to use listToAttrs and that evaluation is still lazy (throw isn't called)
+with import ./lib.nix;
+
+let 
+  asi = name: value : { inherit name value; };
+  list = [ ( asi "a" "A" ) ( asi "b" "B" ) ];
+  a = builtins.listToAttrs list;
+  b = builtins.listToAttrs ( list ++ list );
+  r = builtins.listToAttrs [ (asi "result" [ a b ]) ( asi "throw" (throw "this should not be thrown")) ];
+  x = builtins.listToAttrs [ (asi "foo" "bar") (asi "foo" "bla") ];
+in concat (map (x: x.a) r.result) + x.foo
diff --git a/third_party/nix/tests/lang/eval-okay-logic.exp b/third_party/nix/tests/lang/eval-okay-logic.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-logic.exp
@@ -0,0 +1 @@
+1
diff --git a/third_party/nix/tests/lang/eval-okay-logic.nix b/third_party/nix/tests/lang/eval-okay-logic.nix
new file mode 100644
index 0000000000..fbb1279440
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-logic.nix
@@ -0,0 +1 @@
+assert !false && (true || false) -> true; 1
diff --git a/third_party/nix/tests/lang/eval-okay-map.exp b/third_party/nix/tests/lang/eval-okay-map.exp
new file mode 100644
index 0000000000..dbb64f717b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-map.exp
@@ -0,0 +1 @@
+"foobarblabarxyzzybar"
diff --git a/third_party/nix/tests/lang/eval-okay-map.nix b/third_party/nix/tests/lang/eval-okay-map.nix
new file mode 100644
index 0000000000..a76c1d8114
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-map.nix
@@ -0,0 +1,3 @@
+with import ./lib.nix;
+
+concat (map (x: x + "bar") [ "foo" "bla" "xyzzy" ])
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/eval-okay-mapattrs.exp b/third_party/nix/tests/lang/eval-okay-mapattrs.exp
new file mode 100644
index 0000000000..3f113f17ba
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-mapattrs.exp
@@ -0,0 +1 @@
+{ x = "x-foo"; y = "y-bar"; }
diff --git a/third_party/nix/tests/lang/eval-okay-mapattrs.nix b/third_party/nix/tests/lang/eval-okay-mapattrs.nix
new file mode 100644
index 0000000000..f075b6275e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-mapattrs.nix
@@ -0,0 +1,3 @@
+with import ./lib.nix;
+
+builtins.mapAttrs (name: value: name + "-" + value) { x = "foo"; y = "bar"; }
diff --git a/third_party/nix/tests/lang/eval-okay-nested-with.exp b/third_party/nix/tests/lang/eval-okay-nested-with.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-nested-with.exp
@@ -0,0 +1 @@
+2
diff --git a/third_party/nix/tests/lang/eval-okay-nested-with.nix b/third_party/nix/tests/lang/eval-okay-nested-with.nix
new file mode 100644
index 0000000000..ba9d79aa79
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-nested-with.nix
@@ -0,0 +1,3 @@
+with { x = 1; };
+with { x = 2; };
+x
diff --git a/third_party/nix/tests/lang/eval-okay-new-let.exp b/third_party/nix/tests/lang/eval-okay-new-let.exp
new file mode 100644
index 0000000000..f98b388071
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-new-let.exp
@@ -0,0 +1 @@
+"xyzzyfoobar"
diff --git a/third_party/nix/tests/lang/eval-okay-new-let.nix b/third_party/nix/tests/lang/eval-okay-new-let.nix
new file mode 100644
index 0000000000..7381231415
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-new-let.nix
@@ -0,0 +1,14 @@
+let
+
+  f = z: 
+
+    let
+      x = "foo";
+      y = "bar";
+      body = 1; # compat test
+    in
+      z + x + y;
+
+  arg = "xyzzy";
+
+in f arg
diff --git a/third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.exp b/third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.nix b/third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.nix
new file mode 100644
index 0000000000..b060c0bc98
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-null-dynamic-attrs.nix
@@ -0,0 +1 @@
+{ ${null} = true; } == {}
diff --git a/third_party/nix/tests/lang/eval-okay-overrides.exp b/third_party/nix/tests/lang/eval-okay-overrides.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-overrides.exp
@@ -0,0 +1 @@
+2
diff --git a/third_party/nix/tests/lang/eval-okay-overrides.nix b/third_party/nix/tests/lang/eval-okay-overrides.nix
new file mode 100644
index 0000000000..358742b36e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-overrides.nix
@@ -0,0 +1,9 @@
+let
+
+  overrides = { a = 2; };
+
+in (rec {
+  __overrides = overrides;
+  x = a;
+  a = 1;
+}).x
diff --git a/third_party/nix/tests/lang/eval-okay-partition.exp b/third_party/nix/tests/lang/eval-okay-partition.exp
new file mode 100644
index 0000000000..cd8b8b020c
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-partition.exp
@@ -0,0 +1 @@
+{ right = [ 0 2 4 6 8 10 100 102 104 106 108 110 ]; wrong = [ 1 3 5 7 9 101 103 105 107 109 ]; }
diff --git a/third_party/nix/tests/lang/eval-okay-partition.nix b/third_party/nix/tests/lang/eval-okay-partition.nix
new file mode 100644
index 0000000000..846d2ce494
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-partition.nix
@@ -0,0 +1,5 @@
+with import ./lib.nix;
+
+builtins.partition
+  (x: x / 2 * 2 == x)
+  (builtins.concatLists [ (range 0 10) (range 100 110) ])
diff --git a/third_party/nix/tests/lang/eval-okay-path.nix b/third_party/nix/tests/lang/eval-okay-path.nix
new file mode 100644
index 0000000000..e67168cf3e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-path.nix
@@ -0,0 +1,7 @@
+builtins.path
+  { path = ./.;
+    filter = path: _: baseNameOf path == "data";
+    recursive = true;
+    sha256 = "1yhm3gwvg5a41yylymgblsclk95fs6jy72w0wv925mmidlhcq4sw";
+    name = "output";
+  }
diff --git a/third_party/nix/tests/lang/eval-okay-pathexists.exp b/third_party/nix/tests/lang/eval-okay-pathexists.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-pathexists.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-pathexists.nix b/third_party/nix/tests/lang/eval-okay-pathexists.nix
new file mode 100644
index 0000000000..50c28ee0cd
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-pathexists.nix
@@ -0,0 +1,5 @@
+builtins.pathExists (builtins.toPath ./lib.nix)
+&& builtins.pathExists (builtins.toPath (builtins.toString ./lib.nix))
+&& !builtins.pathExists (builtins.toPath (builtins.toString ./bla.nix))
+&& builtins.pathExists ./lib.nix
+&& !builtins.pathExists ./bla.nix
diff --git a/third_party/nix/tests/lang/eval-okay-patterns.exp b/third_party/nix/tests/lang/eval-okay-patterns.exp
new file mode 100644
index 0000000000..a4304010fe
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-patterns.exp
@@ -0,0 +1 @@
+"abcxyzDDDDEFijk"
diff --git a/third_party/nix/tests/lang/eval-okay-patterns.nix b/third_party/nix/tests/lang/eval-okay-patterns.nix
new file mode 100644
index 0000000000..96fd25a015
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-patterns.nix
@@ -0,0 +1,16 @@
+let
+
+  f = args@{x, y, z}: x + args.y + z;
+
+  g = {x, y, z}@args: f args;
+
+  h = {x ? "d", y ? x, z ? args.x}@args: x + y + z;
+
+  j = {x, y, z, ...}: x + y + z;
+
+in
+  f {x = "a"; y = "b"; z = "c";} +
+  g {x = "x"; y = "y"; z = "z";} +
+  h {x = "D";} +
+  h {x = "D"; y = "E"; z = "F";} +
+  j {x = "i"; y = "j"; z = "k"; bla = "bla"; foo = "bar";}
diff --git a/third_party/nix/tests/lang/eval-okay-readDir.exp b/third_party/nix/tests/lang/eval-okay-readDir.exp
new file mode 100644
index 0000000000..bf8d2c14ea
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-readDir.exp
@@ -0,0 +1 @@
+{ bar = "regular"; foo = "directory"; }
diff --git a/third_party/nix/tests/lang/eval-okay-readDir.nix b/third_party/nix/tests/lang/eval-okay-readDir.nix
new file mode 100644
index 0000000000..a7ec9292aa
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-readDir.nix
@@ -0,0 +1 @@
+builtins.readDir ./readDir
diff --git a/third_party/nix/tests/lang/eval-okay-readfile.exp b/third_party/nix/tests/lang/eval-okay-readfile.exp
new file mode 100644
index 0000000000..a2c87d0c43
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-readfile.exp
@@ -0,0 +1 @@
+"builtins.readFile ./eval-okay-readfile.nix\n"
diff --git a/third_party/nix/tests/lang/eval-okay-readfile.nix b/third_party/nix/tests/lang/eval-okay-readfile.nix
new file mode 100644
index 0000000000..82f7cb1743
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-readfile.nix
@@ -0,0 +1 @@
+builtins.readFile ./eval-okay-readfile.nix
diff --git a/third_party/nix/tests/lang/eval-okay-redefine-builtin.exp b/third_party/nix/tests/lang/eval-okay-redefine-builtin.exp
new file mode 100644
index 0000000000..c508d5366f
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-redefine-builtin.exp
@@ -0,0 +1 @@
+false
diff --git a/third_party/nix/tests/lang/eval-okay-redefine-builtin.nix b/third_party/nix/tests/lang/eval-okay-redefine-builtin.nix
new file mode 100644
index 0000000000..df9fc3f37d
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-redefine-builtin.nix
@@ -0,0 +1,3 @@
+let
+  throw = abort "Error!";
+in (builtins.tryEval <foobaz>).success
diff --git a/third_party/nix/tests/lang/eval-okay-regex-match.exp b/third_party/nix/tests/lang/eval-okay-regex-match.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-regex-match.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-regex-match.nix b/third_party/nix/tests/lang/eval-okay-regex-match.nix
new file mode 100644
index 0000000000..273e259071
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-regex-match.nix
@@ -0,0 +1,29 @@
+with builtins;
+
+let
+
+  matches = pat: s: match pat s != null;
+
+  splitFN = match "((.*)/)?([^/]*)\\.(nix|cc)";
+
+in
+
+assert  matches "foobar" "foobar";
+assert  matches "fo*" "f";
+assert !matches "fo+" "f";
+assert  matches "fo*" "fo";
+assert  matches "fo*" "foo";
+assert  matches "fo+" "foo";
+assert  matches "fo{1,2}" "foo";
+assert !matches "fo{1,2}" "fooo";
+assert !matches "fo*" "foobar";
+assert  matches "[[:space:]]+([^[:space:]]+)[[:space:]]+" "  foo   ";
+assert !matches "[[:space:]]+([[:upper:]]+)[[:space:]]+" "  foo   ";
+
+assert match "(.*)\\.nix" "foobar.nix" == [ "foobar" ];
+assert match "[[:space:]]+([[:upper:]]+)[[:space:]]+" "  FOO   " == [ "FOO" ];
+
+assert splitFN "/path/to/foobar.nix" == [ "/path/to/" "/path/to" "foobar" "nix" ];
+assert splitFN "foobar.cc" == [ null null "foobar" "cc" ];
+
+true
diff --git a/third_party/nix/tests/lang/eval-okay-regex-split.exp b/third_party/nix/tests/lang/eval-okay-regex-split.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-regex-split.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-regex-split.nix b/third_party/nix/tests/lang/eval-okay-regex-split.nix
new file mode 100644
index 0000000000..0073e05778
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-regex-split.nix
@@ -0,0 +1,48 @@
+with builtins;
+
+# Non capturing regex returns empty lists
+assert  split "foobar" "foobar"  == ["" [] ""];
+assert  split "fo*" "f"          == ["" [] ""];
+assert  split "fo+" "f"          == ["f"];
+assert  split "fo*" "fo"         == ["" [] ""];
+assert  split "fo*" "foo"        == ["" [] ""];
+assert  split "fo+" "foo"        == ["" [] ""];
+assert  split "fo{1,2}" "foo"    == ["" [] ""];
+assert  split "fo{1,2}" "fooo"   == ["" [] "o"];
+assert  split "fo*" "foobar"     == ["" [] "bar"];
+
+# Capturing regex returns a list of sub-matches
+assert  split "(fo*)" "f"        == ["" ["f"] ""];
+assert  split "(fo+)" "f"        == ["f"];
+assert  split "(fo*)" "fo"       == ["" ["fo"] ""];
+assert  split "(f)(o*)" "f"      == ["" ["f" ""] ""];
+assert  split "(f)(o*)" "foo"    == ["" ["f" "oo"] ""];
+assert  split "(fo+)" "foo"      == ["" ["foo"] ""];
+assert  split "(fo{1,2})" "foo"  == ["" ["foo"] ""];
+assert  split "(fo{1,2})" "fooo" == ["" ["foo"] "o"];
+assert  split "(fo*)" "foobar"   == ["" ["foo"] "bar"];
+
+# Matches are greedy.
+assert  split "(o+)" "oooofoooo" == ["" ["oooo"] "f" ["oooo"] ""];
+
+# Matches multiple times.
+assert  split "(b)" "foobarbaz"  == ["foo" ["b"] "ar" ["b"] "az"];
+
+# Split large strings containing newlines. null are inserted when a
+# pattern within the current did not match anything.
+assert  split "[[:space:]]+|([',.!?])" ''
+  Nix Rocks!
+  That's why I use it.
+''  == [
+  "Nix" [ null ] "Rocks" ["!"] "" [ null ]
+  "That" ["'"] "s" [ null ] "why" [ null ] "I" [ null ] "use" [ null ] "it" ["."] "" [ null ]
+  ""
+];
+
+# Documentation examples
+assert  split  "(a)b" "abc"      == [ "" [ "a" ] "c" ];
+assert  split  "([ac])" "abc"    == [ "" [ "a" ] "b" [ "c" ] "" ];
+assert  split  "(a)|(c)" "abc"   == [ "" [ "a" null ] "b" [ null "c" ] "" ];
+assert  split  "([[:upper:]]+)" "  FOO   " == [ "  " [ "FOO" ] "   " ];
+
+true
diff --git a/third_party/nix/tests/lang/eval-okay-remove.exp b/third_party/nix/tests/lang/eval-okay-remove.exp
new file mode 100644
index 0000000000..8d38505c16
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-remove.exp
@@ -0,0 +1 @@
+456
diff --git a/third_party/nix/tests/lang/eval-okay-remove.nix b/third_party/nix/tests/lang/eval-okay-remove.nix
new file mode 100644
index 0000000000..4ad5ba897f
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-remove.nix
@@ -0,0 +1,5 @@
+let {
+  attrs = {x = 123; y = 456;};
+
+  body = (removeAttrs attrs ["x"]).y;
+}
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/eval-okay-replacestrings.exp b/third_party/nix/tests/lang/eval-okay-replacestrings.exp
new file mode 100644
index 0000000000..72e8274d8c
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-replacestrings.exp
@@ -0,0 +1 @@
+[ "faabar" "fbar" "fubar" "faboor" "fubar" "XaXbXcX" "X" "a_b" ]
diff --git a/third_party/nix/tests/lang/eval-okay-replacestrings.nix b/third_party/nix/tests/lang/eval-okay-replacestrings.nix
new file mode 100644
index 0000000000..bd8031fc00
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-replacestrings.nix
@@ -0,0 +1,11 @@
+with builtins;
+
+[ (replaceStrings ["o"] ["a"] "foobar")
+  (replaceStrings ["o"] [""] "foobar")
+  (replaceStrings ["oo"] ["u"] "foobar")
+  (replaceStrings ["oo" "a"] ["a" "oo"] "foobar")
+  (replaceStrings ["oo" "oo"] ["u" "i"] "foobar")
+  (replaceStrings [""] ["X"] "abc")
+  (replaceStrings [""] ["X"] "")
+  (replaceStrings ["-"] ["_"] "a-b")
+]
diff --git a/third_party/nix/tests/lang/eval-okay-scope-1.exp b/third_party/nix/tests/lang/eval-okay-scope-1.exp
new file mode 100644
index 0000000000..00750edc07
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-1.exp
@@ -0,0 +1 @@
+3
diff --git a/third_party/nix/tests/lang/eval-okay-scope-1.nix b/third_party/nix/tests/lang/eval-okay-scope-1.nix
new file mode 100644
index 0000000000..fa38a7174e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-1.nix
@@ -0,0 +1,6 @@
+(({x}: x:
+
+  { x = 1;
+    y = x;
+  }
+) {x = 2;} 3).y
diff --git a/third_party/nix/tests/lang/eval-okay-scope-2.exp b/third_party/nix/tests/lang/eval-okay-scope-2.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-2.exp
@@ -0,0 +1 @@
+1
diff --git a/third_party/nix/tests/lang/eval-okay-scope-2.nix b/third_party/nix/tests/lang/eval-okay-scope-2.nix
new file mode 100644
index 0000000000..eb8b02bc49
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-2.nix
@@ -0,0 +1,6 @@
+((x: {x}:
+  rec {
+    x = 1;
+    y = x;
+  }
+) 2 {x = 3;}).y
diff --git a/third_party/nix/tests/lang/eval-okay-scope-3.exp b/third_party/nix/tests/lang/eval-okay-scope-3.exp
new file mode 100644
index 0000000000..b8626c4cff
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-3.exp
@@ -0,0 +1 @@
+4
diff --git a/third_party/nix/tests/lang/eval-okay-scope-3.nix b/third_party/nix/tests/lang/eval-okay-scope-3.nix
new file mode 100644
index 0000000000..10d6bc04d8
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-3.nix
@@ -0,0 +1,6 @@
+((x: as: {x}:
+  rec {
+    inherit (as) x;
+    y = x;
+  }
+) 2 {x = 4;} {x = 3;}).y
diff --git a/third_party/nix/tests/lang/eval-okay-scope-4.exp b/third_party/nix/tests/lang/eval-okay-scope-4.exp
new file mode 100644
index 0000000000..00ff03a46c
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-4.exp
@@ -0,0 +1 @@
+"ccdd"
diff --git a/third_party/nix/tests/lang/eval-okay-scope-4.nix b/third_party/nix/tests/lang/eval-okay-scope-4.nix
new file mode 100644
index 0000000000..dc8243bc85
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-4.nix
@@ -0,0 +1,10 @@
+let {
+
+  x = "a";
+  y = "b";
+
+  f = {x ? y, y ? x}: x + y;
+
+  body = f {x = "c";} + f {y = "d";};
+
+}
diff --git a/third_party/nix/tests/lang/eval-okay-scope-6.exp b/third_party/nix/tests/lang/eval-okay-scope-6.exp
new file mode 100644
index 0000000000..00ff03a46c
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-6.exp
@@ -0,0 +1 @@
+"ccdd"
diff --git a/third_party/nix/tests/lang/eval-okay-scope-6.nix b/third_party/nix/tests/lang/eval-okay-scope-6.nix
new file mode 100644
index 0000000000..0995d4e7e7
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-6.nix
@@ -0,0 +1,7 @@
+let {
+
+  f = {x ? y, y ? x}: x + y;
+
+  body = f {x = "c";} + f {y = "d";};
+
+}
diff --git a/third_party/nix/tests/lang/eval-okay-scope-7.exp b/third_party/nix/tests/lang/eval-okay-scope-7.exp
new file mode 100644
index 0000000000..d00491fd7e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-7.exp
@@ -0,0 +1 @@
+1
diff --git a/third_party/nix/tests/lang/eval-okay-scope-7.nix b/third_party/nix/tests/lang/eval-okay-scope-7.nix
new file mode 100644
index 0000000000..4da02968f6
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-scope-7.nix
@@ -0,0 +1,6 @@
+rec {
+  inherit (x) y;
+  x = {
+    y = 1;
+  };
+}.y
diff --git a/third_party/nix/tests/lang/eval-okay-search-path.exp b/third_party/nix/tests/lang/eval-okay-search-path.exp
new file mode 100644
index 0000000000..4519bc406d
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-search-path.exp
@@ -0,0 +1 @@
+"abccX"
diff --git a/third_party/nix/tests/lang/eval-okay-search-path.flags b/third_party/nix/tests/lang/eval-okay-search-path.flags
new file mode 100644
index 0000000000..a28e682100
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-search-path.flags
@@ -0,0 +1 @@
+-I lang/dir1 -I lang/dir2 -I dir5=lang/dir3
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/eval-okay-search-path.nix b/third_party/nix/tests/lang/eval-okay-search-path.nix
new file mode 100644
index 0000000000..cca41f821f
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-search-path.nix
@@ -0,0 +1,11 @@
+with import ./lib.nix;
+with builtins;
+
+assert pathExists <nix/buildenv.nix>;
+
+assert length __nixPath == 6;
+assert length (filter (x: x.prefix == "nix") __nixPath) == 1;
+assert length (filter (x: baseNameOf x.path == "dir4") __nixPath) == 1;
+
+import <a.nix> + import <b.nix> + import <c.nix> + import <dir5/c.nix>
+  + (let __nixPath = [ { path = ./dir2; } { path = ./dir1; } ]; in import <a.nix>)
diff --git a/third_party/nix/tests/lang/eval-okay-seq.exp b/third_party/nix/tests/lang/eval-okay-seq.exp
new file mode 100644
index 0000000000..0cfbf08886
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-seq.exp
@@ -0,0 +1 @@
+2
diff --git a/third_party/nix/tests/lang/eval-okay-seq.nix b/third_party/nix/tests/lang/eval-okay-seq.nix
new file mode 100644
index 0000000000..0a9a21c03b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-seq.nix
@@ -0,0 +1 @@
+builtins.seq 1 2
diff --git a/third_party/nix/tests/lang/eval-okay-sort.exp b/third_party/nix/tests/lang/eval-okay-sort.exp
new file mode 100644
index 0000000000..148b935163
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-sort.exp
@@ -0,0 +1 @@
+[ [ 42 77 147 249 483 526 ] [ 526 483 249 147 77 42 ] [ "bar" "fnord" "foo" "xyzzy" ] [ { key = 1; value = "foo"; } { key = 1; value = "fnord"; } { key = 2; value = "bar"; } ] ]
diff --git a/third_party/nix/tests/lang/eval-okay-sort.nix b/third_party/nix/tests/lang/eval-okay-sort.nix
new file mode 100644
index 0000000000..8299c3a4a3
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-sort.nix
@@ -0,0 +1,8 @@
+with builtins;
+
+[ (sort lessThan [ 483 249 526 147 42 77 ])
+  (sort (x: y: y < x) [ 483 249 526 147 42 77 ])
+  (sort lessThan [ "foo" "bar" "xyzzy" "fnord" ])
+  (sort (x: y: x.key < y.key)
+    [ { key = 1; value = "foo"; } { key = 2; value = "bar"; } { key = 1; value = "fnord"; } ]) 
+]
diff --git a/third_party/nix/tests/lang/eval-okay-splitversion.exp b/third_party/nix/tests/lang/eval-okay-splitversion.exp
new file mode 100644
index 0000000000..153ceb8186
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-splitversion.exp
@@ -0,0 +1 @@
+[ "1" "2" "3" ]
diff --git a/third_party/nix/tests/lang/eval-okay-splitversion.nix b/third_party/nix/tests/lang/eval-okay-splitversion.nix
new file mode 100644
index 0000000000..9e5c99d2e7
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-splitversion.nix
@@ -0,0 +1 @@
+builtins.splitVersion "1.2.3"
diff --git a/third_party/nix/tests/lang/eval-okay-string.exp b/third_party/nix/tests/lang/eval-okay-string.exp
new file mode 100644
index 0000000000..63f650f73a
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-string.exp
@@ -0,0 +1 @@
+"foobar/a/b/c/d/foo/xyzzy/foo.txt/../foo/x/yescape: \"quote\" \n \\end\nof\nlinefoobarblaatfoo$bar$\"$\"$"
diff --git a/third_party/nix/tests/lang/eval-okay-string.nix b/third_party/nix/tests/lang/eval-okay-string.nix
new file mode 100644
index 0000000000..47cc989ad4
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-string.nix
@@ -0,0 +1,12 @@
+"foo" + "bar"
+  + toString (/a/b + /c/d)
+  + toString (/foo/bar + "/../xyzzy/." + "/foo.txt")
+  + ("/../foo" + toString /x/y)
+  + "escape: \"quote\" \n \\"
+  + "end
+of
+line"
+  + "foo${if true then "b${"a" + "r"}" else "xyzzy"}blaat"
+  + "foo$bar"
+  + "$\"$\""
+  + "$"
diff --git a/third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.exp b/third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.nix b/third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.nix
new file mode 100644
index 0000000000..5e40928dbe
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-strings-as-attrs-names.nix
@@ -0,0 +1,20 @@
+let
+
+  attr = {
+    "key 1" = "test";
+    "key 2" = "caseok";
+  };
+
+  t1 = builtins.getAttr "key 1" attr;
+  t2 = attr."key 2";
+  t3 = attr ? "key 1";
+  t4 = builtins.attrNames { inherit (attr) "key 1"; };
+
+  # This is permitted, but there is currently no way to reference this
+  # variable.
+  "foo bar" = 1;
+
+in t1 == "test"
+   && t2 == "caseok"
+   && t3 == true
+   && t4 == ["key 1"]
diff --git a/third_party/nix/tests/lang/eval-okay-substring.exp b/third_party/nix/tests/lang/eval-okay-substring.exp
new file mode 100644
index 0000000000..6aace04b0f
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-substring.exp
@@ -0,0 +1 @@
+"ooxfoobarybarzobaabbc"
diff --git a/third_party/nix/tests/lang/eval-okay-substring.nix b/third_party/nix/tests/lang/eval-okay-substring.nix
new file mode 100644
index 0000000000..424af00d9b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-substring.nix
@@ -0,0 +1,21 @@
+with builtins;
+
+let
+
+  s = "foobar";
+
+in
+
+substring 1 2 s
++ "x"
++ substring 0 (stringLength s) s
++ "y"
++ substring 3 100 s
++ "z"
++ substring 2 (sub (stringLength s) 3) s
++ "a"
++ substring 3 0 s
++ "b"
++ substring 3 1 s
++ "c"
++ substring 5 10 "perl"
diff --git a/third_party/nix/tests/lang/eval-okay-tail-call-1.exp-disabled b/third_party/nix/tests/lang/eval-okay-tail-call-1.exp-disabled
new file mode 100644
index 0000000000..f7393e847d
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-tail-call-1.exp-disabled
@@ -0,0 +1 @@
+100000
diff --git a/third_party/nix/tests/lang/eval-okay-tail-call-1.nix b/third_party/nix/tests/lang/eval-okay-tail-call-1.nix
new file mode 100644
index 0000000000..a3962ce3fd
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-tail-call-1.nix
@@ -0,0 +1,3 @@
+let
+  f = n: if n == 100000 then n else f (n + 1);
+in f 0
diff --git a/third_party/nix/tests/lang/eval-okay-tojson.exp b/third_party/nix/tests/lang/eval-okay-tojson.exp
new file mode 100644
index 0000000000..e92aae3235
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-tojson.exp
@@ -0,0 +1 @@
+"{\"a\":123,\"b\":-456,\"c\":\"foo\",\"d\":\"foo\\n\\\"bar\\\"\",\"e\":true,\"f\":false,\"g\":[1,2,3],\"h\":[\"a\",[\"b\",{\"foo\\nbar\":{}}]],\"i\":3,\"j\":1.44,\"k\":\"foo\"}"
diff --git a/third_party/nix/tests/lang/eval-okay-tojson.nix b/third_party/nix/tests/lang/eval-okay-tojson.nix
new file mode 100644
index 0000000000..ce67943bea
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-tojson.nix
@@ -0,0 +1,13 @@
+builtins.toJSON
+  { a = 123;
+    b = -456;
+    c = "foo";
+    d = "foo\n\"bar\"";
+    e = true;
+    f = false;
+    g = [ 1 2 3 ];
+    h = [ "a" [ "b" { "foo\nbar" = {}; } ] ];
+    i = 1 + 2;
+    j = 1.44;
+    k = { __toString = self: self.a; a = "foo"; };
+  }
diff --git a/third_party/nix/tests/lang/eval-okay-toxml.exp b/third_party/nix/tests/lang/eval-okay-toxml.exp
new file mode 100644
index 0000000000..828220890e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-toxml.exp
@@ -0,0 +1 @@
+"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n  <attrs>\n    <attr name=\"a\">\n      <string value=\"s\" />\n    </attr>\n  </attrs>\n</expr>\n"
diff --git a/third_party/nix/tests/lang/eval-okay-toxml.nix b/third_party/nix/tests/lang/eval-okay-toxml.nix
new file mode 100644
index 0000000000..068c97a6c1
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-toxml.nix
@@ -0,0 +1,3 @@
+# Make sure the expected XML output is produced; in particular, make sure it
+# doesn't contain source location information.
+builtins.toXML { a = "s"; }
diff --git a/third_party/nix/tests/lang/eval-okay-toxml2.exp b/third_party/nix/tests/lang/eval-okay-toxml2.exp
new file mode 100644
index 0000000000..634a841eb1
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-toxml2.exp
@@ -0,0 +1 @@
+"<?xml version='1.0' encoding='utf-8'?>\n<expr>\n  <list>\n    <string value=\"ab\" />\n    <int value=\"10\" />\n    <attrs>\n      <attr name=\"x\">\n        <string value=\"x\" />\n      </attr>\n      <attr name=\"y\">\n        <string value=\"x\" />\n      </attr>\n    </attrs>\n  </list>\n</expr>\n"
diff --git a/third_party/nix/tests/lang/eval-okay-toxml2.nix b/third_party/nix/tests/lang/eval-okay-toxml2.nix
new file mode 100644
index 0000000000..ff1791b30e
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-toxml2.nix
@@ -0,0 +1 @@
+builtins.toXML [("a" + "b") 10 (rec {x = "x"; y = x;})]
diff --git a/third_party/nix/tests/lang/eval-okay-tryeval.exp b/third_party/nix/tests/lang/eval-okay-tryeval.exp
new file mode 100644
index 0000000000..2b2e6fa711
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-tryeval.exp
@@ -0,0 +1 @@
+{ x = { success = true; value = "x"; }; y = { success = false; value = false; }; z = { success = false; value = false; }; }
diff --git a/third_party/nix/tests/lang/eval-okay-tryeval.nix b/third_party/nix/tests/lang/eval-okay-tryeval.nix
new file mode 100644
index 0000000000..629bc440a8
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-tryeval.nix
@@ -0,0 +1,5 @@
+{
+  x = builtins.tryEval "x";
+  y = builtins.tryEval (assert false; "y");
+  z = builtins.tryEval (throw "bla");
+}
diff --git a/third_party/nix/tests/lang/eval-okay-types.exp b/third_party/nix/tests/lang/eval-okay-types.exp
new file mode 100644
index 0000000000..92a1532993
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-types.exp
@@ -0,0 +1 @@
+[ true false true false true false true false true true true true true true true true true true true false true true true false "int" "bool" "string" "null" "set" "list" "lambda" "lambda" "lambda" "lambda" ]
diff --git a/third_party/nix/tests/lang/eval-okay-types.nix b/third_party/nix/tests/lang/eval-okay-types.nix
new file mode 100644
index 0000000000..9b58be5d1d
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-types.nix
@@ -0,0 +1,37 @@
+with builtins;
+
+[ (isNull null)
+  (isNull (x: x))
+  (isFunction (x: x))
+  (isFunction "fnord")
+  (isString ("foo" + "bar"))
+  (isString [ "x" ])
+  (isInt (1 + 2))
+  (isInt { x = 123; })
+  (isInt (1 / 2))
+  (isInt (1 + 1))
+  (isInt (1 / 2))
+  (isInt (1 * 2))
+  (isInt (1 - 2))
+  (isFloat (1.2))
+  (isFloat (1 + 1.0))
+  (isFloat (1 / 2.0))
+  (isFloat (1 * 2.0))
+  (isFloat (1 - 2.0))
+  (isBool (true && false))
+  (isBool null)
+  (isPath /nix/store)
+  (isPath ./.)
+  (isAttrs { x = 123; })
+  (isAttrs null)
+  (typeOf (3 * 4))
+  (typeOf true)
+  (typeOf "xyzzy")
+  (typeOf null)
+  (typeOf { x = 456; })
+  (typeOf [ 1 2 3 ])
+  (typeOf (x: x))
+  (typeOf ((x: y: x) 1))
+  (typeOf map)
+  (typeOf (map (x: x)))
+]
diff --git a/third_party/nix/tests/lang/eval-okay-versions.exp b/third_party/nix/tests/lang/eval-okay-versions.exp
new file mode 100644
index 0000000000..27ba77ddaf
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-versions.exp
@@ -0,0 +1 @@
+true
diff --git a/third_party/nix/tests/lang/eval-okay-versions.nix b/third_party/nix/tests/lang/eval-okay-versions.nix
new file mode 100644
index 0000000000..e63c36586b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-versions.nix
@@ -0,0 +1,40 @@
+let
+
+  name1 = "hello-1.0.2";
+  name2 = "hello";
+  name3 = "915resolution-0.5.2";
+  name4 = "xf86-video-i810-1.7.4";
+
+  eq = 0;
+  lt = builtins.sub 0 1;
+  gt = 1;
+
+  versionTest = v1: v2: expected:
+    let d1 = builtins.compareVersions v1 v2;
+        d2 = builtins.compareVersions v2 v1;
+    in d1 == builtins.sub 0 d2 && d1 == expected;
+
+  tests = [
+    ((builtins.parseDrvName name1).name == "hello")
+    ((builtins.parseDrvName name1).version == "1.0.2")
+    ((builtins.parseDrvName name2).name == "hello")
+    ((builtins.parseDrvName name2).version == "")
+    ((builtins.parseDrvName name3).name == "915resolution")
+    ((builtins.parseDrvName name3).version == "0.5.2")
+    ((builtins.parseDrvName name4).name == "xf86-video-i810")
+    ((builtins.parseDrvName name4).version == "1.7.4")
+    (versionTest "1.0" "2.3" lt)
+    (versionTest "2.1" "2.3" lt)
+    (versionTest "2.3" "2.3" eq)
+    (versionTest "2.5" "2.3" gt)
+    (versionTest "3.1" "2.3" gt)
+    (versionTest "2.3.1" "2.3" gt)
+    (versionTest "2.3.1" "2.3a" gt)
+    (versionTest "2.3pre1" "2.3" lt)
+    (versionTest "2.3pre3" "2.3pre12" lt)
+    (versionTest "2.3a" "2.3c" lt)
+    (versionTest "2.3pre1" "2.3c" lt)
+    (versionTest "2.3pre1" "2.3q" lt)
+  ];
+
+in (import ./lib.nix).and tests
diff --git a/third_party/nix/tests/lang/eval-okay-with.exp b/third_party/nix/tests/lang/eval-okay-with.exp
new file mode 100644
index 0000000000..378c8dc804
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-with.exp
@@ -0,0 +1 @@
+"xyzzybarxyzzybar"
diff --git a/third_party/nix/tests/lang/eval-okay-with.nix b/third_party/nix/tests/lang/eval-okay-with.nix
new file mode 100644
index 0000000000..033e8d3aba
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-with.nix
@@ -0,0 +1,19 @@
+let {
+
+  a = "xyzzy";
+
+  as = {
+    a = "foo";
+    b = "bar";
+  };
+
+  bs = {
+    a = "bar";
+  };
+
+  x = with as; a + b;
+
+  y = with as; with bs; a + b;
+
+  body = x + y;
+}
diff --git a/third_party/nix/tests/lang/eval-okay-xml.exp.xml b/third_party/nix/tests/lang/eval-okay-xml.exp.xml
new file mode 100644
index 0000000000..92b75e0b8b
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-xml.exp.xml
@@ -0,0 +1,52 @@
+<?xml version='1.0' encoding='utf-8'?>
+<expr>
+  <attrs>
+    <attr name="a">
+      <string value="foo" />
+    </attr>
+    <attr name="at">
+      <function>
+        <attrspat name="args">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="b">
+      <string value="bar" />
+    </attr>
+    <attr name="c">
+      <string value="foobar" />
+    </attr>
+    <attr name="ellipsis">
+      <function>
+        <attrspat ellipsis="1">
+          <attr name="x" />
+          <attr name="y" />
+          <attr name="z" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="f">
+      <function>
+        <attrspat>
+          <attr name="z" />
+          <attr name="x" />
+          <attr name="y" />
+        </attrspat>
+      </function>
+    </attr>
+    <attr name="id">
+      <function>
+        <varpat name="x" />
+      </function>
+    </attr>
+    <attr name="x">
+      <int value="123" />
+    </attr>
+    <attr name="y">
+      <float value="567.89" />
+    </attr>
+  </attrs>
+</expr>
diff --git a/third_party/nix/tests/lang/eval-okay-xml.nix b/third_party/nix/tests/lang/eval-okay-xml.nix
new file mode 100644
index 0000000000..9ee9f8a0b4
--- /dev/null
+++ b/third_party/nix/tests/lang/eval-okay-xml.nix
@@ -0,0 +1,21 @@
+rec {
+
+  x = 123;
+
+  y = 567.890;
+
+  a = "foo";
+
+  b = "bar";
+
+  c = "foo" + "bar";
+
+  f = {z, x, y}: if y then x else z;
+
+  id = x: x;
+
+  at = args@{x, y, z}: x;
+
+  ellipsis = {x, y, z, ...}: x;
+
+}
diff --git a/third_party/nix/tests/lang/imported.nix b/third_party/nix/tests/lang/imported.nix
new file mode 100644
index 0000000000..fb39ee4efa
--- /dev/null
+++ b/third_party/nix/tests/lang/imported.nix
@@ -0,0 +1,3 @@
+# The function ‘range’ comes from lib.nix and was added to the lexical
+# scope by scopedImport.
+range 1 5 ++ import ./imported2.nix
diff --git a/third_party/nix/tests/lang/imported2.nix b/third_party/nix/tests/lang/imported2.nix
new file mode 100644
index 0000000000..6d0a2992b7
--- /dev/null
+++ b/third_party/nix/tests/lang/imported2.nix
@@ -0,0 +1 @@
+range 6 10
diff --git a/third_party/nix/tests/lang/lib.nix b/third_party/nix/tests/lang/lib.nix
new file mode 100644
index 0000000000..028a538314
--- /dev/null
+++ b/third_party/nix/tests/lang/lib.nix
@@ -0,0 +1,61 @@
+with builtins;
+
+rec {
+
+  fold = op: nul: list:
+    if list == []
+    then nul
+    else op (head list) (fold op nul (tail list));
+
+  concat =
+    fold (x: y: x + y) "";
+
+  and = fold (x: y: x && y) true;
+
+  flatten = x:
+    if isList x
+    then fold (x: y: (flatten x) ++ y) [] x
+    else [x];
+
+  sum = foldl' (x: y: add x y) 0;
+
+  hasSuffix = ext: fileName:
+    let lenFileName = stringLength fileName;
+        lenExt = stringLength ext;
+    in !(lessThan lenFileName lenExt) &&
+       substring (sub lenFileName lenExt) lenFileName fileName == ext;
+
+  # Split a list at the given position.
+  splitAt = pos: list:
+    if pos == 0 then {first = []; second = list;} else
+    if list == [] then {first = []; second = [];} else
+    let res = splitAt (sub pos 1) (tail list);
+    in {first = [(head list)] ++ res.first; second = res.second;};
+
+  # Stable merge sort.
+  sortBy = comp: list:
+    if lessThan 1 (length list)
+    then
+      let
+        split = splitAt (div (length list) 2) list;
+        first = sortBy comp split.first;
+        second = sortBy comp split.second;
+      in mergeLists comp first second
+    else list;
+
+  mergeLists = comp: list1: list2:
+    if list1 == [] then list2 else
+    if list2 == [] then list1 else
+    if comp (head list2) (head list1) then [(head list2)] ++ mergeLists comp list1 (tail list2) else
+    [(head list1)] ++ mergeLists comp (tail list1) list2;
+
+  id = x: x;
+
+  const = x: y: x;
+
+  range = first: last:
+    if first > last
+      then []
+      else genList (n: first + n) (last - first + 1);
+
+}
diff --git a/third_party/nix/tests/lang/parse-fail-dup-attrs-1.nix b/third_party/nix/tests/lang/parse-fail-dup-attrs-1.nix
new file mode 100644
index 0000000000..2c02317d2a
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-dup-attrs-1.nix
@@ -0,0 +1,4 @@
+{ x = 123;
+  y = 456;
+  x = 789;
+}
diff --git a/third_party/nix/tests/lang/parse-fail-dup-attrs-2.nix b/third_party/nix/tests/lang/parse-fail-dup-attrs-2.nix
new file mode 100644
index 0000000000..864d9865e0
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-dup-attrs-2.nix
@@ -0,0 +1,13 @@
+let {
+
+  as = {
+    x = 123;
+    y = 456;
+  };
+
+  bs = {
+    x = 789;
+    inherit (as) x;
+  };
+  
+}
diff --git a/third_party/nix/tests/lang/parse-fail-dup-attrs-3.nix b/third_party/nix/tests/lang/parse-fail-dup-attrs-3.nix
new file mode 100644
index 0000000000..114d19779f
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-dup-attrs-3.nix
@@ -0,0 +1,13 @@
+let {
+
+  as = {
+    x = 123;
+    y = 456;
+  };
+
+  bs = rec {
+    x = 789;
+    inherit (as) x;
+  };
+  
+}
diff --git a/third_party/nix/tests/lang/parse-fail-dup-attrs-4.nix b/third_party/nix/tests/lang/parse-fail-dup-attrs-4.nix
new file mode 100644
index 0000000000..77417432b3
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-dup-attrs-4.nix
@@ -0,0 +1,4 @@
+{
+  services.ssh.port = 22;
+  services.ssh.port = 23;
+}
diff --git a/third_party/nix/tests/lang/parse-fail-dup-attrs-7.nix b/third_party/nix/tests/lang/parse-fail-dup-attrs-7.nix
new file mode 100644
index 0000000000..bbc3eb08c0
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-dup-attrs-7.nix
@@ -0,0 +1,9 @@
+rec {
+
+  x = 1;
+
+  as = {
+    inherit x;
+    inherit x;
+  };
+}
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/parse-fail-dup-formals.nix b/third_party/nix/tests/lang/parse-fail-dup-formals.nix
new file mode 100644
index 0000000000..a0edd91a96
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-dup-formals.nix
@@ -0,0 +1 @@
+{x, y, x}: x
\ No newline at end of file
diff --git a/third_party/nix/tests/lang/parse-fail-mixed-nested-attrs1.nix b/third_party/nix/tests/lang/parse-fail-mixed-nested-attrs1.nix
new file mode 100644
index 0000000000..11e40e66fd
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-mixed-nested-attrs1.nix
@@ -0,0 +1,4 @@
+{ 
+  x.z = 3; 
+  x = { y = 3; z = 3; }; 
+}
diff --git a/third_party/nix/tests/lang/parse-fail-mixed-nested-attrs2.nix b/third_party/nix/tests/lang/parse-fail-mixed-nested-attrs2.nix
new file mode 100644
index 0000000000..17da82e5f0
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-mixed-nested-attrs2.nix
@@ -0,0 +1,4 @@
+{ 
+  x.y.y = 3; 
+  x = { y.y= 3; z = 3; }; 
+}
diff --git a/third_party/nix/tests/lang/parse-fail-patterns-1.nix b/third_party/nix/tests/lang/parse-fail-patterns-1.nix
new file mode 100644
index 0000000000..7b40616417
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-patterns-1.nix
@@ -0,0 +1 @@
+args@{args, x, y, z}: x
diff --git a/third_party/nix/tests/lang/parse-fail-regression-20060610.nix b/third_party/nix/tests/lang/parse-fail-regression-20060610.nix
new file mode 100644
index 0000000000..b1934f7e1e
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-regression-20060610.nix
@@ -0,0 +1,11 @@
+let {
+  x =
+    {gcc}:
+    {
+      inherit gcc;
+    };
+
+  body = ({
+    inherit gcc;
+  }).gcc;
+}
diff --git a/third_party/nix/tests/lang/parse-fail-uft8.nix b/third_party/nix/tests/lang/parse-fail-uft8.nix
new file mode 100644
index 0000000000..34948d48ae
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-uft8.nix
@@ -0,0 +1 @@
+123 é 4
diff --git a/third_party/nix/tests/lang/parse-fail-undef-var-2.nix b/third_party/nix/tests/lang/parse-fail-undef-var-2.nix
new file mode 100644
index 0000000000..c10a52b1ea
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-undef-var-2.nix
@@ -0,0 +1,7 @@
+let {
+
+  f = {x, y : ["baz" "bar" z "bat"]}: x + y;
+
+  body = f {x = "foo"; y = "bar";};
+
+}
diff --git a/third_party/nix/tests/lang/parse-fail-undef-var.nix b/third_party/nix/tests/lang/parse-fail-undef-var.nix
new file mode 100644
index 0000000000..7b63008110
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-fail-undef-var.nix
@@ -0,0 +1 @@
+x: y
diff --git a/third_party/nix/tests/lang/parse-okay-1.nix b/third_party/nix/tests/lang/parse-okay-1.nix
new file mode 100644
index 0000000000..23a58ed109
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-1.nix
@@ -0,0 +1 @@
+{x, y, z}: x + y + z
diff --git a/third_party/nix/tests/lang/parse-okay-crlf.nix b/third_party/nix/tests/lang/parse-okay-crlf.nix
new file mode 100644
index 0000000000..21518d4c6d
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-crlf.nix
@@ -0,0 +1,17 @@
+rec {
+
+  /* Dit is

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

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

+  string
+  test\r";
+
+  z = 456;
}
diff --git a/third_party/nix/tests/lang/parse-okay-dup-attrs-5.nix b/third_party/nix/tests/lang/parse-okay-dup-attrs-5.nix
new file mode 100644
index 0000000000..f4b9efd0c5
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-dup-attrs-5.nix
@@ -0,0 +1,4 @@
+{
+  services.ssh = { enable = true; };
+  services.ssh.port = 23;
+}
diff --git a/third_party/nix/tests/lang/parse-okay-dup-attrs-6.nix b/third_party/nix/tests/lang/parse-okay-dup-attrs-6.nix
new file mode 100644
index 0000000000..ae6d7a7693
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-dup-attrs-6.nix
@@ -0,0 +1,4 @@
+{
+  services.ssh.port = 23;
+  services.ssh = { enable = true; };
+}
diff --git a/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-1.nix b/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-1.nix
new file mode 100644
index 0000000000..fd1001c8ca
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-1.nix
@@ -0,0 +1,4 @@
+{ 
+  x = { y = 3; z = 3; }; 
+  x.q = 3; 
+}
diff --git a/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-2.nix b/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-2.nix
new file mode 100644
index 0000000000..ad066b6803
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-2.nix
@@ -0,0 +1,4 @@
+{ 
+  x.q = 3; 
+  x = { y = 3; z = 3; }; 
+}
diff --git a/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-3.nix b/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-3.nix
new file mode 100644
index 0000000000..45a33e4803
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-mixed-nested-attrs-3.nix
@@ -0,0 +1,7 @@
+{
+    services.ssh.enable = true;
+    services.ssh = { port = 123; };
+    services = {
+        httpd.enable = true;
+    };
+}
diff --git a/third_party/nix/tests/lang/parse-okay-regression-20041027.nix b/third_party/nix/tests/lang/parse-okay-regression-20041027.nix
new file mode 100644
index 0000000000..ae2e256eea
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-regression-20041027.nix
@@ -0,0 +1,11 @@
+{stdenv, fetchurl /* pkgconfig, libX11 */ }:
+
+stdenv.mkDerivation {
+  name = "libXi-6.0.1";
+  src = fetchurl {
+    url = http://freedesktop.org/~xlibs/release/libXi-6.0.1.tar.bz2;
+    md5 = "7e935a42428d63a387b3c048be0f2756";
+  };
+/*  buildInputs = [pkgconfig];
+  propagatedBuildInputs = [libX11]; */
+}
diff --git a/third_party/nix/tests/lang/parse-okay-regression-751.nix b/third_party/nix/tests/lang/parse-okay-regression-751.nix
new file mode 100644
index 0000000000..05c78b3016
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-regression-751.nix
@@ -0,0 +1,2 @@
+let const = a: "const"; in
+''${ const { x = "q"; }}''
diff --git a/third_party/nix/tests/lang/parse-okay-subversion.nix b/third_party/nix/tests/lang/parse-okay-subversion.nix
new file mode 100644
index 0000000000..356272815d
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-subversion.nix
@@ -0,0 +1,43 @@
+{ localServer ? false
+, httpServer ? false
+, sslSupport ? false
+, pythonBindings ? false
+, javaSwigBindings ? false
+, javahlBindings ? false
+, stdenv, fetchurl
+, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
+}:
+
+assert expat != null;
+assert localServer -> db4 != null;
+assert httpServer -> httpd != null && httpd.expat == expat;
+assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl);
+assert pythonBindings -> swig != null && swig.pythonSupport;
+assert javaSwigBindings -> swig != null && swig.javaSupport;
+assert javahlBindings -> j2sdk != null;
+
+stdenv.mkDerivation {
+  name = "subversion-1.1.1";
+
+  builder = /foo/bar;
+  src = fetchurl {
+    url = http://subversion.tigris.org/tarballs/subversion-1.1.1.tar.bz2;
+    md5 = "a180c3fe91680389c210c99def54d9e0";
+  };
+
+  # This is a hopefully temporary fix for the problem that
+  # libsvnjavahl.so isn't linked against libstdc++, which causes
+  # loading the library into the JVM to fail.
+  patches = if javahlBindings then [/javahl.patch] else [];
+
+  openssl = if sslSupport then openssl else null;
+  httpd = if httpServer then httpd else null;
+  db4 = if localServer then db4 else null;
+  swig = if pythonBindings || javaSwigBindings then swig else null;
+  python = if pythonBindings then swig.python else null;
+  j2sdk = if javaSwigBindings then swig.j2sdk else
+          if javahlBindings then j2sdk else null;
+
+  inherit expat localServer httpServer sslSupport
+          pythonBindings javaSwigBindings javahlBindings;
+}
diff --git a/third_party/nix/tests/lang/parse-okay-url.nix b/third_party/nix/tests/lang/parse-okay-url.nix
new file mode 100644
index 0000000000..fce3b13ee6
--- /dev/null
+++ b/third_party/nix/tests/lang/parse-okay-url.nix
@@ -0,0 +1,7 @@
+[ x:x
+  https://svn.cs.uu.nl:12443/repos/trace/trunk
+  http://www2.mplayerhq.hu/MPlayer/releases/fonts/font-arial-iso-8859-1.tar.bz2
+  http://losser.st-lab.cs.uu.nl/~armijn/.nix/gcc-3.3.4-static-nix.tar.gz
+  http://fpdownload.macromedia.com/get/shockwave/flash/english/linux/7.0r25/install_flash_player_7_linux.tar.gz
+  ftp://ftp.gtk.org/pub/gtk/v1.2/gtk+-1.2.10.tar.gz
+]
diff --git a/third_party/nix/tests/lang/readDir/bar b/third_party/nix/tests/lang/readDir/bar
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/nix/tests/lang/readDir/bar
diff --git a/third_party/nix/tests/lang/readDir/foo/git-hates-directories b/third_party/nix/tests/lang/readDir/foo/git-hates-directories
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/third_party/nix/tests/lang/readDir/foo/git-hates-directories
diff --git a/third_party/nix/tests/linux-sandbox.sh b/third_party/nix/tests/linux-sandbox.sh
new file mode 100644
index 0000000000..52967d07dd
--- /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/local.mk b/third_party/nix/tests/local.mk
new file mode 100644
index 0000000000..187f96ea20
--- /dev/null
+++ b/third_party/nix/tests/local.mk
@@ -0,0 +1,42 @@
+check:
+	@echo "Warning: Nix has no 'make check'. Please install Nix and run 'make installcheck' instead."
+
+nix_tests = \
+  init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \
+  gc.sh \
+  gc-concurrent.sh \
+  gc-auto.sh \
+  referrers.sh user-envs.sh logging.sh nix-build.sh misc.sh fixed.sh \
+  gc-runtime.sh check-refs.sh filter-source.sh \
+  remote-store.sh export.sh export-graph.sh \
+  timeout.sh secure-drv-outputs.sh nix-channel.sh \
+  multiple-outputs.sh import-derivation.sh fetchurl.sh optimise-store.sh \
+  binary-cache.sh nix-profile.sh repair.sh dump-db.sh case-hack.sh \
+  check-reqs.sh pass-as-file.sh tarball.sh restricted.sh \
+  placeholders.sh nix-shell.sh \
+  linux-sandbox.sh \
+  build-dry.sh \
+  build-remote.sh \
+  nar-access.sh \
+  structured-attrs.sh \
+  fetchGit.sh \
+  fetchMercurial.sh \
+  signing.sh \
+  run.sh \
+  brotli.sh \
+  pure-eval.sh \
+  check.sh \
+  plugins.sh \
+  search.sh \
+  nix-copy-ssh.sh \
+  post-hook.sh \
+  function-trace.sh
+  # parallel.sh
+
+install-tests += $(foreach x, $(nix_tests), tests/$(x))
+
+tests-environment = NIX_REMOTE= $(bash) -e
+
+clean-files += $(d)/common.sh
+
+installcheck: $(d)/common.sh $(d)/plugins/libplugintest.$(SO_EXT)
diff --git a/third_party/nix/tests/logging.sh b/third_party/nix/tests/logging.sh
new file mode 100644
index 0000000000..c894ad3ff0
--- /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 0000000000..eda0164167
--- /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 0000000000..4a9010d186
--- /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 0000000000..bedbc39a4e
--- /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 0000000000..0e2a7f7211
--- /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 0000000000..553d6ca89d
--- /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 0000000000..3952648631
--- /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 0000000000..93f837befc
--- /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 0000000000..0dc147fb34
--- /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 0000000000..eb801548d2
--- /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 0000000000..e2e0d10908
--- /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 0000000000..ee502dddb9
--- /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 0000000000..61e3df2f9f
--- /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 0000000000..d092bc5a6b
--- /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 0000000000..23f142059f
--- /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 0000000000..3b7bbe5a22
--- /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 0000000000..3dfe10baa2
--- /dev/null
+++ b/third_party/nix/tests/pass-as-file.sh
@@ -0,0 +1,17 @@
+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\" ''
+    [ \"\$(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 0000000000..cd1bb7bc2a
--- /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/plugins.sh b/third_party/nix/tests/plugins.sh
new file mode 100644
index 0000000000..4b1baeddce
--- /dev/null
+++ b/third_party/nix/tests/plugins.sh
@@ -0,0 +1,7 @@
+source common.sh
+
+set -o pipefail
+
+res=$(nix eval '(builtins.anotherNull)' --option setting-set true --option plugin-files $PWD/plugins/libplugintest*)
+
+[ "$res"x = "nullx" ]
diff --git a/third_party/nix/tests/plugins/local.mk b/third_party/nix/tests/plugins/local.mk
new file mode 100644
index 0000000000..1d2bac052f
--- /dev/null
+++ b/third_party/nix/tests/plugins/local.mk
@@ -0,0 +1,9 @@
+libraries += libplugintest
+
+libplugintest_DIR := $(d)
+
+libplugintest_SOURCES := $(d)/plugintest.cc
+
+libplugintest_ALLOW_UNDEFINED := 1
+
+libplugintest_EXCLUDE_FROM_LIBRARY_LIST := 1
diff --git a/third_party/nix/tests/plugins/plugintest.cc b/third_party/nix/tests/plugins/plugintest.cc
new file mode 100644
index 0000000000..c085d33295
--- /dev/null
+++ b/third_party/nix/tests/plugins/plugintest.cc
@@ -0,0 +1,24 @@
+#include "config.hh"
+#include "primops.hh"
+
+using namespace nix;
+
+struct MySettings : Config
+{
+    Setting<bool> settingSet{this, false, "setting-set",
+        "Whether the plugin-defined setting was set"};
+};
+
+MySettings mySettings;
+
+static GlobalConfig::Register rs(&mySettings);
+
+static void prim_anotherNull (EvalState & state, const Pos & pos, Value ** args, Value & v)
+{
+    if (mySettings.settingSet)
+        mkNull(v);
+    else
+        mkBool(v, false);
+}
+
+static RegisterPrimOp rp("anotherNull", 0, prim_anotherNull);
diff --git a/third_party/nix/tests/post-hook.sh b/third_party/nix/tests/post-hook.sh
new file mode 100644
index 0000000000..a026572154
--- /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 0000000000..ed25b3d456
--- /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 0000000000..49c8564487
--- /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 0000000000..6aadb916ba
--- /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 0000000000..8ab8e5ddfe
--- /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 0000000000..b867f13b49
--- /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 0000000000..77437658ea
--- /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 0000000000..ec7ad5dcaf
--- /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 0000000000..e0ef584020
--- /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 0000000000..e02becc60e
--- /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 0000000000..77dcbd2a9d
--- /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 0000000000..d1dbfd6bd4
--- /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 0000000000..fea6e7a7a6
--- /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 0000000000..14da3127b0
--- /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 0000000000..b4ac8ff531
--- /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 0000000000..50a9c4428d
--- /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 0000000000..77e83c8d6c
--- /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 0000000000..eb39f9039a
--- /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 0000000000..ea67eb09c1
--- /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 0000000000..f7132043de
--- /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 0000000000..9e29e3fbf0
--- /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 0000000000..569e8ca88c
--- /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 0000000000..4223c0f23a
--- /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 0000000000..37631b648c
--- /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 0000000000..6c77a43913
--- /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 0000000000..9ba2672b68
--- /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 0000000000..ba534c6261
--- /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 0000000000..d0e949e314
--- /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 0000000000..eea9b5731d
--- /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 0000000000..5fafa797f1
--- /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 0000000000..1aa410cc96
--- /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 0000000000..aebf6a2a2b
--- /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 ]