eXist-db / exist

eXist Native XML Database and Application Platform
https://exist-db.org
GNU Lesser General Public License v2.1
421 stars 179 forks source link

[BUG] XQSuite test failure leads to other test failing #4538

Open line-o opened 2 years ago

line-o commented 2 years ago

Describe the bug

While investigating issues with typed fields specified in collection.xconf I came across an issue where calls to ft:field($node, $field, "xs:integer" always returns 1 instead of the integer value stored in the index. This behaviour is dependent on two things:

  1. another failing test
  2. the order of test functions within the source module

The above can be due to leaky test encapsulation or issues within the Lucene implementation or a combination of both.

Expected behavior

The tests to either fail or pass regardless of other tests that are executed before or after it.

To Reproduce

  1. Store the module inside the database as test.xqm
  2. open eXide
  3. open the stored module2.
  4. select XQuery>Run as test from the main menu
  5. the result shows 2 failing tests with
       <testcase name="lf-list-values:all-sorted-by-text-ft-field-fail" class="lf-list-values:all-sorted-by-text-ft-field-fail">
            <failure message="assertEquals failed." type="failure-error-code-1">-2 100 11 -23 0 1 -1</failure>
            <output>1 1 1 1 1 1 1</output>
        </testcase>
        <testcase name="lf-list-values:all-sorted-by-text-hof" class="lf-list-values:all-sorted-by-text-hof">
            <failure message="assertEquals failed." type="failure-error-code-1">-2 100 11 -23 0 1 -1</failure>
            <output>1 1 1 1 1 1 1</output>
        </testcase>
  6. uncomment the pending annotation for test lf-list-values:all-sorted-by-text-ft-field-fail in line 153
  7. select XQuery>Run as test from the main menu again2.
  8. note how all tests now pass including lf-list-values:all-sorted-by-text-hof

By modifying the order of test cases other tests will fail along with lf-list-values:all-sorted-by-text-ft-field-fail. I observed that even the number of failing tests varies (2-3).

xquery version "3.1";

module namespace lf-list-values="http://exist-db.org/xquery/lucene-field-list-values";

declare namespace test="http://exist-db.org/xquery/xqsuite";

declare variable $lf-list-values:test-collection-name := "lucene-field-list-values";
declare variable $lf-list-values:base-path := "/db/";
declare variable $lf-list-values:conf-path := "/db/system/config" || $lf-list-values:base-path;

declare variable $lf-list-values:test-path := $lf-list-values:base-path || $lf-list-values:test-collection-name;
declare variable $lf-list-values:test-conf-path := $lf-list-values:conf-path || $lf-list-values:test-collection-name;

declare variable $lf-list-values:data :=
<data>
  <item n="1">f</item>
  <item n="-1">g</item>
  <item n="11">c</item>
  <item n="0">e</item>
  <item n="-23">d</item>
  <item n="-2">a</item>
  <item n="100">b</item>
 </data>
;

declare variable $lf-list-values:xconf :=
<collection xmlns="http://exist-db.org/collection-config/1.0">
    <index xmlns:xs="http://www.w3.org/2001/XMLSchema">
        <lucene>
            <text qname="item">
                <field name="order" expression="@n" type="xs:integer" />
            </text>
        </lucene>
    </index>
</collection>
;

declare
    %test:setUp
function lf-list-values:setup() {
    let $testCol := xmldb:create-collection($lf-list-values:base-path, $lf-list-values:test-collection-name)
    let $indexCol := xmldb:create-collection($lf-list-values:conf-path, $lf-list-values:test-collection-name)
    return (
        xmldb:store($lf-list-values:test-path, "test.xml", $lf-list-values:data),
        xmldb:store($lf-list-values:test-conf-path, "collection.xconf", $lf-list-values:xconf),
        xmldb:reindex($lf-list-values:test-path)
    )
};

declare
    %test:tearDown
function lf-list-values:tearDown() {
    xmldb:remove($lf-list-values:test-path),
    xmldb:remove($lf-list-values:test-conf-path)
};

declare %private
function lf-list-values:list-field-values (
    $collection as xs:string,
    $node-name as xs:QName,
    $field as xs:string,
    $type as xs:string
) as item()* {
    lf-list-values:list-items-with-field($collection, $node-name, $field)
    ! ft:field(., $field, $type)
};

declare %private
function lf-list-values:list-items-with-field (
    $collection as xs:string,
    $node-name as xs:QName,
    $field as xs:string
) as item()* {
    collection($collection)//*
    [node-name() eq $node-name]
    [
        ft:query(., $field || ":*", map{ 
            "leading-wildcard": "yes",
            "fields": $field
        })
    ]
};

declare
    %test:assertEquals("-23", "-2", "-1", "0", "1", "11", "100")
function lf-list-values:all-returned() {
    lf-list-values:list-field-values(
        $lf-list-values:test-path,
        xs:QName("item"),
        "order",
        "xs:integer"
    )
    => sort()
};

declare
    %test:assertEquals("-2", "100", "11", "-23", "0", "1", "-1")
function lf-list-values:all-sorted-by-text-flwor() {
    let $hits := 
        collection($lf-list-values:test-path)//item
        [
            ft:query(., "order:*", map{ 
                "leading-wildcard": "yes",
                "fields": "order"
            })
        ]

    for $i in $hits
    let $t := $i/text()
    order by $t
    return ft:field($i, "order", "xs:integer")
};

declare
    %test:assertEquals("-2", "100", "11", "-23", "0", "1", "-1")
function lf-list-values:all-sorted-by-array-text() {
    (
        lf-list-values:list-items-with-field(
            $lf-list-values:test-path,
            xs:QName("item"),
            "order"
        )
        ! [./text(), ft:field(., "order", "xs:integer")]
        => sort((), array:get(?, 1))
    )?2
};

(: fails when lf-list-values:all-sorted-by-text-ft-field-fail is run :)
declare
    %test:assertEquals("-2", "100", "11", "-23", "0", "1", "-1")
function lf-list-values:all-sorted-by-text-hof() {
    (
        lf-list-values:list-items-with-field(
            $lf-list-values:test-path,
            xs:QName("item"),
            "order"
        )
        => sort((), function ($i) { $i/text() })
    )
    ! ft:field(., "order", "xs:integer")
};

(:
 : If this test runs, the calls to ft:field return "1" instead of the actual
 : stored field value. Therefore the tests fail.
 : Alarmingly, lf-list-values:all-sorted-by-text-hof also fails, showing the
 : same behaviour of ft:field with the current structure of the testfile.
 : - This is reproducable between runs.
 : - Which other tests fail is dependent on the
 : order of execution.
 :)
declare
(:    %test:pending:)
    %test:assertEquals("-2", "100", "11", "-23", "0", "1", "-1")
function lf-list-values:all-sorted-by-text-ft-field-fail() {
    let $hits := 
        lf-list-values:list-items-with-field(
            $lf-list-values:test-path,
            xs:QName("item"),
            "order"
        )

    for $i in $hits
    let $t := $i/text()
    order by $t
    return ft:field($i, "order", "xs:integer")
};

Tested on:

Additional context

adamretter commented 1 year ago

I have seen similar behaviour when running the tests via XSuite, and I have prepared a fix for that, but it would not account for the behaviour you are seeing when running via eXide.

line-o commented 1 year ago

You are right @adamretter this is a different issue.