Skip to content

Commit cd300ce

Browse files
committed
fix(test-doubles): use builtin echo/printf in spy to prevent recursion when spying on echo or printf
Spying on 'echo' or 'printf' caused bashunit to hang indefinitely because the generated spy function called back into itself through bashunit's internal use of these builtins. Changes: - Use 'builtin echo' and 'builtin printf' inside the eval'd spy function body in bashunit::spy to prevent recursive calls when spying on echo or printf - Use 'builtin echo' in bashunit::mock's no-arg form for the same reason - Use 'builtin echo' in bashunit::helper::normalize_variable_name so that spy assertions remain functional even when echo is spied upon - Add functional tests (with fixtures) verifying that spying on echo and printf completes without hanging Fixes #607
1 parent 8a946e2 commit cd300ce

6 files changed

Lines changed: 40 additions & 10 deletions

File tree

src/helpers.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,11 +273,11 @@ function bashunit::helper::normalize_variable_name() {
273273
normalized_string="${input_string//[^a-zA-Z0-9_]/_}"
274274

275275
local _re='^[a-zA-Z_]'
276-
if [ "$(echo "$normalized_string" | "$GREP" -cE "$_re" || true)" -eq 0 ]; then
276+
if [ "$(builtin echo "$normalized_string" | "$GREP" -cE "$_re" || true)" -eq 0 ]; then
277277
normalized_string="_$normalized_string"
278278
fi
279279

280-
echo "$normalized_string"
280+
builtin echo "$normalized_string"
281281
}
282282

283283
function bashunit::helper::get_provider_data() {

src/test_doubles.sh

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ function bashunit::mock() {
3434
if [ $# -gt 0 ]; then
3535
eval "function $command() { $* \"\$@\"; }"
3636
else
37-
eval "function $command() { echo \"$($CAT)\" ; }"
37+
eval "function $command() { builtin echo \"$($CAT)\" ; }"
3838
fi
3939

4040
export -f "${command?}"
@@ -61,14 +61,14 @@ function bashunit::spy() {
6161
local serialized=\"\"
6262
local arg
6363
for arg in \"\$@\"; do
64-
serialized=\"\$serialized\$(printf '%q' \"\$arg\")$'\\x1f'\"
64+
serialized=\"\$serialized\$(builtin printf '%q' \"\$arg\")$'\\x1f'\"
6565
done
6666
serialized=\${serialized%$'\\x1f'}
67-
printf '%s\x1e%s\\n' \"\$raw\" \"\$serialized\" >> '$params_file'
67+
builtin printf '%s\x1e%s\\n' \"\$raw\" \"\$serialized\" >> '$params_file'
6868
local _c
69-
_c=\$(cat '$times_file' 2>/dev/null || echo 0)
69+
_c=\$(cat '$times_file' 2>/dev/null || builtin echo 0)
7070
_c=\$((_c+1))
71-
echo \"\$_c\" > '$times_file'
71+
builtin echo \"\$_c\" > '$times_file'
7272
}"
7373

7474
export -f "${command?}"
@@ -83,7 +83,7 @@ function assert_have_been_called() {
8383
local file_var="${variable}_times_file"
8484
local times=0
8585
if [ -f "${!file_var-}" ]; then
86-
times=$(cat "${!file_var}" 2>/dev/null || echo 0)
86+
times=$(cat "${!file_var}" 2>/dev/null || builtin echo 0)
8787
fi
8888
local label="${2:-$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")}"
8989

@@ -141,7 +141,7 @@ function assert_have_been_called_times() {
141141
local file_var="${variable}_times_file"
142142
local times=0
143143
if [ -f "${!file_var-}" ]; then
144-
times=$(cat "${!file_var}" 2>/dev/null || echo 0)
144+
times=$(cat "${!file_var}" 2>/dev/null || builtin echo 0)
145145
fi
146146
local label="${3:-$(bashunit::helper::normalize_test_function_name "${FUNCNAME[1]}")}"
147147
if [ "$times" -ne "$expected_count" ]; then
@@ -170,7 +170,7 @@ function assert_have_been_called_nth_with() {
170170

171171
local times=0
172172
if [ -f "${!times_file_var-}" ]; then
173-
times=$(cat "${!times_file_var}" 2>/dev/null || echo 0)
173+
times=$(cat "${!times_file_var}" 2>/dev/null || builtin echo 0)
174174
fi
175175

176176
if [ "$nth" -gt "$times" ]; then

tests/functional/doubles_test.sh

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,21 @@ function test_mock_mktemp_does_not_break_spy_creation() {
106106
assert_have_been_called_times 1 rm
107107
assert_have_been_called_with rm "-f" "/tmp/mocked_temp_file"
108108
}
109+
110+
function test_spy_on_echo_does_not_hang() {
111+
source ./tests/functional/fixtures/echo_function.sh
112+
bashunit::spy echo
113+
114+
write_message "hello world"
115+
116+
assert_have_been_called echo
117+
}
118+
119+
function test_spy_on_printf_does_not_hang() {
120+
source ./tests/functional/fixtures/printf_function.sh
121+
bashunit::spy printf
122+
123+
format_message "hello world"
124+
125+
assert_have_been_called printf
126+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
function write_message() {
4+
echo "message: $*"
5+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
#!/usr/bin/env bash
2+
3+
function format_message() {
4+
printf "formatted: %s\n" "$*"
5+
}

tests/unit/test_doubles_test.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,5 @@ function test_unsuccessful_spy_nth_called_with_invalid_index() {
214214
"expected call" "at index 5 but" "only called 1 times")" \
215215
"$(assert_have_been_called_nth_with 5 ps "first")"
216216
}
217+
218+

0 commit comments

Comments
 (0)