#!/bin/sh
#
# Copyright 2018-2020 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
# This source code is licensed under the GNU General Public License version 2
# or later (GPLv2+) WITHOUT ANY WARRANTY.
set -eu
test -d assets && test -d test-2 \
|| { echo 'Run me from source-tree-like location'; exit 1; }
# $1=reference (can be '-' for stdin), $2=investigated
# alt.: wdiff, colordiff, ...
DIFF=${DIFF:-diff}
DIFFOPTS=${DIFFOPTS--u}
DIFFPAGER=${DIFFPAGER:-less -LRX}
# $1=schema, $2=validated
# alt.: jing -i
RNGVALIDATOR=${RNGVALIDATOR:-xmllint --noout --relaxng}
# $1=stylesheet, $2=source
# alt.: Xalan, saxon, sabcmd/Sablotron (note: only validates reliably with -B)
_xalan_wrapper() {
{ ${_XSLTPROCESSOR} "$2" "$1" 2>&1 >&3 \
| sed -e '/^Source tree node.*$/d' \
-e 's|^XSLT message: \(.*\) (Occurred.*)|\1|'; } 3>&- 3>&1 >&2
}
# Sablotron doesn't translate '-' file specification to stdin
# and limits the length of the output message
_sabcmd_wrapper() {
_sabw_sheet=${1:?}
_sabw_source=${2:?}
test "${_sabw_sheet}" != - || _sabw_sheet=/dev/stdin
test "${_sabw_source}" != - || _sabw_source=/dev/stdin
{ ${_XSLTPROCESSOR} "${_sabw_sheet}" "${_sabw_source}" 2>&1 >&3 \
| sed -e '/^Warning \[code:89\]/d' \
-e 's|^ xsl:message (\(.*\))$|\1|'; } 3>&- 3>&1 >&2
}
# filtered out message: https://bugzilla.redhat.com/show_bug.cgi?id=1577367
_saxon_wrapper() {
{ ${_XSLTPROCESSOR} "-xsl:$1" "-s:$2" -versionmsg:off 2>&1 >&3 \
| sed -e '/^Cannot find CatalogManager.properties$/d'; } 3>&- 3>&1 >&2
}
XSLTPROCESSOR=${XSLTPROCESSOR:-xsltproc --nonet}
_XSLTPROCESSOR=${XSLTPROCESSOR}
case "${XSLTPROCESSOR}" in
[Xx]alan*|*/[Xx]alan*) XSLTPROCESSOR=_xalan_wrapper;;
sabcmd*|*/sabcmd*) XSLTPROCESSOR=_sabcmd_wrapper;;
saxon*|*/saxon*) XSLTPROCESSOR=_saxon_wrapper;;
esac
HTTPPORT=${HTTPPORT:-8000} # Python's default
WEBBROWSER=${WEBBROWSER:-firefox}
tests= # test* names (should go first) here will become preselected default
#
# commons
#
emit_result() {
_er_howmany=${1:?} # how many errors (0/anything else incl. strings)
_er_subject=${2:?}
_er_prefix=${3-}
test -z "${_er_prefix}" || _er_prefix="${_er_prefix}: "
if test "${_er_howmany}" = 0; then
printf "%s%s finished OK\n" "${_er_prefix}" "${_er_subject}"
else
printf "%s%s encountered ${_er_howmany} errors\n" \
"${_er_prefix}" "${_er_subject}"
fi
}
emit_error() {
_ee_msg=${1:?}
printf "%s\n" "${_ee_msg}" >&2
}
# returns 1 + floor of base 2 logaritm for _lo0r_i in 1...255,
# or 0 for _lo0r_i = 0
log2_or_0_return() {
_lo0r_i=${1:?}
return $(((!(_lo0r_i >> 1) && _lo0r_i) * 1 \
+ (!(_lo0r_i >> 2) && _lo0r_i & (1 << 1)) * 2 \
+ (!(_lo0r_i >> 3) && _lo0r_i & (1 << 2)) * 3 \
+ (!(_lo0r_i >> 4) && _lo0r_i & (1 << 3)) * 4 \
+ (!(_lo0r_i >> 5) && _lo0r_i & (1 << 4)) * 5 \
+ (!(_lo0r_i >> 6) && _lo0r_i & (1 << 5)) * 6 \
+ (!(_lo0r_i >> 7) && _lo0r_i & (1 << 6)) * 7 \
+ !!(_lo0r_i >> 7) * 7 ))
}
# rough addition of two base 2 logarithms
log2_or_0_add() {
_lo0a_op1=${1:?}
_lo0a_op2=${2:?}
if test ${_lo0a_op1} -gt ${_lo0a_op2}; then
return ${_lo0a_op1}
elif test ${_lo0a_op2} -gt ${_lo0a_op1}; then
return ${_lo0a_op2}
elif test ${_lo0a_op1} -gt 0; then
return $((_lo0a_op1 + 1))
else
return ${_lo0a_op1}
fi
}
#
# test phases
#
# stdin: input file per line
test_browser() {
_tb_cleanref=0
_tb_serverpid=
while test $# -gt 0; do
case "$1" in
-r) _tb_cleanref=1;;
esac
shift
done
if ! read _tb_first; then
return 1
fi
cat >/dev/null 2>/dev/null # read out the rest
test -f assets/diffview.js \
|| curl -SsLo assets/diffview.js \
'https://raw.githubusercontent.com/prettydiff/prettydiff/2.2.8/lib/diffview.js'
{ which python3 >/dev/null 2>/dev/null \
&& { python3 -m http.server "${HTTPPORT}" -b 127.0.0.1 \
|| emit_error "Python3 HTTP server fail"; return; } \
|| emit_error 'Cannot run Python-based HTTP server' ; } &
_tb_serverpid=$!
${WEBBROWSER} "http://localhost:${HTTPPORT}/${_tb_first}" &
printf "When finished, just press Ctrl+C or kill %d, please\n" \
"${_tb_serverpid}"
wait
test "${_tb_cleanref}" -eq 0 || rm -f assets/diffview.js
}
# -r ... whether to remove referential files as well
# stdin: input file per line
test_cleaner() {
_tc_cleanref=0
while test $# -gt 0; do
case "$1" in
-r) _tc_cleanref=1;;
esac
shift
done
while read _tc_origin; do
_tc_origin=${_tc_origin%.*}
rm -f "${_tc_origin}.up" "${_tc_origin}.up.err"
rm -f "$(dirname "${_tc_origin}")/.$(basename "${_tc_origin}").up"
test ${_tc_cleanref} -eq 0 \
|| rm -f "${_tc_origin}.ref" "${_tc_origin}.ref.err"
done
}
# -a= ... action modifier to derive template name from (if any; enter/leave)
# -o= ... which conventional version to deem as the transform origin
test_selfcheck() {
_tsc_cleanref=0
_tsc_ret=0
_tsc_action=
_tsc_template=
_tsc_validator=
while test $# -gt 0; do
case "$1" in
-r) _tsc_cleanref=1;;
-a=*) _tsc_action="${1#-a=}";;
-o=*) _tsc_template="${1#-o=}";;
esac
shift
done
_tsc_validator="${_tsc_template:?}"
_tsc_validator="cibtr-${_tsc_validator%%.*}.rng"
_tsc_action=${_tsc_action:+-${_tsc_action}}
_tsc_template="upgrade-${_tsc_template}${_tsc_action}.xsl"
# alt. https://relaxng.org/relaxng.rng
_tsc_rng_relaxng=https://raw.githubusercontent.com/relaxng/relaxng.org/master/relaxng.rng
# alt. https://github.com/ndw/xslt-relax-ng/blob/master/1.0/xslt10.rnc
_tsc_rng_xslt=https://raw.githubusercontent.com/relaxng/jing-trang/master/eg/xslt.rng
case "${RNGVALIDATOR}" in
*xmllint*)
test -f "assets/$(basename "${_tsc_rng_relaxng}")" \
|| curl -SsLo "assets/$(basename "${_tsc_rng_relaxng}")" \
"${_tsc_rng_relaxng}"
test -f "assets/$(basename "${_tsc_rng_xslt}")" \
|| curl -SsLo "assets/$(basename "${_tsc_rng_xslt}")" \
"${_tsc_rng_xslt}"
test -f assets/xmlcatalog || >assets/xmlcatalog cat <<-EOF
EOF
RNGVALIDATOR=\
"eval env \"XML_CATALOG_FILES=/etc/xml/catalog $(pwd)/assets/xmlcatalog\"
${RNGVALIDATOR}"
# not needed
#_tsc_rng_relaxng="assets/$(basename "${_tsc_rng_relaxng}")"
#_tsc_rng_xslt="assets/$(basename "${_tsc_rng_xslt}")"
;;
esac
# check schema (sub-grammar) for custom transformation mapping alone;
if test -z "${_tsc_action}" \
&& ! ${RNGVALIDATOR} "${_tsc_rng_relaxng}" "${_tsc_validator}"; then
_tsc_ret=$((_tsc_ret + 1))
fi
# check the overall XSLT per the main grammar + said sub-grammar;
test -f "${_tsc_validator}" && _tsc_validator="xslt_${_tsc_validator}" \
|| _tsc_validator="${_tsc_rng_xslt}"
if ! ${RNGVALIDATOR} "${_tsc_validator}" "${_tsc_template}"; then
_tsc_ret=$((_tsc_ret + 1))
fi
test "${_tsc_cleanref}" -eq 0 \
|| rm -f assets/relaxng.rng assets/xslt.rng assets/xmlcatalog
log2_or_0_return ${_tsc_ret}
}
test_explanation() {
_tsc_template=
while test $# -gt 0; do
case "$1" in
-o=*) _tsc_template="upgrade-${1#-o=}.xsl";;
esac
shift
done
${XSLTPROCESSOR} upgrade-detail.xsl "${_tsc_template}"
}
# stdout: filename of the transformed file
test_runner_upgrade() {
_tru_template=${1:?}
_tru_source=${2:?} # filename
_tru_mode=${3:?} # extra modes wrt. "referential" outcome, see below
_tru_ref="${_tru_source%.*}.ref"
{ test "$((_tru_mode & (1 << 0)))" -ne 0 \
|| test -f "${_tru_ref}.err"; } \
&& _tru_ref_err="${_tru_ref}.err" || _tru_ref_err=/dev/null
_tru_target="${_tru_source%.*}.up"
_tru_target_err="${_tru_target}.err"
if test $((_tru_mode & (1 << 2))) -eq 0; then
${XSLTPROCESSOR} "${_tru_template}" "${_tru_source}" \
> "${_tru_target}" 2> "${_tru_target_err}" \
|| { _tru_ref=$?; echo "${_tru_target_err}"
return ${_tru_ref}; }
else
# when -B (deblanked outcomes handling) requested, we:
# - drop blanks from the source XML
# (effectively emulating pacemaker handling)
# - re-drop blanks from the XSLT outcome,
# which is compared with referential outcome
# processed with even greedier custom deblanking
# (extraneous inter-element whitespace like blank
# lines will not get removed otherwise, see lower)
xmllint --noblanks "${_tru_source}" \
| ${XSLTPROCESSOR} "${_tru_template}" - \
> "${_tru_target}" 2> "${_tru_target_err}" \
|| { _tru_ref=$?; echo "${_tru_target_err}"
return ${_tru_ref}; }
# reusing variable no longer needed
_tru_template="$(dirname "${_tru_target}")"
_tru_template="${_tru_template}/.$(basename "${_tru_target}")"
mv "${_tru_target}" "${_tru_template}"
${XSLTPROCESSOR} - "${_tru_template}" > "${_tru_target}" <<-EOF
EOF
fi
# only respond with the flags except for "-B", i.e., when both:
# - _tru_mode non-zero
# - "-B" in _tru_mode is zero (hence non-zero when flipped with XOR)
if test "$((_tru_mode * ((_tru_mode ^ (1 << 2)) & (1 << 2))))" -ne 0; then
if test $((_tru_mode & (1 << 0))) -ne 0; then
cp -a "${_tru_target}" "${_tru_ref}"
cp -a "${_tru_target_err}" "${_tru_ref_err}"
fi
if test $((_tru_mode & (1 << 1))) -ne 0; then
{ "${DIFF}" ${DIFFOPTS} "${_tru_source}" "${_tru_ref}" \
&& printf '\n(files match)\n'; } | ${DIFFPAGER} >&2
if test $? -ne 0; then
printf "\npager failure\n" >&2
return 1
fi
printf '\nIs comparison OK? ' >&2
if read _tru_answer &2; return 1;;
esac
else
return 1
fi
fi
elif test -f "${_tru_ref}" && test -e "${_tru_ref_err}"; then
{ test "$((_tru_mode & (1 << 2)))" -eq 0 && cat "${_tru_ref}" \
|| ${XSLTPROCESSOR} - "${_tru_ref}" <<-EOF
EOF
} \
| "${DIFF}" ${DIFFOPTS} - "${_tru_target}" >&2 \
&& "${DIFF}" ${DIFFOPTS} "${_tru_ref_err}" \
"${_tru_target_err}" >&2
if test $? -ne 0; then
emit_error "Outputs differ from referential ones"
echo "/dev/null"
return 1
fi
else
emit_error "Referential file(s) missing: ${_tru_ref}"
echo "/dev/null"
return 1
fi
echo "${_tru_target}"
}
test_runner_validate() {
_trv_schema=${1:?}
_trv_target=${2:?} # filename
if ! ${RNGVALIDATOR} "${_trv_schema}" "${_trv_target}" \
2>/dev/null; then
${RNGVALIDATOR} "${_trv_schema}" "${_trv_target}"
fi
}
# -a= ... action modifier completing template name (e.g. 2.10-(enter|leave))
# -o= ... which conventional version to deem as the transform origin
# -t= ... which conventional version to deem as the transform target
# -B
# -D
# -G ... see usage
# stdin: input file per line
test_runner() {
_tr_mode=0
_tr_ret=0
_tr_action=
_tr_schema_o=
_tr_schema_t=
_tr_target=
_tr_template=
while test $# -gt 0; do
case "$1" in
-a=*) _tr_action="${1#-a=}";;
-o=*) _tr_template="${1#-o=}"
_tr_schema_o="pacemaker-${1#-o=}.rng";;
-t=*) _tr_schema_t="pacemaker-${1#-t=}.rng";;
-G) _tr_mode=$((_tr_mode | (1 << 0)));;
-D) _tr_mode=$((_tr_mode | (1 << 1)));;
-B) _tr_mode=$((_tr_mode | (1 << 2)));;
esac
shift
done
_tr_template="upgrade-${_tr_action:-${_tr_template:?}}.xsl"
if ! test -f "${_tr_schema_o:?}" || ! test -f "${_tr_schema_t:?}"; then
emit_error "Origin and/or target schema missing, rerun make"
return 1
fi
while read _tr_origin; do
printf '%-60s' "${_tr_origin}... "
# pre-validate
if ! test_runner_validate "${_tr_schema_o}" "${_tr_origin}"; then
_tr_ret=$((_tr_ret + 1)); echo "E:pre-validate"; continue
fi
# upgrade
if ! _tr_target=$(test_runner_upgrade "${_tr_template}" \
"${_tr_origin}" "${_tr_mode}"); then
_tr_ret=$((_tr_ret + 1));
test -n "${_tr_target}" || break
echo "E:upgrade"
test -s "${_tr_target}" \
&& { echo ---; cat "${_tr_target}" || :; echo ---; }
continue
fi
# post-validate
if ! test_runner_validate "${_tr_schema_t}" "${_tr_target}"; then
_tr_ret=$((_tr_ret + 1)); echo "E:post-validate"; continue
fi
echo "OK"
done
log2_or_0_return ${_tr_ret}
}
#
# particular test variations
# -C
# -S
# -X
# -W ... see usage
# stdin: granular test specification(s) if any
#
test2to3() {
_t23_cleanopt=
_t23_pattern=
while read _t23_spec; do
_t23_spec=${_t23_spec%.xml}
_t23_spec=${_t23_spec%\*}
_t23_pattern="${_t23_pattern} -name ${_t23_spec}*.xml -o"
done
test -z "${_t23_pattern}" || _t23_pattern="( ${_t23_pattern%-o} )"
case " $* " in *\ -r\ *) _t23_cleanopt=-r; esac
find test-2 -name test-2 -o -type d -prune \
-o -name '*.xml' ${_t23_pattern} -print | env LC_ALL=C sort \
| { case " $* " in
*\ -C\ *) test_cleaner;;
*\ -S\ *) test_selfcheck -o=2.10 ${_t23_cleanopt};;
*\ -X\ *) test_explanation -o=2.10;;
*\ -W\ *) test_browser ${_t23_cleanopt};;
*) test_runner -o=2.10 -t=3.0 "$@" || return $?;;
esac; }
}
tests="${tests} test2to3"
test2to3enter() {
_t23e_cleanopt=
_t23e_pattern=
while read _t23e_spec; do
_t23e_spec=${_t23e_spec%.xml}
_t23e_spec=${_t23e_spec%\*}
_t23e_pattern="${_t23e_pattern} -name ${_t23e_spec}*.xml -o"
done
test -z "${_t23e_pattern}" || _t23e_pattern="( ${_t23e_pattern%-o} )"
case " $* " in *\ -r\ *) _t23e_cleanopt=-r; esac
find test-2-enter -name test-2-enter -o -type d -prune \
-o -name '*.xml' ${_t23e_pattern} -print | env LC_ALL=C sort \
| { case " $* " in
*\ -C\ *) test_cleaner;;
*\ -S\ *) test_selfcheck -a=enter -o=2.10 ${_t23e_cleanopt};;
*\ -W\ *) emit_result "not implemented" "option -W";;
*\ -X\ *) emit_result "not implemented" "option -X";;
*) test_runner -a=2.10-enter -o=2.10 -t=2.10 "$@" || return $?;;
esac; }
}
tests="${tests} test2to3enter"
test2to3leave() {
_t23l_cleanopt=
_t23l_pattern=
while read _t23l_spec; do
_t23l_spec=${_t23l_spec%.xml}
_t23l_spec=${_t23l_spec%\*}
_t23l_pattern="${_t23l_pattern} -name ${_t23l_spec}*.xml -o"
done
test -z "${_t23l_pattern}" || _t23l_pattern="( ${_t23l_pattern%-o} )"
case " $* " in *\ -r\ *) _t23l_cleanopt=-r; esac
find test-2-leave -name test-2-leave -o -type d -prune \
-o -name '*.xml' ${_t23l_pattern} -print | env LC_ALL=C sort \
| { case " $* " in
*\ -C\ *) test_cleaner;;
*\ -S\ *) test_selfcheck -a=leave -o=2.10 ${_t23l_cleanopt};;
*\ -W\ *) emit_result "not implemented" "option -W";;
*\ -X\ *) emit_result "not implemented" "option -X";;
*) test_runner -a=2.10-leave -o=3.0 -t=3.0 "$@" || return $?;;
esac; }
}
tests="${tests} test2to3leave"
test2to3roundtrip() {
_t23rt_cleanopt=
_t23rt_pattern=
while read _t23tr_spec; do
_t23rt_spec=${_t23rt_spec%.xml}
_t23rt_spec=${_t23rt_spec%\*}
_t23rt_pattern="${_t23rt_pattern} -name ${_t23rt_spec}*.xml -o"
done
test -z "${_t23rt_pattern}" || _t23rt_pattern="( ${_t23rt_pattern%-o} )"
case " $* " in *\ -r\ *) _t23rt_cleanopt=-r; esac
find test-2-roundtrip -name test-2-roundtrip -o -type d -prune \
-o -name '*.xml' ${_t23rt_pattern} -print | env LC_ALL=C sort \
| { case " $* " in
*\ -C\ *) test_cleaner;;
*\ -S\ *) test_selfcheck -a=roundtrip -o=2.10 ${_t23rt_cleanopt};;
*\ -W\ *) test_browser ${_t23rt_cleanopt};;
*\ -X\ *) emit_result "not implemented" "option -X";;
*) test_runner -a=2.10-roundtrip -o=2.10 -t=3.0 "$@" || return $?;;
esac; }
}
tests="${tests} test2to3roundtrip"
# -B
# -D
# -G ... see usage
cts_scheduler() {
_tcp_mode=0
_tcp_ret=0
_tcp_validatewith=
_tcp_schema_o=
_tcp_schema_t=
_tcp_template=
find ../cts/scheduler -name scheduler -o -type d -prune \
-o -name '*.xml' -print | env LC_ALL=C sort \
| { case " $* " in
*\ -C\ *) test_cleaner -r;;
*\ -S\ *) emit_result "not implemented" "option -S";;
*\ -W\ *) emit_result "not implemented" "option -W";;
*\ -X\ *) emit_result "not implemented" "option -X";;
*)
while test $# -gt 0; do
case "$1" in
-G) _tcp_mode=$((_tcp_mode | (1 << 0)));;
-D) _tcp_mode=$((_tcp_mode | (1 << 1)));;
-B) _tcp_mode=$((_tcp_mode | (1 << 2)));;
esac
shift
done
while read _tcp_origin; do
_tcp_validatewith=$(${XSLTPROCESSOR} - "${_tcp_origin}" <<-EOF
EOF
)
_tcp_schema_t=${_tcp_validatewith}
case "${_tcp_validatewith}" in
1) _tcp_schema_o=1.3;;
2) _tcp_schema_o=2.10;;
# only for gradual refinement as upgrade-2.10.xsl under
# active development, move to 3.x when schema v4 emerges
3) _tcp_schema_o=2.10
_tcp_schema_t=2;;
*) emit_error \
"need to skip ${_tcp_origin} (schema: ${_tcp_validatewith})"
continue;;
esac
_tcp_template="upgrade-${_tcp_schema_o}.xsl"
_tcp_schema_t="pacemaker-$((_tcp_schema_t + 1)).0.rng"
test "${_tcp_schema_o%%.*}" = "${_tcp_validatewith}" \
&& _tcp_schema_o="pacemaker-${_tcp_schema_o}.rng" \
|| _tcp_schema_o="${_tcp_schema_t}"
# pre-validate
if test "${_tcp_schema_o}" != "${_tcp_schema_t}" \
&& ! test_runner_validate "${_tcp_schema_o}" "${_tcp_origin}"; then
_tcp_ret=$((_tcp_ret + 1)); echo "E:pre-validate"; continue
fi
# upgrade
test "$((_tcp_mode & (1 << 0)))" -ne 0 \
|| ln -fs "$(pwd)/${_tcp_origin}" "${_tcp_origin%.*}.ref"
if ! _tcp_target=$(test_runner_upgrade "${_tcp_template}" \
"${_tcp_origin}" "${_tcp_mode}"); then
_tcp_ret=$((_tcp_ret + 1));
test -n "${_tcp_target}" || break
echo "E:upgrade"
test -s "${_tcp_target}" \
&& { echo ---; cat "${_tcp_target}" || :; echo ---; }
continue
fi
test "$((_tcp_mode & (1 << 0)))" -ne 0 \
|| rm -f "${_tcp_origin%.*}.ref"
# post-validate
if ! test_runner_validate "${_tcp_schema_t}" "${_tcp_target}"; then
_tcp_ret=$((_tcp_ret + 1)); echo "E:post-validate"; continue
fi
test "$((_tcp_mode & (1 << 0)))" -eq 0 \
|| mv "${_tcp_target}" "${_tcp_origin}"
done; log2_or_0_return ${_tcp_ret};;
esac; }
}
tests="${tests} cts_scheduler"
#
# "framework"
#
# option-likes ... options to be passed down
# argument-likes ... drives a test selection
test_suite() {
_ts_pass=
_ts_select=
_ts_select_full=
_ts_test_specs=
_ts_global_ret=0
_ts_ret=0
while test $# -gt 0; do
case "$1" in
-) printf '%s\n' 'waiting for tests specified at stdin...';
while read _ts_spec; do _ts_select="${_ts_spec}@$1"; done;;
-*) _ts_pass="${_ts_pass} $1";;
*) _ts_select_full="${_ts_select_full}@$1"
_ts_select="${_ts_select}@${1%%/*}";;
esac
shift
done
_ts_select="${_ts_select}@"
_ts_select_full="${_ts_select_full}@"
for _ts_test in ${tests}; do
_ts_test_specs=
while true; do
case "${_ts_select}" in
*@${_ts_test}@*)
_ts_test_specs="${_ts_select%%@${_ts_test}@*}"\
"@${_ts_select#*@${_ts_test}@}"
if test "${_ts_test_specs}" = @; then
_ts_select= # nothing left
else
_ts_select="${_ts_test_specs}"
fi
continue
;;
@) case "${_ts_test}" in test*) break;; esac # filter
;;
esac
test -z "${_ts_test_specs}" || break
continue 2 # move on to matching with next local test
done
_ts_test_specs=
while true; do
case "${_ts_select_full}" in
*@${_ts_test}/*)
_ts_test_full="${_ts_test}/${_ts_select_full#*@${_ts_test}/}"
_ts_test_full="${_ts_test_full%%@*}"
_ts_select_full="${_ts_select_full%%@${_ts_test_full}@*}"\
"@${_ts_select_full#*@${_ts_test_full}@}"
_ts_test_specs="${_ts_test_specs} ${_ts_test_full#*/}"
;;
*)
break
;;
esac
done
for _ts_test_spec in ${_ts_test_specs}; do
printf '%s\n' "${_ts_test_spec}"
done | "${_ts_test}" ${_ts_pass} || _ts_ret=$?
test ${_ts_ret} = 0 \
&& emit_result ${_ts_ret} "${_ts_test}" \
|| emit_result "at least 2^$((_ts_ret - 1))" "${_ts_test}"
log2_or_0_add ${_ts_global_ret} ${_ts_ret}
_ts_global_ret=$?
done
if test -n "${_ts_select#@}"; then
emit_error "Non-existing test(s):$(echo "${_ts_select}" \
| tr '@' ' ')"
log2_or_0_add ${_ts_global_ret} 1 || _ts_global_ret=$?
fi
return ${_ts_global_ret}
}
# NOTE: big letters are dedicated for per-test-set behaviour,
# small ones for generic/global behaviour
usage() {
printf \
'%s\n%s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n' \
"usage: $0 [-{B,C,D,G,S,X}]* \\" \
" [-|{${tests## }}*]" \
"- when no suites (arguments) provided, \"test*\" ones get used" \
"- with '-' suite specification the actual ones grabbed on stdin" \
"- use '-B' to run validate-only check suppressing blanks first" \
"- use '-C' to only cleanup ephemeral byproducts" \
"- use '-D' to review originals vs. \"referential\" outcomes" \
"- use '-G' to generate \"referential\" outcomes" \
"- use '-S' for template self-check (requires net access)" \
"- use '-W' to run browser-based, on-the-fly diff'ing test drive" \
"- use '-X' to show explanatory details about the upgrade" \
"- some modes (e.g. -{S,W}) take also '-r' for cleanup afterwards" \
"- test specification can be granular, e.g. 'test2to3/022'"
printf \
'\n%s\n %s\n %s\n %s\n %s\n %s\n %s\n %s\n' \
'environment variables affecting the run + default/current values:' \
"- DIFF (${DIFF}): tool to compute and show differences of 2 files" \
"- DIFFOPTS (${DIFFOPTS}): options to the above tool" \
"- DIFFPAGER (${DIFFPAGER}): possibly accompanying the above tool" \
"- RNGVALIDATOR (${RNGVALIDATOR}): RelaxNG validator" \
"- XSLTPROCESSOR (${_XSLTPROCESSOR}): XSLT 1.0 capable processor" \
"- HTTPPORT (${HTTPPORT}): port used by test drive HTTP server run" \
"- WEBBROWSER (${WEBBROWSER}): used for in-browser test drive"
}
main() {
_main_pass=
_main_bailout=0
_main_ret=0
while test $# -gt 0; do
case "$1" in
-h) usage; exit;;
-C|-G|-S|-X) _main_bailout=1;;
esac
_main_pass="${_main_pass} $1"
shift
done
test_suite ${_main_pass} || _main_ret=$?
test ${_main_bailout} -ne 0 \
|| test_suite -C ${_main_pass} >/dev/null || true
test ${_main_ret} = 0 && emit_result ${_main_ret} "Overall suite" \
|| emit_result "at least 2^$((_main_ret - 1))" "Overall suite"
return ${_main_ret}
}
main "$@"