mirror of
https://github.com/NixOS/nix.git
synced 2025-11-23 02:39:37 +01:00
Polling every 1 second means that even the simplest test takes at least
2 seconds. We can reasonably poll 1/10 of that to make things much
quicker (esp. given that most of the time 0.1s is enough for the
daemon to be started or stopped)
(cherry picked from commit 9c470cb969)
251 lines
6.2 KiB
Bash
251 lines
6.2 KiB
Bash
set -eu -o pipefail
|
||
|
||
if [[ -z "${COMMON_SH_SOURCED-}" ]]; then
|
||
|
||
COMMON_SH_SOURCED=1
|
||
|
||
export PS4='+(${BASH_SOURCE[0]}:$LINENO) '
|
||
|
||
export TEST_ROOT=$(realpath ${TMPDIR:-/tmp}/nix-test)/${TEST_NAME:-default}
|
||
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/daemon-socket
|
||
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
|
||
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@
|
||
|
||
export dot=@dot@
|
||
export xmllint="@xmllint@"
|
||
export SHELL="@bash@"
|
||
export PAGER=cat
|
||
export HAVE_SODIUM="@HAVE_SODIUM@"
|
||
|
||
export version=@PACKAGE_VERSION@
|
||
export system=@system@
|
||
|
||
export IMPURE_VAR1=foo
|
||
export IMPURE_VAR2=bar
|
||
|
||
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"
|
||
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_REMOTE" == daemon ]]; then
|
||
return
|
||
fi
|
||
# Start the daemon, wait for the socket to appear. !!!
|
||
# ‘nix-daemon’ should have an option to fork into the background.
|
||
rm -f $NIX_DAEMON_SOCKET_PATH
|
||
PATH=$DAEMON_PATH nix-daemon &
|
||
pidDaemon=$!
|
||
for ((i = 0; i < 300; i++)); do
|
||
if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi
|
||
sleep 0.1
|
||
done
|
||
trap "killDaemon" EXIT
|
||
# Save for if daemon is killed
|
||
NIX_REMOTE_OLD=$NIX_REMOTE
|
||
export NIX_REMOTE=daemon
|
||
}
|
||
|
||
killDaemon() {
|
||
kill $pidDaemon
|
||
for i in {0..100}; do
|
||
kill -0 $pidDaemon || break
|
||
sleep 0.1
|
||
done
|
||
kill -9 $pidDaemon || true
|
||
wait $pidDaemon || true
|
||
# Restore old nix remote
|
||
NIX_REMOTE=$NIX_REMOTE_OLD
|
||
trap "" EXIT
|
||
}
|
||
|
||
restartDaemon() {
|
||
[[ -z "${pidDaemon:-}" ]] && return 0
|
||
|
||
killDaemon
|
||
startDaemon
|
||
}
|
||
|
||
if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true; then
|
||
_canUseSandbox=1
|
||
fi
|
||
|
||
isDaemonOlder () {
|
||
[[ -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''") -lt 0 ]]
|
||
}
|
||
|
||
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 ]]
|
||
}
|
||
|
||
requireDaemonOlderThan () {
|
||
isDaemonOlder "$1" || exit 99
|
||
}
|
||
|
||
requireDaemonNewerThan () {
|
||
isDaemonNewer "$1" || exit 99
|
||
}
|
||
|
||
canUseSandbox() {
|
||
if [[ ! ${_canUseSandbox-} ]]; then
|
||
echo "Sandboxing not supported, skipping this test..."
|
||
return 1
|
||
fi
|
||
|
||
return 0
|
||
}
|
||
|
||
fail() {
|
||
echo "$1"
|
||
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
|
||
set +e
|
||
"$@" && res=0 || res="$?"
|
||
set -e
|
||
if [[ $res -ne $expected ]]; then
|
||
echo "Expected '$expected' but got '$res' while running '${*@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 '$expected' but got '$res' while running '${*@Q}'" >&2
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
needLocalStore() {
|
||
if [[ "$NIX_REMOTE" == "daemon" ]]; then
|
||
echo "Can’t run through the daemon ($1), skipping this test..."
|
||
return 99
|
||
fi
|
||
}
|
||
|
||
# Just to make it easy to find which tests should be fixed
|
||
buggyNeedLocalStore () {
|
||
needLocalStore "$1"
|
||
}
|
||
|
||
# `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
|
||
}
|
||
|
||
set -x
|
||
|
||
if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then
|
||
startDaemon
|
||
fi
|
||
|
||
fi # COMMON_SH_SOURCED
|