mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-10 08:16:15 +02:00
33ca905cdb
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...
294 lines
7.3 KiB
Bash
294 lines
7.3 KiB
Bash
# 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
|
||
|
||
COMMON_VARS_AND_FUNCTIONS_SH_SOURCED=1
|
||
|
||
set +x
|
||
|
||
export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default/tests\/functional//}
|
||
export NIX_STORE_DIR
|
||
if ! NIX_STORE_DIR=$(readlink -f $TEST_ROOT/store 2> /dev/null); then
|
||
# Maybe the build directory is symlinked.
|
||
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_DAEMON_SOCKET_PATH=$TEST_ROOT/dSocket
|
||
unset NIX_USER_CONF_FILES
|
||
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_STATE_HOME
|
||
unset XDG_DATA_HOME
|
||
unset XDG_CONFIG_HOME
|
||
unset XDG_CONFIG_DIRS
|
||
unset XDG_CACHE_HOME
|
||
|
||
export PATH=@bindir@:$PATH
|
||
if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then
|
||
export PATH="$NIX_CLIENT_PACKAGE/bin":$PATH
|
||
fi
|
||
DAEMON_PATH="$PATH"
|
||
if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then
|
||
DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH"
|
||
fi
|
||
coreutils=@coreutils@
|
||
lsof=@lsof@
|
||
|
||
export dot=@dot@
|
||
export SHELL="@bash@"
|
||
export PAGER=cat
|
||
export busybox="@sandbox_shell@"
|
||
|
||
export version=@PACKAGE_VERSION@
|
||
export system=@system@
|
||
|
||
export BUILD_SHARED_LIBS=@BUILD_SHARED_LIBS@
|
||
|
||
export IMPURE_VAR1=foo
|
||
export IMPURE_VAR2=bar
|
||
|
||
cacheDir=$TEST_ROOT/binary-cache
|
||
|
||
readLink() {
|
||
ls -l "$1" | sed 's/.*->\ //'
|
||
}
|
||
|
||
clearProfiles() {
|
||
profiles="$HOME"/.local/state/nix/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"
|
||
clearProfiles
|
||
}
|
||
|
||
clearCache() {
|
||
rm -rf "$cacheDir"
|
||
}
|
||
|
||
clearCacheCache() {
|
||
rm -f $TEST_HOME/.cache/nix/binary-cache*
|
||
}
|
||
|
||
startDaemon() {
|
||
# Don’t start the daemon twice, as this would just make it loop indefinitely
|
||
if [[ "${_NIX_TEST_DAEMON_PID-}" != '' ]]; then
|
||
return
|
||
fi
|
||
# Start the daemon, wait for the socket to appear.
|
||
rm -f $NIX_DAEMON_SOCKET_PATH
|
||
PATH=$DAEMON_PATH nix --extra-experimental-features 'nix-command' daemon &
|
||
_NIX_TEST_DAEMON_PID=$!
|
||
export _NIX_TEST_DAEMON_PID
|
||
for ((i = 0; i < 300; i++)); do
|
||
if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then
|
||
DAEMON_STARTED=1
|
||
break;
|
||
fi
|
||
sleep 0.1
|
||
done
|
||
if [[ -z ${DAEMON_STARTED+x} ]]; then
|
||
fail "Didn’t manage to start the daemon"
|
||
fi
|
||
trap "killDaemon" EXIT
|
||
# Save for if daemon is killed
|
||
NIX_REMOTE_OLD=$NIX_REMOTE
|
||
export NIX_REMOTE=daemon
|
||
}
|
||
|
||
killDaemon() {
|
||
# Don’t fail trying to stop a non-existant daemon twice
|
||
if [[ "${_NIX_TEST_DAEMON_PID-}" == '' ]]; then
|
||
return
|
||
fi
|
||
kill $_NIX_TEST_DAEMON_PID
|
||
for i in {0..100}; do
|
||
kill -0 $_NIX_TEST_DAEMON_PID 2> /dev/null || break
|
||
sleep 0.1
|
||
done
|
||
kill -9 $_NIX_TEST_DAEMON_PID 2> /dev/null || true
|
||
wait $_NIX_TEST_DAEMON_PID || true
|
||
rm -f $NIX_DAEMON_SOCKET_PATH
|
||
# Indicate daemon is stopped
|
||
unset _NIX_TEST_DAEMON_PID
|
||
# Restore old nix remote
|
||
NIX_REMOTE=$NIX_REMOTE_OLD
|
||
trap "" EXIT
|
||
}
|
||
|
||
restartDaemon() {
|
||
[[ -z "${_NIX_TEST_DAEMON_PID:-}" ]] && return 0
|
||
|
||
killDaemon
|
||
startDaemon
|
||
}
|
||
|
||
if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then
|
||
_canUseSandbox=1
|
||
fi
|
||
|
||
isDaemonNewer () {
|
||
[[ -n "${NIX_DAEMON_PACKAGE:-}" ]] || return 0
|
||
local requiredVersion="$1"
|
||
local daemonVersion=$($NIX_DAEMON_PACKAGE/bin/nix daemon --version | cut -d' ' -f3)
|
||
[[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]]
|
||
}
|
||
|
||
skipTest () {
|
||
echo "$1, skipping this test..." >&2
|
||
exit 99
|
||
}
|
||
|
||
requireDaemonNewerThan () {
|
||
isDaemonNewer "$1" || skipTest "Daemon is too old"
|
||
}
|
||
|
||
canUseSandbox() {
|
||
[[ ${_canUseSandbox-} ]]
|
||
}
|
||
|
||
requireSandboxSupport () {
|
||
canUseSandbox || skipTest "Sandboxing not supported"
|
||
}
|
||
|
||
requireGit() {
|
||
[[ $(type -p git) ]] || skipTest "Git not installed"
|
||
}
|
||
|
||
fail() {
|
||
echo "$1" >&2
|
||
exit 1
|
||
}
|
||
|
||
# Run a command failing if it didn't exit with the expected exit code.
|
||
#
|
||
# Has two advantages over the built-in `!`:
|
||
#
|
||
# 1. `!` conflates all non-0 codes. `expect` allows testing for an exact
|
||
# code.
|
||
#
|
||
# 2. `!` unexpectedly negates `set -e`, and cannot be used on individual
|
||
# pipeline stages with `set -o pipefail`. It only works on the entire
|
||
# pipeline, which is useless if we want, say, `nix ...` invocation to
|
||
# *fail*, but a grep on the error message it outputs to *succeed*.
|
||
expect() {
|
||
local expected res
|
||
expected="$1"
|
||
shift
|
||
"$@" && res=0 || res="$?"
|
||
if [[ $res -ne $expected ]]; then
|
||
echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# Better than just doing `expect ... >&2` because the "Expected..."
|
||
# message below will *not* be redirected.
|
||
expectStderr() {
|
||
local expected res
|
||
expected="$1"
|
||
shift
|
||
"$@" 2>&1 && res=0 || res="$?"
|
||
if [[ $res -ne $expected ]]; then
|
||
echo "Expected exit code '$expected' but got '$res' from command ${*@Q}" >&2
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
# Run a command and check whether the stderr matches stdin.
|
||
# Show a diff when output does not match.
|
||
# Usage:
|
||
#
|
||
# assertStderr nix profile remove nothing << EOF
|
||
# error: This error is expected
|
||
# EOF
|
||
assertStderr() {
|
||
diff -u /dev/stdin <($@ 2>/dev/null 2>&1)
|
||
}
|
||
|
||
needLocalStore() {
|
||
if [[ "$NIX_REMOTE" == "daemon" ]]; then
|
||
skipTest "Can’t run through the daemon ($1)"
|
||
fi
|
||
}
|
||
|
||
# Just to make it easy to find which tests should be fixed
|
||
buggyNeedLocalStore() {
|
||
needLocalStore "$1"
|
||
}
|
||
|
||
enableFeatures() {
|
||
local features="$1"
|
||
sed -i 's/experimental-features .*/& '"$features"'/' "$NIX_CONF_DIR"/nix.conf
|
||
}
|
||
|
||
set -x
|
||
|
||
onError() {
|
||
set +x
|
||
echo "$0: test failed at:" >&2
|
||
for ((i = 1; i < ${#BASH_SOURCE[@]}; i++)); do
|
||
if [[ -z ${BASH_SOURCE[i]} ]]; then break; fi
|
||
echo " ${FUNCNAME[i]} in ${BASH_SOURCE[i]}:${BASH_LINENO[i-1]}" >&2
|
||
done
|
||
}
|
||
|
||
# `grep -v` doesn't work well for exit codes. We want `!(exist line l. l
|
||
# matches)`. It gives us `exist line l. !(l matches)`.
|
||
#
|
||
# `!` normally doesn't work well with `set -e`, but when we wrap in a
|
||
# function it *does*.
|
||
grepInverse() {
|
||
! grep "$@"
|
||
}
|
||
|
||
# A shorthand, `> /dev/null` is a bit noisy.
|
||
#
|
||
# `grep -q` would seem to do this, no function necessary, but it is a
|
||
# bad fit with pipes and `set -o pipefail`: `-q` will exit after the
|
||
# first match, and then subsequent writes will result in broken pipes.
|
||
#
|
||
# Note that reproducing the above is a bit tricky as it depends on
|
||
# non-deterministic properties such as the timing between the match and
|
||
# the closing of the pipe, the buffering of the pipe, and the speed of
|
||
# the producer into the pipe. But rest assured we've seen it happen in
|
||
# CI reliably.
|
||
grepQuiet() {
|
||
grep "$@" > /dev/null
|
||
}
|
||
|
||
# The previous two, combined
|
||
grepQuietInverse() {
|
||
! grep "$@" > /dev/null
|
||
}
|
||
|
||
# Return the number of arguments
|
||
count() {
|
||
echo $#
|
||
}
|
||
|
||
trap onError ERR
|
||
|
||
fi # COMMON_VARS_AND_FUNCTIONS_SH_SOURCED
|