5 # This test suite uses the following terminology:
6 # - scenario: a series of commands to test. Each must be in a
7 # separate file, and must be completely self-contained
8 # (other than the variables listed below).
9 # - check: a series of commands that produces an exit code which
10 # the test suite should check. A scenario may contain any
15 # The main function is scenario_runner(scenario_filename), which
16 # takes a scenario file as the argument, and runs a
18 # function which was defined in that file.
22 # Wherever possible, this suite uses local variables and
23 # explicitly-passed arguments, with the following exceptions:
24 # - s_basename: this is the basename for the scenario's temporary
26 # - s_val_basename: this is the basename for the scenario's
28 # - s_count: this is the count of the scenario's checks (so that
29 # each check can have distinct files).
30 # - s_retval: this is the overall exit code of the scenario.
31 # - c_exitfile: this contains the exit code of each check.
32 # - c_valgrind_min: this is the minimum value of USE_VALGRIND
33 # which will enable valgrind checking for this check.
34 # - c_valgrind_cmd: this is the valgrind command (including
35 # appropriate log file) if necessary, or is "" otherwise.
41 out_valgrind
="tests-valgrind"
42 valgrind_suppressions
="${out_valgrind}/suppressions"
43 valgrind_suppressions_log
="${out_valgrind}/suppressions.pre"
45 # Keep the user-specified ${USE_VALGRIND}, or initialize to 0.
46 USE_VALGRIND
=${USE_VALGRIND:-0}
48 # A non-zero value unlikely to be used as an exit code by the programs being
50 valgrind_exit_code
=108
52 ## prepare_directories():
53 # Delete any old directories, and create new ones as necessary. Must be run
54 # after check_optional_valgrind().
55 prepare_directories
() {
56 # Clean up previous directories.
57 if [ -d "${out}" ]; then
60 if [ -d "${out_valgrind}" ]; then
61 rm -rf ${out_valgrind}
64 # Make new directories.
66 if [ "$USE_VALGRIND" -gt 0 ]; then
71 ## find_system (cmd, args...):
72 # Looks for ${cmd} in the $PATH, and ensure that it supports ${args}.
77 system_binary
=`command -v ${cmd}`
78 if [ -z "${system_binary}" ]; then
80 printf "System ${cmd} not found.\n" 1>&2
81 # If the command exists, check it ensures the ${args}.
82 elif ${cmd_with_args} 2>&1 >/dev
/null | \
83 grep -qE "(invalid|illegal) option"; then
85 printf "Cannot use system ${cmd}; does not" 1>&2
86 printf " support necessary arguments.\n" 1>&2
88 echo "${system_binary}"
91 ## check_optional_valgrind ():
92 # Return a $USE_VALGRIND variable defined; if it was previously defined and
93 # was greater than 0, then check that valgrind is available in the $PATH.
94 check_optional_valgrind
() {
95 if [ "$USE_VALGRIND" -gt 0 ]; then
96 # Look for valgrind in $PATH.
97 if ! command -v valgrind
>/dev
/null
2>&1; then
98 printf "valgrind not found\n" 1>&2
104 ## ensure_valgrind_suppresssion (potential_memleaks_binary):
105 # Runs the ${potential_memleaks_binary} through valgrind, keeping
106 # track of any apparent memory leak in order to suppress reporting
107 # those leaks when testing other binaries.
108 ensure_valgrind_suppression
() {
109 potential_memleaks_binary
=$1
111 # Quit if we're not using valgrind.
112 if [ ! "$USE_VALGRIND" -gt 0 ]; then
115 printf "Generating valgrind suppressions... "
117 # Run valgrind on the binary, sending it a "\n" so that
118 # a test which uses STDIN will not wait for user input.
119 printf "\n" |
(valgrind
--leak-check=full
--show-leak-kinds=all \
120 --gen-suppressions=all \
121 --log-file=${valgrind_suppressions_log} \
122 ${potential_memleaks_binary})
124 # Strip out useless parts from the log file, as well as
125 # removing references to the main and "pl_*" ("potential
126 # loss") functions so that the suppressions can apply to
128 (grep -v "^==" ${valgrind_suppressions_log} \
129 |
grep -v " fun:pl_" - \
130 |
grep -v " fun:main" - \
131 > ${valgrind_suppressions} )
134 rm -f ${valgrind_suppressions_log}
138 ## setup_check_variables ():
139 # Set up the "check" variables ${c_exitfile} and ${c_valgrind_cmd}, the
140 # latter depending on the previously-defined ${c_valgrind_min}.
141 # Advances the number of checks ${s_count} so that the next call to this
142 # function will set up new filenames.
143 setup_check_variables
() {
144 # Set up the "exit" file.
145 c_exitfile
="${s_basename}-`printf %02d ${s_count}`.exit"
147 # If we don't have a suppressions file, don't try to use it.
148 if [ ! -e ${valgrind_suppressions} ]; then
149 valgrind_suppressions
=/dev
/null
152 # Set up the valgrind command if $USE_VALGRIND is greater
153 # than or equal to ${valgrind_min}; otherwise, produce an
154 # empty string. Using --error-exitcode means that if
155 # there is a serious problem (such that scrypt calls
156 # exit(1)) *and* a memory leak, the test suite reports an
157 # exit value of ${valgrind_exit_code}. However, if there
158 # is a serious problem but no memory leak, we still
159 # receive a non-zero exit code. The most important thing
160 # is that we only receive an exit code of 0 if both the
161 # program and valgrind are happy.
162 if [ "$USE_VALGRIND" -ge "${c_valgrind_min}" ]; then
163 val_logfilename
=${s_val_basename}-`printf %02d ${s_count}`.log
164 c_valgrind_cmd
="valgrind \
165 --log-file=${val_logfilename} \
166 --leak-check=full --show-leak-kinds=all \
167 --errors-for-leak-kinds=all \
168 --suppressions=${valgrind_suppressions} \
169 --error-exitcode=${valgrind_exit_code} "
174 # Advances the number of checks.
175 s_count
=$
((s_count
+ 1))
178 ## get_val_logfile (val_basename, exitfile):
179 # Return the valgrind logfile corresponding to ${exitfile}.
183 num
=`echo "${exitfile}" | rev | cut -c 1-7 | rev | cut -c 1-2 `
184 echo "${val_basename}-${num}.log"
187 ## notify_success_or_fail (log_basename, val_log_basename):
188 # Examine all "exit code" files beginning with ${log_basename} and
189 # print "SUCCESS!" or "FAILED!" as appropriate. If the test failed
190 # with the code ${valgrind_exit_code}, output the appropriate
191 # valgrind logfile to stdout.
192 notify_success_or_fail
() {
196 # Check each exitfile.
197 for exitfile
in `ls ${log_basename}-*.exit | sort`; do
198 ret
=`cat ${exitfile}`
199 if [ "${ret}" -lt 0 ]; then
203 if [ "${ret}" -gt 0 ]; then
206 if [ "${ret}" -eq "${valgrind_exit_code}" ]; then
207 val_logfilename
=$
( get_val_logfile \
208 ${val_log_basename} ${exitfile} )
209 cat ${val_logfilename}
219 ## scenario_runner (scenario_filename):
220 # Runs a test scenario from ${scenario_filename}.
223 basename=`basename ${scenario_filename} .sh`
224 printf " ${basename}... " 1>&2
226 # Initialize "scenario" and "check" variables.
227 s_basename
=${out}/${basename}
228 s_val_basename
=${out_valgrind}/${basename}
234 # Load scenario_cmd() from the scenario file.
236 .
${scenario_filename}
237 if ! command -v scenario_cmd
1>/dev
/null
; then
238 printf "ERROR: scenario_cmd() is not defined in\n"
239 printf " ${scenario_filename}\n"
243 # Run the scenario command.
246 # Print PASS or FAIL, and return result.
248 notify_success_or_fail
${s_basename} ${s_val_basename}
253 ## run_scenarios (scenario_filenames):
254 # Runs all scenarios matching ${scenario_filenames}.
256 printf -- "Running tests\n"
257 printf -- "-------------\n"
258 scenario_filenames
=$@
259 for scenario
in ${scenario_filenames}; do
260 # We can't call this function with $( ... ) because we
261 # want to allow it to echo values to stdout.
262 scenario_runner
${scenario}
264 if [ ${retval} -gt 0 ]; then