66# a Bats assertion function complies with the expectations outlined in
77# `$_GO_CORE_DIR/lib/bats/assertions`, namely that it:
88#
9- # - begins with `set "$BATS_ASSERTION_DISABLE_SHELL_OPTIONS "`
10- # - calls `return_from_bats_assertion ` directly via every return path
9+ # - begins with `set "$DISABLE_BATS_SHELL_OPTIONS "`
10+ # - calls `restore_bats_shell_options ` directly via every return path
1111#
1212# They also enforce that the assertion produces no output when successful, and
1313# that it writes only to standard error (`>&2`) when it fails.
@@ -30,14 +30,19 @@ if [[ -z "$ASSERTION_SOURCE" ]]; then
3030fi
3131
3232. " $ASSERTION_SOURCE "
33- . " ${BASH_SOURCE%/* } /helpers"
33+
34+ # Temp directory for assertion test artifacts.
35+ #
36+ # Defined the same as `BATS_TEST_ROOTDIR` from `lib/bats/helpers`, but not
37+ # dependent on it, so this file remains self-contained.
38+ export ASSERTION_TEST_ROOTDIR=" ${BATS_TEST_ROOTDIR:- $BATS_TMPDIR / test rootdir} "
3439
3540# This is the file written to by `printf_to_test_output_file`.
36- export TEST_OUTPUT_FILE=" $BATS_TEST_ROOTDIR /test-output.txt"
41+ export TEST_OUTPUT_FILE=" $ASSERTION_TEST_ROOTDIR /test-output.txt"
3742
3843# Path to the script generated by `expect_assertion_*` to test whether
39- # `return_from_bats_assertion ` was called or not.
40- readonly ASSERTION_TEST_SCRIPT=" $BATS_TEST_ROOTDIR /assertion-test.bats"
44+ # `restore_bats_shell_options ` was called or not.
45+ readonly ASSERTION_TEST_SCRIPT=" $ASSERTION_TEST_ROOTDIR /assertion-test.bats"
4146
4247# Format for the error message emitted when `ASSERTION_TEST_SCRIPT` fails.
4348# Should be called as:
@@ -48,11 +53,11 @@ export ASSERTION_TEST_SCRIPT_FAILURE_MESSAGE=
4853! read -rd ' ' ASSERTION_TEST_SCRIPT_FAILURE_MESSAGE << END_OF_FAILURE_MESSAGE
4954The very first line of '%s' must be
5055
51- set "\$ BATS_ASSERTION_DISABLE_SHELL_OPTIONS "
56+ set "\$ DISABLE_BATS_SHELL_OPTIONS "
5257
53- and it must call 'return_from_bats_assertion ' directly from every return path.
58+ and it must call 'restore_bats_shell_options ' directly from every return path.
5459
55- For details, see the comments for 'return_from_bats_assertion ' from:
60+ For details, see the comments for 'restore_bats_shell_options ' from:
5661
5762 ${BASH_SOURCE%/* } /assertions
5863END_OF_FAILURE_MESSAGE
@@ -62,116 +67,32 @@ END_OF_FAILURE_MESSAGE
6267# Will execute the command and assertion directly using the `run` command to
6368# make sure the condition is satisfied as expected, then it will execute them
6469# in a test script to make sure the assertion calls `set
65- # "$BATS_ASSERTION_DISABLE_SHELL_OPTIONS` and `return_from_bats_assertion`
66- # appropriately.
70+ # "$DISABLE_BATS_SHELL_OPTIONS` and `restore_bats_shell_options` appropriately.
6771#
6872# Arguments:
6973# run_cmd: The full command to pass to `run` as a single string
7074# assertion: The full assertion to evaluate as a single string
7175expect_assertion_success () {
7276 set +eET
73- local run_cmd=" $1 "
74- local assertion=" $2 "
75- local test_script=" $ASSERTION_TEST_SCRIPT "
76- local __assertion_output
77- local __assertion_status
78- local expected_output=()
79-
80- if ! __run_command_and_assertion_in_subshell " $run_cmd " " $assertion " ; then
81- __return_from_expect_assertion ' 1'
82- elif [[ " $__assertion_status " -ne ' 0' ]]; then
83- printf " In subshell: expected passing status, actual %d\nOutput:\n%s\n" \
84- " $__assertion_status " " $__assertion_output " >&2
85- __return_from_expect_assertion ' 1'
86- elif ! __check_expected_output " $__assertion_output " ; then
87- printf " '%s' should not produce output when successful.\n" \
88- " ${assertion%% * } " >&2
89- __return_from_expect_assertion ' 1'
90- else
91- # Although we expect the assertion under test to pass, this script injects a
92- # failing assertion after it to check that the assertion under test directly
93- # calls `return_from_bats_assertion` upon returning. If it doesn't, `set
94- # -eET` will not be in effect, so the failing assertion will not trigger the
95- # ERR trap or fail the test case.
96- #
97- # In an earlier incarnation of `return_from_bats_assertion` that only
98- # restored `set -o functrace` (and when tests only started with `set +o
99- # functrace`, equivalent to `set +T`), the failing assertion would fire the
100- # ERR trap and exit the test case, but Bats would show the passing
101- # assertion's stack, per issue #48.
102- __run_assertion_test_script \
103- " run $run_cmd " \
104- " failing_assertion() { [ 0 -eq 1 ]; }" \
105- " $assertion " \
106- " failing_assertion"
107-
108- expected_output=(' 1..1'
109- " not ok 1 $BATS_TEST_DESCRIPTION "
110- " # (from function \` failing_assertion' in file $test_script , line 5,"
111- " # in test file $test_script , line 7)"
112- " # \` failing_assertion' failed" )
113-
114- if ! __check_expected_output " $output " " ${expected_output[@]} " ; then
115- printf " $ASSERTION_TEST_SCRIPT_FAILURE_MESSAGE " " ${assertion%% * } " >&2
116- __return_from_expect_assertion ' 1'
117- else
118- __return_from_expect_assertion
119- fi
120- fi
77+ __expect_assertion_success " $@ "
78+ __return_from_expect_assertion " $? "
12179}
12280
12381# Validates that an assertion fails for the provided inputs
12482#
12583# Will execute the command and assertion directly using the `run` command to
12684# make sure the condition fails and the output is as expected, then it will
12785# execute them in a test script to make sure the assertion calls `set
128- # "$BATS_ASSERTION_DISABLE_SHELL_OPTIONS` and `return_from_bats_assertion`
129- # appropriately.
86+ # "$DISABLE_BATS_SHELL_OPTIONS` and `restore_bats_shell_options` appropriately.
13087#
13188# Arguments:
13289# run_cmd: The full command to pass to `run` as a single string
13390# assertion: The full assertion to evaluate as a single string
13491# expected: The expected output of the assertion failure
13592expect_assertion_failure () {
13693 set +eET
137- local run_cmd=" $1 "
138- local assertion=" $2 "
139- shift 2
140- local __assertion_output
141- local __assertion_status
142- local with_status
143- local expected_output=()
144-
145- if ! __run_command_and_assertion_in_subshell " $run_cmd " " $assertion " ; then
146- __return_from_expect_assertion ' 1'
147- elif [[ " $__assertion_status " -eq ' 0' ]]; then
148- printf " In subshell: expected failure, but succeeded\nOutput:\n%s\n" \
149- " $__assertion_output " >&2
150- __return_from_expect_assertion ' 1'
151- else
152- if [[ " $__assertion_status " -ne ' 1' ]]; then
153- with_status=" with status $__assertion_status "
154- fi
155-
156- if ! __check_expected_output " $__assertion_output " " $@ " ; then
157- __return_from_expect_assertion ' 1'
158- else
159- __run_assertion_test_script " run $run_cmd " " $assertion "
160-
161- expected_output=(' 1..1'
162- " not ok 1 $BATS_TEST_DESCRIPTION "
163- " # (in test file $ASSERTION_TEST_SCRIPT , line 5)"
164- " # \` $assertion ' failed${with_status} "
165- " ${@/#/# } " )
166-
167- if ! __check_expected_output " $output " " ${expected_output[@]} " ; then
168- printf " $ASSERTION_TEST_SCRIPT_FAILURE_MESSAGE " " ${assertion%% * } " >&2
169- __return_from_expect_assertion ' 1'
170- else
171- __return_from_expect_assertion
172- fi
173- fi
174- fi
94+ __expect_assertion_failure " $@ "
95+ __return_from_expect_assertion " $? "
17596}
17697
17798# Calls `printf` on its arguments and returns an error
@@ -204,8 +125,8 @@ export -f printf_with_error
204125# Arguments:
205126# ...: Arguments to `printf`
206127printf_to_test_output_file () {
207- if [[ ! -d " $BATS_TEST_ROOTDIR " ]]; then
208- mkdir " $BATS_TEST_ROOTDIR "
128+ if [[ ! -d " $ASSERTION_TEST_ROOTDIR " ]]; then
129+ mkdir " $ASSERTION_TEST_ROOTDIR "
209130 fi
210131 printf " $@ " > " $TEST_OUTPUT_FILE "
211132}
@@ -217,6 +138,103 @@ export -f printf_to_test_output_file
217138# None of the functions below this line are part of the public interface.
218139# --------------------------------
219140
141+ # Implementation for `expect_assertion_success`
142+ #
143+ # Arguments:
144+ # run_cmd: The full command to pass to `run` as a single string
145+ # assertion: The full assertion to evaluate as a single string
146+ __expect_assertion_success () {
147+ local run_cmd=" $1 "
148+ local assertion=" $2 "
149+ local test_script=" $ASSERTION_TEST_SCRIPT "
150+ local __assertion_output
151+ local __assertion_status
152+ local expected_output=()
153+
154+ if ! __run_command_and_assertion_in_subshell " $run_cmd " " $assertion " ; then
155+ return ' 1'
156+ elif [[ " $__assertion_status " -ne ' 0' ]]; then
157+ printf " In subshell: expected passing status, actual %d\nOutput:\n%s\n" \
158+ " $__assertion_status " " $__assertion_output " >&2
159+ return ' 1'
160+ elif ! __check_expected_output " $__assertion_output " ; then
161+ printf " '%s' should not produce output when successful.\n" \
162+ " ${assertion%% * } " >&2
163+ return ' 1'
164+ fi
165+
166+ # Although we expect the assertion under test to pass, this script injects a
167+ # failing assertion after it to check that the assertion under test directly
168+ # calls `restore_bats_shell_options` upon returning. If it doesn't, `set
169+ # -eET` will not be in effect, so the failing assertion will not trigger the
170+ # ERR trap or fail the test case.
171+ #
172+ # In an earlier incarnation of `restore_bats_shell_options` that only
173+ # restored `set -o functrace` (and when tests only started with `set +o
174+ # functrace`, equivalent to `set +T`), the failing assertion would fire the
175+ # ERR trap and exit the test case, but Bats would show the passing
176+ # assertion's stack, per issue #48.
177+ __run_assertion_test_script \
178+ " run $run_cmd " \
179+ " failing_assertion() { [ 0 -eq 1 ]; }" \
180+ " $assertion " \
181+ " failing_assertion"
182+
183+ expected_output=(' 1..1'
184+ " not ok 1 $BATS_TEST_DESCRIPTION "
185+ " # (from function \` failing_assertion' in file $test_script , line 5,"
186+ " # in test file $test_script , line 7)"
187+ " # \` failing_assertion' failed" )
188+
189+ if ! __check_expected_output " $output " " ${expected_output[@]} " ; then
190+ printf " $ASSERTION_TEST_SCRIPT_FAILURE_MESSAGE " " ${assertion%% * } " >&2
191+ return ' 1'
192+ fi
193+ }
194+
195+ # Implementation for `expect_assertion_failure`
196+ #
197+ # Arguments:
198+ # run_cmd: The full command to pass to `run` as a single string
199+ # assertion: The full assertion to evaluate as a single string
200+ # expected: The expected output of the assertion failure
201+ __expect_assertion_failure () {
202+ local run_cmd=" $1 "
203+ local assertion=" $2 "
204+ shift 2
205+ local __assertion_output
206+ local __assertion_status
207+ local with_status
208+ local expected_output=()
209+
210+ if ! __run_command_and_assertion_in_subshell " $run_cmd " " $assertion " ; then
211+ return ' 1'
212+ elif [[ " $__assertion_status " -eq ' 0' ]]; then
213+ printf " In subshell: expected failure, but succeeded\nOutput:\n%s\n" \
214+ " $__assertion_output " >&2
215+ return ' 1'
216+ fi
217+
218+ if [[ " $__assertion_status " -ne ' 1' ]]; then
219+ with_status=" with status $__assertion_status "
220+ elif ! __check_expected_output " $__assertion_output " " $@ " ; then
221+ return ' 1'
222+ fi
223+
224+ __run_assertion_test_script " run $run_cmd " " $assertion "
225+
226+ expected_output=(' 1..1'
227+ " not ok 1 $BATS_TEST_DESCRIPTION "
228+ " # (in test file $ASSERTION_TEST_SCRIPT , line 5)"
229+ " # \` $assertion ' failed${with_status} "
230+ " ${@/#/# } " )
231+
232+ if ! __check_expected_output " $output " " ${expected_output[@]} " ; then
233+ printf " $ASSERTION_TEST_SCRIPT_FAILURE_MESSAGE " " ${assertion%% * } " >&2
234+ return ' 1'
235+ fi
236+ }
237+
220238# Passes the target command to `run`, the executes the `assertion` in a subshell
221239#
222240# The `run_cmd` is executed in-process to set `output`, `status`, and `lines`.
@@ -259,7 +277,7 @@ __run_command_and_assertion_in_subshell() {
259277 elif [[ -z " $__assertion_status " ]]; then
260278 __assertion_output=" ${__assertion_output% exit:* } "
261279
262- if [[ " ${FUNCNAME[1]} " == ' expect_assertion_failure ' ]]; then
280+ if [[ " ${FUNCNAME[1]} " == ' __expect_assertion_failure ' ]]; then
263281 printf ' "%s" output does not end with a newline character:\n%s\n' \
264282 " ${assertion%% * } " " ${__assertion_output% exit:* } " >&2
265283 return 1
@@ -272,12 +290,26 @@ __run_command_and_assertion_in_subshell() {
272290# Arguments:
273291# ...: Body of the test case to execute
274292__run_assertion_test_script () {
275- create_bats_test_script " ${ASSERTION_TEST_SCRIPT# $BATS_TEST_ROOTDIR / } " \
276- ' #! /usr/bin/env bats' \
277- " load '$ASSERTION_SOURCE '" \
278- " @test \" $BATS_TEST_DESCRIPTION \" {" \
279- " $@ " \
280- ' }'
293+ local script_dir=" ${ASSERTION_TEST_SCRIPT%/* } "
294+ local script_impl=(' #! /usr/bin/env bats'
295+ " load '$ASSERTION_SOURCE '"
296+ " @test \" $BATS_TEST_DESCRIPTION \" {"
297+ " $@ "
298+ ' }' )
299+
300+ if [[ ! -d " $script_dir " ]] && ! mkdir -p " $script_dir " ; then
301+ printf ' Failed to create parent directory for assertion test script: %s\n' \
302+ " $ASSERTION_TEST_SCRIPT " >&2
303+ return ' 1'
304+ elif ! printf ' %s\n' " ${script_impl[@]} " > " $ASSERTION_TEST_SCRIPT " ; then
305+ printf ' Failed to create assertion test script: %s\n' \
306+ " $ASSERTION_TEST_SCRIPT " >&2
307+ return ' 1'
308+ elif ! chmod 755 " $ASSERTION_TEST_SCRIPT " ; then
309+ printf ' Failed to set permissions for assertion test script: %s\n' \
310+ " $ASSERTION_TEST_SCRIPT " >&2
311+ return ' 1'
312+ fi
281313 run " $ASSERTION_TEST_SCRIPT "
282314}
283315
@@ -300,7 +332,7 @@ __check_expected_output() {
300332 fi
301333}
302334
303- # Basically the same as `return_from_bats_assertion `, but specific to this file.
335+ # Basically the same as `restore_bats_shell_options `, but specific to this file.
304336#
305337# Arguments:
306338# result: Return value of the calling assertion; defaults to 0
0 commit comments