]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Use xmllint in system tests inspecting XML data
authorMichał Kępień <michal@isc.org>
Sat, 25 Oct 2025 05:37:48 +0000 (07:37 +0200)
committerMichał Kępień <michal@isc.org>
Sat, 25 Oct 2025 06:01:46 +0000 (08:01 +0200)
Inspecting XML data using sed and grep is error-prone, overly lax in
some ways, overly strict in others, and neither accurate nor expressive.
Use xmllint and XPath expressions for inspecting XML data in the
"statistics", "statschannel", and "synthfromdnssec" system tests to
address these deficiencies.

(cherry picked from commit 5872000d9ee352f1ab23c51a0cc537fb5546b603)

bin/tests/system/statistics/tests.sh
bin/tests/system/statschannel/tests.sh
bin/tests/system/synthfromdnssec/tests.sh

index a840d9e27b0a4e709b7a0770a8672c3f931907dc..a0364057c1e7e4dbda1501297916384db0c79b2e 100644 (file)
@@ -150,11 +150,11 @@ n=$((n + 1))
 
 ret=0
 echo_i "checking that zones with slash are properly shown in XML output ($n)"
-if $FEATURETEST --have-libxml2 && [ -x ${CURL} ]; then
+if $FEATURETEST --have-libxml2 && [ -x "${CURL}" ] && [ -x "${XMLLINT}" ]; then
   ${CURL} http://10.53.0.1:${EXTRAPORT1}/xml/v3/zones >curl.out.${n} 2>/dev/null || ret=1
-  grep '<zone name="32/1.0.0.127-in-addr.example" rdataclass="IN">' curl.out.${n} >/dev/null || ret=1
+  test -n "$("$XMLLINT" --xpath '/statistics/views/view[@name="_default"]/zones/zone[@name="32/1.0.0.127-in-addr.example"]' curl.out.${n})" || ret=1
 else
-  echo_i "skipping test as libxml2 and/or curl was not found"
+  echo_i "skipping test as libxml2 and/or curl and/or xmllint was not found"
 fi
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
@@ -162,11 +162,11 @@ n=$((n + 1))
 
 ret=0
 echo_i "checking that zones return their type ($n)"
-if $FEATURETEST --have-libxml2 && [ -x ${CURL} ]; then
+if $FEATURETEST --have-libxml2 && [ -x "${CURL}" ] && [ -x "${XMLLINT}" ]; then
   ${CURL} http://10.53.0.1:${EXTRAPORT1}/xml/v3/zones >curl.out.${n} 2>/dev/null || ret=1
-  grep '<zone name="32/1.0.0.127-in-addr.example" rdataclass="IN"><type>primary</type>' curl.out.${n} >/dev/null || ret=1
+  test -n "$("$XMLLINT" --xpath '/statistics/views/view[@name="_default"]/zones/zone[@name="32/1.0.0.127-in-addr.example"]/type[text()="primary"]' curl.out.${n})" || ret=1
 else
-  echo_i "skipping test as libxml2 and/or curl was not found"
+  echo_i "skipping test as libxml2 and/or curl and/or xmllint was not found"
 fi
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
@@ -230,23 +230,23 @@ n=$((n + 1))
 
 ret=0
 echo_i "checking bind9.xml socket statistics ($n)"
-if $FEATURETEST --have-libxml2 && [ -e stats.xml.out ] && [ -x "${XSLTPROC}" ]; then
+if $FEATURETEST --have-libxml2 && [ -e stats.xml.out ] && [ -x "${XSLTPROC}" ] && [ -x "${XMLLINT}" ]; then
   # Socket statistics (expect no errors)
-  grep "<counter name=\"TCP4AcceptFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP4BindFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP4ConnFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP4OpenFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP4RecvErr\">0</counter>" stats.xml.out >/dev/null || ret=1
-  # grep "<counter name=\"TCP4SendErr\">0</counter>" stats.xml.out >/dev/null || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP4AcceptFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP4BindFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP4ConnFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP4OpenFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP4RecvErr" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  # [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP4SendErr" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
 
-  grep "<counter name=\"TCP6AcceptFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP6BindFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP6ConnFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP6OpenFail\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP6RecvErr\">0</counter>" stats.xml.out >/dev/null || ret=1
-  grep "<counter name=\"TCP6SendErr\">0</counter>" stats.xml.out >/dev/null || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP6AcceptFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP6BindFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP6ConnFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP6OpenFail" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP6RecvErr" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
+  [ "$("$XMLLINT" --xpath 'count(/statistics/server/counters[@type="sockstat"]/counter[@name="TCP6SendErr" and text()="0"])' stats.xml.out)" -eq 1 ] || ret=1
 else
-  echo_i "skipping test as libxml2 and/or stats.xml.out file and/or xsltproc was not found"
+  echo_i "skipping test as libxml2 and/or stats.xml.out file and/or xsltproc and/or xmllint was not found"
 fi
 if [ $ret != 0 ]; then echo_i "failed"; fi
 status=$((status + ret))
index c535e85de21715130520aca53dd8fca5032c2ba7..8a343fe1b53187abc2771cd630223fcfb8dd1ff6 100644 (file)
@@ -715,22 +715,19 @@ status=$((status + ret))
 n=$((n + 1))
 
 _wait_for_transfers() {
-  if [ "$PERL_XML" ]; then
+  if [ "$PERL_XML" ] && [ -x "$XMLLINT" ]; then
     getxfrins xml x$n || return 1
 
-    # XML is encoded in one line, use awk to separate each transfer
-    # with a newline
-
     # We expect 4 transfers
-    count=$(awk '{ gsub("<xfrin ", "\n<xfrin ") } 1' xfrins.xml.x$n | grep -c -E '<state>(Zone Transfer Request|First Data|Receiving AXFR Data)</state>')
+    count=$("$XMLLINT" --xpath 'count(/statistics/views/view[@name="_default"]/xfrins/xfrin)' xfrins.xml.x$n)
     if [ $count != 4 ]; then return 1; fi
 
     # We expect 3 of 4 to be retransfers
-    count=$(awk '{ gsub("<xfrin ", "\n<xfrin ") } 1' xfrins.xml.x$n | grep -c -F '<firstrefresh>No</firstrefresh>')
+    count=$("$XMLLINT" --xpath 'count(/statistics/views/view[@name="_default"]/xfrins/xfrin[firstrefresh[text()="No"]])' xfrins.xml.x$n)
     if [ $count != 3 ]; then return 1; fi
 
     # We expect 1 of 4 to be a new transfer
-    count=$(awk '{ gsub("<xfrin ", "\n<xfrin ") } 1' xfrins.xml.x$n | grep -c -F '<firstrefresh>Yes</firstrefresh>')
+    count=$("$XMLLINT" --xpath 'count(/statistics/views/view[@name="_default"]/xfrins/xfrin[firstrefresh[text()="Yes"]])' xfrins.xml.x$n)
     if [ $count != 1 ]; then return 1; fi
   fi
 
index 7d011ce7e32470ba305df80e7d29d4f9952f127d..2d45e5ba634c6bcb855147b84b464a31e4d23caf 100644 (file)
@@ -715,7 +715,7 @@ for ns in 2 4 5 6; do
     status=$((status + ret))
   done
 
-  if ${FEATURETEST} --have-libxml2 && [ -x "${CURL}" ]; then
+  if ${FEATURETEST} --have-libxml2 && [ -x "${CURL}" ] && [ -x "${XMLLINT}" ]; then
     echo_i "getting XML statisistcs for (synth-from-dnssec ${description};) ($n)"
     ret=0
     xml=xml.out$n
@@ -726,10 +726,9 @@ for ns in 2 4 5 6; do
 
     echo_i "check XML for 'CoveringNSEC' with (synth-from-dnssec ${description};) ($n)"
     ret=0
-    counter=$(sed -n 's;.*<view name="_default">.*\(<counter name="CoveringNSEC">[0-9]*</counter>\).*</view><view.*;\1;gp' $xml)
-    count=$(echo "$counter" | grep CoveringNSEC | wc -l)
+    count=$("${XMLLINT}" --xpath 'count(/statistics/views/view[@name="_default"]/counters[@type="cachestats"]/counter[@name="CoveringNSEC"])' $xml)
     test $count = 1 || ret=1
-    zero=$(echo "$counter" | grep ">0<" | wc -l)
+    zero=$("${XMLLINT}" --xpath 'count(/statistics/views/view[@name="_default"]/counters[@type="cachestats"]/counter[@name="CoveringNSEC" and text()="0"])' $xml)
     if [ ${synth} = yes ]; then
       test $zero = 0 || ret=1
     else
@@ -741,10 +740,9 @@ for ns in 2 4 5 6; do
 
     echo_i "check XML for 'CacheNSECNodes' with (synth-from-dnssec ${description};) ($n)"
     ret=0
-    counter=$(sed -n 's;.*<view name="_default">.*\(<counter name="CacheNSECNodes">[0-9]*</counter>\).*</view><view.*;\1;gp' $xml)
-    count=$(echo "$counter" | grep CacheNSECNodes | wc -l)
+    count=$("${XMLLINT}" --xpath 'count(/statistics/views/view[@name="_default"]/counters[@type="cachestats"]/counter[@name="CacheNSECNodes"])' $xml)
     test $count = 1 || ret=1
-    zero=$(echo "$counter" | grep ">0<" | wc -l)
+    zero=$("${XMLLINT}" --xpath 'count(/statistics/views/view[@name="_default"]/counters[@type="cachestats"]/counter[@name="CacheNSECNodes" and text()="0"])' $xml)
     if [ ${ad} = yes ]; then
       test $zero = 0 || ret=1
     else
@@ -763,11 +761,10 @@ for ns in 2 4 5 6; do
 
       echo_i "check XML for '$synthesized}' with (synth-from-dnssec ${description};) ($n)"
       ret=0
-      if [ ${synth} = yes ]; then
-        grep '<counter name="'$synthesized'">'$count'</counter>' $xml >/dev/null || ret=1
-      else
-        grep '<counter name="'$synthesized'">'0'</counter>' $xml >/dev/null || ret=1
+      if [ ${synth} != yes ]; then
+        count=0
       fi
+      test $("${XMLLINT}" --xpath '/statistics/server/counters[@type="nsstat"]/counter[@name="'"${synthesized}"'"]/text()' $xml) -eq $count || ret=1
       n=$((n + 1))
       if [ $ret != 0 ]; then echo_i "failed"; fi
       status=$((status + ret))