Skip to content

Commit 17ff46d

Browse files
authored
Merge pull request #161 from mbland/bats-function
Use `lib/bats/helper-function` in tests, `lib/testing/stack-trace`
2 parents 6cc7c95 + f9511d3 commit 17ff46d

File tree

8 files changed

+183
-159
lines changed

8 files changed

+183
-159
lines changed

lib/testing/stack-trace

Lines changed: 154 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#
55
# You must source `_GO_CORE_DIR/lib/testing/environment` before this file.
66

7+
. "$_GO_CORE_DIR/lib/bats/helper-function"
8+
79
# Get the stack trace of a line from a file or function as it would appear in
810
# `@go.print_stack_trace` output.
911
#
@@ -12,25 +14,10 @@
1214
# function_name: Function in which the line appears, 'main', or 'source'
1315
# target_line: Line for which to produce a stack trace line
1416
@go.stack_trace_item() {
15-
local filepath="$1"
16-
local function_name="$2"
17-
local target_line="$3"
18-
19-
__@go.check_file_path_specified_and_present "$filepath"
20-
21-
if [[ -z "$function_name" ]]; then
22-
printf 'No function name specified for `%s`.\n' "$FUNCNAME" >&2
23-
exit 1
24-
elif [[ "$function_name" =~ ^(main|source)$ && -z "$target_line" ]]; then
25-
printf 'No target line from `%s` specified for `%s`.\n' \
26-
"$function_name" "$FUNCNAME" >&2
27-
exit 1
28-
fi
29-
30-
# Seriously, it's faster to run a script containing a `for` or `while read`
31-
# loop over a file as a new process than it is to run the function in-process
32-
# under Bats. Haven't yet figured out why.
33-
"${BASH_SOURCE%/*}/stack-trace-item" "$@"
17+
set "$DISABLE_BATS_SHELL_OPTIONS"
18+
__@go.check_file_path_specified_and_present "$1"
19+
__@go.stack_trace_item "$@"
20+
restore_bats_shell_options "$?"
3421
}
3522

3623
# Creates `GO_CORE_STACK_TRACE_COMPONENTS` to help validate stack trace output.
@@ -49,27 +36,9 @@
4936
# Stack trace lines from `go-core.bash` comprising the command script
5037
# dispatch mechanism
5138
@go.set_go_core_stack_trace_components() {
52-
local go_core_file="$_GO_CORE_DIR/go-core.bash"
53-
local stack_item
54-
local script="$TEST_GO_ROOTDIR/generate-go-core-stack-trace"
55-
local IFS=$'\n'
56-
57-
if [[ "${#GO_CORE_STACK_TRACE_COMPONENTS[@]}" -eq '0' ]]; then
58-
create_bats_test_script "${script#$TEST_GO_ROOTDIR}" \
59-
". '$_GO_CORE_DIR/go-core.bash' '$TEST_GO_SCRIPTS_RELATIVE_DIR'" \
60-
'@go "$@"'
61-
@go.create_test_command_script 'print-stack-trace' '@go.print_stack_trace'
62-
63-
for stack_item in $("$script" 'print-stack-trace'); do
64-
if [[ "$stack_item" =~ $go_core_file ]]; then
65-
GO_CORE_STACK_TRACE_COMPONENTS+=("$stack_item")
66-
elif [[ "${#_GO_CORE_STACK_TRACE_COMPONENTS[@]}" -ne '0' ]]; then
67-
return
68-
fi
69-
done
70-
rm "$script" "$TEST_GO_SCRIPTS_DIR/print-stack-trace"
71-
export GO_CORE_STACK_TRACE_COMPONENTS
72-
fi
39+
set "$DISABLE_BATS_SHELL_OPTIONS"
40+
__@go.set_go_core_stack_trace_components
41+
restore_bats_shell_options "$?"
7342
}
7443

7544
# Creates a stack trace line using an offset from the last line of `filename`.
@@ -82,6 +51,7 @@
8251
# offset: Offset from the last line to get the line number (default 0)
8352
# funcname: Name of the function expected in the stack trace (default 'main')
8453
@go.stack_trace_item_from_offset() {
54+
set "$DISABLE_BATS_SHELL_OPTIONS"
8555
local filepath="${1}"
8656
local offset="${2:-0}"
8757
local funcname="${3:-main}"
@@ -92,6 +62,7 @@
9262
@go.count_lines "$filepath" 'num_lines'
9363
lineno="$((num_lines - offset))"
9464
printf ' %s:%d %s\n' "$filepath" "$lineno" "$funcname"
65+
restore_bats_shell_options "$?"
9566
}
9667

9768
# Counts the number of lines in a file.
@@ -100,14 +71,155 @@
10071
# filepath: Path to the file
10172
# result_name: Name of the caller's variable in which to store the result
10273
@go.count_lines() {
74+
set "$DISABLE_BATS_SHELL_OPTIONS"
75+
__@go.check_file_path_specified_and_present "$1"
76+
__@go.count_lines "$@"
77+
restore_bats_shell_options "$?"
78+
}
79+
80+
# --------------------------------
81+
# IMPLEMENTATION - HERE BE DRAGONS
82+
#
83+
# None of the functions below this line are part of the public interface.
84+
# --------------------------------
85+
86+
# Implementation for `@go.stack_trace_item`, extracted for efficiency
87+
#
88+
# Arguments:
89+
# filepath: Path to the file containing the line
90+
# function_name: Function in which the line appears, 'main', or 'source'
91+
# target_line: Line for which to produce a stack trace line
92+
__@go.stack_trace_item() {
93+
local filepath="$1"
94+
local function_name="$2"
95+
local target_line="$3"
96+
local initial_state='FUNCTION'
97+
local state
98+
local lineno=0
99+
local line
100+
101+
# Granted, this will match an illegal line like 'foo {' that has neither
102+
# 'function' nor '()', but it's probably not worth splitting hairs here.
103+
local function_opening='^(function )? *([[:alnum:]_.@-]+) *(\(\))? *\{'
104+
105+
if [[ -z "$function_name" ]]; then
106+
printf 'No function name specified for `%s`.\n' "${FUNCNAME[1]}" >&2
107+
exit 1
108+
elif [[ "$function_name" =~ ^(main|source)$ && -z "$target_line" ]]; then
109+
printf 'No target line from `%s` specified for `%s`.\n' \
110+
"$function_name" "${FUNCNAME[1]}" >&2
111+
exit 1
112+
fi
113+
114+
if [[ "$function_name" == 'main' || "$function_name" == 'source' ]]; then
115+
initial_state='MAIN'
116+
fi
117+
state="$initial_state"
118+
119+
# We have to set `IFS=` to preserve leading spaces, and then for Windows
120+
# compatibility, strip off the trailing carriage return (the newline is
121+
# already chomped off).
122+
while IFS= read -r line; do
123+
line="${line%$'\r'}"
124+
((++lineno))
125+
126+
case "$state" in
127+
MAIN)
128+
if [[ "$line" == "$target_line" ]]; then
129+
state='SUCCESS'
130+
break
131+
elif [[ "$line" =~ $function_opening ]]; then
132+
state='SKIP_FUNCTION'
133+
fi
134+
;;
135+
FUNCTION)
136+
if [[ "$line" =~ $function_opening ]]; then
137+
if [[ "${BASH_REMATCH[2]}" != "$function_name" ]]; then
138+
state='SKIP_FUNCTION'
139+
elif [[ -z "$target_line" ]]; then
140+
state='SUCCESS'
141+
break
142+
else
143+
state='INSIDE_TARGET_FUNCTION'
144+
fi
145+
fi
146+
;;
147+
INSIDE_TARGET_FUNCTION)
148+
if [[ "$line" == "$target_line" ]]; then
149+
state='SUCCESS'
150+
break
151+
elif [[ "$line" == '}' ]]; then
152+
state='FOUND_FUNCTION'
153+
break
154+
fi
155+
;;
156+
SKIP_FUNCTION)
157+
if [[ "$line" == '}' ]]; then
158+
state="$initial_state"
159+
fi
160+
;;
161+
esac
162+
done <"$filepath"
163+
164+
case "$state" in
165+
SUCCESS)
166+
printf ' %s:%d %s\n' "$filepath" "$lineno" "$function_name"
167+
return
168+
;;
169+
MAIN|FOUND_FUNCTION)
170+
printf 'Line not found in `%s` from "%s": "%s"\n' \
171+
"$function_name" "$filepath" "$target_line" >&2
172+
;;
173+
*)
174+
printf 'Function `%s` not found in "%s".\n' "$function_name" "$filepath" >&2
175+
;;
176+
esac
177+
return '1'
178+
}
179+
180+
# Implementation for `@go.set_go_core_stack_trace_components`
181+
#
182+
# Globals:
183+
# GO_CORE_STACK_TRACE_COMPONENTS:
184+
# Stack trace lines from `go-core.bash` comprising the command script
185+
# dispatch mechanism
186+
__@go.set_go_core_stack_trace_components() {
187+
local go_core_file="$_GO_CORE_DIR/go-core.bash"
188+
local stack_item
189+
local script="$TEST_GO_ROOTDIR/generate-go-core-stack-trace"
190+
local IFS=$'\n'
191+
192+
if [[ "${#GO_CORE_STACK_TRACE_COMPONENTS[@]}" -eq '0' ]]; then
193+
create_bats_test_script "${script#$TEST_GO_ROOTDIR}" \
194+
". '$_GO_CORE_DIR/go-core.bash' '$TEST_GO_SCRIPTS_RELATIVE_DIR'" \
195+
'@go "$@"'
196+
@go.create_test_command_script 'print-stack-trace' '@go.print_stack_trace'
197+
198+
for stack_item in $("$script" 'print-stack-trace'); do
199+
if [[ "$stack_item" =~ $go_core_file ]]; then
200+
GO_CORE_STACK_TRACE_COMPONENTS+=("$stack_item")
201+
elif [[ "${#_GO_CORE_STACK_TRACE_COMPONENTS[@]}" -ne '0' ]]; then
202+
return
203+
fi
204+
done
205+
rm "$script" "$TEST_GO_SCRIPTS_DIR/print-stack-trace"
206+
export GO_CORE_STACK_TRACE_COMPONENTS
207+
fi
208+
}
209+
210+
# Implementation for @go.count_lines, extracted for efficiency
211+
#
212+
# Arguments:
213+
# filepath: Path to the file
214+
# result_name: Name of the caller's variable in which to store the result
215+
__@go.count_lines() {
103216
local filepath="$1"
104217
local result_name="$2"
105218
local line
106219
local i='0'
107220

108-
__@go.check_file_path_specified_and_present "$filepath"
109221
if [[ -z "$result_name" ]]; then
110-
printf 'No result variable specified for `%s`.\n' "$FUNCNAME" >&2
222+
printf 'No result variable specified for `%s`.\n' "${FUNCNAME[1]}" >&2
111223
exit 1
112224
fi
113225

@@ -117,12 +229,6 @@
117229
printf -v "$result_name" '%d' "$i"
118230
}
119231

120-
# --------------------------------
121-
# IMPLEMENTATION - HERE BE DRAGONS
122-
#
123-
# None of the functions below this line are part of the public interface.
124-
# --------------------------------
125-
126232
# Confirms that a file name is specified and the file exists.
127233
#
128234
# Will print a message to standard error and exit on error.

lib/testing/stack-trace-item

Lines changed: 0 additions & 87 deletions
This file was deleted.

tests/commands/find.bats

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@ teardown() {
2525
}
2626

2727
assert_command_scripts_equal() {
28-
set "$BATS_ASSERTION_DISABLE_SHELL_OPTIONS"
28+
set "$DISABLE_BATS_SHELL_OPTIONS"
2929
unset 'lines[0]' 'lines[1]'
3030
lines=("${lines[@]}")
3131
assert_lines_equal "$@"
32-
return_from_bats_assertion "$?"
32+
restore_bats_shell_options "$?"
3333
}
3434

3535
@test "$SUITE: return only builtin commands" {

tests/complete/command-path.bats

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@ teardown() {
2828
}
2929

3030
assert_completions_match() {
31-
set "$BATS_ASSERTION_DISABLE_SHELL_OPTIONS"
31+
set "$DISABLE_BATS_SHELL_OPTIONS"
32+
__assert_completions_match_impl "$@"
33+
restore_bats_shell_options "$?"
34+
}
35+
36+
__assert_completions_match_impl() {
3237
# Trim the last three lines of output from the script to compare separately
3338
# from the output of _@go.complete_command_path.
3439
local num_lines="${#lines[@]}"
@@ -55,11 +60,7 @@ assert_completions_match() {
5560
if ! assert_equal "${__expected_argv[*]}" "${var_lines[2]}" 'argv list'; then
5661
((++num_errors))
5762
fi
58-
if [[ "$num_errors" -ne '0' ]]; then
59-
return_from_bats_assertion '1'
60-
else
61-
return_from_bats_assertion
62-
fi
63+
[[ "$num_errors" -eq '0' ]]
6364
}
6465

6566
@test "$SUITE: error on empty arguments" {

0 commit comments

Comments
 (0)