-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Harden tests' bash #5754
Harden tests' bash #5754
Conversation
cd9496c
to
c26a5a4
Compare
fd33ded
to
7a9e6e6
Compare
I marked this as stale due to inactivity. → More info |
7a9e6e6
to
63bc4a6
Compare
63bc4a6
to
a26d86e
Compare
06bcc77
to
073ee96
Compare
Writing tests in "modern bash" will be great.
I am a bit puzzled by this. How would I reproduce this problem? $ (set -euo pipefail; ({ ! sh -c 'exit 0'; } | cat; echo $?)); echo $?
1
$ (set -euo pipefail; ({ ! sh -c 'exit 2'; } | cat; echo $?)); echo $?
0
0 If this is related to |
It's a Here is a simaple example: bash -e -c 'true; echo foo'
bash -e -c '! true; echo bar'
bash -e -c '! true || false; echo baz' prints
|
Perhaps we should avoid the expectExit() {
local r=0 expect=$1
shift
"$@" || r=$?
if [[ $r != $expect ]]; then
echo "Got exit code $r, expecting $expect from: ${*@Q}" 1>&2
return 1
fi
} $ (set -e; ! true; echo bar)
bar
$ (set -e; expectExit 1 true; echo bar)
Got exit code 0, expecting 1 from: 'true' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Awesome (and it managed to find a couple fishy things, so it proved its worth).
Just one real concern regarding the removal tests/plugins/libplugintest.$(SO_EXT)
from the test dependencies, but looks good otherwise
@@ -10,7 +10,8 @@ expect_trace() { | |||
--trace-function-calls \ | |||
--expr "$expr" 2>&1 \ | |||
| grep "function-trace" \ | |||
| sed -e 's/ [0-9]*$//' | |||
| sed -e 's/ [0-9]*$//' \ | |||
|| true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(nit) Since there's a set +e
just below, I guess we can just move it a few lines above to make the whole function more consistent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I have a way to just get rid of all the set +e
instead 😀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does that mean that you want to keep doing more work on this? Or do you mean as a follow-up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did as part of this PR (small change), but now I have a seemingly unrelated failure I couldn't reproduce just running make installcheck
. Frustrating..
@@ -96,6 +96,8 @@ nix develop -f "$shellDotNix" shellDrv -c echo foo |& grep -q foo | |||
# Test 'nix print-dev-env'. | |||
[[ $(nix print-dev-env -f "$shellDotNix" shellDrv --json | jq -r .variables.arr1.value[2]) = '3 4' ]] | |||
|
|||
set +u # TODO fix? | |||
|
|||
source <(nix print-dev-env -f "$shellDotNix" shellDrv) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Opened #7951 to track that
a239a30
to
da2b291
Compare
I changed one little thing (in the overall runner) and now this broken pipe error in |
Could be
Maybe use |
4cefd4e
to
e542dad
Compare
68df860
to
1259169
Compare
# 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*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc and explanation
# Better than just doing `expect ... >&2` becaise the "Expected..." message | ||
# below will *not* be redirected. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc and explanation
# grep -v doesn't work well for exit codes. We want `!(exist line l. l | ||
# matches)`. It gives us `exist line l. !(l matches)`. | ||
# | ||
# BTW `!` normally doesn't work well with `set -e`, but when we wrap in a | ||
# function it *does*. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc and explanation
# 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 `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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doc and explanation
1259169
to
4b49656
Compare
grepQuietInverse() { | ||
! grep "$@" > /dev/null | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you add tests for the new test support functions?
We'll inevitably want to improve them and it'd be a waste not to reify the requirements that we've figured out for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent idea. I've added a test for that @roberth. It felt like putting on a mischievous bash magic show, kinda fun to write!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I figured you'd enjoy taming the shell! 🦁 🪄
4b49656
to
3f1114d
Compare
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. Co-authored-by: Robert Hensing <[email protected]>
0ad3ed4
to
c118361
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks!
A bit cumbersome to review, but I like the extra trust gained for the tests!
Thanks @thufschmitt! Yes sorry it grew and grew, but hopefully this helps us catch things faster going forward. |
Motivation
Use
set -u
andset -o pipefail
to catch accidental mistakes and failures more strongly.set -u
catches the use of undefined variablesset -o pipefail
catches failures (likeset -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.
Context
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 withset -o pipefail
it won't be, and we have to do something so the expected failure doesn't accidentally fail the test.expectStderr
To do that we use
expect
and a newexpectStderr
to check for the. See the comments on each for why.grepQuiet
grep -q
is replaced withgrepQuiet
, see the comments on that function for why.grepInverse
grep -v
when we just want the exit code is replaced withgrepInverse
, see the comments on that function for why.grepQuietInverse
grep -q -v
together is, surprise surprise, replaced withgrepQuietInverse
, which is both combined.Checklist for maintainers
Maintainers: tick if completed or explain if not relevant
tests/**.sh
src/*/tests
tests/nixos/*