exist
exist copied to clipboard
[BUG] XQsuite `%test:assertEquals` not correctly parsed as XML
Describe the bug
When writing an XQsuite test designed to test some XML element content, the content of the %test:assertEquals string is not parsed as XML, as promised by the documentation.
Expected behavior From the XQsuite assertEquals documentation:
If the sequence returned by the function contains one or more XML elements, they will be normalized (ignorable whitespace is stripped). The assertion argument is then parsed into XML and the two node trees are compared using the deep-equals function.
I would expect the xquery below to return a passed test; it currently fails.
To Reproduce
xquery version "3.1";
import module namespace test="http://exist-db.org/xquery/xqsuite"
at "resource:org/exist/xquery/lib/xqsuite/xqsuite.xql";
declare namespace output = "http://www.w3.org/2010/xslt-xquery-serialization";
declare option output:indent "yes";
declare
%test:assertEquals('
<span type="xml">Success!</span>
')
function local:test-nodes() as element(span) {
<span type="xml">Success!</span>
};
test:suite(local:test-nodes#0)
Context (please always complete the following information):
- OS: Ubuntu 20.04.4 LTS (GNU/Linux 5.4.0-1058-kvm x86_64)
- eXist-db version: 6.1.0-SNAPSHOT
Additional context
- How is eXist-db installed? Docker
- Any custom changes in e.g.
conf.xml? Nope
@yamahito %test:assertEquals('<span type="xml">Success!</span>') works.
So the problem is the whitespace in assertEquals?
It seems unhelpful that the whitespace is normalized for the output of the tested function, but not the value in asserts to be parsed...
I believe this is a limitation of the use of function annotations. But correct me, if I am wrong.
It looks to me that the parser is incorrectly configured around whitespace for parsing the content of annotations.
XQSuite docs (https://exist-db.org/exist/apps/doc/xqsuite) say:
The assertion argument is then parsed into XML and the two node trees are compared using the deep-equals function.
However, looking at the XQSuite code, it seems that the output is normalised and then serialized to a string, and then that is compared with the input. The input is never parsed into a node tree. See: https://github.com/eXist-db/exist/blob/develop/exist-core/src/main/resources/org/exist/xquery/lib/xqsuite/xqsuite.xql#L740-L754
When strings (containing XML) are parsed into a DOM (node tree), then the XML spec is quite prescriptive about when and how white-space normalisation should be performed.
So it seems likely XQSuite needs a bit of adjustment to how it handles and compares expected vs actual results for XML.
Isn't it cast to a node type on line 747?
I think part of the problem that @line-o pointed out in the slack channel is that, even if it is parsed into a node tree, the whitespace nodes mean that it isn't deep equaled. The $normValue isn't normalized in the same way that the $normResult is, so comparing the two will often behave unexpectedly.
Perhaps something like:
declare %private function test:equals($value as item(), $result as item()) as xs:boolean {
let $normResult :=
typeswitch ($result)
case node() return
test:normalize($result)
default return
$result
let $normValue :=
typeswitch ($result)
case node() return
test:cast-to-type($value, $result) => test:normalize()
default return
test:cast-to-type($value, $result)
return
typeswitch ($normResult)
case node() return
deep-equal($normValue, $normResult)
default return
$normResult eq $normValue
};
(I'll try to make a PR at some point)