From e0e9f91049124c3f2a28a03abd4daf4c4264f60e Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 8 Jun 2020 10:01:14 +0200 Subject: [PATCH 01/18] Actually test nix-env with a remote store The `remote-store` test loads the `user-env` one to test nix-env when using the daemon, but actually does it incorrectly because every test starts (in `common.sh`) by resetting the value of `NIX_REMOTE`, meaning that the `user-env` test will never use the daemon. Fix this by setting `NIX_REMOTE_` before sourcing `user-env.sh` in the `remote-store` test, so that `NIX_REMOTE` is correctly set inside the test (cherry picked from commit f6ac888d3e1897f37a8accb9c46ebdf4fc2ca204) --- tests/remote-store.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/remote-store.sh b/tests/remote-store.sh index 77437658e..4cc73465a 100644 --- a/tests/remote-store.sh +++ b/tests/remote-store.sh @@ -4,7 +4,7 @@ clearStore startDaemon -storeCleared=1 $SHELL ./user-envs.sh +storeCleared=1 NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs.sh nix-store --dump-db > $TEST_ROOT/d1 NIX_REMOTE= nix-store --dump-db > $TEST_ROOT/d2 From 02a186883bdbbdf52523e0ef868d8cbeaed5aa9b Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 10 Nov 2020 10:43:33 +0100 Subject: [PATCH 02/18] Add a test ensuring compatibility with an old daemon This requires adding `nix` to its own closure which is a bit unfortunate, but as it is optional (the test will be disabled if `OUTER_NIX` is unset) it shouldn't be too much of an issue. (Ideally this should go in another derivation so that we can build Nix and run the test independently, but as the tests are running in the same derivation as the build it's a bit complicated to do so). (cherry picked from commit 5716345adf2e794fd62229ea52352e74e92e8e63) --- tests/common.sh.in | 3 +-- tests/remote-store.sh | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index e8233bf72..09f0c1c32 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -55,7 +55,6 @@ clearStore() { mkdir "$NIX_STORE_DIR" rm -rf "$NIX_STATE_DIR" mkdir "$NIX_STATE_DIR" - nix-store --init clearProfiles } @@ -71,7 +70,7 @@ 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 & + ${NIX_DAEMON_COMMAND:-nix-daemon} & for ((i = 0; i < 30; i++)); do if [ -e $NIX_DAEMON_SOCKET_PATH ]; then break; fi sleep 1 diff --git a/tests/remote-store.sh b/tests/remote-store.sh index 4cc73465a..78353c959 100644 --- a/tests/remote-store.sh +++ b/tests/remote-store.sh @@ -6,12 +6,12 @@ startDaemon storeCleared=1 NIX_REMOTE_=$NIX_REMOTE $SHELL ./user-envs.sh +nix-store --gc --max-freed 1K + 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) From 62a42ab43e2d79fd6f7cff7d3a4aca1a07caa175 Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 16 Mar 2021 13:43:08 +0100 Subject: [PATCH 03/18] Make the tests (optionnally) run in another derivation That way we can run them without rebuilding Nix (cherry picked from commit a0866c8ea4bc66f9aacc7ad19139d57946b3df18) --- tests/common.sh.in | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/common.sh.in b/tests/common.sh.in index 09f0c1c32..cdb84f4da 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -26,6 +26,12 @@ 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 +if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + export NIX_DAEMON_COMMAND="$NIX_DAEMON_PACKAGE/bin/nix-daemon" +fi coreutils=@coreutils@ export dot=@dot@ From c434a11a0497606385b6b64516763184d99a9ea9 Mon Sep 17 00:00:00 2001 From: regnat Date: Mon, 26 Jul 2021 06:54:55 +0200 Subject: [PATCH 04/18] Allow running all the tests with the daemon MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When `NIX_DAEMON_PACKAGE` is set, make all the tests use the Nix daemon. That way we can test every piece of Nix functionality both with and without the daemon. Tests for which using the daemon isn’t possible or doesn’t make sens can selectively be disabled with `needLocalStore` (cherry picked from commit addacfce4a71f33b50b6af321d6a8d67ca44ceed) --- tests/binary-cache.sh | 2 ++ tests/check.sh | 3 +++ tests/common.sh.in | 57 +++++++++++++++++++++++++++++++++++++-- tests/dump-db.sh | 2 ++ tests/fixed.sh | 3 --- tests/gc-auto.sh | 2 ++ tests/init.sh | 1 + tests/linux-sandbox.sh | 2 ++ tests/multiple-outputs.sh | 2 +- tests/optimise-store.sh | 3 ++- tests/post-hook.sh | 5 ++++ tests/referrers.sh | 2 ++ tests/repair.sh | 2 ++ tests/structured-attrs.sh | 4 +++ tests/timeout.sh | 2 ++ 15 files changed, 85 insertions(+), 7 deletions(-) diff --git a/tests/binary-cache.sh b/tests/binary-cache.sh index eb58ae7c1..774a260a9 100644 --- a/tests/binary-cache.sh +++ b/tests/binary-cache.sh @@ -1,5 +1,7 @@ source common.sh +needLocalStore "“--no-require-sigs” can’t be used with the daemon" + clearStore clearCache diff --git a/tests/check.sh b/tests/check.sh index bc23a6634..6f985984c 100644 --- a/tests/check.sh +++ b/tests/check.sh @@ -1,5 +1,8 @@ source common.sh +# XXX: This shouldn’t be, but #4813 cause this test to fail +buggyNeedLocalStore "see #4813" + clearStore nix-build dependencies.nix --no-out-link diff --git a/tests/common.sh.in b/tests/common.sh.in index cdb84f4da..27271135f 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -1,5 +1,9 @@ set -e +if [[ -z "$COMMON_SH_SOURCED" ]]; then + +COMMON_SH_SOURCED=1 + 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 @@ -43,6 +47,9 @@ 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() { @@ -73,6 +80,10 @@ clearCacheCache() { } 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_STATE_DIR/daemon-socket/socket @@ -82,20 +93,44 @@ startDaemon() { sleep 1 done pidDaemon=$! - trap "kill -9 $pidDaemon" EXIT + trap "killDaemon" EXIT export NIX_REMOTE=daemon } killDaemon() { - kill -9 $pidDaemon + kill $pidDaemon + for i in {0.10}; do + kill -0 $pidDaemon || break + sleep 1 + done + kill -9 $pidDaemon || true wait $pidDaemon || true trap "" EXIT } +restartDaemon() { + [[ -z "${pidDaemon:-}" ]] && return 0 + + killDaemon + unset NIX_REMOTE + 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) + return [[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''2.4''") -ge 0 ]] +} + +requireDaemonNewerThan () { + isDaemonNewer "$1" || exit 99 +} + canUseSandbox() { if [[ ! $_canUseSandbox ]]; then echo "Sandboxing not supported, skipping this test..." @@ -121,4 +156,22 @@ expect() { [[ $res -eq $expected ]] } +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 +} + set -x + +if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + startDaemon +fi + +fi # COMMON_SH_SOURCED diff --git a/tests/dump-db.sh b/tests/dump-db.sh index d6eea42aa..48647f403 100644 --- a/tests/dump-db.sh +++ b/tests/dump-db.sh @@ -1,5 +1,7 @@ source common.sh +needLocalStore "--dump-db requires a local store" + clearStore path=$(nix-build dependencies.nix -o $TEST_ROOT/result) diff --git a/tests/fixed.sh b/tests/fixed.sh index 8f51403a7..90c4c8c32 100644 --- a/tests/fixed.sh +++ b/tests/fixed.sh @@ -2,9 +2,6 @@ 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...' diff --git a/tests/gc-auto.sh b/tests/gc-auto.sh index de1e2cfe4..880742839 100644 --- a/tests/gc-auto.sh +++ b/tests/gc-auto.sh @@ -1,5 +1,7 @@ source common.sh +needLocalStore "“min-free” and “max-free” are daemon options" + clearStore garbage1=$(nix add-to-store --name garbage1 ./nar-access.sh) diff --git a/tests/init.sh b/tests/init.sh index 19a12c1e2..cbbf8af9d 100644 --- a/tests/init.sh +++ b/tests/init.sh @@ -18,6 +18,7 @@ build-users-group = keep-derivations = false sandbox = false include nix.conf.extra +trusted-users = $(whoami) EOF cat > "$NIX_CONF_DIR"/nix.conf.extra <> $NIX_CONF_DIR/nix.conf + +restartDaemon # 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 diff --git a/tests/referrers.sh b/tests/referrers.sh index 8ab8e5ddf..d71bb5c4b 100644 --- a/tests/referrers.sh +++ b/tests/referrers.sh @@ -1,5 +1,7 @@ source common.sh +needLocalStore "uses some low-level store manipulations that aren’t available through the daemon" + clearStore max=500 diff --git a/tests/repair.sh b/tests/repair.sh index ec7ad5dca..6184e0bd5 100644 --- a/tests/repair.sh +++ b/tests/repair.sh @@ -1,5 +1,7 @@ source common.sh +needLocalStore "--repair needs a local store" + clearStore path=$(nix-build dependencies.nix -o $TEST_ROOT/result) diff --git a/tests/structured-attrs.sh b/tests/structured-attrs.sh index 9ba2672b6..fcc00c4e0 100644 --- a/tests/structured-attrs.sh +++ b/tests/structured-attrs.sh @@ -1,5 +1,9 @@ source common.sh +# 27ce722638 required some incompatible changes to the nix file, so skip this +# tests for the older versions +requireDaemonNewerThan "2.4pre20210622" + clearStore outPath=$(nix-build structured-attrs.nix --no-out-link) diff --git a/tests/timeout.sh b/tests/timeout.sh index eea9b5731..e3fb3ebcc 100644 --- a/tests/timeout.sh +++ b/tests/timeout.sh @@ -2,6 +2,8 @@ source common.sh +# XXX: This shouldn’t be, but #4813 cause this test to fail +needLocalStore "see #4813" set +e messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) From 6e595ff55f416fbb6d254c94ea18ebbdb915396c Mon Sep 17 00:00:00 2001 From: regnat Date: Tue, 27 Jul 2021 11:12:32 +0200 Subject: [PATCH 05/18] Remove the right socket before starting the daemon For some reason, an old socket occasionally stays here on OSX, causing the subsequent tests to fail (cherry picked from commit c2c0dba792effa4de6ad16950684c1ce80ab6730) --- tests/common.sh.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index 27271135f..e13d92f8e 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -86,10 +86,10 @@ startDaemon() { fi # 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 + rm -f $NIX_DAEMON_SOCKET_PATH ${NIX_DAEMON_COMMAND:-nix-daemon} & for ((i = 0; i < 30; i++)); do - if [ -e $NIX_DAEMON_SOCKET_PATH ]; then break; fi + if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi sleep 1 done pidDaemon=$! From c239a1652e57214f51a2b04a5f1027e882310895 Mon Sep 17 00:00:00 2001 From: Sergei Trofimovich Date: Sat, 2 Oct 2021 11:09:30 +0100 Subject: [PATCH 06/18] mk/tests.mk: document 'installcheck' in 'make help' (cherry picked from commit 1e6faa7d06389b20946038dcdc85f7a1211c628d) --- mk/tests.mk | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mk/tests.mk b/mk/tests.mk index c1e140bac..f2015e1b8 100644 --- a/mk/tests.mk +++ b/mk/tests.mk @@ -13,3 +13,6 @@ define run-install-test endef .PHONY: check installcheck + +print-top-help += \ + echo " installcheck: Run functional tests"; From 5d324b2d0ab46baaee72540e224f34e6b660f771 Mon Sep 17 00:00:00 2001 From: regnat Date: Thu, 14 Oct 2021 15:42:54 +0200 Subject: [PATCH 07/18] Fix the `isDaemonNewer` check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Don’t hardcode the “newer” version - Remove an ill-placed `return` (cherry picked from commit 3a2fc9ce1dd3b176bdef257be75439bde33c210a) --- tests/common.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index e13d92f8e..4af062bd0 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -124,7 +124,7 @@ isDaemonNewer () { [[ -n "${NIX_DAEMON_PACKAGE:-}" ]] || return 0 local requiredVersion="$1" local daemonVersion=$($NIX_DAEMON_PACKAGE/bin/nix-daemon --version | cut -d' ' -f3) - return [[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''2.4''") -ge 0 ]] + [[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]] } requireDaemonNewerThan () { From 918861b196a523d7eca6d2f065bd0e5a491b691b Mon Sep 17 00:00:00 2001 From: regnat Date: Fri, 5 Nov 2021 11:11:33 +0100 Subject: [PATCH 08/18] Make the post-build-hook use the daemon Nix package MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having the `post-build-hook` use `nix` from the client package can lead to a deadlock in case there’s a db migration to do between both, as a `nix` command running inside the hook will run as root (and as such will bypass the daemon), so might trigger a db migration, which will get stuck trying to get a global lock on the DB (as the daemon that ran the hook already has a lock on it). (cherry picked from commit 93eadd5803caa6d28be056c58194be974a87aeb8) --- tests/common.sh.in | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index 4af062bd0..5b79ecb58 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -33,8 +33,9 @@ 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 - export NIX_DAEMON_COMMAND="$NIX_DAEMON_PACKAGE/bin/nix-daemon" + DAEMON_PATH="${NIX_DAEMON_PACKAGE}:$DAEMON_PATH" fi coreutils=@coreutils@ @@ -87,7 +88,7 @@ startDaemon() { # 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 - ${NIX_DAEMON_COMMAND:-nix-daemon} & + PATH=$DAEMON_PATH nix-daemon & for ((i = 0; i < 30; i++)); do if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi sleep 1 From 73582929244cf61c828ed2f6bd6e73cfa0b006a3 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 18 Nov 2021 22:22:33 +0000 Subject: [PATCH 09/18] Fix testing the other daemon The eventual PATH entry needs the `.../bin` or we will not use the right daemon. (cherry picked from commit 06fb6aecea43218e66ccc879471ffebb7dbfee78) --- tests/common.sh.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index 5b79ecb58..979b68e15 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -35,7 +35,7 @@ if [[ -n "${NIX_CLIENT_PACKAGE:-}" ]]; then fi DAEMON_PATH="$PATH" if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then - DAEMON_PATH="${NIX_DAEMON_PACKAGE}:$DAEMON_PATH" + DAEMON_PATH="${NIX_DAEMON_PACKAGE}/bin:$DAEMON_PATH" fi coreutils=@coreutils@ From ac295a5f33e96213643351ec121aac2e80ae06d8 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Tue, 30 Nov 2021 05:54:12 +0000 Subject: [PATCH 10/18] Flip condition on daemon version for structured attrs We want the old behavior, since this is Nix 2.3. --- tests/common.sh.in | 11 +++++++++++ tests/structured-attrs.sh | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index 979b68e15..21f633607 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -121,6 +121,13 @@ if [[ $(uname) == Linux ]] && [[ -L /proc/self/ns/user ]] && unshare --user true _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" @@ -128,6 +135,10 @@ isDaemonNewer () { [[ $(nix eval --expr "builtins.compareVersions ''$daemonVersion'' ''$requiredVersion''") -ge 0 ]] } +requireDaemonOlderThan () { + isDaemonOlder "$1" || exit 99 +} + requireDaemonNewerThan () { isDaemonNewer "$1" || exit 99 } diff --git a/tests/structured-attrs.sh b/tests/structured-attrs.sh index fcc00c4e0..e5018e15b 100644 --- a/tests/structured-attrs.sh +++ b/tests/structured-attrs.sh @@ -1,8 +1,8 @@ source common.sh # 27ce722638 required some incompatible changes to the nix file, so skip this -# tests for the older versions -requireDaemonNewerThan "2.4pre20210622" +# tests for the newer versions +requireDaemonOlderThan "2.4pre20210622" clearStore From 0a53374a4b500d456322e999c6ebf7cede0b2a1a Mon Sep 17 00:00:00 2001 From: Robert Hensing Date: Thu, 9 Feb 2023 22:14:53 +0100 Subject: [PATCH 11/18] tests: Add command source locations to test log (cherry picked from commit 9813e54a74eb9c12494d1870dc660fd1cf2efd1b) --- tests/common.sh.in | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/common.sh.in b/tests/common.sh.in index 21f633607..89fae7803 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -4,6 +4,8 @@ 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 From 51b03401fbc29876dcc3ee2c454a1f38cfa62c18 Mon Sep 17 00:00:00 2001 From: Andrea Ciceri Date: Wed, 23 Nov 2022 17:25:04 +0100 Subject: [PATCH 12/18] Tighten up the `exportReferencesGraph` tests Add an `$` at the end of the `grep` regex. Without it, `checkRef foo` would always imply `checkRef foo.drv`. We want to tell these situations apart to more precisely test what is going on. (cherry picked from commit f58759816de03ac2d233576b8740410a68e9192f) --- tests/export-graph.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/export-graph.sh b/tests/export-graph.sh index a6fd69054..9a1161a8f 100644 --- a/tests/export-graph.sh +++ b/tests/export-graph.sh @@ -4,7 +4,7 @@ clearStore clearProfiles checkRef() { - nix-store -q --references $TEST_ROOT/result | grep -q "$1" || fail "missing reference $1" + nix-store -q --references $TEST_ROOT/result | grep -q "$1"'$' || fail "missing reference $1" } # Test the export of the runtime dependency graph. From 5e13c907b41aeb55e1aaff94fb38e32a3bf7864c Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 9 Dec 2021 15:16:18 +0000 Subject: [PATCH 13/18] Make init.sh safe to run twice (cherry picked from commit 5dbbf233327bff1c89088ed0fc45882563240b1a) --- tests/init.sh | 6 ++++++ 1 file changed, 6 insertions(+) mode change 100644 => 100755 tests/init.sh diff --git a/tests/init.sh b/tests/init.sh old mode 100644 new mode 100755 index cbbf8af9d..9503a83a3 --- a/tests/init.sh +++ b/tests/init.sh @@ -1,8 +1,14 @@ +set -eu -o pipefail + source common.sh test -n "$TEST_ROOT" if test -d "$TEST_ROOT"; then chmod -R u+w "$TEST_ROOT" + # We would delete any daemon socket, so let's stop the daemon first. + if [[ -n "${NIX_DAEMON_PACKAGE:-}" ]]; then + killDaemon + fi rm -rf "$TEST_ROOT" fi mkdir "$TEST_ROOT" From 67ab776e154862802b9eb84958d5a439bb816648 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Thu, 9 Dec 2021 15:26:46 +0000 Subject: [PATCH 14/18] Harden tests' bash Use `set -u` and `set -o pipefail` to catch accidental mistakes and failures more strongly. - `set -u` catches the use of undefined variables - `set -o pipefail` catches failures (like `set -e`) earlier in the pipeline. This makes the tests a bit more robust. It is nice to read code not worrying about these spurious success paths (via uncaught) errors undermining the tests. Indeed, I caught some bugs doing this. There are a few tests where we run a command that should fail, and then search its output to make sure the failure message is one that we expect. Before, since the `grep` was the last command in the pipeline the exit code of those failing programs was silently ignored. Now with `set -o pipefail` it won't be, and we have to do something so the expected failure doesn't accidentally fail the test. To do that we use `expect` and a new `expectStderr` to check for the exact failing exit code. See the comments on each for why. `grep -q` is replaced with `grepQuiet`, see the comments on that function for why. `grep -v` when we just want the exit code is replaced with `grepInverse, see the comments on that function for why. `grep -q -v` together is, surprise surprise, replaced with `grepQuietInverse`, which is both combined. (cherry picked from commit c11836126b5148b6796c2470404a0bdf25cdfbe3) --- mk/run_test.sh | 7 ++-- tests/binary-cache.sh | 6 +-- tests/build-dry.sh | 2 +- tests/check-refs.sh | 10 ++--- tests/check-reqs.sh | 4 +- tests/common.sh.in | 80 +++++++++++++++++++++++++++++++----- tests/dependencies.sh | 8 ++-- tests/export-graph.sh | 2 +- tests/fetchurl.sh | 2 +- tests/function-trace.sh | 12 ++---- tests/hash.sh | 2 +- tests/lang.sh | 6 +-- tests/local.mk | 3 +- tests/misc.sh | 14 +++---- tests/multiple-outputs.sh | 4 +- tests/nar-access.sh | 4 +- tests/nix-channel.sh | 10 ++--- tests/search.sh | 6 +-- tests/tarball.sh | 2 +- tests/test-infra.sh | 85 +++++++++++++++++++++++++++++++++++++++ tests/timeout.sh | 7 +--- tests/user-envs.sh | 50 +++++++++++------------ 22 files changed, 233 insertions(+), 93 deletions(-) create mode 100644 tests/test-infra.sh diff --git a/mk/run_test.sh b/mk/run_test.sh index 6af5b070a..ceb83128d 100755 --- a/mk/run_test.sh +++ b/mk/run_test.sh @@ -1,6 +1,6 @@ #!/bin/sh -set -u +set -eu -o pipefail red="" green="" @@ -15,8 +15,9 @@ if [ -t 1 ]; then normal="" fi (cd $(dirname $1) && env ${TESTS_ENVIRONMENT} init.sh 2>/dev/null > /dev/null) -log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)" -status=$? + +log="$(cd $(dirname $1) && env ${TESTS_ENVIRONMENT} $(basename $1) 2>&1)" && status=0 || status=$? + if [ $status -eq 0 ]; then echo "$post_run_msg [${green}PASS$normal]" elif [ $status -eq 99 ]; then diff --git a/tests/binary-cache.sh b/tests/binary-cache.sh index 774a260a9..5cceb1449 100644 --- a/tests/binary-cache.sh +++ b/tests/binary-cache.sh @@ -62,8 +62,8 @@ 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 +expect 1 nix-build --substituters "file://$cacheDir" --no-require-sigs dependencies.nix -o $TEST_ROOT/result 2>&1 | tee $TEST_ROOT/log +grepQuiet "hash mismatch" $TEST_ROOT/log mv $nar.good $nar @@ -110,7 +110,7 @@ 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 +grepQuiet "copying path" $TEST_ROOT/log if [ -n "$HAVE_SODIUM" ]; then diff --git a/tests/build-dry.sh b/tests/build-dry.sh index e72533e70..2da0d3bef 100644 --- a/tests/build-dry.sh +++ b/tests/build-dry.sh @@ -20,7 +20,7 @@ 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 +if [ -n "${ISSUE_1795_IS_FIXED-}" ]; then clearStore clearCache diff --git a/tests/check-refs.sh b/tests/check-refs.sh index 16bbabc40..ad6faacc5 100644 --- a/tests/check-refs.sh +++ b/tests/check-refs.sh @@ -8,14 +8,14 @@ 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 +nix-store -q --references $test1 | grepQuietInverse $test1 +nix-store -q --references $test1 | grepQuiet $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 +nix-store -q --references $test2 | grepQuietInverse $test2 +nix-store -q --references $test2 | grepQuietInverse $dep +nix-store -q --references $test2 | grepQuiet aux-ref # test3 should fail (unallowed ref). (! nix-build -o $RESULT check-refs.nix -A test3) diff --git a/tests/check-reqs.sh b/tests/check-reqs.sh index e9f65fc2a..856c94cec 100644 --- a/tests/check-reqs.sh +++ b/tests/check-reqs.sh @@ -8,8 +8,8 @@ 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 test4) 2>&1 | grepQuiet 'check-reqs-dep1' +(! nix-build -o $RESULT check-reqs.nix -A test4) 2>&1 | grepQuiet 'check-reqs-dep2' (! nix-build -o $RESULT check-reqs.nix -A test5) (! nix-build -o $RESULT check-reqs.nix -A test6) diff --git a/tests/common.sh.in b/tests/common.sh.in index 89fae7803..b43c0cb81 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -1,6 +1,6 @@ -set -e +set -eu -o pipefail -if [[ -z "$COMMON_SH_SOURCED" ]]; then +if [[ -z "${COMMON_SH_SOURCED-}" ]]; then COMMON_SH_SOURCED=1 @@ -24,7 +24,7 @@ if [[ -n $NIX_STORE ]]; then fi export _NIX_IN_TEST=$TEST_ROOT/shared export _NIX_TEST_NO_LSOF=1 -export NIX_REMOTE=$NIX_REMOTE_ +export NIX_REMOTE=${NIX_REMOTE_-} unset NIX_PATH export TEST_HOME=$TEST_ROOT/test-home export HOME=$TEST_HOME @@ -91,12 +91,14 @@ startDaemon() { # ‘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 < 30; i++)); do if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi sleep 1 done - pidDaemon=$! trap "killDaemon" EXIT + # Save for if daemon is killed + NIX_REMOTE_OLD=$NIX_REMOTE export NIX_REMOTE=daemon } @@ -108,6 +110,8 @@ killDaemon() { done kill -9 $pidDaemon || true wait $pidDaemon || true + # Restore old nix remote + NIX_REMOTE=$NIX_REMOTE_OLD trap "" EXIT } @@ -115,7 +119,6 @@ restartDaemon() { [[ -z "${pidDaemon:-}" ]] && return 0 killDaemon - unset NIX_REMOTE startDaemon } @@ -146,7 +149,7 @@ requireDaemonNewerThan () { } canUseSandbox() { - if [[ ! $_canUseSandbox ]]; then + if [[ ! ${_canUseSandbox-} ]]; then echo "Sandboxing not supported, skipping this test..." return 1 fi @@ -159,15 +162,43 @@ fail() { 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="$?" + "$@" && res=0 || res="$?" set -e - [[ $res -eq $expected ]] + 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() { @@ -179,7 +210,36 @@ needLocalStore() { # Just to make it easy to find which tests should be fixed buggyNeedLocalStore () { - needLocalStore + 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 diff --git a/tests/dependencies.sh b/tests/dependencies.sh index df204d185..a3ee3707d 100644 --- a/tests/dependencies.sh +++ b/tests/dependencies.sh @@ -22,7 +22,7 @@ nix-store -q --graph "$outPath" > $TEST_ROOT/graph if test -n "$dot"; then # Does it parse? $dot < $TEST_ROOT/graph -fi +fi nix-store -q --tree "$outPath" | grep '+---.*dependencies-input-2' @@ -36,10 +36,10 @@ deps=$(nix-store -quR "$drvPath") echo "output closure contains $deps" # The output path should be in the closure. -echo "$deps" | grep -q "$outPath" +echo "$deps" | grepQuiet "$outPath" # Input-1 is not retained. -if echo "$deps" | grep -q "dependencies-input-1"; then exit 1; fi +if echo "$deps" | grepQuiet "dependencies-input-1"; then exit 1; fi # Input-2 is retained. input2OutPath=$(echo "$deps" | grep "dependencies-input-2") @@ -49,4 +49,4 @@ 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" +nix-store -q --deriver "$input2OutPath" | grepQuiet -- "-input-2.drv" diff --git a/tests/export-graph.sh b/tests/export-graph.sh index 9a1161a8f..24906d3ed 100644 --- a/tests/export-graph.sh +++ b/tests/export-graph.sh @@ -4,7 +4,7 @@ clearStore clearProfiles checkRef() { - nix-store -q --references $TEST_ROOT/result | grep -q "$1"'$' || fail "missing reference $1" + nix-store -q --references $TEST_ROOT/result | grepQuiet "$1"'$' || fail "missing reference $1" } # Test the export of the runtime dependency graph. diff --git a/tests/fetchurl.sh b/tests/fetchurl.sh index 7319ced2b..defdf95a3 100644 --- a/tests/fetchurl.sh +++ b/tests/fetchurl.sh @@ -60,7 +60,7 @@ hash=$(nix-hash --flat --type sha256 $nar) outPath=$(nix-build '' --argstr url file://$nar --argstr sha256 $hash \ --arg unpack true --argstr name xyzzy --no-out-link) -echo $outPath | grep -q 'xyzzy' +echo $outPath | grepQuiet 'xyzzy' test -x $outPath/fetchurl.sh test -L $outPath/symlink diff --git a/tests/function-trace.sh b/tests/function-trace.sh index 182a4d5c2..104bd48e3 100755 --- a/tests/function-trace.sh +++ b/tests/function-trace.sh @@ -10,17 +10,15 @@ expect_trace() { --trace-function-calls \ --expr "$expr" 2>&1 \ | grep "function-trace" \ - | sed -e 's/ [0-9]*$//' - ); + | sed -e 's/ [0-9]*$//' \ + || true + ) echo -n "Tracing expression '$expr'" - set +e msg=$(diff -swB \ <(echo "$expect") \ <(echo "$actual") - ); - result=$? - set -e + ) && result=0 || result=$? if [ $result -eq 0 ]; then echo " ok." else @@ -81,5 +79,3 @@ 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/tests/hash.sh b/tests/hash.sh index 4cfc97901..2b5ace14e 100644 --- a/tests/hash.sh +++ b/tests/hash.sh @@ -2,7 +2,7 @@ source common.sh try () { printf "%s" "$2" > $TEST_ROOT/vector - hash=$(nix hash-file --base16 $EXTRA --type "$1" $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 diff --git a/tests/lang.sh b/tests/lang.sh index c797a2a74..b8c287ba1 100644 --- a/tests/lang.sh +++ b/tests/lang.sh @@ -2,9 +2,9 @@ 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 +nix-instantiate --eval -E 'builtins.trace "Hello" 123' 2>&1 | grepQuiet Hello +nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" 123' 2>&1 | grepQuietInverse Hello +expectStderr 1 nix-instantiate --show-trace --eval -E 'builtins.addErrorContext "Hello" (throw "Foo")' 2>&1 | grepQuiet Hello set +x diff --git a/tests/local.mk b/tests/local.mk index 83e93b123..792983b09 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -31,7 +31,8 @@ nix_tests = \ search.sh \ nix-copy-ssh.sh \ post-hook.sh \ - function-trace.sh + function-trace.sh \ + test-infra.sh \ # parallel.sh install-tests += $(foreach x, $(nix_tests), tests/$(x)) diff --git a/tests/misc.sh b/tests/misc.sh index eda016416..6cdd798d2 100644 --- a/tests/misc.sh +++ b/tests/misc.sh @@ -3,17 +3,17 @@ 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 +#nix-env --help | grepQuiet install +#nix-store --help | grepQuiet realise +#nix-instantiate --help | grepQuiet eval +#nix-hash --help | grepQuiet 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" +expect 1 nix-env --foo 2>&1 | grep "no operation" +expect 1 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$" +expect 1 nix-instantiate --eval -E 'let a = {} // a; in a.foo' 2>&1 | grep "infinite recursion encountered, at .*(string).*:1:15$" diff --git a/tests/multiple-outputs.sh b/tests/multiple-outputs.sh index db1695dc5..fb1f6b0be 100644 --- a/tests/multiple-outputs.sh +++ b/tests/multiple-outputs.sh @@ -13,8 +13,8 @@ echo "evaluating c..." # 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 +grepQuiet 'multiple-outputs-a.drv",\["first","second"\]' $drvPath +grepQuiet '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) diff --git a/tests/nar-access.sh b/tests/nar-access.sh index 553d6ca89..83551941b 100644 --- a/tests/nar-access.sh +++ b/tests/nar-access.sh @@ -34,8 +34,8 @@ diff -u baz.cat-nar $storePath/foo/baz [[ $(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' +expect 1 nix ls-store --json -R $storePath/xyzzy 2>&1 | grep 'does not exist in NAR' +expect 1 nix ls-store $storePath/xyzzy 2>&1 | grep 'does not exist' # Test failure to dump. if nix-store --dump $storePath >/dev/full ; then diff --git a/tests/nix-channel.sh b/tests/nix-channel.sh index 93f837bef..87024ed46 100644 --- a/tests/nix-channel.sh +++ b/tests/nix-channel.sh @@ -6,7 +6,7 @@ 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 --list | grepQuiet http://foo/bar nix-channel --remove xyzzy [ -e $TEST_HOME/.nix-channels ] @@ -31,8 +31,8 @@ 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 +grepQuiet 'meta.*description.*Random test package' $TEST_ROOT/meta.xml +grepQuiet 'item.*attrPath="foo".*name="dependencies"' $TEST_ROOT/meta.xml # Do an install. nix-env -i dependencies @@ -50,8 +50,8 @@ 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 +grepQuiet 'meta.*description.*Random test package' $TEST_ROOT/meta.xml +grepQuiet 'item.*attrPath="foo".*name="dependencies"' $TEST_ROOT/meta.xml # Do an install. nix-env -i dependencies diff --git a/tests/search.sh b/tests/search.sh index 14da3127b..50e5edaa8 100644 --- a/tests/search.sh +++ b/tests/search.sh @@ -38,6 +38,6 @@ clearCache ## Search expressions # Check that empty search string matches all -nix search|grep -q foo -nix search|grep -q bar -nix search|grep -q hello +nix search|grepQuiet foo +nix search|grepQuiet bar +nix search|grepQuiet hello diff --git a/tests/tarball.sh b/tests/tarball.sh index b2acb9eb9..ac0066330 100644 --- a/tests/tarball.sh +++ b/tests/tarball.sh @@ -13,7 +13,7 @@ cp config.nix dependencies.builder*.sh $tarroot/ tarball=$TEST_ROOT/tarball.tar.xz (cd $TEST_ROOT && tar cf - tarball) | xz > $tarball -nix-env -f file://$tarball -qa --out-path | grep -q dependencies +nix-env -f file://$tarball -qa --out-path | grepQuiet dependencies nix-build -o $TEST_ROOT/result file://$tarball diff --git a/tests/test-infra.sh b/tests/test-infra.sh new file mode 100644 index 000000000..54ae120e7 --- /dev/null +++ b/tests/test-infra.sh @@ -0,0 +1,85 @@ +# Test the functions for testing themselves! +# Also test some assumptions on how bash works that they rely on. +source common.sh + +# `true` should exit with 0 +expect 0 true + +# `false` should exit with 1 +expect 1 false + +# `expect` will fail when we get it wrong +expect 1 expect 0 false + +noisyTrue () { + echo YAY! >&2 + true +} + +noisyFalse () { + echo NAY! >&2 + false +} + +# These should redirect standard error to standard output +expectStderr 0 noisyTrue | grepQuiet YAY +expectStderr 1 noisyFalse | grepQuiet NAY + +# `set -o pipefile` is enabled + +pipefailure () { + # shellcheck disable=SC2216 + true | false | true +} +expect 1 pipefailure +unset pipefailure + +pipefailure () { + # shellcheck disable=SC2216 + false | true | true +} +expect 1 pipefailure +unset pipefailure + +commandSubstitutionPipeFailure () { + # shellcheck disable=SC2216 + res=$(set -eu -o pipefail; false | true | echo 0) +} +expect 1 commandSubstitutionPipeFailure + +# `set -u` is enabled + +# note (...), making function use subshell, as unbound variable errors +# in the outer shell are *rightly* not recoverable. +useUnbound () ( + set -eu + # shellcheck disable=SC2154 + echo "$thisVariableIsNotBound" +) +expect 1 useUnbound + +# ! alone unfortunately negates `set -e`, but it works in functions: +# shellcheck disable=SC2251 +! true +funBang () { + ! true +} +expect 1 funBang +unset funBang + +# `grep -v -q` is not what we want for exit codes, but `grepInverse` is +# Avoid `grep -v -q`. The following line proves the point, and if it fails, +# we'll know that `grep` had a breaking change or `-v -q` may not be portable. +{ echo foo; echo bar; } | grep -v -q foo +{ echo foo; echo bar; } | expect 1 grepInverse foo + +# `grepQuiet` is quiet +res=$(set -eu -o pipefail; echo foo | grepQuiet foo | wc -c) +(( res == 0 )) +unset res + +# `greqQietInverse` is both +{ echo foo; echo bar; } | expect 1 grepQuietInverse foo +res=$(set -eu -o pipefail; echo foo | expect 1 grepQuietInverse foo | wc -c) +(( res == 0 )) +unset res diff --git a/tests/timeout.sh b/tests/timeout.sh index e3fb3ebcc..b179b79a2 100644 --- a/tests/timeout.sh +++ b/tests/timeout.sh @@ -5,17 +5,14 @@ source common.sh # XXX: This shouldn’t be, but #4813 cause this test to fail needLocalStore "see #4813" -set +e -messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) -status=$? -set -e +messages=$(nix-build -Q timeout.nix -A infiniteLoop --timeout 2 2>&1) && status=0 || status=$? 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 +if echo "$messages" | grepQuietInvert "timed out"; then echo "error: build may have failed for reasons other than timeout; output:" echo "$messages" >&2 exit 1 diff --git a/tests/user-envs.sh b/tests/user-envs.sh index aebf6a2a2..e178bdc7e 100644 --- a/tests/user-envs.sh +++ b/tests/user-envs.sh @@ -1,6 +1,6 @@ source common.sh -if [ -z "$storeCleared" ]; then +if [ -z "${storeCleared-}" ]; then clearStore fi @@ -19,13 +19,13 @@ drvPath10=$(nix-env -f ./user-envs.nix -qa --drv-path --no-name '*' | grep foo-1 [ -n "$outPath10" -a -n "$drvPath10" ] # Query descriptions. -nix-env -f ./user-envs.nix -qa '*' --description | grep -q silly +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 | grep -q silly +nix-env -qa '*' --description | grepQuiet silly # Query the system. -nix-env -qa '*' --system | grep -q $system +nix-env -qa '*' --system | grepQuiet $system # Install "foo-1.0". nix-env -i foo-1.0 @@ -33,19 +33,19 @@ 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 +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 '*' | grep -q '< 2.0' -nix-env -qac '*' | grep -q '> 1.0' +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 | grep -q 'IP- foo-1.0' -nix-env -qas | grep -q -- '--- bar-0.1' +nix-env -qas | grepQuiet 'IP- foo-1.0' +nix-env -qas | grepQuiet -- '--- bar-0.1' # Disable foo. nix-env --set-flag active false foo @@ -65,15 +65,15 @@ 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 +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 '' -u foo +NIX_PATH=nixpkgs=./user-envs.nix:${NIX_PATH-} nix-env -f '' -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 +nix-env -q '*' | grepQuiet foo-2.0 test "$($profiles/test/bin/foo)" = "foo-2.0" # Store the path of foo-2.0. @@ -85,20 +85,20 @@ 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 +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 '*' | grep -q foo-2.0 -nix-env -q '*' | grep -q bar +nix-env -q '*' | grepQuiet foo-2.0 +nix-env -q '*' | grepQuiet 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 +nix-env -q '*' | grepQuiet foo-2.0 +if nix-env -q '*' | grepQuiet bar; then false; fi # Count generations. nix-env --list-generations @@ -120,7 +120,7 @@ nix-env --switch-generation 7 # Install foo-1.0, now using its store path. nix-env -i "$outPath10" -nix-env -q '*' | grep -q foo-1.0 +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 ] @@ -128,12 +128,12 @@ nix-store -q --referrers-closure $profiles/test | grep "$(nix-store -q --resolve # 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") +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 '*' | grep -q foo +nix-env -q '*' | grepQuiet foo # Delete all old generations. nix-env --delete-generations old @@ -151,7 +151,7 @@ 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 +nix-env -q '*' | grepQuiet foo-2.0 # On the other hand, this should install both (and should fail due to # a collision). @@ -162,8 +162,8 @@ nix-env -e '*' 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 +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 From e813f9c4cd69677cb3b792d68d4684c654e3ae81 Mon Sep 17 00:00:00 2001 From: John Ericson Date: Fri, 14 Feb 2025 00:57:56 -0500 Subject: [PATCH 15/18] Add self-version daemon test This is not a cherry-pick because enough reworking had to happen. --- release.nix | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/release.nix b/release.nix index 2e71f3796..7f8f53740 100644 --- a/release.nix +++ b/release.nix @@ -223,8 +223,41 @@ let FONTCONFIG_FILE = texFunctions.fontsConf; }; + testNixVersions = client: daemon: pkgs: + + with import ./release-common.nix { inherit pkgs; }; + + pkgs.releaseTools.nixBuild { + NIX_DAEMON_PACKAGE = daemon; + NIX_CLIENT_PACKAGE = client; + name = "nix-tests-${client.src.version}-against-${daemon.src.version}"; + + src = tarball; + + VERSION_SUFFIX = tarball.versionSuffix; + + buildInputs = buildDeps; + propagatedBuildInputs = propagatedDeps; + + enableParallelBuilding = true; + + dontBuild = true; + doInstallCheck = true; + + installPhase = '' + mkdir -p $out + ''; + installCheckPhase = "make installcheck"; + }; + + testAgainstSelf = pkgs.lib.genAttrs systems (system: + + let pkgs = import nixpkgs { inherit system; }; in + + testNixVersions build.${system} build.${system} pkgs); # System tests. + tests.remoteBuilds = (import ./tests/remote-builds.nix rec { inherit nixpkgs; nix = build.x86_64-linux; system = "x86_64-linux"; From 196670af766d4ce77bf677c72edbcd94fb0afa60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?= Date: Wed, 4 Oct 2023 17:10:54 +0200 Subject: [PATCH 16/18] Don't run the tests that require building if we're not building A couple of tests require building some libraries that depend on Nix, and assume it to be built locally. Don't run these if we only want to run the install tests. This prevents the CI from rebuilding several times Nix (like in https://github.com/NixOS/nix/actions/runs/6404422275/job/17384964033#step:6:6412), thus removing a fair amount of build time. (cherry picked from commit eb68454be61bd6e8baf40136ae69c2b5fec3006a) --- release.nix | 1 + tests/local.mk | 13 +++++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/release.nix b/release.nix index 7f8f53740..4ed34c638 100644 --- a/release.nix +++ b/release.nix @@ -230,6 +230,7 @@ let pkgs.releaseTools.nixBuild { NIX_DAEMON_PACKAGE = daemon; NIX_CLIENT_PACKAGE = client; + HAVE_LOCAL_NIX_BUILD = false; name = "nix-tests-${client.src.version}-against-${daemon.src.version}"; src = tarball; diff --git a/tests/local.mk b/tests/local.mk index 792983b09..447a20957 100644 --- a/tests/local.mk +++ b/tests/local.mk @@ -1,6 +1,10 @@ check: @echo "Warning: Nix has no 'make check'. Please install Nix and run 'make installcheck' instead." +# whether to run the tests that assume that we have a local build of +# Nix +HAVE_LOCAL_NIX_BUILD ?= 1 + nix_tests = \ init.sh hash.sh lang.sh add.sh simple.sh dependencies.sh \ gc.sh \ @@ -27,7 +31,6 @@ nix_tests = \ brotli.sh \ pure-eval.sh \ check.sh \ - plugins.sh \ search.sh \ nix-copy-ssh.sh \ post-hook.sh \ @@ -35,10 +38,16 @@ nix_tests = \ test-infra.sh \ # parallel.sh +ifeq ($(HAVE_LOCAL_NIX_BUILD), 1) + nix_tests += plugins.sh +endif + +tests/plugins.sh.test: tests/plugins/libplugintest.$(SO_EXT) + install-tests += $(foreach x, $(nix_tests), tests/$(x)) tests-environment = NIX_REMOTE= $(bash) -e clean-files += $(d)/common.sh -test-deps += tests/common.sh tests/plugins/libplugintest.$(SO_EXT) +test-deps += tests/common.sh From ddbb122df1a8dbf9389ec747e37f11fa7155ff08 Mon Sep 17 00:00:00 2001 From: regnat Date: Thu, 24 Feb 2022 14:57:27 +0100 Subject: [PATCH 17/18] testS: poll more eagerly for the daemon start/stop 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 9c470cb9694408078fa2c289b9b776820e611923) --- tests/common.sh.in | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/common.sh.in b/tests/common.sh.in index b43c0cb81..247b28fba 100644 --- a/tests/common.sh.in +++ b/tests/common.sh.in @@ -92,9 +92,9 @@ startDaemon() { rm -f $NIX_DAEMON_SOCKET_PATH PATH=$DAEMON_PATH nix-daemon & pidDaemon=$! - for ((i = 0; i < 30; i++)); do + for ((i = 0; i < 300; i++)); do if [[ -S $NIX_DAEMON_SOCKET_PATH ]]; then break; fi - sleep 1 + sleep 0.1 done trap "killDaemon" EXIT # Save for if daemon is killed @@ -104,9 +104,9 @@ startDaemon() { killDaemon() { kill $pidDaemon - for i in {0.10}; do + for i in {0..100}; do kill -0 $pidDaemon || break - sleep 1 + sleep 0.1 done kill -9 $pidDaemon || true wait $pidDaemon || true From 68d6ebd9844422fc2d68dde70a38395df2b12bff Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Tue, 14 Sep 2021 11:34:17 +0200 Subject: [PATCH 18/18] nix-tests: Run 'make installcheck' in parallel (cherry picked from commit 6ff19ce137e4fa91e0a4b6c67c3af7181faafc10) --- release.nix | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release.nix b/release.nix index 4ed34c638..1c24167a5 100644 --- a/release.nix +++ b/release.nix @@ -248,7 +248,7 @@ let installPhase = '' mkdir -p $out ''; - installCheckPhase = "make installcheck"; + installCheckPhase = "make installcheck -j$NIX_BUILD_CORES -l$NIX_BUILD_CORE"; }; testAgainstSelf = pkgs.lib.genAttrs systems (system: