forked from buildkite-plugins/bats-mock
-
Notifications
You must be signed in to change notification settings - Fork 0
/
binstub
executable file
·184 lines (154 loc) · 5.46 KB
/
binstub
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
#!/usr/bin/env bash
set -e
# If stdin comes from a pipe, save its content for later
if ! [ -t 0 ]; then
input="$(cat)"
fi
status=0
program="${0##*/}"
PROGRAM="$(echo "$program" | tr a-z-. A-Z__)"
_STUB_PLAN="${PROGRAM}_STUB_PLAN"
_STUB_RUN="${PROGRAM}_STUB_RUN"
_STUB_INDEX="${PROGRAM}_STUB_INDEX"
_STUB_RESULT="${PROGRAM}_STUB_RESULT"
_STUB_END="${PROGRAM}_STUB_END"
_STUB_DEBUG="${PROGRAM}_STUB_DEBUG"
# Indent debug/detail output further than ordinary BATS output
_LDSPC=' '
# Use for debug-level output when user asks for it regarding a specific stub by setting i.e.:
# export MYPROGRAM_STUB_DEBUG=/dev/tty
debug() {
if [ -n "${!_STUB_DEBUG}" ] ; then
echo "${_LDSPC}bats-mock($program): $*" >&${!_STUB_DEBUG}
fi
}
# This print helper keeps track of whether the overall expected and actual invocation (initialized
# later below) has been printed, and prints it before the first printing of error detail.
printed_expectation=0
printed_invocation=0
expected_invocation=''
actual_invocation=''
detail_overview() {
if [ -z "${BATS_MOCK_DETAIL}" ] ; then return; fi
if [ $printed_expectation -ne 1 ]; then
echo '' >&${BATS_MOCK_DETAIL} # Work around problem with interrupting normal BATS output
echo "${_LDSPC}bats-mock($program): $expected_invocation" >&${BATS_MOCK_DETAIL}
printed_expectation=1
fi
if [ $printed_invocation -ne 1 ]; then
echo "${_LDSPC}bats-mock($program): $actual_invocation" >&${BATS_MOCK_DETAIL}
printed_invocation=1
fi
}
# Use for detailed output on failure when user asks for it across all mocks by setting i.e.:
# export BATS_MOCK_DETAIL=/dev/tty
# NOTE: When you want to output both debug-level and error detail, use this output helper.
detail() {
if [ -n "${BATS_MOCK_DETAIL}" ] ; then
detail_overview
echo "${_LDSPC}bats-mock($program): $*" >&${BATS_MOCK_DETAIL}
elif [ -n "${!_STUB_DEBUG}" ] ; then
echo "${_LDSPC}bats-mock($program): $*" >&${!_STUB_DEBUG}
fi
}
[ -e "${!_STUB_PLAN}" ] || exit 1
[ -n "${!_STUB_RUN}" ] || eval "${_STUB_RUN}"="${BATS_MOCK_TMPDIR}/${program}-stub-run"
# Initialize or load the stub run information.
eval "${_STUB_INDEX}"=1
eval "${_STUB_RESULT}"=0
[ ! -e "${!_STUB_RUN}" ] || source "${!_STUB_RUN}"
actual_invocation="got ${program}$(printf " '%q'" "$*")"
if [ -z "${!_STUB_END}" ] && [ -n "${!_STUB_DEBUG}" ]; then
echo '' >&${!_STUB_DEBUG} # Work around problem with interrupting normal BATS output
debug "$actual_invocation"
printed_invocation=1 # No need to re-print soon after for debug mode
fi
# Loop over each line in the plan.
index=0
while IFS= read -r line; do
index=$(($index + 1))
# debug "[idx $index, want ${!_STUB_INDEX}] $line"
if [ -z "${!_STUB_END}" ] && [ $index -eq "${!_STUB_INDEX}" ]; then
# We found the plan line we're interested in.
# Start off by assuming success.
result=0
# Split the line into an array of arguments to
# match and a command to run to produce output.
command=" $line"
if [ "$command" != "${command/ : }" ]; then
patterns="${command%% : *}"
command="${command#* : }"
fi
arguments=("$@")
parsed_patterns=()
# xargs respects quoted substrings
while IFS= read -rd '' token; do
parsed_patterns+=("$token")
done < <(xargs printf '%b\0' <<< "${patterns}")
debug "patterns [${#parsed_patterns[@]}] = $(printf "'%q' " "${parsed_patterns[@]}")"
debug "arguments [${#arguments[@]}] = $(printf "'%q' " "${arguments[@]}")"
expected_invocation="expected ${program}$(printf " '%q'" "${parsed_patterns[@]}")"
# Match the expected argument patterns to actual
# arguments.
for (( i=0; i<${#parsed_patterns[@]}; i++ )); do
pattern="${parsed_patterns[$i]}"
argument="${arguments[$i]}"
if [[ "$pattern" != "$argument" ]] && [[ "$pattern" != "*" ]] ; then
detail "$(printf "match failed at idx %d, expected '%q', got '%q'" $i "$pattern" "$argument")"
result=1
break
fi
done
# Error out with debugging for any extra parameters
for (( i=${#parsed_patterns[@]}; i<${#arguments[@]}; i++ )); do
argument="${arguments[$i]}"
detail "$(printf "unexpected argument at idx %d, got '%q'" $i "${arguments[$i]}")"
result=2
break
done
# If the arguments matched, evaluate the command
# in a subshell. Otherwise, log the failure.
if [ $result -eq 0 ] ; then
debug "running $command"
debug "command input is $input"
set +e
( eval "$command" <<< "$input" )
status="$?"
debug "command result was $status"
set -e
# eval "${_STUB_RESULT}"=$status
else
eval "${_STUB_RESULT}"=1
fi
fi
done < "${!_STUB_PLAN}"
if [ -n "${!_STUB_END}" ]; then
echo "${_STUB_DEBUG}"
debug "unstubbing"
if [ ! -f "${!_STUB_RUN}" ] ; then
debug "The stub for ${program} wasn't run"
exit 1
fi
# Clean up the run file.
rm -f "${!_STUB_RUN}"
# If the number of lines in the plan is larger than
# the requested index, we failed.
if [ $index -ge "${!_STUB_INDEX}" ]; then
eval "${_STUB_RESULT}"=1
fi
# Return the result.
exit "${!_STUB_RESULT}"
else
# If the requested index is larger than the number
# of lines in the plan file, we failed.
if [ "${!_STUB_INDEX}" -gt $index ]; then
debug "no plan row found"
eval "${_STUB_RESULT}"=1
fi
# Write out the run information.
{ echo "${_STUB_INDEX}=$((${!_STUB_INDEX} + 1))"
echo "${_STUB_RESULT}=${!_STUB_RESULT}"
} > "${!_STUB_RUN}"
debug "result $status"
exit "$status"
fi