summaryrefslogtreecommitdiffstats
path: root/xml/regression.sh
diff options
context:
space:
mode:
Diffstat (limited to 'xml/regression.sh')
-rwxr-xr-xxml/regression.sh782
1 files changed, 782 insertions, 0 deletions
diff --git a/xml/regression.sh b/xml/regression.sh
new file mode 100755
index 0000000..7269779
--- /dev/null
+++ b/xml/regression.sh
@@ -0,0 +1,782 @@
+#!/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
+ <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog">
+ <rewriteURI uriStartString="$(dirname "${_tsc_rng_relaxng}")"
+ rewritePrefix="file://$(pwd)/assets"/>
+ <rewriteURI uriStartString="$(dirname "${_tsc_rng_xslt}")"
+ rewritePrefix="file://$(pwd)/assets"/>
+ </catalog>
+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
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/>
+ <xsl:template match="@*|*|comment()|processing-instruction()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+ <xsl:template match="text()">
+ <xsl:value-of select="normalize-space(.)"/>
+ </xsl:template>
+ </xsl:stylesheet>
+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 </dev/tty; then
+ case "${_tru_answer}" in
+ y|yes) ;;
+ *) echo "Answer not 'y' nor 'yes'" >&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
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="xml" encoding="UTF-8" omit-xml-declaration="yes"/>
+ <xsl:template match="@*|*|comment()|processing-instruction()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+ <xsl:template match="text()">
+ <xsl:value-of select="normalize-space(.)"/>
+ </xsl:template>
+ </xsl:stylesheet>
+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
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+ <xsl:output method="text" encoding="UTF-8"/>
+ <xsl:template match="/">
+ <xsl:choose>
+ <xsl:when test="starts-with(cib/@validate-with, 'pacemaker-')">
+ <xsl:variable name="Version" select="substring-after(cib/@validate-with, 'pacemaker-')"/>
+ <xsl:choose>
+ <xsl:when test="contains(\$Version, '.')">
+ <xsl:value-of select="substring-before(\$Version, '.')"/>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="cib/@validate-with"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:when>
+ <xsl:otherwise>
+ <xsl:value-of select="cib/@validate-with"/>
+ </xsl:otherwise>
+ </xsl:choose>
+ </xsl:template>
+ </xsl:stylesheet>
+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 "$@"