@@ -81,3 +81,92 @@ create_parent_and_subcommands() {
8181remove_test_go_rootdir () {
8282 remove_bats_test_dirs
8383}
84+
85+ # Get the stack trace of a line from a file or function as it would appear in
86+ # @go.print_stack_trace output.
87+ #
88+ # Arguments:
89+ # haystack_file: File containing the line
90+ # function_name: Function in which the line appears, 'main', or 'source'
91+ # needle_line: Line for which to produce a stack trace line
92+ stack_trace_item () {
93+ # Seriously, it's faster to create a script containing a `for` or `while read`
94+ # loop over a file and run it as a new process than it is to run it in-process
95+ # under Bats. Haven't yet figured out why.
96+ create_bats_test_script ' stack-trace-item' \
97+ " $( declare -f __stack_trace_item_impl) " \
98+ ' __stack_trace_item_impl "$@"'
99+ " $BATS_TEST_ROOTDIR /stack-trace-item" " $@ "
100+ }
101+
102+ log_command_stack_trace_item () {
103+ if [[ -z " $LOG_COMMAND_STACK_TRACE_ITEM " ]]; then
104+ export LOG_COMMAND_STACK_TRACE_ITEM=" $( stack_trace_item \
105+ " $_GO_CORE_DIR /lib/log" ' @go.log_command' ' "${args[@]}"' ) "
106+ fi
107+ echo " $LOG_COMMAND_STACK_TRACE_ITEM "
108+ }
109+
110+ # Call this before using "${GO_CORE_STACK_TRACE_COMPONENTS[@]}" to inject
111+ # entries from go-core.bash into your expected stack trace output.
112+ set_go_core_stack_trace_components () {
113+ local go_core_file=" $_GO_CORE_DIR /go-core.bash"
114+ local stack_item
115+ local IFS=$' \n '
116+
117+ if [[ " ${# GO_CORE_STACK_TRACE_COMPONENTS[@]} " -eq ' 0' ]]; then
118+ create_test_go_script ' @go "$@"'
119+ create_test_command_script ' print-stack-trace' ' @go.print_stack_trace'
120+
121+ for stack_item in $( " $TEST_GO_SCRIPT " ' print-stack-trace' ) ; do
122+ if [[ " $stack_item " =~ $go_core_file ]]; then
123+ GO_CORE_STACK_TRACE_COMPONENTS+=(" $stack_item " )
124+ elif [[ " ${# _GO_CORE_STACK_TRACE_COMPONENTS[@]} " -ne ' 0' ]]; then
125+ return
126+ fi
127+ done
128+ export GO_CORE_STACK_TRACE_COMPONENTS
129+ fi
130+ }
131+
132+ __stack_trace_item_impl () {
133+ local haystack_file=" $1 "
134+ local function_name=" $2 "
135+ local function_pattern=" ^$function_name \\ (\\ ) ?{\$ "
136+ local needle=" $3 "
137+ local skip
138+ local inside_function
139+ local lineno=0
140+ local line
141+ local result=1
142+
143+ if [[ " $function_name " == ' main' || " $function_name " == ' source' ]]; then
144+ inside_function=' false'
145+ fi
146+
147+ local IFS=$' \n '
148+ while read -r line; do
149+ (( ++ lineno))
150+ if [[ -n " $skip " ]]; then
151+ if [[ " $line " == ' }' ]]; then
152+ skip=
153+ fi
154+ elif [[ -z " $inside_function " && " $line " =~ $function_pattern ]]; then
155+ inside_function=' true'
156+ elif [[ " $line " =~ ()\ {$ ]]; then
157+ skip=' true'
158+ elif [[ " $inside_function " == ' true' && " $line " == ' }' ]]; then
159+ break
160+ elif [[ " $line " == " $needle " ]]; then
161+ result=0
162+ break
163+ fi
164+ done < " $haystack_file "
165+
166+ if [[ " $result " -eq ' 0' ]]; then
167+ echo " $haystack_file :$lineno $function_name "
168+ else
169+ printf " ERROR: Line not found in $function_name : \" $needle \" \nat:\n" >&2
170+ fi
171+ return " $result "
172+ }
0 commit comments