exist icon indicating copy to clipboard operation
exist copied to clipboard

[BUG] XQsuite `%test:assertEquals` not correctly parsed as XML

Open yamahito opened this issue 3 years ago • 8 comments

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 avatar Apr 11 '22 09:04 yamahito

@yamahito %test:assertEquals('<span type="xml">Success!</span>') works.

line-o avatar Apr 11 '22 09:04 line-o

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...

yamahito avatar Apr 11 '22 09:04 yamahito

I believe this is a limitation of the use of function annotations. But correct me, if I am wrong.

line-o avatar Apr 11 '22 10:04 line-o

It looks to me that the parser is incorrectly configured around whitespace for parsing the content of annotations.

adamretter avatar Apr 11 '22 13:04 adamretter

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.

adamretter avatar Apr 11 '22 18:04 adamretter

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.

yamahito avatar Apr 12 '22 16:04 yamahito

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
};

yamahito avatar Apr 12 '22 16:04 yamahito

(I'll try to make a PR at some point)

yamahito avatar Apr 12 '22 16:04 yamahito