tests: simplify initialisation and wiring

pararameterisation is not actually needed the way things are currently
set up, and it confused me when trying to understand what the code does.

all but one test sources vars-and-functions.sh, which nominally only
defines variables, but in practice is always coupled with the actual
initialisation. while the cleaner way of making this more legible would
be to source variables and initialisation separately, this would produce
a huge diff.

the change requires a few small fixes to keep the tests working:

- only create test home directory during initialisation

  that vars-and-functions.sh wrote to the file system seems not write

- fix creation of the test directory

  due to statefulness, the test home directory was implicitly creating
  the test root, too. decoupling that made it apparent that this was
  probably not intentional, and certainly confusing.

- only source vars-and-functions.sh if init.sh is not needed

  there is one test case that only needs a helper function but no
  initialisation side effects

- remove some unnecessary cleanups and split parts of re-used test code

  there were confusing bits in how initialisation code was repurposed,
  which break if trying to refactor the outer layers naively...
This commit is contained in:
Valentin Gagarin 2024-05-06 15:42:49 +02:00
parent 56abd341bb
commit 33ca905cdb
25 changed files with 227 additions and 227 deletions

View File

@ -162,14 +162,14 @@ ran test tests/functional/${testName}.sh... [PASS]
or without `make`:
```shell-session
$ ./mk/run-test.sh tests/functional/${testName}.sh tests/functional/init.sh
$ ./mk/run-test.sh tests/functional/${testName}.sh
ran test tests/functional/${testName}.sh... [PASS]
```
To see the complete output, one can also run:
```shell-session
$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
$ ./mk/debug-test.sh tests/functional/${testName}.sh
+(${testName}.sh:1) foo
output from foo
+(${testName}.sh:2) bar
@ -204,7 +204,7 @@ edit it like so:
Then, running the test with `./mk/debug-test.sh` will drop you into GDB once the script reaches that point:
```shell-session
$ ./mk/debug-test.sh tests/functional/${testName}.sh tests/functional/init.sh
$ ./mk/debug-test.sh tests/functional/${testName}.sh
...
+ gdb blash blub
GNU gdb (GDB) 12.1

View File

@ -3,12 +3,8 @@
set -eu -o pipefail
test=$1
init=${2-}
dir="$(dirname "${BASH_SOURCE[0]}")"
source "$dir/common-test.sh"
if [ -n "$init" ]; then
(run "$init" 2>/dev/null > /dev/null)
fi
run "$test"

View File

@ -87,15 +87,14 @@ $(foreach script, $(bin-scripts), $(eval $(call install-program-in,$(script),$(b
$(foreach script, $(bin-scripts), $(eval programs-list += $(script)))
$(foreach script, $(noinst-scripts), $(eval programs-list += $(script)))
$(foreach template, $(template-files), $(eval $(call instantiate-template,$(template))))
install_test_init=tests/functional/init.sh
$(foreach test, $(install-tests), \
$(eval $(call run-test,$(test),$(install_test_init))) \
$(eval $(call run-test,$(test))) \
$(eval installcheck: $(test).test))
$(foreach test-group, $(install-tests-groups), \
$(eval $(call run-test-group,$(test-group),$(install_test_init))) \
$(eval $(call run-test-group,$(test-group))) \
$(eval installcheck: $(test-group).test-group) \
$(foreach test, $($(test-group)-tests), \
$(eval $(call run-test,$(test),$(install_test_init))) \
$(eval $(call run-test,$(test))) \
$(eval $(test-group).test-group: $(test).test)))
# Compilation database.

View File

@ -8,7 +8,6 @@ yellow=""
normal=""
test=$1
init=${2-}
dir="$(dirname "${BASH_SOURCE[0]}")"
source "$dir/common-test.sh"
@ -22,9 +21,6 @@ if [ -t 1 ]; then
fi
run_test () {
if [ -n "$init" ]; then
(run "$init" 2>/dev/null > /dev/null)
fi
log="$(run "$test" 2>&1)" && status=0 || status=$?
}

View File

@ -12,8 +12,8 @@ endef
define run-test
$(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1 $2))
$(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1 $2))
$(eval $(call run-bash,$1.test,$1 $(test-deps),mk/run-test.sh $1))
$(eval $(call run-bash,$1.test-debug,$1 $(test-deps),mk/debug-test.sh $1))
endef

View File

@ -4,7 +4,11 @@ if [[ -z "${COMMON_SH_SOURCED-}" ]]; then
COMMON_SH_SOURCED=1
source "$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")/common/vars-and-functions.sh"
dir="$(readlink -f "$(dirname "${BASH_SOURCE[0]-$0}")")"
source "$dir"/common/vars-and-functions.sh
source "$dir"/common/init.sh
if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then
startDaemon
fi

View File

@ -1,6 +1,3 @@
# Don't start the daemon
source common/vars-and-functions.sh
test -n "$TEST_ROOT"
if test -d "$TEST_ROOT"; then
chmod -R u+rw "$TEST_ROOT"
@ -8,7 +5,8 @@ if test -d "$TEST_ROOT"; then
killDaemon
rm -rf "$TEST_ROOT"
fi
mkdir "$TEST_ROOT"
mkdir -p "$TEST_ROOT"
mkdir "$TEST_HOME"
mkdir "$NIX_STORE_DIR"
mkdir "$NIX_LOCALSTATE_DIR"
@ -36,7 +34,7 @@ extra-experimental-features = flakes
EOF
# Initialise the database.
# The flag itself does nothing, but running the command touches the store
nix-store --init
# Did anything happen?
# Sanity check
test -e "$NIX_STATE_DIR"/db/db.sqlite

View File

@ -1,3 +1,5 @@
# NOTE: instances of @variable@ are substituted as defined in /mk/templates.mk
set -eu -o pipefail
if [[ -z "${COMMON_VARS_AND_FUNCTIONS_SH_SOURCED-}" ]]; then
@ -34,7 +36,6 @@ unset XDG_DATA_HOME
unset XDG_CONFIG_HOME
unset XDG_CONFIG_DIRS
unset XDG_CACHE_HOME
mkdir -p $TEST_HOME
export PATH=@bindir@:$PATH
if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then

View File

@ -72,7 +72,7 @@ for i in lang/eval-fail-*.nix; do
if [[ -e "lang/$i.flags" ]]; then
sed -e 's/#.*//' < "lang/$i.flags"
else
# note that show-trace is also set by init.sh
# note that show-trace is also set by common/init.sh
echo "--eval --strict --show-trace"
fi
)"

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,4 @@
source ../common.sh
source ../common/vars-and-functions.sh
# The new Linux mount interface does not seem to support remounting
# OverlayFS mount points.
@ -37,10 +37,9 @@ addConfig () {
setupConfig () {
addConfig "require-drop-supplementary-groups = false"
addConfig "build-users-group = "
enableFeatures "local-overlay-store"
}
enableFeatures "local-overlay-store"
setupStoreDirs () {
# Attempt to create store dirs on tmpfs volume.
# This ensures lowerdir, upperdir and workdir will be on

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,4 +1,5 @@
source common.sh
source ../common/init.sh
requireEnvironment
setupConfig

View File

@ -1,6 +1,5 @@
nix_tests = \
test-infra.sh \
init.sh \
flakes/flakes.sh \
flakes/develop.sh \
flakes/run.sh \

View File

@ -23,7 +23,7 @@ fi
# Test import-from-derivation through the daemon.
[[ $(nix eval --impure --raw --file ./ifd.nix) = hi ]]
storeCleared=1 NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs.sh
NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs-test-case.sh
nix-store --gc --max-freed 1K

View File

@ -0,0 +1,191 @@
clearProfiles
# Query installed: should be empty.
test "$(nix-env -p $profiles/test -q '*' | wc -l)" -eq 0
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 with json
nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [
.outputName == "out",
.outputs.out == null
] | all'
nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [
.outputName == "out",
(.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1"))
] | all'
nix-env -f ./user-envs.nix -qa --json --drv-path | jq -e '.[] | select(.name == "bar-0.1") | (.drvPath | test("'$NIX_STORE_DIR'.*-0\\.1\\.drv"))'
# Query descriptions.
nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly
rm -rf $HOME/.nix-defexpr
ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr
nix-env -qa '*' --description | grepQuiet silly
# Query the system.
nix-env -qa '*' --system | grepQuiet $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 '*' | grepQuiet 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 '*' | grepQuiet '< 2.0'
nix-env -qac '*' | grepQuiet '> 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 | grepQuiet 'IP- foo-1.0'
nix-env -qas | grepQuiet -- '--- 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 '*' | grepQuiet 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 '*' | grepQuiet 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 '*' | grepQuiet foo; then false; fi
nix-env -q '*' | grepQuiet 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 '*' | grepQuiet foo-2.0
nix-env -q '*' | grepQuiet bar
# Rollback again: should remove "bar".
nix-env --rollback
nix-env -q '*' | grepQuiet foo-2.0
if nix-env -q '*' | grepQuiet 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 '*' | grepQuiet 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 '*' | grepQuiet foo; then false; fi
nix-store -qR $profiles/test | grepInverse "$outPath10"
# Install foo-1.0, now using a symlink to its store path.
nix-env -i $TEST_ROOT/symlink
nix-env -q '*' | grepQuiet 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 '*' | grepQuiet 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 '*' | grepQuiet foo-2.0
nix-env -q '*' | grepQuiet 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 ]
# Test the case where $HOME contains a symlink.
mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels
ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home
ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo
HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1

View File

@ -1,197 +1,3 @@
source common.sh
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
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 with json
nix-env -f ./user-envs.nix -qa --json | jq -e '.[] | select(.name == "bar-0.1") | [
.outputName == "out",
.outputs.out == null
] | all'
nix-env -f ./user-envs.nix -qa --json --out-path | jq -e '.[] | select(.name == "bar-0.1") | [
.outputName == "out",
(.outputs.out | test("'$NIX_STORE_DIR'.*-0\\.1"))
] | all'
nix-env -f ./user-envs.nix -qa --json --drv-path | jq -e '.[] | select(.name == "bar-0.1") | (.drvPath | test("'$NIX_STORE_DIR'.*-0\\.1\\.drv"))'
# Query descriptions.
nix-env -f ./user-envs.nix -qa '*' --description | grepQuiet silly
rm -rf $HOME/.nix-defexpr
ln -s $(pwd)/user-envs.nix $HOME/.nix-defexpr
nix-env -qa '*' --description | grepQuiet silly
# Query the system.
nix-env -qa '*' --system | grepQuiet $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 '*' | grepQuiet 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 '*' | grepQuiet '< 2.0'
nix-env -qac '*' | grepQuiet '> 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 | grepQuiet 'IP- foo-1.0'
nix-env -qas | grepQuiet -- '--- 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 '*' | grepQuiet 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 '*' | grepQuiet 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 '*' | grepQuiet foo; then false; fi
nix-env -q '*' | grepQuiet 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 '*' | grepQuiet foo-2.0
nix-env -q '*' | grepQuiet bar
# Rollback again: should remove "bar".
nix-env --rollback
nix-env -q '*' | grepQuiet foo-2.0
if nix-env -q '*' | grepQuiet 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 '*' | grepQuiet 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 '*' | grepQuiet foo; then false; fi
nix-store -qR $profiles/test | grepInverse "$outPath10"
# Install foo-1.0, now using a symlink to its store path.
nix-env -i $TEST_ROOT/symlink
nix-env -q '*' | grepQuiet 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 '*' | grepQuiet 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 '*' | grepQuiet foo-2.0
nix-env -q '*' | grepQuiet 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 ]
# Test the case where $HOME contains a symlink.
mkdir -p $TEST_ROOT/real-home/alice/.nix-defexpr/channels
ln -sfn $TEST_ROOT/real-home $TEST_ROOT/home
ln -sfn $(pwd)/user-envs.nix $TEST_ROOT/home/alice/.nix-defexpr/channels/foo
HOME=$TEST_ROOT/home/alice nix-env -i foo-0.1
source ./user-envs-test-case.sh