Subject: Re: bug#62406: “! failing-command” pattern in shell tests is wrong

From: Eric Bavier <[email-masked]>
Date: Thu, 20 Apr 2023 05:48:55 +0000

On Tue, 2023-03-28 at 18:21 +0200, Ludovic Courtès wrote:
> Hi Eric,
> 
> Eric Bavier <[email-masked]> skribis:
> 
> > The purpose of d89343 was to ease visual parsing of the tests.  I mentioned
> > having used the '!' syntax in my own shell tests, but I realize now that I
> > was not relying on `set -e` like guix is.
> > 
> > I'll consider a few options.
> 
> Neat.  I guess we could have a ‘lib.sh’ with an ‘expect_fail’ function
> or something.
> 

Instead of a shared 'lib.sh', the attached patch uses 'cmd && false', which
has the desired semantics under 'set -e' and is no more verbose than a
wrapping function call.

If 'cmd' fails, the return status is ignored by 'set -e', which considers
only the return status of a command following the final '&&' or '||'.  And
because 'cmd' failed the statement short-circuits without executing the
'false.  Otherwise, if 'cmd' succeeds, the 'false' is executed and the shell
exits immediately.

In other places the '! test ...' pattern is replaced with 'test ! ...'.

There was some small amount of fall-out.  I fixed a couple issues where I
could:

  - tests/guix-archive.sh: added '--export' to command

  - tests/guix-style.sh: added an escape to a sed pattern

But a couple others have failures I'm not as confident in fixing myself:

  - tests/guix-refresh.sh: 'guix refresh' seems to not exit with a failure
status if a warning is issued, but the tests seem to think it should.

  - tests/guix-git-authenticate.sh: A general failure to authenticate a
particular commit.  This could be an issue with my test environment.

This patch should probably not be applied until those tests are fixed.  I
would appreciate any help with that.

`~Eric
From 625ec5da4179f0ea791d6cda2d452617cd1b96d8 Mon Sep 17 00:00:00 2001
From: Eric Bavier <[email-masked]>
Date: Thu, 20 Apr 2023 00:11:27 -0500
Subject: [PATCH] tests: Fix checks for expected failures.

Addresses <https://issues.guix.gnu.org/62406>.

With 'set -e', a return status inverted with '!' does not cause the shell to
exit immediately.  Instead use '&& false' to indicate an expected failure.

* tests/guix-archive.sh, tests/guix-build-branch.sh, tests/guix-build.sh,
tests/guix-daemon.sh, tests/guix-download.sh,
tests/guix-environment-container.sh, tests/guix-environment.sh,
tests/guix-gc.sh, tests/guix-git-authenticate.sh, tests/guix-graph.sh,
tests/guix-hash.sh, tests/guix-home.sh, tests/guix-pack-relocatable.sh,
tests/guix-pack.sh, tests/guix-package-aliases.sh, tests/guix-package-net.sh,
tests/guix-package.sh, tests/guix-refresh.sh, tests/guix-shell.sh,
tests/guix-style.sh, tests/guix-system.sh: Replace uses of '! ...' with
'... && false' or `test ! ...` as appropriate.
---
 tests/guix-archive.sh               |  8 +++--
 tests/guix-build-branch.sh          |  4 ++-
 tests/guix-build.sh                 | 22 +++++++-------
 tests/guix-daemon.sh                |  2 +-
 tests/guix-download.sh              | 10 ++++---
 tests/guix-environment-container.sh |  7 ++---
 tests/guix-environment.sh           |  6 ++--
 tests/guix-gc.sh                    | 18 +++++------
 tests/guix-git-authenticate.sh      |  8 ++---
 tests/guix-graph.sh                 |  6 ++--
 tests/guix-hash.sh                  | 11 +++----
 tests/guix-home.sh                  | 14 ++++-----
 tests/guix-pack-relocatable.sh      |  2 +-
 tests/guix-pack.sh                  |  4 +--
 tests/guix-package-aliases.sh       | 12 ++++----
 tests/guix-package-net.sh           | 22 +++++++-------
 tests/guix-package.sh               | 46 ++++++++++++++---------------
 tests/guix-refresh.sh               |  6 ++--
 tests/guix-shell.sh                 | 10 +++----
 tests/guix-style.sh                 |  6 ++--
 tests/guix-system.sh                |  2 +-
 21 files changed, 117 insertions(+), 109 deletions(-)

diff --git a/tests/guix-archive.sh b/tests/guix-archive.sh
index 00b87ff0ac..0866b5a4d8 100644
--- a/tests/guix-archive.sh
+++ b/tests/guix-archive.sh
@@ -44,7 +44,7 @@ cmp "$archive" "$archive_alt"
 # Check the exit value upon import.
 guix archive --import < "$archive"
 
-! guix archive something-that-does-not-exist
+guix archive --export something-that-does-not-exist && false
 
 # This one must not be listed as missing.
 guix build guile-bootstrap > "$archive"
@@ -61,7 +61,7 @@ cmp "$archive" "$archive_alt"
 
 # This is not a valid store file name, so an error.
 echo something invalid > "$archive"
-! guix archive --missing < "$archive"
+guix archive --missing < "$archive" && false
 
 # Check '--extract'.
 guile -c "(use-modules (guix serialization))
@@ -77,4 +77,6 @@ guix archive -t < "$archive" | grep "^D /share/guile"
 guix archive -t < "$archive" | grep "^x /bin/guile"
 guix archive -t < "$archive" | grep "^r /share/guile.*/boot-9\.scm"
 
-! echo foo | guix archive --authorize
+echo foo | guix archive --authorize && false
+
+exit 0
diff --git a/tests/guix-build-branch.sh b/tests/guix-build-branch.sh
index 7bf6a318ca..2d3b115e78 100644
--- a/tests/guix-build-branch.sh
+++ b/tests/guix-build-branch.sh
@@ -58,4 +58,6 @@ guix gc -R "$v0_1_0_drv" | grep guile-gcrypt-9e3eacd
 test "$v0_1_0_drv" != "$latest_drv"
 test "$v0_1_0_drv" != "$orig_drv"
 
-! guix build guix --with-commit=guile-gcrypt=000 -d
+guix build guix --with-commit=guile-gcrypt=000 -d && false
+
+exit 0
diff --git a/tests/guix-build.sh b/tests/guix-build.sh
index 9cbf8fe26d..2c59177c86 100644
--- a/tests/guix-build.sh
+++ b/tests/guix-build.sh
@@ -25,7 +25,7 @@
 guix build --version
 
 # Should fail.
-! guix build -e +
+guix build -e + && false
 
 # Source-less packages are accepted; they just return nothing.
 guix build -e '(@ (gnu packages bootstrap) %bootstrap-glibc)' -S
@@ -92,7 +92,7 @@ cat > "$module_dir/foo.scm" <<EOF
 (use-modules (guix))
 ) ;extra closing paren
 EOF
-! guix build -f "$module_dir/foo.scm" 2> "$module_dir/stderr"
+guix build -f "$module_dir/foo.scm" 2> "$module_dir/stderr" && false
 grep "read error" "$module_dir/stderr"
 rm "$module_dir/stderr" "$module_dir/foo.scm"
 
@@ -199,7 +199,7 @@ cat > "$module_dir/foo.scm" <<EOF
     (inputs (quasiquote (("sed" ,sed))))))  ;unbound variable
 EOF
 
-! guix build package-with-something-wrong -n
+guix build package-with-something-wrong -n && false
 guix build package-with-something-wrong -n 2> "$module_dir/err" || true
 grep "unbound" "$module_dir/err"		     # actual error
 grep "forget.*(gnu packages base)" "$module_dir/err" # hint
@@ -240,7 +240,7 @@ cat > "$module_dir/cc-user.scm" <<EOF
 (define-module (cc-user))
 (make-thing 42)
 EOF
-! guix build -f "$module_dir/cc-user.scm" -n 2> "$module_dir/err"
+guix build -f "$module_dir/cc-user.scm" -n 2> "$module_dir/err" && false
 cat "$module_dir/err"
 grep "make-thing.*unbound" "$module_dir/err"		 # actual error
 grep "forget.*(bb-public)" "$module_dir/err"		 # hint
@@ -270,7 +270,7 @@ test "`guix build --log-file guile-bootstrap`" = "$log"
 test "`guix build --log-file $out`" = "$log"
 
 # Should fail because the name/version combination could not be found.
-! guix build hello-0.0.1 -n
+guix build hello-0.0.1 -n && false
 
 # Keep a symlink to the result, registered as a root.
 result="t-result-$$"
@@ -279,7 +279,7 @@ guix build -r "$result"					\
 test -x "$result/bin/guile"
 
 # Should fail, because $result already exists.
-! guix build -r "$result" -e '(@@ (gnu packages bootstrap) %bootstrap-guile)'
+guix build -r "$result" -e '(@@ (gnu packages bootstrap) %bootstrap-guile)' && false
 
 rm -f "$result"
 
@@ -323,7 +323,7 @@ drv2=`guix build hello -d --with-input=gcc=gcc-toolchain`
 test "$drv1" != "$drv2"
 guix gc -R "$drv2" | grep `guix build -d gcc-toolchain`
 
-! guix build guile --with-input=libunistring=something-really-silly
+guix build guile --with-input=libunistring=something-really-silly && false
 
 # Deprecated/superseded packages.
 test "`guix build superseded -d`" = "`guix build bar -d`"
@@ -331,8 +331,8 @@ test "`guix build superseded -d`" = "`guix build bar -d`"
 # Parsing package names and versions.
 guix build -n time		# PASS
 guix build -n [email-masked]		# PASS, version found
-! guix build -n [email-masked]	# FAIL, version not found
-! guix build -n something-that-will-never-exist # FAIL
+guix build -n [email-masked] && false	# FAIL, version not found
+guix build -n something-that-will-never-exist && false	# FAIL
 
 # Invoking a monadic procedure.
 guix build -e "(begin
@@ -404,4 +404,6 @@ export GUIX_BUILD_OPTIONS
 guix build emacs
 
 GUIX_BUILD_OPTIONS="--something-completely-crazy"
-! guix build emacs
+guix build emacs && false
+
+exit 0
diff --git a/tests/guix-daemon.sh b/tests/guix-daemon.sh
index 4b09c8c162..d85727c955 100644
--- a/tests/guix-daemon.sh
+++ b/tests/guix-daemon.sh
@@ -224,7 +224,7 @@ daemon_pid=$!
 GUIX_DAEMON_SOCKET="guix://$tcp_socket"
 export GUIX_DAEMON_SOCKET
 
-! guix gc
+guix gc && false
 
 unset GUIX_DAEMON_SOCKET
 kill "$daemon_pid"
diff --git a/tests/guix-download.sh b/tests/guix-download.sh
index 5475d43e60..f4cb335eef 100644
--- a/tests/guix-download.sh
+++ b/tests/guix-download.sh
@@ -23,11 +23,11 @@
 guix download --version
 
 # Make sure it fails here.
-! guix download http://does.not/exist
+guix download http://does.not/exist && false
 
-! guix download unknown://some/where;
+guix download unknown://some/where && false
 
-! guix download /does-not-exist
+guix download /does-not-exist && false
 
 # This one should succeed.
 guix download "file://$abs_top_srcdir/README"
@@ -43,4 +43,6 @@ GUIX_DAEMON_SOCKET="/nowhere" guix download -o "$output" \
 cmp "$output" "$abs_top_srcdir/README"
 
 # This one should fail.
-! guix download "file:///does-not-exist" "file://$abs_top_srcdir/README"
+guix download "file:///does-not-exist" "file://$abs_top_srcdir/README" && false
+
+exit 0
diff --git a/tests/guix-environment-container.sh b/tests/guix-environment-container.sh
index a30d6b7fb2..a3bc1ab572 100644
--- a/tests/guix-environment-container.sh
+++ b/tests/guix-environment-container.sh
@@ -260,16 +260,15 @@ guix shell --bootstrap guile-bootstrap --container \
      /usr/bin/guile --version
 
 # A dangling symlink causes the command to fail.
-! guix shell --bootstrap -CS /usr/bin/python=bin/python guile-bootstrap -- exit
+guix shell --bootstrap -CS /usr/bin/python=bin/python guile-bootstrap -- exit && false
 
 # An invalid symlink spec causes the command to fail.
-! guix shell --bootstrap -CS bin/guile=/usr/bin/guile guile-bootstrap -- exit
+guix shell --bootstrap -CS bin/guile=/usr/bin/guile guile-bootstrap -- exit && false
 
 # Check whether '--nesting' works.
 guix build hello -d
 env="$(type -P pre-inst-env)"
-if guix shell -C -D guix -- "$env" guix build hello -d # cannot work
-then false; else true; fi
+guix shell -C -D guix -- "$env" guix build hello -d && false # cannot work
 hello_drv="$(guix build hello -d)"
 hello_drv_nested="$(cd "$(dirname env)" && guix shell --bootstrap -CW -D guix -- "$env" guix build hello -d)"
 test "$hello_drv" = "$hello_drv_nested"
diff --git a/tests/guix-environment.sh b/tests/guix-environment.sh
index 95fe95b437..1424ea9a88 100644
--- a/tests/guix-environment.sh
+++ b/tests/guix-environment.sh
@@ -60,7 +60,7 @@ guix environment --bootstrap --ad-hoc guile-bootstrap --pure	\
 grep '^PATH=' "$tmpdir/a"
 grep '^GUIX_TEST_ABC=' "$tmpdir/a"
 grep '^GUIX_TEST_DEF=' "$tmpdir/a"
-! grep '^GUIX_TEST_XYZ=' "$tmpdir/a"
+grep '^GUIX_TEST_XYZ=' "$tmpdir/a" && false
 
 # Make sure the exit value is preserved.
 if guix environment --bootstrap --ad-hoc guile-bootstrap --pure \
@@ -207,7 +207,7 @@ then
     done
 
     # 'make-boot0' itself must not be listed.
-    ! guix gc --references "$profile" | grep make-boot0
+    guix gc --references "$profile" | grep make-boot0 && false
 
     # Make sure that the shell spawned with '--exec' sees the same environment
     # as returned by '--search-paths'.
@@ -224,7 +224,7 @@ then
     test "x$make_boot0_debug" != "x"
 
     # Make sure the "debug" output is not listed.
-    ! guix gc --references "$profile" | grep "$make_boot0_debug"
+    guix gc --references "$profile" | grep "$make_boot0_debug" && false
 
     # Compute the build environment for the initial GNU Make, but add in the
     # bootstrap Guile as an ad-hoc addition.
diff --git a/tests/guix-gc.sh b/tests/guix-gc.sh
index f40619876d..675a13115d 100644
--- a/tests/guix-gc.sh
+++ b/tests/guix-gc.sh
@@ -36,11 +36,11 @@ unset out
 # For some operations, passing extra arguments is an error.
 for option in "" "-C 500M" "--verify" "--optimize" "--list-roots"
 do
-    ! guix gc $option whatever
+    guix gc $option whatever && false
 done
 
 # This should fail.
-! guix gc --verify=foo
+guix gc --verify=foo && false
 
 # Check the references of a .drv.
 drv="`guix build guile-bootstrap -d`"
@@ -51,7 +51,7 @@ guix gc --references "$drv" | grep -e -bash
 guix gc --references "$out"
 guix gc --references "$out/bin/guile"
 
-! guix gc --references /dev/null;
+guix gc --references /dev/null && false
 
 # Check derivers.
 guix gc --derivers "$out" | grep "$drv"
@@ -62,7 +62,7 @@ test -f "$drv"
 
 guix gc --list-dead | grep "$drv"
 guix gc --delete "$drv"
-! test -f "$drv"
+test ! -f "$drv"
 
 # Add a .drv, register it as a root.
 drv="`guix build --root=guix-gc-root lsh -d`"
@@ -71,18 +71,18 @@ test -f "$drv" && test -L guix-gc-root
 guix gc --list-roots | grep "$PWD/guix-gc-root"
 
 guix gc --list-live | grep "$drv"
-! guix gc --delete "$drv";
+guix gc --delete "$drv" && false
 
 rm guix-gc-root
 guix gc --list-dead | grep "$drv"
 guix gc --delete "$drv"
-! test -f "$drv"
+test ! -f "$drv"
 
 # Try a random collection.
 guix gc -C 1KiB
 
 # Check trivial error cases.
-! guix gc --delete /dev/null;
+guix gc --delete /dev/null && false
 
 # Bug #19757
 out="`guix build guile-bootstrap`"
@@ -90,14 +90,14 @@ test -d "$out"
 
 guix gc --delete "$out"
 
-! test -d "$out"
+test ! -d "$out"
 
 out="`guix build guile-bootstrap`"
 test -d "$out"
 
 guix gc --delete "$out/"
 
-! test -d "$out"
+test ! -d "$out"
 
 out="`guix build guile-bootstrap`"
 test -d "$out"
diff --git a/tests/guix-git-authenticate.sh b/tests/guix-git-authenticate.sh
index 2b90d8a4af..ec89f941e6 100644
--- a/tests/guix-git-authenticate.sh
+++ b/tests/guix-git-authenticate.sh
@@ -35,9 +35,9 @@ intro_signer="BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA"
 cache_key="test-$$"
 
 # This must fail because the end commit is not a descendant of $intro_commit.
-! guix git authenticate "$intro_commit" "$intro_signer"	\
+guix git authenticate "$intro_commit" "$intro_signer"	\
      --cache-key="$cache_key" --stats			\
-     --end=9549f0283a78fe36f2d4ff2a04ef8ad6b0c02604
+     --end=9549f0283a78fe36f2d4ff2a04ef8ad6b0c02604 && false
 
 # The v1.2.0 commit is a descendant of $intro_commit and it satisfies the
 # authorization invariant.
@@ -59,8 +59,8 @@ guix git authenticate "$intro_commit" "$intro_signer"	\
      --end="$v1_0_0_commit"
 
 # This should fail because these commits lack '.guix-authorizations'.
-! guix git authenticate "$v1_0_0_commit" "$v1_0_0_signer" \
-       --cache-key="$cache_key" --end="$v1_0_1_commit"
+guix git authenticate "$v1_0_0_commit" "$v1_0_0_signer" \
+       --cache-key="$cache_key" --end="$v1_0_1_commit" && false
 
 # This should work thanks to '--historical-authorizations'.
 guix git authenticate "$v1_0_0_commit" "$v1_0_0_signer" 	\
diff --git a/tests/guix-graph.sh b/tests/guix-graph.sh
index e813e01c31..9824c6a65a 100644
--- a/tests/guix-graph.sh
+++ b/tests/guix-graph.sh
@@ -59,7 +59,7 @@ guix graph -t references guile-bootstrap | grep guile-bootstrap
 guix graph -e '(@ (gnu packages bootstrap) %bootstrap-guile)' \
     | grep guile-bootstrap
 
-! guix graph -e +
+guix graph -e + && false
 
 # Try passing store file names.
 
@@ -76,13 +76,13 @@ cmp "$tmpfile1" "$tmpfile2"
 # Try package transformation options.
 guix graph git | grep 'label = "openssl'
 guix graph git --with-input=openssl=libressl | grep 'label = "libressl'
-! guix graph git --with-input=openssl=libressl | grep 'label = "openssl'
+guix graph git --with-input=openssl=libressl | grep 'label = "openssl' && false
 
 # Try --load-path
 guix graph -L $module_dir dummy | grep 'label = "dummy'
 
 # Displaying shortest paths (or lack thereof).
-! guix graph --path emacs vim
+guix graph --path emacs vim && false
 
 path="\
 emacs
diff --git a/tests/guix-hash.sh b/tests/guix-hash.sh
index 8b03c7985d..e260396fd8 100644
--- a/tests/guix-hash.sh
+++ b/tests/guix-hash.sh
@@ -38,12 +38,12 @@ test `guix hash -H sha1 -f base64 /dev/null` = "2jmj7l5rSw0yVb/vlWAYkK/YBwk="
 test "`guix hash /dev/null "$abs_top_srcdir/README"`" = "`guix hash /dev/null ; guix hash "$abs_top_srcdir/README"`"
 
 # Zero files.
-! guix hash
+guix hash && false
 
 # idem as `cat /dev/null | git hash-object --stdin`
 test `guix hash -S git -H sha1 -f hex  /dev/null` = e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
 
-! guix hash -H abcd1234 /dev/null
+guix hash -H abcd1234 /dev/null && false
 
 mkdir "$tmpdir"
 echo -n executable > "$tmpdir/exe"
@@ -61,11 +61,11 @@ test `guix hash -r "$tmpdir" 2>/dev/null` = 10k1lw41wyrjf9mxydi0is5nkpynlsvgslin
 test `guix hash -r "$tmpdir" -H sha512 2>/dev/null` = 301ra58c2vahczzxiyfin41mpyb0ljh4dh9zn3ijvwviaw1j40sfzw5skh9x945da88n3785ggifzig7acd6k72h0mpsc20m1f66m9n
 
 # Without '-r', this should fail.
-! guix hash "$tmpdir"
+guix hash "$tmpdir" && false
 
 # This should fail because /dev/null is a character device, which
 # the archive format doesn't support.
-! guix hash -S nar /dev/null
+guix hash -S nar /dev/null && false
 
 # Adding a .git directory
 mkdir "$tmpdir/.git"
@@ -80,5 +80,6 @@ test `guix hash -S nar $tmpdir -x` = 10k1lw41wyrjf9mxydi0is5nkpynlsvgslinics4ppi
 test `guix hash -S git $tmpdir -x` = 1m9yxz99g7askm88h6hzyv4g2bfv57rp5wvwp3iq5ypsplq1xkkk
 
 # Without '-r', this should fail.
-! guix hash "$tmpdir"
+guix hash "$tmpdir" && false
 
+exit 0
diff --git a/tests/guix-home.sh b/tests/guix-home.sh
index 11b068ca43..e9ef76c862 100644
--- a/tests/guix-home.sh
+++ b/tests/guix-home.sh
@@ -109,7 +109,7 @@ EOF
     guix home extension-graph "home.scm" | grep 'label = "home"'
 
     # There are no Shepherd services so the one below must fail.
-    ! guix home shepherd-graph "home.scm"
+    guix home shepherd-graph "home.scm" && false
 
     if container_supported
     then
@@ -118,17 +118,17 @@ EOF
         # TODO: Make container independent from external environment variables.
         SHELL=bash
 	guix home container home.scm -- true
-	! guix home container home.scm -- false
+	guix home container home.scm -- false && false
 	test "$(guix home container home.scm -- echo '$HOME')" = "$HOME"
 	guix home container home.scm -- cat '~/.config/test.conf' | \
 	    grep "the content of"
 	guix home container home.scm -- test -h '~/.bashrc'
 	test "$(guix home container home.scm -- id -u)" = 1000
-	! guix home container home.scm -- test -f '$HOME/sample/home.scm'
+	guix home container home.scm -- test -f '$HOME/sample/home.scm' && false
 	guix home container home.scm --expose="$PWD=$HOME/sample" -- \
 	     test -f '$HOME/sample/home.scm'
-	! guix home container home.scm --expose="$PWD=$HOME/sample" -- \
-	     rm -v '$HOME/sample/home.scm'
+	guix home container home.scm --expose="$PWD=$HOME/sample" -- \
+	     rm -v '$HOME/sample/home.scm' && false
     else
 	echo "'guix home container' test SKIPPED" >&2
     fi
@@ -206,8 +206,8 @@ EOF
 # the NEW content of bashrc-test-config.sh"
 
     # This file must have been removed and not backed up.
-    ! test -e "$HOME/.config/test.conf"
-    ! test -e "$HOME"/*guix-home*backup/.config/test.conf
+    test ! -e "$HOME/.config/test.conf"
+    test ! -e "$HOME"/*guix-home*backup/.config/test.conf
 
     test "$(cat "$(configuration_file)")" == "$(cat home.scm)"
     test "$(canonical_file_name)" == "$(readlink "${HOME}/.guix-home")"
diff --git a/tests/guix-pack-relocatable.sh b/tests/guix-pack-relocatable.sh
index 46120c9ee6..2fc9fde0bd 100644
--- a/tests/guix-pack-relocatable.sh
+++ b/tests/guix-pack-relocatable.sh
@@ -92,7 +92,7 @@ then
     grep 'GNU sed' "$test_directory/output"
 
     # Check whether the exit code is preserved.
-    ! run_without_store "$test_directory/Bin/sed" --does-not-exist
+    run_without_store "$test_directory/Bin/sed" --does-not-exist && false
 
     chmod -Rf +w "$test_directory"; rm -rf "$test_directory"/*
 else
diff --git a/tests/guix-pack.sh b/tests/guix-pack.sh
index a13e0ededf..4042e54aeb 100644
--- a/tests/guix-pack.sh
+++ b/tests/guix-pack.sh
@@ -37,7 +37,7 @@ test_directory="`mktemp -d`"
 trap 'chmod -Rf +w "$test_directory"; rm -rf "$test_directory"' EXIT
 
 # Reject unsuppoted packages.
-! guix pack intelmetool -s armhf-linux -n
+guix pack intelmetool -s armhf-linux -n && false
 
 # Compute the derivation of a pack.
 drv="`guix pack coreutils -d --no-grafts`"
@@ -48,7 +48,7 @@ guix gc -R "$drv" | grep "`guix build coreutils -d --no-grafts`"
 drv="`guix pack idutils -d --no-grafts --target=arm-linux-gnueabihf`"
 guix gc -R "$drv" | \
     grep "`guix build idutils --target=arm-linux-gnueabihf -d --no-grafts`"
-! guix gc -R "$drv" | grep "`guix build idutils -d --no-grafts`"
+guix gc -R "$drv" | grep "`guix build idutils -d --no-grafts`" && false
 
 # Build a tarball with no compression.
 guix pack --compression=none --bootstrap guile-bootstrap
diff --git a/tests/guix-package-aliases.sh b/tests/guix-package-aliases.sh
index 311838b768..f68d24b9a7 100644
--- a/tests/guix-package-aliases.sh
+++ b/tests/guix-package-aliases.sh
@@ -36,7 +36,7 @@ guix install --bootstrap guile-bootstrap -p "$profile"
 test -x "$profile/bin/guile"
 
 # Make sure '-r' isn't passed as-is to 'guix package'.
-! guix install -r guile-bootstrap -p "$profile" --bootstrap
+guix install -r guile-bootstrap -p "$profile" --bootstrap && false
 test -x "$profile/bin/guile"
 
 # Use a package transformation option and make sure it's recorded.
@@ -48,16 +48,16 @@ grep "libreoffice=inkscape" "$profile/manifest"
 guix upgrade --version
 guix upgrade -n
 guix upgrade gui.e -n
-! guix upgrade foo bar -n;
+guix upgrade foo bar -n && false
 
 guix remove --version
 guix remove --bootstrap guile-bootstrap -p "$profile"
-! test -x "$profile/bin/guile"
+test ! -x "$profile/bin/guile"
 test `guix package -p "$profile" -I | wc -l` -eq 0
 
-! guix remove -p "$profile" this-is-not-installed --bootstrap
+guix remove -p "$profile" this-is-not-installed --bootstrap && false
 
-! guix remove -i guile-bootstrap -p "$profile" --bootstrap
+guix remove -i guile-bootstrap -p "$profile" --bootstrap && false
 
 guix search '\<board\>' game | grep '^name: gnubg'
 
@@ -66,7 +66,7 @@ guix show guile
 guix show python@3 | grep "^name: python"
 
 # "python@2" exists but is deprecated; make sure it doesn't show up.
-! guix show python@2
+guix show python@2 && false
 
 # Specifying multiple packages.
 output="`guix show sed grep | grep ^name:`"
diff --git a/tests/guix-package-net.sh b/tests/guix-package-net.sh
index 1cdeff773a..62e17815bf 100644
--- a/tests/guix-package-net.sh
+++ b/tests/guix-package-net.sh
@@ -58,7 +58,7 @@ trap 'rm -f "$profile" "$profile_alt" "$profile.lock" "$profile_alt.lock" "$prof
 
 guix package --bootstrap -p "$profile" -i guile-bootstrap
 test -L "$profile" && test -L "$profile-1-link"
-! test -f "$profile-2-link"
+test ! -f "$profile-2-link"
 test -f "$profile/bin/guile"
 
 boot_make="(@ (guix tests) gnu-make-for-tests)"
@@ -98,13 +98,13 @@ test "`guix package -p "$profile" -l | cut -f1 | grep guile | head -n1`" \
      = "  guile-bootstrap"
 
 # Exit with 1 when a generation does not exist.
-! guix package -p "$profile" --list-generations=42
-! guix package -p "$profile" --switch-generation=99
+guix package -p "$profile" --list-generations=42 && false
+guix package -p "$profile" --switch-generation=99 && false
 
 # Remove a package.
 guix package --bootstrap -p "$profile" -r "guile-bootstrap"
 test -L "$profile-3-link"
-test -f "$profile/bin/make" && ! test -f "$profile/bin/guile"
+test -f "$profile/bin/make" && test ! -f "$profile/bin/guile"
 
 # Roll back.
 guix package --roll-back -p "$profile"
@@ -112,7 +112,7 @@ test "`readlink_base "$profile"`" = "$profile-2-link"
 test -x "$profile/bin/guile" && test -x "$profile/bin/make"
 guix package --roll-back -p "$profile"
 test "`readlink_base "$profile"`" = "$profile-1-link"
-test -x "$profile/bin/guile" && ! test -x "$profile/bin/make"
+test -x "$profile/bin/guile" && test ! -x "$profile/bin/make"
 
 # Switch to the rolled generation and switch back.
 guix package -p "$profile" --switch-generation=2
@@ -124,8 +124,8 @@ test "`readlink_base "$profile"`" = "$profile-1-link"
 for i in `seq 1 3`
 do
     guix package --bootstrap --roll-back -p "$profile"
-    ! test -f "$profile/bin"
-    ! test -f "$profile/lib"
+    test ! -f "$profile/bin"
+    test ! -f "$profile/lib"
     test "`readlink_base "$profile"`" = "$profile-0-link"
 done
 
@@ -135,7 +135,7 @@ test -z "`guix package -p "$profile" -l 0`"
 # Reinstall after roll-back to the empty profile.
 guix package --bootstrap -p "$profile" -e "$boot_make"
 test "`readlink_base "$profile"`" = "$profile-1-link"
-test -x "$profile/bin/guile" && ! test -x "$profile/bin/make"
+test -x "$profile/bin/guile" && test ! -x "$profile/bin/make"
 
 # Check that the first generation is the current one.
 test "`guix package -p "$profile" -l 1 | cut -f3 | head -n1`" = "(current)"
@@ -143,7 +143,7 @@ test "`guix package -p "$profile" -l 1 | cut -f3 | head -n1`" = "(current)"
 # Roll-back to generation 0, and install---all at once.
 guix package --bootstrap -p "$profile" --roll-back -i guile-bootstrap
 test "`readlink_base "$profile"`" = "$profile-1-link"
-test -x "$profile/bin/guile" && ! test -x "$profile/bin/make"
+test -x "$profile/bin/guile" && test ! -x "$profile/bin/make"
 
 # Install Make.
 guix package --bootstrap -p "$profile" -e "$boot_make"
@@ -175,7 +175,7 @@ test -z "`guix package -p "$profile" -l 3`"
 rm "$profile"
 guix package --bootstrap -p "$profile" -i guile-bootstrap
 guix package --bootstrap -p "$profile_alt" -i gcc-bootstrap
-! guix package -p "$profile" --search-paths | grep LIBRARY_PATH
+guix package -p "$profile" --search-paths | grep LIBRARY_PATH && false
 guix package -p "$profile" -p "$profile_alt" --search-paths \
      | grep "LIBRARY_PATH.*$profile/lib.$profile_alt/lib"
 
@@ -234,4 +234,4 @@ guix package --bootstrap -e "$boot_make"
 test -f "$HOME/.guix-profile/bin/make"
 
 guix package --bootstrap --roll-back
-! test -f "$HOME/.guix-profile/bin/make"
+test ! -f "$HOME/.guix-profile/bin/make"
diff --git a/tests/guix-package.sh b/tests/guix-package.sh
index cc416ec6a1..945d59cdfb 100644
--- a/tests/guix-package.sh
+++ b/tests/guix-package.sh
@@ -37,7 +37,7 @@ rm -f "$profile" "$tmpfile"
 trap 'rm -f "$profile" "$profile.lock" "$profile-"[0-9]* "$tmpfile"; rm -rf "$module_dir" t-home-'"$$" EXIT
 
 # Use `-e' with a non-package expression.
-! guix package --bootstrap -e +
+guix package --bootstrap -e + && false
 
 # Install a store item and make sure the version and output in the manifest
 # are correct.
@@ -57,23 +57,23 @@ guix gc --list-live | grep "`readlink "$profile-1-link"`"
 # Installing the same package a second time does nothing.
 guix package --bootstrap -p "$profile" -i guile-bootstrap
 test -L "$profile" && test -L "$profile-1-link"
-! test -f "$profile-2-link"
+test ! -f "$profile-2-link"
 test -f "$profile/bin/guile"
 
 # Unsupported packages cannot be installed.
-! guix package -e '(begin (use-modules (guix) (gnu packages base)) (package (inherit sed) (supported-systems (list))))' -n
+guix package -e '(begin (use-modules (guix) (gnu packages base)) (package (inherit sed) (supported-systems (list))))' -n && false
 case $(uname -m) in
     x86_64|i[3456]86)
-	! guix package -i novena-eeprom -n
+	guix package -i novena-eeprom -n && false
 	break;;
     *)
-	! guix package -i intelmetool -n
+	guix package -i intelmetool -n && false
 	break;;
 esac
 
 # Collisions are properly flagged (in this case, 'g-wrap' propagates
 # [email-masked], which conflicts with [email-masked].)
-! guix package --bootstrap -n -p "$profile" -i g-wrap [email-masked]
+guix package --bootstrap -n -p "$profile" -i g-wrap [email-masked] && false
 
 guix package --bootstrap -n -p "$profile" -i g-wrap [email-masked] \
      --allow-collisions
@@ -88,7 +88,7 @@ test "`guix package -p "$profile" --search-paths | wc -l`" = 1  # $PATH
   type -P rm )
 
 # Exit with 1 when a generation does not exist.
-! guix package -p "$profile" --delete-generations=42
+guix package -p "$profile" --delete-generations=42 && false
 
 # Exit with 0 when trying to delete the zeroth generation.
 guix package -p "$profile" --delete-generations=0
@@ -101,12 +101,12 @@ guix package --bootstrap -i "glibc:debug" -p "$profile" -n
 
 # Make sure nonexistent outputs are reported.
 guix package --bootstrap -i "guile-bootstrap:out" -p "$profile" -n
-! guix package --bootstrap -i "guile-bootstrap:does-not-exist" -p "$profile" -n
-! guix package --bootstrap -i "guile-bootstrap:does-not-exist" -p "$profile"
+guix package --bootstrap -i "guile-bootstrap:does-not-exist" -p "$profile" -n && false
+guix package --bootstrap -i "guile-bootstrap:does-not-exist" -p "$profile" && false
 
 # Make sure we get an error when trying to remove something that's not
 # installed.
-! guix package --bootstrap -r something-not-installed -p "$profile"
+guix package --bootstrap -r something-not-installed -p "$profile" && false
 
 # Check whether `--list-available' returns something sensible.
 guix package -p "$profile" -A 'gui.*e' | grep guile
@@ -118,8 +118,8 @@ guix package --show=guile | grep "^name: guile"
 guix package --show=texlive
 
 # Fail for non-existent packages or package/version pairs.
-! guix package --show=does-not-exist
-! guix package --show=emacs@42
+guix package --show=does-not-exist && false
+guix package --show=emacs@42 && false
 
 # Search.
 LC_MESSAGES=C
@@ -163,19 +163,19 @@ guix package --search="" > /dev/null
 # There's no generation older than 12 months, so the following command should
 # have no effect.
 generation="`readlink_base "$profile"`"
-! guix package -p "$profile" --delete-generations=12m
+guix package -p "$profile" --delete-generations=12m && false
 test "`readlink_base "$profile"`" = "$generation"
 
 # The following command should not delete the current generation, even though
 # it matches the given pattern (see <http://bugs.gnu.org/19978>.)  And since
 # there's nothing else to delete, it should just fail.
 guix package --list-generations -p "$profile"
-! guix package --bootstrap -p "$profile" --delete-generations=1..
+guix package --bootstrap -p "$profile" --delete-generations=1.. && false
 test "`readlink_base "$profile"`" = "$generation"
 
 # Make sure $profile is a GC root at this point.
 real_profile="`readlink -f "$profile"`"
-! guix gc -d "$real_profile"
+guix gc -d "$real_profile" && false
 test -d "$real_profile"
 
 # Now, let's remove all the symlinks to $real_profile, and make sure
@@ -278,22 +278,22 @@ do
     guix gc --list-live | grep "`readlink "$profile_link"`"
 
     guix package --bootstrap --roll-back
-    ! test -f "$HOME/.guix-profile/bin"
-    ! test -f "$HOME/.guix-profile/lib"
+    test ! -f "$HOME/.guix-profile/bin"
+    test ! -f "$HOME/.guix-profile/lib"
     test "`readlink "$default_profile"`" = "`basename $default_profile-0-link`"
 done
 
 # Check whether '-p ~/.guix-profile' makes any difference.
 # See <http://bugs.gnu.org/17939>.
-! test -e "$HOME/.guix-profile-0-link"
-! test -e "$HOME/.guix-profile-1-link"
+test ! -e "$HOME/.guix-profile-0-link"
+test ! -e "$HOME/.guix-profile-1-link"
 guix package --bootstrap -p "$HOME/.guix-profile" -i guile-bootstrap
-! test -e "$HOME/.guix-profile-1-link"
+test ! -e "$HOME/.guix-profile-1-link"
 guix package --bootstrap --roll-back -p "$HOME/.guix-profile"
-! test -e "$HOME/.guix-profile-0-link"
+test ! -e "$HOME/.guix-profile-0-link"
 
 # Extraneous argument.
-! guix package install foo-bar
+guix package install foo-bar && false
 
 # Make sure the "broken pipe" doesn't yield an error.
 # Note: 'pipefail' is a Bash-specific option.
@@ -382,7 +382,7 @@ cat > "$module_dir/package.scm"<<EOF
 
 (define my-package coreutils)   ;returns *unspecified*
 EOF
-! guix package --bootstrap --install-from-file="$module_dir/package.scm"
+guix package --bootstrap --install-from-file="$module_dir/package.scm" && false
 
 rm "$module_dir/package.scm"
 
diff --git a/tests/guix-refresh.sh b/tests/guix-refresh.sh
index c5214e1d6e..1167703b74 100644
--- a/tests/guix-refresh.sh
+++ b/tests/guix-refresh.sh
@@ -37,12 +37,12 @@ GUIX_TEST_UPDATER_TARGETS='
    ("the-test-package" "" (("5.5" "file://'$PWD/$module_dir'/source"))))'
 
 # No newer version available.
-! guix refresh -t test idutils
+guix refresh -t test idutils && false
 case "$(guix refresh -t test idutils 2>&1)" in
     *"$idutils_version"*"already the latest version"*) true;;
     *) false;;
 esac
-! guix refresh -t test libreoffice
+guix refresh -t test libreoffice && false
 case "$(guix refresh -t test libreoffice 2>&1)" in
     *"greater than the latest known version"*"1.0"*) true;;
     *) false;;
@@ -100,7 +100,7 @@ grep 'version "5.5"' "$module_dir/sample.scm"
 grep "$(guix hash -H sha256 -f nix-base32 "$module_dir/source")" "$module_dir/sample.scm"
 
 # Specifying a target version.
-! guix refresh -t test guile=2.0.0
+guix refresh -t test guile=2.0.0 && false
 case "$(guix refresh -t test guile=2.0.0 2>&1)" in
     *"failed to find"*"2.0.0"*) true;;
     *) false;;
diff --git a/tests/guix-shell.sh b/tests/guix-shell.sh
index cb2b53466d..ed368515eb 100644
--- a/tests/guix-shell.sh
+++ b/tests/guix-shell.sh
@@ -33,13 +33,13 @@ export XDG_CONFIG_HOME
 guix shell --bootstrap --pure guile-bootstrap -- guile --version
 
 # '--symlink' can only be used with --container.
-! guix shell --bootstrap guile-bootstrap -S /dummy=bin/guile
+guix shell --bootstrap guile-bootstrap -S /dummy=bin/guile && false
 
 # '--ad-hoc' is a thing of the past.
-! guix shell --ad-hoc guile-bootstrap
+guix shell --ad-hoc guile-bootstrap && false
 
 # Rejecting unsupported packages.
-! guix shell -s armhf-linux intelmetool -n
+guix shell -s armhf-linux intelmetool -n && false
 
 # Test approximately that the child process does not inherit extra file
 # descriptors.  Ideally we'd check there's nothing more than 0, 1, and 2, but
@@ -55,7 +55,7 @@ test "$(echo $fd_list | wc -w)" -le "$(echo $initial_fd_list | wc -w)"
 cat > "$tmpdir/guix.scm" <<EOF
 This is a broken guix.scm file.
 EOF
-! (cd "$tmpdir"; SHELL="$(type -P true)" guix shell --bootstrap 2> "stderr")
+(cd "$tmpdir"; SHELL="$(type -P true)" guix shell --bootstrap 2> "stderr") && false
 grep "not authorized" "$tmpdir/stderr"
 rm "$tmpdir/stderr"
 
@@ -122,7 +122,7 @@ then
     done
 
     # 'make-boot0' itself must not be listed.
-    ! guix gc --references "$profile" | grep make-boot0
+    guix gc --references "$profile" | grep make-boot0 && false
 
     # Honoring the local 'guix.scm' file.
     echo '(@ (guix tests) gnu-make-for-tests)' > "$tmpdir/guix.scm"
diff --git a/tests/guix-style.sh b/tests/guix-style.sh
index 58f953a0ec..2de879d5e3 100644
--- a/tests/guix-style.sh
+++ b/tests/guix-style.sh
@@ -65,7 +65,7 @@ cp "$tmpfile" "$tmpfile.bak"
 initial_hash="$(guix hash "$tmpfile")"
 
 guix style -f "$tmpfile"
-if ! test "$initial_hash" = "$(guix hash "$tmpfile")"
+if test "$initial_hash" != "$(guix hash "$tmpfile")"
 then
     cat "$tmpfile"
     diff -u "$tmpfile.bak" "$tmpfile"
@@ -73,8 +73,8 @@ then
 fi
 
 # Introduce random changes and try again.
-sed -i "$tmpfile" -e's/ +/ /g'
-! test "$initial_hash" = "$(guix hash "$tmpfile")"
+sed -i "$tmpfile" -e's/ \+/ /g'
+test "$initial_hash" != "$(guix hash "$tmpfile")"
 
 guix style -f "$tmpfile"
 test "$initial_hash" = "$(guix hash "$tmpfile")"
diff --git a/tests/guix-system.sh b/tests/guix-system.sh
index 1dbd5a0b85..adc0c44a6f 100644
--- a/tests/guix-system.sh
+++ b/tests/guix-system.sh
@@ -318,7 +318,7 @@ cat > "$tmpdir/config.scm" <<EOF
 
 (bad-local-file "whatever.scm")
 EOF
-! guix system build "$tmpdir/config.scm" -n
+guix system build "$tmpdir/config.scm" -n && false
 guix system build "$tmpdir/config.scm" -n 2>&1 | \
     grep "config\.scm:4:2: warning:.*whatever.*relative to current directory"
 
-- 
2.39.2