xpathts.pp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685
  1. {**********************************************************************
  2. This file is part of the Free Component Library (FCL)
  3. Test suite for the xpath.pp unit.
  4. Largely based on expressions from libxml2 source tree.
  5. Copyright (c) 2009 by Sergei Gorelkin, [email protected]
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. program xpathts;
  13. {$mode delphi}{$h+}
  14. uses
  15. Classes, SysUtils, Math,
  16. dom, xmlread, xmlwrite, xpath;
  17. type
  18. TResultType = (rtString, rtNumber, rtBool, rtNodeset);
  19. TTestRec = record
  20. data: string; // UTF-8 encoded
  21. expr: DOMString;
  22. case rt: TResultType of
  23. rtString: (s: DOMPChar); // cannot use DOMString here
  24. rtNumber: (n: Extended);
  25. rtBool: (b: Boolean);
  26. end;
  27. {$warnings off}
  28. const
  29. BaseTests: array[0..4] of TTestRec = (
  30. (expr: '1'; rt: rtNumber; n: 1),
  31. (expr: '1+2'; rt: rtNumber; n: 3),
  32. (expr: '2*3'; rt: rtNumber; n: 6),
  33. (expr: '1+2*3+4'; rt: rtNumber; n: 11),
  34. (expr: '(1+2)*(3+4)'; rt: rtNumber; n: 21)
  35. );
  36. CompareTests: array[0..46] of TTestRec = (
  37. (expr: '0<0'; rt: rtBool; b: False),
  38. (expr: '0<=0'; rt: rtBool; b: True),
  39. (expr: '0>0'; rt: rtBool; b: False),
  40. (expr: '0>=0'; rt: rtBool; b: True),
  41. (expr: '0<1'; rt: rtBool; b: True),
  42. (expr: '0<=1'; rt: rtBool; b: True),
  43. (expr: '0>1'; rt: rtBool; b: False),
  44. (expr: '0>=1'; rt: rtBool; b: False),
  45. (expr: '1<0'; rt: rtBool; b: False),
  46. (expr: '1<=0'; rt: rtBool; b: False),
  47. (expr: '1>0'; rt: rtBool; b: True),
  48. (expr: '1>=0'; rt: rtBool; b: True),
  49. (expr: '1<1'; rt: rtBool; b: False),
  50. (expr: '1<=1'; rt: rtBool; b: True),
  51. (expr: '1>1'; rt: rtBool; b: False),
  52. (expr: '1>=1'; rt: rtBool; b: True),
  53. (expr: '0>-0'; rt: rtBool; b: False),
  54. (expr: '"0"<1'; rt: rtBool; b: True),
  55. (expr: '"0"<=1'; rt: rtBool; b: True),
  56. (expr: '"0">1'; rt: rtBool; b: False),
  57. (expr: '"0">=1'; rt: rtBool; b: False),
  58. (expr: '0<"1.2"'; rt: rtBool; b: True),
  59. (expr: '0<="1.2"'; rt: rtBool; b: True),
  60. (expr: '0>"1.2"'; rt: rtBool; b: False),
  61. (expr: '0>="1.2"'; rt: rtBool; b: False),
  62. (expr: '0<"-0.2"'; rt: rtBool; b: False),
  63. (expr: '0<="-0.2"'; rt: rtBool; b: False),
  64. (expr: '0>"-0.2"'; rt: rtBool; b: True),
  65. (expr: '0>="-0.2"'; rt: rtBool; b: True),
  66. (expr: 'false()<1'; rt: rtBool; b: True),
  67. (expr: 'false()<=1'; rt: rtBool; b: True),
  68. (expr: '0>true()'; rt: rtBool; b: False),
  69. (expr: '0>=true()'; rt: rtBool; b: False),
  70. (expr: '"a" > "a"'; rt: rtBool; b: False),
  71. (expr: '"a" > "b"'; rt: rtBool; b: False),
  72. (expr: '"b" > "a"'; rt: rtBool; b: False),
  73. (expr: '"a" < "a"'; rt: rtBool; b: False),
  74. (expr: '"a" < "b"'; rt: rtBool; b: False),
  75. (expr: '"b" < "a"'; rt: rtBool; b: False),
  76. (expr: '"a" >= "a"'; rt: rtBool; b: False),
  77. (expr: '"a" >= "b"'; rt: rtBool; b: False),
  78. (expr: '"b" >= "a"'; rt: rtBool; b: False),
  79. (expr: '"a" <= "a"'; rt: rtBool; b: False),
  80. (expr: '"a" <= "b"'; rt: rtBool; b: False),
  81. (expr: '"b" <= "a"'; rt: rtBool; b: False),
  82. (expr: '"a" > "0.0"'; rt: rtBool; b: False),
  83. (expr: '"a" < "0.0"'; rt: rtBool; b: False)
  84. );
  85. nscmp = '<doc>'#10+
  86. '<j l="12" w="33">first</j>'#10+
  87. '<j l="17" w="45">second</j>'#10+
  88. '<j l="16" w="78">third</j>'#10+
  89. '<j l="12" w="33">fourth</j>'#10+
  90. '</doc>';
  91. nscmp2 = '<doc>'#10+
  92. '<j l="12" w="45">first</j>'#10+
  93. '<j l="17" w="45">second</j>'#10+
  94. '<j l="16" w="78">third</j>'#10+
  95. '<j l="12" w="33">fourth</j>'#10+
  96. '</doc>';
  97. simple = '<doc>test</doc>';
  98. bool58 = '<doc>'#10+
  99. '<av>'#10+
  100. ' <a>'#10+
  101. ' <b>b</b>'#10+
  102. ' <c>c</c>'#10+
  103. ' <d>d</d>'#10+
  104. ' <e>e</e>'#10+
  105. ' </a>'#10+
  106. ' <v>'#10+
  107. ' <w>w</w>'#10+
  108. ' <x>x</x>'#10+
  109. ' <y>y</y>'#10+
  110. ' <z>z</z>'#10+
  111. ' </v>'#10+
  112. ' <a>'#10+
  113. ' <b>fe</b>'#10+
  114. ' <c>fi</c>'#10+
  115. ' <d>fo</d>'#10+
  116. ' <e>fu</e>'#10+
  117. ' </a>'#10+
  118. ' <v>'#10+
  119. ' <w>fee</w>'#10+
  120. ' <x>fii</x>'#10+
  121. ' <y>foo</y>'#10+
  122. ' <z>fom</z>'#10+
  123. ' </v>'#10+
  124. ' <j>foo</j>'#10+
  125. ' <j>foo</j>'#10+
  126. ' <j>foo</j>'#10+
  127. ' <j>foo</j>'#10+
  128. '</av>'#10+
  129. '</doc>';
  130. bool84='<doc>'#10+
  131. '<avj>'#10+
  132. ' <good>'#10+
  133. ' <b>12</b>'#10+
  134. ' <c>34</c>'#10+
  135. ' <d>56</d>'#10+
  136. ' <e>78</e>'#10+
  137. ' </good>'#10+
  138. '</avj>'#10+
  139. '</doc>';
  140. bool85='<doc>'#10+
  141. '<avj>'#10+
  142. ' <bool>'#10+
  143. ' <b>true</b>'#10+
  144. ' <c></c>'#10+
  145. ' <d>false?</d>'#10+
  146. ' <e>1</e>'#10+
  147. ' <f>0</f>'#10+
  148. ' </bool>'#10+
  149. '</avj>'#10+
  150. '</doc>';
  151. str04='<doc>'#10+
  152. '<a>Testing this</a>'#10+
  153. '<b>and this too</b>'#10+
  154. '</doc>';
  155. NodesetCompareTests: array[0..38] of TTestRec = (
  156. { same nodeset }
  157. (data: nscmp; expr: 'j[@l="12"] = j[@w="33"]'; rt: rtBool; b: True), // #70
  158. { disjoint nodesets }
  159. (data: nscmp; expr: 'j[@l="12"] = j[@l="17"]'; rt: rtBool; b: False), // #71
  160. { both have one common node }
  161. (data: nscmp2; expr: 'j[@l="12"] = j[@w="45"]'; rt: rtBool; b: True), // #72
  162. { same nodeset - unequal }
  163. (data: nscmp; expr: 'j[@l="12"] != j[@w="33"]'; rt: rtBool; b: True), // #73
  164. { disjoint - unequal }
  165. (data: nscmp; expr: 'j[@l="12"] != j[@l="17"]'; rt: rtBool; b: True), // #74
  166. { one common node - unequal }
  167. (data: nscmp2; expr: 'j[@l="12"] != j[@w="45"]'; rt: rtBool; b: True), // #75
  168. { single common node - unequal }
  169. (data: nscmp2; expr: 'j[@l="16"] != j[@w="78"]'; rt: rtBool; b: False),// #76
  170. { nodeset vs. string }
  171. (data: bool58; expr: '/doc/av//*="foo"'; rt: rtBool; b: True), // #58.1
  172. (data: bool58; expr: 'not(/doc/av//*!="foo")'; rt: rtBool; b: False), // #58.2
  173. (data: bool58; expr: '/doc/av//j="foo"'; rt: rtBool; b: True), // #58.3
  174. (data: bool58; expr: 'not(/doc/av//j!="foo")'; rt: rtBool; b: True), // #58.4
  175. { empty nodeset vs. string. Data differs, but that doesn't matter }
  176. (data: bool58; expr: '/doc/avj//k="foo"'; rt: rtBool; b: False), // #59.1
  177. (data: bool58; expr: 'not(/doc/avj//k="foo")'; rt: rtBool; b: True), // #59.2
  178. (data: bool58; expr: '/doc/avj//k!="foo"'; rt: rtBool; b: False), // #59.3
  179. (data: bool58; expr: 'not(/doc/avj//k!="foo")'; rt: rtBool; b: True), // #59.4
  180. { nodeset vs. number }
  181. (data: bool84; expr: '/doc/avj/good/*=34'; rt: rtBool; b: True), // #84.1
  182. (data: bool84; expr: 'not(/doc/avj/good/*=34)'; rt: rtBool; b: False), // #84.2
  183. (data: bool84; expr: '/doc/avj/good/*!=34'; rt: rtBool; b: True), // #84.3
  184. (data: bool84; expr: 'not(/doc/avj/good/*!=34)'; rt: rtBool; b: False),// #84.4
  185. { same with reversed order of operands }
  186. (data: bool84; expr: '34=/doc/avj/good/*'; rt: rtBool; b: True), // #84.5
  187. (data: bool84; expr: 'not(34=/doc/avj/good/*)'; rt: rtBool; b: False), // #84.6
  188. (data: bool84; expr: '34!=/doc/avj/good/*'; rt: rtBool; b: True), // #84.7
  189. (data: bool84; expr: 'not(34!=/doc/avj/good/*)'; rt: rtBool; b: False),// #84.8
  190. { nodeset vs. boolean }
  191. (data: bool85; expr: '/doc/avj/bool/*=true()'; rt: rtBool; b: True), // #85.1
  192. (data: bool85; expr: 'not(/doc/avj/bool/*=true())'; rt: rtBool; b: False), // #85.2
  193. (data: bool85; expr: '/doc/avj/bool/*!=true()'; rt: rtBool; b: False), // #85.3
  194. (data: bool85; expr: 'not(/doc/avj/bool/*!=true())'; rt: rtBool; b: True), // #85.4
  195. { same with reversed order of operands }
  196. (data: bool85; expr: 'true()=/doc/avj/bool/*'; rt: rtBool; b: True), // #85.5
  197. (data: bool85; expr: 'not(true()=/doc/avj/bool/*)'; rt: rtBool; b: False), // #85.6
  198. (data: bool85; expr: 'true()!=/doc/avj/bool/*'; rt: rtBool; b: False), // #85.7
  199. (data: bool85; expr: 'not(true()!=/doc/avj/bool/*)'; rt: rtBool; b: True), // #85.8
  200. { empty nodeset vs. boolean }
  201. (data: bool85; expr: '/doc/avj/none/*=true()'; rt: rtBool; b: False), // #86.1
  202. (data: bool85; expr: 'not(/doc/avj/none/*=true())'; rt: rtBool; b: True), // #86.2
  203. (data: bool85; expr: '/doc/avj/none/*!=true()'; rt: rtBool; b: True), // #86.3
  204. (data: bool85; expr: 'not(/doc/avj/none/*!=true())'; rt: rtBool; b: False),// #86.4
  205. { same with reversed order of operands }
  206. (data: bool85; expr: 'true()=/doc/avj/none/*'; rt: rtBool; b: False), // #86.5
  207. (data: bool85; expr: 'not(true()=/doc/avj/none/*)'; rt: rtBool; b: True), // #86.6
  208. (data: bool85; expr: 'true()!=/doc/avj/none/*'; rt: rtBool; b: True), // #86.7
  209. (data: bool85; expr: 'not(true()!=/doc/avj/none/*)'; rt: rtBool; b: False) // #86.8
  210. );
  211. EqualityTests: array[0..25] of TTestRec = (
  212. (expr: '1=1'; rt: rtBool; b: True),
  213. (expr: '1!=1'; rt: rtBool; b: False),
  214. (expr: '1=0'; rt: rtBool; b: False),
  215. (expr: '1!=0'; rt: rtBool; b: True),
  216. (expr: 'true()=true()'; rt: rtBool; b: True),
  217. (expr: 'true()!=true()'; rt: rtBool; b: False),
  218. (expr: 'true()=false()'; rt: rtBool; b: False),
  219. (expr: 'false()!=true()'; rt: rtBool; b: True),
  220. (expr: '"test"="test"'; rt: rtBool; b: True),
  221. (expr: '"test"!="test"'; rt: rtBool; b: False),
  222. (expr: '"test2"="test"'; rt: rtBool; b: False),
  223. (expr: '"test2"!="test"'; rt: rtBool; b: True),
  224. (expr: 'false()=0'; rt: rtBool; b: True),
  225. (expr: 'false()!=0'; rt: rtBool; b: False),
  226. (expr: 'false()=1'; rt: rtBool; b: False),
  227. (expr: 'false()!=1'; rt: rtBool; b: True),
  228. (expr: '0=true()'; rt: rtBool; b: False),
  229. (expr: '0!=true()'; rt: rtBool; b: True),
  230. (expr: '1=true()'; rt: rtBool; b: True),
  231. (expr: '1!=true()'; rt: rtBool; b: False),
  232. (expr: 'true()="test"'; rt: rtBool; b: True),
  233. (expr: 'false()="test"'; rt: rtBool; b: False),
  234. (expr: '"test"!=true()'; rt: rtBool; b: False),
  235. (expr: '"test"!=false()'; rt: rtBool; b: True),
  236. (expr: '"a"=0.0'; rt: rtBool; b: False),
  237. (expr: '"a"!=0.0'; rt: rtBool; b: True)
  238. );
  239. math88='<doc>'+
  240. '<n0>0</n0>'+
  241. '<n1>1</n1>'+
  242. '<n2>2</n2>'+
  243. '<n3>3</n3>'+
  244. '<n4>4</n4>'+
  245. '<n5>5</n5>'+
  246. '<n6>6</n6>'+
  247. '<n7>2</n7>'+
  248. '<n8>6</n8>'+
  249. '<n9>10</n9>'+
  250. '<n10>3</n10>'+
  251. '</doc>';
  252. math85='<doc>'+
  253. '<n0>0</n0>'+
  254. '<n1>1</n1>'+
  255. '<n2>2</n2>'+
  256. '<n3>3</n3>'+
  257. '<n4>4</n4>'+
  258. '<e>five</e>'+
  259. '</doc>';
  260. math80='<doc>'+
  261. '<n1 attrib="10">5</n1>'+
  262. '<n2 attrib="4">2</n2>'+
  263. '<div attrib="-5">-5</div>'+
  264. '<mod attrib="-2">2</mod>'+
  265. '</doc>';
  266. math69='<doc>'+
  267. '<n-1 attrib="9">3</n-1>'+
  268. '<n-2 attrib="1">7</n-2>'+
  269. '</doc>';
  270. FloatTests: array[0..70] of TTestRec = (
  271. (expr: '1'; rt: rtNumber; n: 1),
  272. (expr: '123'; rt: rtNumber; n: 123),
  273. (expr: '1.23'; rt: rtNumber; n: 1.23),
  274. (expr: '0.123'; rt: rtNumber; n: 0.123),
  275. (expr: '4.'; rt: rtNumber; n: 4),
  276. (expr: '.4'; rt: rtNumber; n: 0.4),
  277. //(expr: '1.23e3'; rt: rtNumber; n: 1230),
  278. //(expr: '1.23e-3'; rt: rtNumber; n: 0.00123),
  279. (expr: '1 div 0'; rt: rtNumber; n: Infinity),
  280. (expr: '-1 div 0'; rt: rtNumber; n: -Infinity),
  281. (expr: '0 div 0'; rt: rtNumber; n: NaN),
  282. (expr: '1 div -0'; rt: rtNumber; n: -Infinity),
  283. (expr: '(1 div 0) > 0'; rt: rtBool; b: True),
  284. (expr: '(1 div 0) < 0'; rt: rtBool; b: False),
  285. (expr: '(-1 div 0) > 0'; rt: rtBool; b: False),
  286. (expr: '(-1 div 0) < 0'; rt: rtBool; b: True),
  287. (expr: '(0 div 0) > 0'; rt: rtBool; b: False),
  288. (expr: '(0 div 0) < 0'; rt: rtBool; b: False),
  289. (expr: '(1 div -0) > 0'; rt: rtBool; b: False),
  290. (expr: '(1 div -0) < 0'; rt: rtBool; b: True),
  291. (expr: '0 div 0 = 0 div 0'; rt: rtBool; b: False),
  292. (expr: '0 div 0 != 0 div 0'; rt: rtBool; b: True),
  293. (expr: '0 div 0 > 0 div 0'; rt: rtBool; b: False),
  294. (expr: '0 div 0 < 0 div 0'; rt: rtBool; b: False),
  295. (expr: '0 div 0 >= 0 div 0'; rt: rtBool; b: False),
  296. (expr: '0 div 0 <= 0 div 0'; rt: rtBool; b: False),
  297. (expr: '1 div 0 = -1 div 0'; rt: rtBool; b: False),
  298. (expr: '1 div 0 != -1 div 0'; rt: rtBool; b: True),
  299. (expr: '1 div 0 > -1 div 0'; rt: rtBool; b: True),
  300. (expr: '1 div 0 < -1 div 0'; rt: rtBool; b: False),
  301. (expr: '1 div 0 >= -1 div 0'; rt: rtBool; b: True),
  302. (expr: '1 div 0 <= -1 div 0'; rt: rtBool; b: False),
  303. (expr: '1 div 0 = 1 div 0'; rt: rtBool; b: True),
  304. (expr: '1 div 0 != 1 div 0'; rt: rtBool; b: False),
  305. (expr: '1 div 0 > 1 div 0'; rt: rtBool; b: False),
  306. (expr: '1 div 0 < 1 div 0'; rt: rtBool; b: False),
  307. (expr: '1 div 0 >= -1 div 0'; rt: rtBool; b: True),
  308. (expr: '1 div 0 <= -1 div 0'; rt: rtBool; b: False),
  309. (expr: '-2 div 0 = -1 div 0'; rt: rtBool; b: True),
  310. (expr: '1 div floor(0.1)'; rt: rtNumber; n: Infinity),
  311. (expr: '1 div floor(-0.1)'; rt: rtNumber; n: -1),
  312. (expr: '1 div floor(-0)'; rt: rtNumber; n: -Infinity),
  313. (expr: '1 div floor(0)'; rt: rtNumber; n: Infinity),
  314. (expr: '1 div ceiling(0.1)'; rt: rtNumber; n: 1),
  315. (expr: '1 div ceiling(-0.1)'; rt: rtNumber; n: -Infinity),
  316. (expr: '1 div ceiling(-0)'; rt: rtNumber; n: -Infinity),
  317. (expr: '1 div ceiling(0)'; rt: rtNumber; n: Infinity),
  318. (expr: '1 div round(0.1)'; rt: rtNumber; n: Infinity),
  319. (expr: '1 div round(-0.1)'; rt: rtNumber; n: -Infinity),
  320. (expr: '1 div round(-0)'; rt: rtNumber; n: -Infinity),
  321. (expr: '1 div round(0)'; rt: rtNumber; n: Infinity),
  322. (expr: '1 div number("f")'; rt: rtNumber; n: NaN),
  323. (expr: 'number("f") div 1'; rt: rtNumber; n: NaN),
  324. (expr: '1 div (1 div 0)'; rt: rtNumber; n: 0),
  325. (expr: '(1 div 0) div 1'; rt: rtNumber; n: Infinity),
  326. (expr: '-(1 div 0) div 1'; rt: rtNumber; n: -Infinity),
  327. (expr: '5 mod 2'; rt: rtNumber; n: 1),
  328. (expr: '5 mod -2'; rt: rtNumber; n: 1),
  329. (expr: '-5 mod 2'; rt: rtNumber; n: -1),
  330. (expr: '-5 mod -2'; rt: rtNumber; n: -1),
  331. (expr: '2 mod number("xxx")'; rt: rtNumber; n: NaN),
  332. (expr: 'number("xxx") mod 3'; rt: rtNumber; n: NaN),
  333. (expr: '8 mod 3 = 2'; rt: rtBool; b: True),
  334. (data: math88; expr: '(n1*n2*n3*n4*n5*n6)div n7 div n8 div n9 div n10'; rt: rtNumber; n: 2),
  335. (data: math85; expr: '((((((n3+5)*(3)+(((n2)+2)*(n1 - 6)))-(n4 - n2))+(-(4-6)))))'; rt: rtNumber; n: 4),
  336. (data: math80; expr: 'div mod mod'; rt: rtNumber; n: -1),
  337. (data: math69; expr: '-(n-2/@attrib) - -(n-1/@attrib)'; rt: rtNumber; n: 8),
  338. (data: math69; expr: '-n-2/@attrib --n-1/@attrib'; rt: rtNumber; n: 8),
  339. (data: math69; expr: '-n-2 --n-1'; rt: rtNumber; n: -4),
  340. // test boolean operator short-circuting; "count(5)" acts as an error
  341. (expr: '10+30*20 or count(5)'; rt: rtBool; b: True),
  342. (expr: '75-50-25 and count(5)'; rt: rtBool; b: False),
  343. (expr: '"1" and "0"'; rt: rtBool; b: True),
  344. (expr: '0 or ""'; rt: rtBool; b: False)
  345. );
  346. math95='<doc>'+
  347. '<e>1</e>'+
  348. '<e>2</e>'+
  349. '<e>3</e>'+
  350. '<e>4</e>'+
  351. '<e>five</e>'+
  352. '</doc>';
  353. math96='<doc>'+
  354. '<e>17</e>'+
  355. '<e>-5</e>'+
  356. '<e>8</e>'+
  357. '<e>-37</e>'+
  358. '</doc>';
  359. expr01='<doc>'+
  360. '<para id="1" xml:lang="en">en</para>'+
  361. '<div xml:lang="en">'+
  362. ' <para>en</para>'+
  363. '</div>'+
  364. '<para id="3" xml:lang="EN">EN</para>'+
  365. '<para id="4" xml:lang="en-us">en-us</para>'+
  366. '</doc>';
  367. FunctionTests: array[0..49] of TTestRec = (
  368. // last()
  369. // position()
  370. // count()
  371. // id()
  372. // local-name()
  373. // namespace-uri()
  374. // name()
  375. (expr: 'boolean(0)'; rt: rtBool; b: False),
  376. (expr: 'boolean(-0)'; rt: rtBool; b: False),
  377. (expr: 'boolean(1 div 0)'; rt: rtBool; b: True),
  378. (expr: 'boolean(-1 div 0)'; rt: rtBool; b: True),
  379. (expr: 'boolean(0 div 0)'; rt: rtBool; b: False),
  380. (expr: 'boolean("")'; rt: rtBool; b: False),
  381. (expr: 'boolean("abc")'; rt: rtBool; b: True),
  382. (data: simple; expr: 'boolean(/doc)'; rt: rtBool; b: True), // #40
  383. (data: simple; expr: 'boolean(foo)'; rt: rtBool; b: False), // #41
  384. (expr: 'true()'; rt: rtBool; b: True),
  385. (expr: 'false()'; rt: rtBool; b: False),
  386. (expr: 'not(true())'; rt: rtBool; b: False),
  387. (expr: 'not(false())'; rt: rtBool; b: True),
  388. (expr: 'not("")'; rt: rtBool; b: True),
  389. // lang() tests. These ones, however, test much more than lang().
  390. // Moreover, I've added string(), otherwise result would be a nodeset
  391. (data: expr01; expr: 'string(para[@id="1" and lang("en")])'; rt: rtString; s: 'en'), // expression01
  392. (data: expr01; expr: 'string(para[@id="4" and lang("en")])'; rt: rtString; s: 'en-us'), // expression03
  393. (data: expr01; expr: 'string(div/para[lang("en")])'; rt: rtString; s: 'en'), // expression04
  394. (data: expr01; expr: 'string(para[@id="3" and lang("en")])'; rt: rtString; s: 'EN'), // expression05
  395. (expr: 'number("1.5")'; rt: rtNumber; n: 1.5),
  396. (expr: 'number("abc")'; rt: rtNumber; n: NaN),
  397. (expr: '-number("abc")'; rt: rtNumber; n: NaN),
  398. (expr: 'number(true())'; rt: rtNumber; n: 1.0),
  399. (expr: 'number(false())'; rt: rtNumber; n: 0),
  400. (data: math95; expr: 'sum(e)'; rt: rtNumber; n: NaN),
  401. (data: math96; expr: 'sum(e)'; rt: rtNumber; n: -17),
  402. (expr: 'floor(0.1)'; rt: rtNumber; n: 0),
  403. (expr: 'floor(-0.1)'; rt: rtNumber; n: -1),
  404. (expr: 'floor(-0)'; rt: rtNumber; n: 0),
  405. (expr: 'floor(0)'; rt: rtNumber; n: 0),
  406. (expr: 'floor(5.2)'; rt: rtNumber; n: 5),
  407. (expr: 'floor(-5.2)'; rt: rtNumber; n: -6),
  408. (expr: 'floor("NaN")'; rt: rtNumber; n: NaN),
  409. (expr: 'ceiling(0.1)'; rt: rtNumber; n: 1),
  410. (expr: 'ceiling(-0.1)'; rt: rtNumber; n: 0),
  411. (expr: 'ceiling(-0)'; rt: rtNumber; n: 0),
  412. (expr: 'ceiling(0)'; rt: rtNumber; n: 0),
  413. (expr: 'ceiling(5.2)'; rt: rtNumber; n: 6),
  414. (expr: 'ceiling(-5.2)'; rt: rtNumber; n: -5),
  415. (expr: 'ceiling("NaN")'; rt: rtNumber; n: NaN),
  416. (expr: 'round(0.1)'; rt: rtNumber; n: 0),
  417. (expr: 'round(5.2)'; rt: rtNumber; n: 5),
  418. (expr: 'round(5.5)'; rt: rtNumber; n: 6),
  419. (expr: 'round(5.6)'; rt: rtNumber; n: 6),
  420. (expr: 'round(-0.1)'; rt: rtNumber; n: 0),
  421. (expr: 'round(-5.2)'; rt: rtNumber; n: -5),
  422. (expr: 'round(-5.5)'; rt: rtNumber; n: -5),
  423. (expr: 'round(-5.6)'; rt: rtNumber; n: -6),
  424. (expr: 'round("NaN")'; rt: rtNumber; n: NaN),
  425. (expr: 'round(1 div 0)'; rt: rtNumber; n: Infinity),
  426. (expr: 'round(-1 div 0)'; rt: rtNumber; n: -Infinity)
  427. );
  428. str14 ='<doc>'#10+
  429. ' <av>'#10+
  430. ' <a>'#10+
  431. ' <b>b</b>'#10+
  432. ' <c>c</c>'#10+
  433. ' <d>d</d>'#10+
  434. ' <e>e</e>'#10+
  435. ' </a>'#10+
  436. ' <v>'#10+
  437. ' <w>w</w>'#10+
  438. ' <x>x</x>'#10+
  439. ' <y>y</y>'#10+
  440. ' <z>z</z>'#10+
  441. ' </v>'#10+
  442. ' </av>'#10+
  443. '</doc>';
  444. out14 =#10+
  445. ' b'#10+
  446. ' c'#10+
  447. ' d'#10+
  448. ' e'#10+
  449. ' ';
  450. StringTests: array[0..59] of TTestRec = (
  451. (expr: 'string(5)'; rt: rtString; s: '5'),
  452. (expr: 'string(0.5)'; rt: rtString; s: '0.5'),
  453. (expr: 'string(-0.5)'; rt: rtString; s: '-0.5'),
  454. (expr: 'string(true())'; rt: rtString; s: 'true'),
  455. (expr: 'string(false())'; rt: rtString; s: 'false'),
  456. (expr: 'string(0 div 0)'; rt: rtString; s: 'NaN'),
  457. (expr: 'string(1 div 0)'; rt: rtString; s: 'Infinity'),
  458. (expr: 'string(-1 div 0)'; rt: rtString; s: '-Infinity'),
  459. // maybe other checks for correct numeric formats
  460. (data: str14; expr: 'string(av//*)'; rt: rtString; s: out14),
  461. (expr: 'concat("titi","toto")'; rt: rtString; s: 'tititoto'),
  462. (expr: 'concat("titi","toto","tata")'; rt: rtString; s: 'tititototata'),
  463. (expr: 'concat("titi",''toto'')'; rt: rtString; s: 'tititoto'),
  464. (expr: 'concat("titi",''toto'',"tata","last")'; rt: rtString; s: 'tititototatalast'),
  465. (expr: 'concat("cd", 34)'; rt: rtString; s: 'cd34'), // #101
  466. (expr: 'concat(false(), "ly")'; rt: rtString; s: 'falsely'), // #104
  467. (expr: 'starts-with("tititoto","titi")'; rt: rtBool; b: True),
  468. (expr: 'starts-with("tititoto","to")'; rt: rtBool; b: False),
  469. (expr: 'starts-with("ab", "abc")'; rt: rtBool; b: False),
  470. (expr: 'starts-with("abc", "")'; rt: rtBool; b: True), // xalan/string/string48
  471. (expr: 'starts-with("", "")'; rt: rtBool; b: True), // #49
  472. (expr: 'contains("tititototata","titi")'; rt: rtBool; b: True),
  473. (expr: 'contains("tititototata","toto")'; rt: rtBool; b: True),
  474. (expr: 'contains("tititototata","tata")'; rt: rtBool; b: True),
  475. (expr: 'contains("tititototata","tita")'; rt: rtBool; b: False),
  476. (expr: 'contains("ab", "abc")'; rt: rtBool; b: False), // #59
  477. (expr: 'contains("abc", "bcd")'; rt: rtBool; b: False), // #60
  478. (expr: 'contains("abc", "")'; rt: rtBool; b: True), // #61
  479. (expr: 'contains("", "")'; rt: rtBool; b: True), // #62
  480. // 'contains(concat(.,'BC'),concat('A','B','C'))' == true
  481. (expr: 'substring("12345",2,3)'; rt: rtString; s: '234'),
  482. (expr: 'substring("12345",2)'; rt: rtString; s: '2345'),
  483. (expr: 'substring("12345",-4)'; rt: rtString; s: '12345'),
  484. (expr: 'substring("12345",3.4)'; rt: rtString; s: '345'),
  485. (expr: 'substring("12345",3.6)'; rt: rtString; s: '45'),
  486. (expr: 'substring("12345",1.5,2.6)'; rt: rtString; s: '234'),
  487. (expr: 'substring("12345",2.2,2.2)'; rt: rtString; s: '23'),
  488. (expr: 'substring("12345",0,3)'; rt: rtString; s: '12'),
  489. (expr: 'substring("12345",-8,10)'; rt: rtString; s: '1'),
  490. (expr: 'substring("12345",4,-10)'; rt: rtString; s: ''),
  491. (expr: 'substring("12345",0 div 0, 3)'; rt: rtString; s: ''),
  492. (expr: 'substring("12345",1, 0 div 0)'; rt: rtString; s: ''),
  493. (expr: 'substring("12345",1 div 0, 3)'; rt: rtString; s: ''),
  494. (expr: 'substring("12345",3,-1 div 0)'; rt: rtString; s: ''),
  495. (expr: 'substring("12345",-42, 1 div 0)'; rt: rtString; s: '12345'),
  496. (expr: 'substring("12345",-1 div 0, 1 div 0)'; rt: rtString; s: ''),
  497. (expr: 'substring("12345",-1 div 0,5)'; rt: rtString; s: ''),
  498. (expr: 'substring-before("1999/04/01","/")'; rt: rtString; s: '1999'),
  499. (expr: 'substring-before("1999/04/01","a")'; rt: rtString; s: ''),
  500. (expr: 'substring-after("1999/04/01","/")'; rt: rtString; s: '04/01'),
  501. (expr: 'substring-after("1999/04/01","19")'; rt: rtString; s: '99/04/01'),
  502. (expr: 'substring-after("1999/04/01","a")'; rt: rtString; s: ''),
  503. (expr: 'string-length("")'; rt: rtNumber; n: 0),
  504. (expr: 'string-length("titi")'; rt: rtNumber; n: 4),
  505. (data: simple; expr: 'string-length(.)'; rt: rtNumber; n: 4), // #02 modified
  506. (data: str04; expr: 'string-length(/)'; rt: rtNumber; n:27), // #04.1 modified
  507. (data: str04; expr: 'string-length(/doc/a)'; rt: rtNumber; n: 12), // #04.2
  508. (data: str04; expr: 'string-length()'; rt: rtNumber; n: 27),
  509. (expr: 'normalize-space("'#9#10#13' ab cd'#10#13#9'ef'#9#10#13' ")'; rt: rtString; s: 'ab cd ef'),
  510. (expr: 'translate("bar", "abc", "ABC")'; rt: rtString; s: 'BAr'),
  511. (expr: 'translate("--aaa--","abc-","ABC")'; rt: rtString; s: 'AAA'),
  512. (expr: 'translate("ddaaadddd","abcd","ABCxy")'; rt: rtString; s: 'xxAAAxxxx') // #96
  513. );
  514. {$warnings on}
  515. var
  516. FailCount: Integer = 0;
  517. procedure CheckResult(const t: TTestRec; r: TXPathVariable);
  518. begin
  519. case t.rt of
  520. rtBool:
  521. begin
  522. if (r is TXPathBooleanVariable) and (r.AsBoolean = t.b) then
  523. Exit;
  524. writeln;
  525. writeln('Failed: ', t.expr);
  526. writeln('Expected: ', t.b, ' got: ', r.AsBoolean);
  527. end;
  528. rtNumber:
  529. begin
  530. if (r is TXPathNumberVariable) then
  531. begin
  532. if IsNan(t.n) and IsNan(r.AsNumber) then
  533. Exit;
  534. if IsInfinite(t.n) and (t.n = r.AsNumber) then
  535. Exit;
  536. if SameValue(r.AsNumber, t.n) then
  537. Exit;
  538. end;
  539. writeln;
  540. writeln('Failed: ', t.expr);
  541. writeln('Expected: ', t.n, ' got: ', r.AsNumber);
  542. end;
  543. rtString:
  544. begin
  545. if (r is TXPathStringVariable) and (r.AsText = DOMString(t.s)) then
  546. Exit;
  547. writeln;
  548. writeln('Failed: ', t.expr);
  549. writeln('Expected: ', DOMString(t.s), ' got: ', r.AsText);
  550. end;
  551. end;
  552. Inc(FailCount);
  553. end;
  554. function ParseString(const data: string): TXMLDocument;
  555. var
  556. parser: TDOMParser;
  557. src: TXMLInputSource;
  558. begin
  559. parser := TDOMParser.Create;
  560. try
  561. parser.Options.PreserveWhitespace := True;
  562. src := TXMLInputSource.Create(data);
  563. try
  564. parser.Parse(src, Result);
  565. finally
  566. src.Free;
  567. end;
  568. finally
  569. parser.Free;
  570. end;
  571. end;
  572. procedure DoSuite(const tests: array of TTestRec);
  573. var
  574. i: Integer;
  575. doc: TXMLDocument;
  576. rslt: TXPathVariable;
  577. begin
  578. for i := 0 to High(tests) do
  579. begin
  580. if tests[i].data <> '' then
  581. doc := ParseString(tests[i].data)
  582. else
  583. doc := TXMLDocument.Create;
  584. try
  585. try
  586. rslt := EvaluateXPathExpression(tests[i].expr, doc.DocumentElement);
  587. try
  588. CheckResult(tests[i], rslt);
  589. finally
  590. rslt.Free;
  591. end;
  592. except
  593. writeln;
  594. writeln('Failed: ', tests[i].expr);
  595. SysUtils.ShowException(ExceptObject, ExceptAddr);
  596. Inc(FailCount);
  597. end;
  598. finally
  599. doc.Free;
  600. end;
  601. end;
  602. end;
  603. begin
  604. DecimalSeparator := '.';
  605. DoSuite(BaseTests);
  606. DoSuite(CompareTests);
  607. DoSuite(NodesetCompareTests);
  608. DoSuite(EqualityTests);
  609. DoSuite(FloatTests);
  610. DoSuite(FunctionTests);
  611. DoSuite(StringTests);
  612. writeln;
  613. writeln('Total failed tests: ', FailCount);
  614. end.