{********************************************************************** This file is part of the Free Component Library (FCL) Test suite for the xpath.pp unit. Largely based on expressions from libxml2 source tree. Copyright (c) 2009 by Sergei Gorelkin, sergei_gorelkin@mail.ru See the file COPYING.FPC, included in this distribution, for details about the copyright. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. **********************************************************************} program xpathts; {$mode delphi}{$h+} uses Classes, SysUtils, Math, dom, xmlread, xmlwrite, xpath; type TResultType = (rtString, rtNumber, rtBool, rtNodeStr, rtOther); TTestRec = record data: string; // UTF-8 encoded expr: DOMString; case rt: TResultType of rtString, rtNodeStr: (s: DOMPChar); // cannot use DOMString here rtNumber: (n: Extended); rtBool: (b: Boolean); end; TTestRec3 = record data: string; // UTF-8 encoded re: string; expr: DOMString; case rt: TResultType of rtString, rtNodeStr: (s: DOMPChar); // cannot use DOMString here rtNumber: (n: Extended); rtBool: (b: Boolean); end; {$warnings off} const BaseTests: array[0..4] of TTestRec = ( (expr: '1'; rt: rtNumber; n: 1), (expr: '1+2'; rt: rtNumber; n: 3), (expr: '2*3'; rt: rtNumber; n: 6), (expr: '1+2*3+4'; rt: rtNumber; n: 11), (expr: '(1+2)*(3+4)'; rt: rtNumber; n: 21) ); CompareTests: array[0..46] of TTestRec = ( (expr: '0<0'; rt: rtBool; b: False), (expr: '0<=0'; rt: rtBool; b: True), (expr: '0>0'; rt: rtBool; b: False), (expr: '0>=0'; rt: rtBool; b: True), (expr: '0<1'; rt: rtBool; b: True), (expr: '0<=1'; rt: rtBool; b: True), (expr: '0>1'; rt: rtBool; b: False), (expr: '0>=1'; rt: rtBool; b: False), (expr: '1<0'; rt: rtBool; b: False), (expr: '1<=0'; rt: rtBool; b: False), (expr: '1>0'; rt: rtBool; b: True), (expr: '1>=0'; rt: rtBool; b: True), (expr: '1<1'; rt: rtBool; b: False), (expr: '1<=1'; rt: rtBool; b: True), (expr: '1>1'; rt: rtBool; b: False), (expr: '1>=1'; rt: rtBool; b: True), (expr: '0>-0'; rt: rtBool; b: False), (expr: '"0"<1'; rt: rtBool; b: True), (expr: '"0"<=1'; rt: rtBool; b: True), (expr: '"0">1'; rt: rtBool; b: False), (expr: '"0">=1'; rt: rtBool; b: False), (expr: '0<"1.2"'; rt: rtBool; b: True), (expr: '0<="1.2"'; rt: rtBool; b: True), (expr: '0>"1.2"'; rt: rtBool; b: False), (expr: '0>="1.2"'; rt: rtBool; b: False), (expr: '0<"-0.2"'; rt: rtBool; b: False), (expr: '0<="-0.2"'; rt: rtBool; b: False), (expr: '0>"-0.2"'; rt: rtBool; b: True), (expr: '0>="-0.2"'; rt: rtBool; b: True), (expr: 'false()<1'; rt: rtBool; b: True), (expr: 'false()<=1'; rt: rtBool; b: True), (expr: '0>true()'; rt: rtBool; b: False), (expr: '0>=true()'; rt: rtBool; b: False), (expr: '"a" > "a"'; rt: rtBool; b: False), (expr: '"a" > "b"'; rt: rtBool; b: False), (expr: '"b" > "a"'; rt: rtBool; b: False), (expr: '"a" < "a"'; rt: rtBool; b: False), (expr: '"a" < "b"'; rt: rtBool; b: False), (expr: '"b" < "a"'; rt: rtBool; b: False), (expr: '"a" >= "a"'; rt: rtBool; b: False), (expr: '"a" >= "b"'; rt: rtBool; b: False), (expr: '"b" >= "a"'; rt: rtBool; b: False), (expr: '"a" <= "a"'; rt: rtBool; b: False), (expr: '"a" <= "b"'; rt: rtBool; b: False), (expr: '"b" <= "a"'; rt: rtBool; b: False), (expr: '"a" > "0.0"'; rt: rtBool; b: False), (expr: '"a" < "0.0"'; rt: rtBool; b: False) ); nscmp = ''#10+ 'first'#10+ 'second'#10+ 'third'#10+ 'fourth'#10+ ''; nscmp2 = ''#10+ 'first'#10+ 'second'#10+ 'third'#10+ 'fourth'#10+ ''; simple = 'test'; bool58 = ''#10+ ''#10+ ' '#10+ ' b'#10+ ' c'#10+ ' d'#10+ ' e'#10+ ' '#10+ ' '#10+ ' w'#10+ ' x'#10+ ' y'#10+ ' z'#10+ ' '#10+ ' '#10+ ' fe'#10+ ' fi'#10+ ' fo'#10+ ' fu'#10+ ' '#10+ ' '#10+ ' fee'#10+ ' fii'#10+ ' foo'#10+ ' fom'#10+ ' '#10+ ' foo'#10+ ' foo'#10+ ' foo'#10+ ' foo'#10+ ''#10+ ''; bool84=''#10+ ''#10+ ' '#10+ ' 12'#10+ ' 34'#10+ ' 56'#10+ ' 78'#10+ ' '#10+ ''#10+ ''; bool85=''#10+ ''#10+ ' '#10+ ' true'#10+ ' '#10+ ' false?'#10+ ' 1'#10+ ' 0'#10+ ' '#10+ ''#10+ ''; str04=''#10+ 'Testing this'#10+ 'and this too'#10+ ''; NodesetCompareTests: array[0..38] of TTestRec = ( { same nodeset } (data: nscmp; expr: 'j[@l="12"] = j[@w="33"]'; rt: rtBool; b: True), // #70 { disjoint nodesets } (data: nscmp; expr: 'j[@l="12"] = j[@l="17"]'; rt: rtBool; b: False), // #71 { both have one common node } (data: nscmp2; expr: 'j[@l="12"] = j[@w="45"]'; rt: rtBool; b: True), // #72 { same nodeset - unequal } (data: nscmp; expr: 'j[@l="12"] != j[@w="33"]'; rt: rtBool; b: True), // #73 { disjoint - unequal } (data: nscmp; expr: 'j[@l="12"] != j[@l="17"]'; rt: rtBool; b: True), // #74 { one common node - unequal } (data: nscmp2; expr: 'j[@l="12"] != j[@w="45"]'; rt: rtBool; b: True), // #75 { single common node - unequal } (data: nscmp2; expr: 'j[@l="16"] != j[@w="78"]'; rt: rtBool; b: False),// #76 { nodeset vs. string } (data: bool58; expr: '/doc/av//*="foo"'; rt: rtBool; b: True), // #58.1 (data: bool58; expr: 'not(/doc/av//*!="foo")'; rt: rtBool; b: False), // #58.2 (data: bool58; expr: '/doc/av//j="foo"'; rt: rtBool; b: True), // #58.3 (data: bool58; expr: 'not(/doc/av//j!="foo")'; rt: rtBool; b: True), // #58.4 { empty nodeset vs. string. Data differs, but that doesn't matter } (data: bool58; expr: '/doc/avj//k="foo"'; rt: rtBool; b: False), // #59.1 (data: bool58; expr: 'not(/doc/avj//k="foo")'; rt: rtBool; b: True), // #59.2 (data: bool58; expr: '/doc/avj//k!="foo"'; rt: rtBool; b: False), // #59.3 (data: bool58; expr: 'not(/doc/avj//k!="foo")'; rt: rtBool; b: True), // #59.4 { nodeset vs. number } (data: bool84; expr: '/doc/avj/good/*=34'; rt: rtBool; b: True), // #84.1 (data: bool84; expr: 'not(/doc/avj/good/*=34)'; rt: rtBool; b: False), // #84.2 (data: bool84; expr: '/doc/avj/good/*!=34'; rt: rtBool; b: True), // #84.3 (data: bool84; expr: 'not(/doc/avj/good/*!=34)'; rt: rtBool; b: False),// #84.4 { same with reversed order of operands } (data: bool84; expr: '34=/doc/avj/good/*'; rt: rtBool; b: True), // #84.5 (data: bool84; expr: 'not(34=/doc/avj/good/*)'; rt: rtBool; b: False), // #84.6 (data: bool84; expr: '34!=/doc/avj/good/*'; rt: rtBool; b: True), // #84.7 (data: bool84; expr: 'not(34!=/doc/avj/good/*)'; rt: rtBool; b: False),// #84.8 { nodeset vs. boolean } (data: bool85; expr: '/doc/avj/bool/*=true()'; rt: rtBool; b: True), // #85.1 (data: bool85; expr: 'not(/doc/avj/bool/*=true())'; rt: rtBool; b: False), // #85.2 (data: bool85; expr: '/doc/avj/bool/*!=true()'; rt: rtBool; b: False), // #85.3 (data: bool85; expr: 'not(/doc/avj/bool/*!=true())'; rt: rtBool; b: True), // #85.4 { same with reversed order of operands } (data: bool85; expr: 'true()=/doc/avj/bool/*'; rt: rtBool; b: True), // #85.5 (data: bool85; expr: 'not(true()=/doc/avj/bool/*)'; rt: rtBool; b: False), // #85.6 (data: bool85; expr: 'true()!=/doc/avj/bool/*'; rt: rtBool; b: False), // #85.7 (data: bool85; expr: 'not(true()!=/doc/avj/bool/*)'; rt: rtBool; b: True), // #85.8 { empty nodeset vs. boolean } (data: bool85; expr: '/doc/avj/none/*=true()'; rt: rtBool; b: False), // #86.1 (data: bool85; expr: 'not(/doc/avj/none/*=true())'; rt: rtBool; b: True), // #86.2 (data: bool85; expr: '/doc/avj/none/*!=true()'; rt: rtBool; b: True), // #86.3 (data: bool85; expr: 'not(/doc/avj/none/*!=true())'; rt: rtBool; b: False),// #86.4 { same with reversed order of operands } (data: bool85; expr: 'true()=/doc/avj/none/*'; rt: rtBool; b: False), // #86.5 (data: bool85; expr: 'not(true()=/doc/avj/none/*)'; rt: rtBool; b: True), // #86.6 (data: bool85; expr: 'true()!=/doc/avj/none/*'; rt: rtBool; b: True), // #86.7 (data: bool85; expr: 'not(true()!=/doc/avj/none/*)'; rt: rtBool; b: False) // #86.8 ); EqualityTests: array[0..25] of TTestRec = ( (expr: '1=1'; rt: rtBool; b: True), (expr: '1!=1'; rt: rtBool; b: False), (expr: '1=0'; rt: rtBool; b: False), (expr: '1!=0'; rt: rtBool; b: True), (expr: 'true()=true()'; rt: rtBool; b: True), (expr: 'true()!=true()'; rt: rtBool; b: False), (expr: 'true()=false()'; rt: rtBool; b: False), (expr: 'false()!=true()'; rt: rtBool; b: True), (expr: '"test"="test"'; rt: rtBool; b: True), (expr: '"test"!="test"'; rt: rtBool; b: False), (expr: '"test2"="test"'; rt: rtBool; b: False), (expr: '"test2"!="test"'; rt: rtBool; b: True), (expr: 'false()=0'; rt: rtBool; b: True), (expr: 'false()!=0'; rt: rtBool; b: False), (expr: 'false()=1'; rt: rtBool; b: False), (expr: 'false()!=1'; rt: rtBool; b: True), (expr: '0=true()'; rt: rtBool; b: False), (expr: '0!=true()'; rt: rtBool; b: True), (expr: '1=true()'; rt: rtBool; b: True), (expr: '1!=true()'; rt: rtBool; b: False), (expr: 'true()="test"'; rt: rtBool; b: True), (expr: 'false()="test"'; rt: rtBool; b: False), (expr: '"test"!=true()'; rt: rtBool; b: False), (expr: '"test"!=false()'; rt: rtBool; b: True), (expr: '"a"=0.0'; rt: rtBool; b: False), (expr: '"a"!=0.0'; rt: rtBool; b: True) ); math88=''+ '0'+ '1'+ '2'+ '3'+ '4'+ '5'+ '6'+ '2'+ '6'+ '10'+ '3'+ ''; math85=''+ '0'+ '1'+ '2'+ '3'+ '4'+ 'five'+ ''; math80=''+ '5'+ '2'+ '
-5
'+ '2'+ '
'; math69=''+ '3'+ '7'+ ''; FloatTests: array[0..70] of TTestRec = ( (expr: '1'; rt: rtNumber; n: 1), (expr: '123'; rt: rtNumber; n: 123), (expr: '1.23'; rt: rtNumber; n: 1.23), (expr: '0.123'; rt: rtNumber; n: 0.123), (expr: '4.'; rt: rtNumber; n: 4), (expr: '.4'; rt: rtNumber; n: 0.4), //(expr: '1.23e3'; rt: rtNumber; n: 1230), //(expr: '1.23e-3'; rt: rtNumber; n: 0.00123), (expr: '1 div 0'; rt: rtNumber; n: Infinity), (expr: '-1 div 0'; rt: rtNumber; n: -Infinity), (expr: '0 div 0'; rt: rtNumber; n: NaN), (expr: '1 div -0'; rt: rtNumber; n: -Infinity), (expr: '(1 div 0) > 0'; rt: rtBool; b: True), (expr: '(1 div 0) < 0'; rt: rtBool; b: False), (expr: '(-1 div 0) > 0'; rt: rtBool; b: False), (expr: '(-1 div 0) < 0'; rt: rtBool; b: True), (expr: '(0 div 0) > 0'; rt: rtBool; b: False), (expr: '(0 div 0) < 0'; rt: rtBool; b: False), (expr: '(1 div -0) > 0'; rt: rtBool; b: False), (expr: '(1 div -0) < 0'; rt: rtBool; b: True), (expr: '0 div 0 = 0 div 0'; rt: rtBool; b: False), (expr: '0 div 0 != 0 div 0'; rt: rtBool; b: True), (expr: '0 div 0 > 0 div 0'; rt: rtBool; b: False), (expr: '0 div 0 < 0 div 0'; rt: rtBool; b: False), (expr: '0 div 0 >= 0 div 0'; rt: rtBool; b: False), (expr: '0 div 0 <= 0 div 0'; rt: rtBool; b: False), (expr: '1 div 0 = -1 div 0'; rt: rtBool; b: False), (expr: '1 div 0 != -1 div 0'; rt: rtBool; b: True), (expr: '1 div 0 > -1 div 0'; rt: rtBool; b: True), (expr: '1 div 0 < -1 div 0'; rt: rtBool; b: False), (expr: '1 div 0 >= -1 div 0'; rt: rtBool; b: True), (expr: '1 div 0 <= -1 div 0'; rt: rtBool; b: False), (expr: '1 div 0 = 1 div 0'; rt: rtBool; b: True), (expr: '1 div 0 != 1 div 0'; rt: rtBool; b: False), (expr: '1 div 0 > 1 div 0'; rt: rtBool; b: False), (expr: '1 div 0 < 1 div 0'; rt: rtBool; b: False), (expr: '1 div 0 >= -1 div 0'; rt: rtBool; b: True), (expr: '1 div 0 <= -1 div 0'; rt: rtBool; b: False), (expr: '-2 div 0 = -1 div 0'; rt: rtBool; b: True), (expr: '1 div floor(0.1)'; rt: rtNumber; n: Infinity), (expr: '1 div floor(-0.1)'; rt: rtNumber; n: -1), (expr: '1 div floor(-0)'; rt: rtNumber; n: -Infinity), (expr: '1 div floor(0)'; rt: rtNumber; n: Infinity), (expr: '1 div ceiling(0.1)'; rt: rtNumber; n: 1), (expr: '1 div ceiling(-0.1)'; rt: rtNumber; n: -Infinity), (expr: '1 div ceiling(-0)'; rt: rtNumber; n: -Infinity), (expr: '1 div ceiling(0)'; rt: rtNumber; n: Infinity), (expr: '1 div round(0.1)'; rt: rtNumber; n: Infinity), (expr: '1 div round(-0.1)'; rt: rtNumber; n: -Infinity), (expr: '1 div round(-0)'; rt: rtNumber; n: -Infinity), (expr: '1 div round(0)'; rt: rtNumber; n: Infinity), (expr: '1 div number("f")'; rt: rtNumber; n: NaN), (expr: 'number("f") div 1'; rt: rtNumber; n: NaN), (expr: '1 div (1 div 0)'; rt: rtNumber; n: 0), (expr: '(1 div 0) div 1'; rt: rtNumber; n: Infinity), (expr: '-(1 div 0) div 1'; rt: rtNumber; n: -Infinity), (expr: '5 mod 2'; rt: rtNumber; n: 1), (expr: '5 mod -2'; rt: rtNumber; n: 1), (expr: '-5 mod 2'; rt: rtNumber; n: -1), (expr: '-5 mod -2'; rt: rtNumber; n: -1), (expr: '2 mod number("xxx")'; rt: rtNumber; n: NaN), (expr: 'number("xxx") mod 3'; rt: rtNumber; n: NaN), (expr: '8 mod 3 = 2'; rt: rtBool; b: True), (data: math88; expr: '(n1*n2*n3*n4*n5*n6)div n7 div n8 div n9 div n10'; rt: rtNumber; n: 2), (data: math85; expr: '((((((n3+5)*(3)+(((n2)+2)*(n1 - 6)))-(n4 - n2))+(-(4-6)))))'; rt: rtNumber; n: 4), (data: math80; expr: 'div mod mod'; rt: rtNumber; n: -1), (data: math69; expr: '-(n-2/@attrib) - -(n-1/@attrib)'; rt: rtNumber; n: 8), (data: math69; expr: '-n-2/@attrib --n-1/@attrib'; rt: rtNumber; n: 8), (data: math69; expr: '-n-2 --n-1'; rt: rtNumber; n: -4), // test boolean operator short-circuting; "count(5)" acts as an error (expr: '10+30*20 or count(5)'; rt: rtBool; b: True), (expr: '75-50-25 and count(5)'; rt: rtBool; b: False), (expr: '"1" and "0"'; rt: rtBool; b: True), (expr: '0 or ""'; rt: rtBool; b: False) ); math95=''+ '1'+ '2'+ '3'+ '4'+ 'five'+ ''; math96=''+ '17'+ '-5'+ '8'+ '-37'+ ''; expr01=''+ 'en'+ '
'+ ' en'+ '
'+ 'EN'+ 'en-us'+ '
'; id04=''+ ''+ ''+ ']>'+ ''+ ''+ ''+ ''+ ''+ ''; pos04=''+ '1'+ '1191'+ '263'+ '2'+ '827'+ '256'+ '3'+ '4'+ ''; FunctionTests: array[0..51] of TTestRec = ( // last() // local-name() // namespace-uri() // name() (expr: 'boolean(0)'; rt: rtBool; b: False), (expr: 'boolean(-0)'; rt: rtBool; b: False), (expr: 'boolean(1 div 0)'; rt: rtBool; b: True), (expr: 'boolean(-1 div 0)'; rt: rtBool; b: True), (expr: 'boolean(0 div 0)'; rt: rtBool; b: False), (expr: 'boolean("")'; rt: rtBool; b: False), (expr: 'boolean("abc")'; rt: rtBool; b: True), (data: simple; expr: 'boolean(/doc)'; rt: rtBool; b: True), // #40 (data: simple; expr: 'boolean(foo)'; rt: rtBool; b: False), // #41 (expr: 'true()'; rt: rtBool; b: True), (expr: 'false()'; rt: rtBool; b: False), (expr: 'not(true())'; rt: rtBool; b: False), (expr: 'not(false())'; rt: rtBool; b: True), (expr: 'not("")'; rt: rtBool; b: True), // lang() tests. These ones, however, test much more than lang(). (data: expr01; expr: 'para[@id="1" and lang("en")]'; rt: rtNodeStr; s: 'en'), // expression01 (data: expr01; expr: 'para[@id="4" and lang("en")]'; rt: rtNodeStr; s: 'en-us'), // expression03 (data: expr01; expr: 'div/para[lang("en")]'; rt: rtNodeStr; s: 'en'), // expression04 (data: expr01; expr: 'para[@id="3" and lang("en")]'; rt: rtNodeStr; s: 'EN'), // expression05 (data: id04; expr: 'id("c")/@id'; rt: rtNodeStr; s: 'c'), // idkey04 // position() tests (data: pos04; expr: '*[@test][position()=4]/num'; rt: rtNodeStr; s: '4'), (expr: 'number("1.5")'; rt: rtNumber; n: 1.5), (expr: 'number("abc")'; rt: rtNumber; n: NaN), (expr: '-number("abc")'; rt: rtNumber; n: NaN), (expr: 'number(true())'; rt: rtNumber; n: 1.0), (expr: 'number(false())'; rt: rtNumber; n: 0), (data: math95; expr: 'sum(e)'; rt: rtNumber; n: NaN), (data: math96; expr: 'sum(e)'; rt: rtNumber; n: -17), (expr: 'floor(0.1)'; rt: rtNumber; n: 0), (expr: 'floor(-0.1)'; rt: rtNumber; n: -1), (expr: 'floor(-0)'; rt: rtNumber; n: 0), (expr: 'floor(0)'; rt: rtNumber; n: 0), (expr: 'floor(5.2)'; rt: rtNumber; n: 5), (expr: 'floor(-5.2)'; rt: rtNumber; n: -6), (expr: 'floor("NaN")'; rt: rtNumber; n: NaN), (expr: 'ceiling(0.1)'; rt: rtNumber; n: 1), (expr: 'ceiling(-0.1)'; rt: rtNumber; n: 0), (expr: 'ceiling(-0)'; rt: rtNumber; n: 0), (expr: 'ceiling(0)'; rt: rtNumber; n: 0), (expr: 'ceiling(5.2)'; rt: rtNumber; n: 6), (expr: 'ceiling(-5.2)'; rt: rtNumber; n: -5), (expr: 'ceiling("NaN")'; rt: rtNumber; n: NaN), (expr: 'round(0.1)'; rt: rtNumber; n: 0), (expr: 'round(5.2)'; rt: rtNumber; n: 5), (expr: 'round(5.5)'; rt: rtNumber; n: 6), (expr: 'round(5.6)'; rt: rtNumber; n: 6), (expr: 'round(-0.1)'; rt: rtNumber; n: 0), (expr: 'round(-5.2)'; rt: rtNumber; n: -5), (expr: 'round(-5.5)'; rt: rtNumber; n: -5), (expr: 'round(-5.6)'; rt: rtNumber; n: -6), (expr: 'round("NaN")'; rt: rtNumber; n: NaN), (expr: 'round(1 div 0)'; rt: rtNumber; n: Infinity), (expr: 'round(-1 div 0)'; rt: rtNumber; n: -Infinity) ); str14 =''#10+ ' '#10+ ' '#10+ ' b'#10+ ' c'#10+ ' d'#10+ ' e'#10+ ' '#10+ ' '#10+ ' w'#10+ ' x'#10+ ' y'#10+ ' z'#10+ ' '#10+ ' '#10+ ''; out14 =#10+ ' b'#10+ ' c'#10+ ' d'#10+ ' e'#10+ ' '; node08=''#10+ 'A'#10+ 'B-C'#10+ 'TextNode_between_F_and_G'#10+ '

Yahoo'#10+ '

SecondNode_after_H
'#10+ '
'; out08=#10+ 'A'#10+ 'B-C'#10+ 'TextNode_between_F_and_G'#10+ 'Yahoo'#10+ 'SecondNode_after_H'#10; str30=''#10+ ''#10+ ''#10+ ''; ns11=''+ ' Hello'+ ' '+ ' '+ ' '+ ''; pidata=''; StringTests: array[0..87] of TTestRec = ( // numbers refer to xalan/string/stringXX (expr: 'string(0)'; rt: rtString; s: '0'), (expr: 'string(5)'; rt: rtString; s: '5'), // #38/39 (expr: 'string(0.5)'; rt: rtString; s: '0.5'), (expr: 'string(-0.5)'; rt: rtString; s: '-0.5'), (expr: 'string("test")'; rt: rtString; s: 'test'), // #40 (expr: 'string("")'; rt: rtString; s: ''), // #41 (expr: 'string(true())'; rt: rtString; s: 'true'), (expr: 'string(false())'; rt: rtString; s: 'false'), (expr: 'string(0 div 0)'; rt: rtString; s: 'NaN'), (expr: 'string(1 div 0)'; rt: rtString; s: 'Infinity'), (expr: 'string(-1 div 0)'; rt: rtString; s: '-Infinity'), // maybe other checks for correct numeric formats (data: str14; expr: 'string(av//*)'; rt: rtString; s: out14), (data: node08; expr: '/'; rt: rtNodeStr; s: out08), (expr: 'concat("titi","toto")'; rt: rtString; s: 'tititoto'), (expr: 'concat("titi","toto","tata")'; rt: rtString; s: 'tititototata'), (expr: 'concat("titi",''toto'')'; rt: rtString; s: 'tititoto'), (expr: 'concat("titi",''toto'',"tata","last")'; rt: rtString; s: 'tititototatalast'), (expr: 'concat("cd", 34)'; rt: rtString; s: 'cd34'), // #101 (expr: 'concat(false(), "ly")'; rt: rtString; s: 'falsely'), // #104 (expr: 'starts-with("tititoto","titi")'; rt: rtBool; b: True), (expr: 'starts-with("tititoto","to")'; rt: rtBool; b: False), (expr: 'starts-with("ab", "abc")'; rt: rtBool; b: False), // #46 (expr: 'starts-with("abc", "bc")'; rt: rtBool; b: False), // #47 (expr: 'starts-with("abc", "")'; rt: rtBool; b: True), // #48 (expr: 'starts-with("", "")'; rt: rtBool; b: True), // #49 (expr: 'starts-with(true(), "tr")'; rt: rtBool; b: True), // #50 (expr: 'contains("tititototata","titi")'; rt: rtBool; b: True), (expr: 'contains("tititototata","toto")'; rt: rtBool; b: True), (expr: 'contains("tititototata","tata")'; rt: rtBool; b: True), (expr: 'contains("tititototata","tita")'; rt: rtBool; b: False), // 'contains(concat(.,'BC'),concat('A','B','C'))' == true // #57 (expr: 'contains("ab", "abc")'; rt: rtBool; b: False), // #58 (expr: 'contains("abc", "bcd")'; rt: rtBool; b: False), // #60 (expr: 'contains("abc", "")'; rt: rtBool; b: True), // #61 (expr: 'contains("", "")'; rt: rtBool; b: True), // #62 (expr: 'contains(true(), "e")'; rt: rtBool; b: True), // #63 (expr: 'substring("12345",2,3)'; rt: rtString; s: '234'), (expr: 'substring("12345",2)'; rt: rtString; s: '2345'), (expr: 'substring("12345",-4)'; rt: rtString; s: '12345'), (expr: 'substring("12345",3.4)'; rt: rtString; s: '345'), (expr: 'substring("12345",3.6)'; rt: rtString; s: '45'), (expr: 'substring("12345",1.5,2.6)'; rt: rtString; s: '234'), // #16 (expr: 'substring("12345",2.2,2.2)'; rt: rtString; s: '23'), (expr: 'substring("12345",0,3)'; rt: rtString; s: '12'), // #17 (expr: 'substring("12345",-8,10)'; rt: rtString; s: '1'), (expr: 'substring("12345",4,-10)'; rt: rtString; s: ''), (expr: 'substring("12345",0 div 0, 3)'; rt: rtString; s: ''), // #18 (expr: 'substring("12345",1, 0 div 0)'; rt: rtString; s: ''), // #19 (expr: 'substring("12345",1 div 0, 3)'; rt: rtString; s: ''), (expr: 'substring("12345",3,-1 div 0)'; rt: rtString; s: ''), (expr: 'substring("12345",-42, 1 div 0)'; rt: rtString; s: '12345'), // #20 (expr: 'substring("12345",-1 div 0, 1 div 0)'; rt: rtString; s: ''), // #21 (expr: 'substring("12345",-1 div 0,5)'; rt: rtString; s: ''), (expr: 'substring-before("1999/04/01","/")'; rt: rtString; s: '1999'), // #08 (expr: 'substring-before("1999/04/01","a")'; rt: rtString; s: ''), // #68 modified (expr: 'substring-after("1999/04/01","/")'; rt: rtString; s: '04/01'), // #09 (expr: 'substring-after("1999/04/01","19")'; rt: rtString; s: '99/04/01'), (expr: 'substring-after("1999/04/01","a")'; rt: rtString; s: ''), (expr: 'string-length("")'; rt: rtNumber; n: 0), (expr: 'string-length("titi")'; rt: rtNumber; n: 4), (data: simple; expr: 'string-length(.)'; rt: rtNumber; n: 4), // #02 modified (data: str04; expr: 'string-length(/)'; rt: rtNumber; n:27), // #04.1 modified (data: str04; expr: 'string-length(/doc/a)'; rt: rtNumber; n: 12), // #04.2 (data: str04; expr: 'string-length()'; rt: rtNumber; n: 27), (expr: 'normalize-space("'#9#10#13' ab cd'#10#13#9'ef'#9#10#13' ")'; rt: rtString; s: 'ab cd ef'), // #10 (expr: 'translate("bar", "abc", "ABC")'; rt: rtString; s: 'BAr'), // #11 (expr: 'translate("--aaa--","abc-","ABC")'; rt: rtString; s: 'AAA'), (expr: 'translate("ddaaadddd","abcd","ABCxy")'; rt: rtString; s: 'xxAAAxxxx'), // #96 (data: node08; expr: 'name(a/@attr1)'; rt: rtString; s: 'attr1'), // namespace08 modified (data: node08; expr: 'namespace-uri(a/@attr1)'; rt: rtString; s: ''), (data: node08; expr: 'local-name(a/@attr1)'; rt: rtString; s: 'attr1'), (data: pidata; expr: 'name(/processing-instruction())'; rt: rtString; s: 'a-pi'), // namespace29 modified (data: pidata; expr: 'name(/processing-instruction("b-pi"))'; rt: rtString; s: 'b-pi'), (data: pidata; expr: 'local-name(/processing-instruction())'; rt: rtString; s: 'a-pi'), (data: pidata; expr: 'namespace-uri(/processing-instruction())'; rt: rtString; s: ''), (data: node08; expr: 'name(//comment())'; rt: rtString; s: ''), // namespace30 modified (data: node08; expr: 'local-name(//comment())'; rt: rtString; s: ''), (data: node08; expr: 'namespace-uri(//comment())'; rt: rtString; s: ''), (data: node08; expr: 'name(//text())'; rt: rtString; s: ''), // namespace31 modified (data: node08; expr: 'local-name(//text())'; rt: rtString; s: ''), (data: node08; expr: 'namespace-uri(//text())'; rt: rtString; s: ''), // tests for number->string conversions at boundary conditions (expr: 'string(123456789012345678)'; rt: rtString; s: '123456789012345680'), // #132.1 (expr: 'string(-123456789012345678)'; rt: rtString; s: '-123456789012345680'), // #132.2 (expr: 'string(.10123456789234567893)'; rt: rtString; s: '0.10123456789234568'), // #133.1 (expr: 'string(-.10123456789234567893)'; rt: rtString; s: '-0.10123456789234568'), // #133.2 (expr: 'string(9.87654321012345)'; rt: rtString; s: '9.87654321012345'), // #134.1 (expr: 'string(98765432101234.5)'; rt: rtString; s: '98765432101234.5'), // #134.2 (expr: 'string(.0000000000000000000000000000000000000000123456789)'; rt: rtString; // #135.1 s: '0.0000000000000000000000000000000000000000123456789'), (expr: 'string(-.0000000000000000000000000000000000000000123456789)'; rt: rtString; // #135.2 s: '-0.0000000000000000000000000000000000000000123456789') ); res1 = ''; nameTests: array[0..17] of TTestRec3 = ( (data: str30; re: res1; expr: 'namespace-uri(baz1:a/@baz2:attrib1)'; rt: rtString; s: ''), // #30 (data: str30; re: res1; expr: 'namespace-uri(baz2:b/@baz1:attrib2)'; rt: rtString; s: 'http://xsl.lotus.com/ns1'), // #31 (data: str30; re: res1; expr: 'name(*)'; rt: rtString; s: 'ns1:a'), // #32 (data: str30; re: res1; expr: 'name(baz1:a)'; rt: rtString; s: 'ns1:a'), // #33 (data: str30; re: res1; expr: 'name(baz2:b)'; rt: rtString; s: 'b'), // #34 (data: str30; re: res1; expr: 'name(baz1:a/@baz2:attrib1)'; rt: rtString; s: ''), // #35 (data: str30; re: res1; expr: 'name(baz2:b/@baz1:attrib2)'; rt: rtString; s: 'ns1:attrib2'), // #36 (data: str30; re: res1; expr: 'local-name(baz2:b)'; rt: rtString; s: 'b'), // namespace07 (data: str30; re: res1; expr: 'local-name(baz2:b/@baz1:attrib2)'; rt: rtString; s: 'attrib2'), // namespace09 (data: str30; re: res1; expr: 'local-name()'; rt: rtString; s: 'doc'), // namespace26 (data: str30; re: res1; expr: 'namespace-uri()'; rt: rtString; s: 'http://xsl.lotus.com/ns2'), // namespace27 (data: ns11; re: res1; expr: 'namespace-uri(baz1:a-two)'; rt: rtString; s: 'http://xsl.lotus.com/ns1'), // namespace11 (data: ns11; re: res1; expr: 'namespace-uri(baz1:a-two/@attrib1)'; rt: rtString; s: ''), (data: ns11; re: res1; expr: 'namespace-uri(baz2:b-three)'; rt: rtString; s: 'http://xsl.lotus.com/ns2'), (data: ns11; re: res1; expr: 'namespace-uri(baz2:b-three/@baz1:attrib2)'; rt: rtString; s: 'http://xsl.lotus.com/ns1'), {*} (data: ns11; re: res1; expr: 'namespace-uri(baz2:b-three/c-four)'; rt: rtString; s: ''), (data: ns11; re: res1; expr: 'namespace-uri(bogus)'; rt: rtString; s: ''), (data: str30; re: res1; expr: 'name(baz1:*)'; rt: rtString; s: 'ns1:a') ); ax114=''+ ''+ ' '+ ' '+ ' '+ ''+ ''+ ''; ax115=''+ ''+ ''+ ''+ ''+ ''; ax117=''+ '
'+ ' hello'+ ' ahoy'+ '
'+ '
'+ ' goodbye'+ ' sayonara'+ ' adios'+ '
'+ '
'+ ' aloha'+ ' '+ ' A3b-1'+ ' A3b-2'+ ' '+ ' shalom'+ '
'+ '
'; AxesTests: array[0..15] of TTestRec = ( (data: ax117; expr: 'count(//@*)'; rt: rtNumber; n: 16), (data: ax117; expr: 'count(//@title)'; rt: rtNumber; n: 12), (data: ax117; expr: 'count(//section//@*)'; rt: rtNumber; n: 14), (data: ax117; expr: 'count(//section//@title)'; rt: rtNumber; n: 11), (data: ax117; expr: 'count(/chapter/.//@*)'; rt: rtNumber; n: 16), (data: ax117; expr: 'count(/chapter/.//@title)'; rt: rtNumber; n: 12), (data: ax117; expr: 'count(/chapter/section[1]//@*)'; rt: rtNumber; n: 5), (data: ax117; expr: 'count(/chapter/section[1]//@title)'; rt: rtNumber; n: 3), (data: ax117; expr: 'count(/chapter/section[2]//@*)'; rt: rtNumber; n: 4), (data: ax117; expr: 'count(/chapter/section[2]//@title)'; rt: rtNumber; n: 4), (data: ax117; expr: 'count(/chapter/section[3]//@*)'; rt: rtNumber; n: 5), (data: ax117; expr: 'count(/chapter/section[3]//@title)'; rt: rtNumber; n: 4), (data: simple; expr: 'local-name(namespace::*[1])'; rt: rtString; s: 'xml'), // namespace28a (data: simple; expr: 'name(namespace::*[1])'; rt: rtString; s: 'xml'), // namespace28b (data: ax117; expr: 'name(//subsection[@title="A3b"]/@title/parent::*)'; rt: rtString; s: 'subsection'), // axes96 modified (data: ax117; expr: 'name(//subsection[@title="A3b"]/@title/ancestor::*[1])'; rt: rtString; s: 'subsection') // axes97 modified ); AxesTests2: array[0..3] of TTestRec3 = ( (data: ax114; re: '//baz'; expr: 'preceding::foo[1]/@att1'; rt: rtNodeStr; s: 'a'), (data: ax114; re: '//baz'; expr: '(preceding::foo)[1]/@att1'; rt: rtNodeStr; s: 'c'), // won't parse (data: ax115; re: '//baz'; expr: 'preceding-sibling::foo[1]/@att1'; rt: rtNodeStr; s: 'a'), (data: ax115; re: '//baz'; expr: '(preceding-sibling::foo)[1]/@att1'; rt: rtNodeStr; s: 'c') // won't parse ); pred44 = ''+ ''+ 'Success'+ 'child2'+ ''+ ''+ 'Wrong node selected!!'+ ''+ ''+ 'Wrong node selected!!'+ ''+ ''; pred11 = ''+ '1'+ '2'+ 'target'+ ''+ '3'+ 'target'+ ''; PredicateTests: array [0..4] of TTestRec = ( (data: pred44; expr: '//child1[parent::element1]'; rt: rtNodeStr; s: 'Success'), // predicate44 {should select all but last elements named 'e' } (data: math96; expr: 'sum(e[true()=following-sibling::*])'; rt: rtNumber; n: 20), // predicate03 {should select two first elements} (data: math96; expr: 'sum(e[8=following-sibling::*])'; rt: rtNumber; n: 12), // predicate05 (data: pred11; expr: 'a["target"=descendant::*]'; rt: rtNodeStr; s: '2target'), // predicate06 (data: pred11; expr: 'a[following-sibling::*=descendant::*]'; rt: rtNodeStr; s: '2target') // predicate11 ); {$warnings on} var FailCount: Integer = 0; procedure CheckResult(const t: TTestRec; r: TXPathVariable); overload; begin case t.rt of rtBool: begin if (r is TXPathBooleanVariable) and (r.AsBoolean = t.b) then Exit; writeln; writeln('Failed: ', t.expr); writeln('Expected: ', t.b, ' got: ', r.AsBoolean); end; rtNumber: begin if (r is TXPathNumberVariable) then begin if IsNan(t.n) then begin if IsNan(r.AsNumber) then Exit; end else begin if IsInfinite(t.n) and (t.n = r.AsNumber) then Exit; if not IsNan(TXPathNumberVariable(r).Value) and SameValue(TXPathNumberVariable(r).Value, t.n) then Exit; end; end; writeln; writeln('Failed: ', t.expr); writeln('Expected: ', t.n, ' got: ', r.AsNumber); end; rtString: begin if (r is TXPathStringVariable) and (r.AsText = DOMString(t.s)) then Exit; writeln; writeln('Failed: ', t.expr); writeln('Expected: ', DOMString(t.s), ' got: ', r.AsText); end; rtNodeStr: begin if (r is TXPathNodeSetVariable) and (r.AsNodeSet.Count = 1) and (r.AsText = DOMString(t.s)) then Exit; writeln; writeln('Failed: ', t.expr); if r.AsNodeSet.Count > 1 then writeln('Result is not a single node'); writeln('Expected: ', DOMString(t.s), ' got: ', r.AsText); end; end; Inc(FailCount); end; procedure CheckResult(const t: TTestRec3; r: TXPathVariable); overload; var temp: TTestRec; begin temp.data := t.data; temp.expr := t.expr; temp.rt := t.rt; temp.n := t.n; CheckResult(temp, r); end; function ParseString(const data: string): TXMLDocument; var parser: TDOMParser; src: TXMLInputSource; begin parser := TDOMParser.Create; try parser.Options.PreserveWhitespace := True; parser.Options.Namespaces := True; src := TXMLInputSource.Create(data); try parser.Parse(src, Result); finally src.Free; end; finally parser.Free; end; end; procedure DoSuite(const tests: array of TTestRec); var i: Integer; doc: TXMLDocument; rslt: TXPathVariable; begin for i := 0 to High(tests) do begin if tests[i].data <> '' then doc := ParseString(tests[i].data) else doc := TXMLDocument.Create; try try rslt := EvaluateXPathExpression(tests[i].expr, doc.DocumentElement); try CheckResult(tests[i], rslt); finally rslt.Free; end; except writeln; writeln('Failed: ', tests[i].expr); SysUtils.ShowException(ExceptObject, ExceptAddr); Inc(FailCount); end; finally doc.Free; end; end; end; procedure DoSuite_WithResolver(const tests: array of TTestRec3); var i: Integer; doc: TXMLDocument; rslt: TXPathVariable; nsdoc: TXMLDocument; resolver: TXPathNSResolver; begin for i := 0 to High(tests) do begin doc := ParseString(tests[i].data); try nsdoc := ParseString(tests[i].re); try try resolver := TXPathNSResolver.Create(nsdoc.DocumentElement); try rslt := EvaluateXPathExpression(tests[i].expr, doc.DocumentElement, resolver); try CheckResult(tests[i], rslt); finally rslt.Free; end; finally resolver.Free; end; except writeln; writeln('Failed: ', tests[i].expr); SysUtils.ShowException(ExceptObject, ExceptAddr); Inc(FailCount); end; finally nsdoc.Free; end; finally doc.Free; end; end; end; procedure DoSuite_WithContext(const tests: array of TTestRec3); var i: Integer; doc: TXMLDocument; rslt: TXPathVariable; context: TXPathVariable; ctxNs: TNodeSet; begin for i := 0 to High(tests) do begin doc := ParseString(tests[i].data); try context := EvaluateXPathExpression(tests[i].re, doc.DocumentElement); try try ctxNs := context.AsNodeSet; if ctxNs.Count <> 1 then raise Exception.CreateFmt('Context expression "%s" does not evaluate to a single node', [tests[i].re]); rslt := EvaluateXPathExpression(tests[i].expr, TDOMNode(ctxNs[0])); try CheckResult(tests[i], rslt); finally rslt.Free; end; except writeln; writeln('Failed: ', tests[i].expr); SysUtils.ShowException(ExceptObject, ExceptAddr); Inc(FailCount); end; finally context.Free; end; finally doc.Free; end; end; end; begin DoSuite(BaseTests); DoSuite(CompareTests); DoSuite(NodesetCompareTests); DoSuite(EqualityTests); DoSuite(FloatTests); DoSuite(FunctionTests); DoSuite(StringTests); DoSuite(AxesTests); DoSuite_WithContext(AxesTests2); DoSuite_WithResolver(nameTests); DoSuite(PredicateTests); writeln; writeln('Total failed tests: ', FailCount); end.