| 1 |
/* |
|---|
| 2 |
This is an example source code and a regression test program for the TinyXPath project |
|---|
| 3 |
We load a very small test.xml file and test the return value of the string variant of |
|---|
| 4 |
TinyXPath against a known output. |
|---|
| 5 |
The LIBXML_CHECK define may be turned ON if we need to verify the output against libxml |
|---|
| 6 |
(from the Gnome project). |
|---|
| 7 |
*/ |
|---|
| 8 |
|
|---|
| 9 |
|
|---|
| 10 |
//#include "xpath_static.h" |
|---|
| 11 |
//#include "htmlutil.h" |
|---|
| 12 |
|
|---|
| 13 |
import xpath.xpath_static; |
|---|
| 14 |
import xpath.xpath_processor; |
|---|
| 15 |
import xpath.htmlutil; |
|---|
| 16 |
import std.stream; |
|---|
| 17 |
import std.stdio; |
|---|
| 18 |
File out_html; |
|---|
| 19 |
|
|---|
| 20 |
void v_out_one_line (char[] cp_expr, char[] cp_res, char[] cp_expected, bool o_ok) |
|---|
| 21 |
{ |
|---|
| 22 |
out_html.writef("<tr><td>%s</td>", cp_expr); |
|---|
| 23 |
if (o_ok) |
|---|
| 24 |
out_html.writef("<td>%s</td><td>%s</td></tr>\n", cp_res, cp_expected); |
|---|
| 25 |
else |
|---|
| 26 |
out_html.writef("<td><em>%s</em></td><td><em>%s</em></td></tr>\n", cp_res, cp_expected); |
|---|
| 27 |
} |
|---|
| 28 |
|
|---|
| 29 |
|
|---|
| 30 |
void v_test_one_string_tiny (TiXmlNode XNp_root, char[] cp_expr, char[] cp_expected) |
|---|
| 31 |
{ |
|---|
| 32 |
char[] S_res; |
|---|
| 33 |
bool o_ok; |
|---|
| 34 |
|
|---|
| 35 |
|
|---|
| 36 |
writefln("-- expr : [%s] --", cp_expr); |
|---|
| 37 |
S_res = S_xpath_string (XNp_root, cp_expr); |
|---|
| 38 |
o_ok = (S_res == cp_expected); |
|---|
| 39 |
v_out_one_line (cp_expr, S_res, cp_expected, o_ok); |
|---|
| 40 |
} |
|---|
| 41 |
|
|---|
| 42 |
alias v_test_one_string_tiny v_test_one_string ; |
|---|
| 43 |
|
|---|
| 44 |
|
|---|
| 45 |
int main () |
|---|
| 46 |
{ |
|---|
| 47 |
TiXmlDocument XDp_doc; |
|---|
| 48 |
TiXmlElement XEp_main; |
|---|
| 49 |
int i_res; |
|---|
| 50 |
char[] ca_res; |
|---|
| 51 |
bool o_ok, o_res; |
|---|
| 52 |
double d_out; |
|---|
| 53 |
|
|---|
| 54 |
XDp_doc = new TiXmlDocument; |
|---|
| 55 |
if (! XDp_doc . LoadFile ("test.xml")) |
|---|
| 56 |
{ |
|---|
| 57 |
printf ("Can't find test.xml !\n"); |
|---|
| 58 |
return 99; |
|---|
| 59 |
} |
|---|
| 60 |
out_html = new File("out.htm", FileMode.OutNew); |
|---|
| 61 |
out_html.writeLine("<html><head><title>Result</title>\n<style>"); |
|---|
| 62 |
out_html.writeLine("em{color: red;}</style>\n"); |
|---|
| 63 |
out_html.writeLine("</head><body>"); |
|---|
| 64 |
|
|---|
| 65 |
out_html.writeLine("<h1>TinyXPath examples / regression tests</h1>"); |
|---|
| 66 |
out_html.writeLine("<h2>Input XML tree</h2>"); |
|---|
| 67 |
v_out_html (out_html, XDp_doc, 0); |
|---|
| 68 |
out_html.writeLine("<br /><br />"); |
|---|
| 69 |
out_html.writeLine("<table border='1'><tr><th>Expression</th><th>Result</th><th>Expected (compiled)</th></tr>\n"); |
|---|
| 70 |
XEp_main = XDp_doc . RootElement (); |
|---|
| 71 |
|
|---|
| 72 |
out_html.writeLine("<h2>Results</h2>"); |
|---|
| 73 |
TiXmlElement XEp_sub = XEp_main . FirstChildElement ("b"); |
|---|
| 74 |
|
|---|
| 75 |
v_test_one_string (XEp_sub, "@val", "123" ); |
|---|
| 76 |
|
|---|
| 77 |
// v_test_one_string (XEp_main, "//x/y/text()", "inner text" ); |
|---|
| 78 |
// v_test_one_string (XEp_main, "//x[/y/text()='inner text']/@target", "123" ); |
|---|
| 79 |
v_test_one_string (XEp_main, "//x/text()", "sub text"); |
|---|
| 80 |
v_test_one_string (XEp_main, "//*/comment()", " -122.0 "); |
|---|
| 81 |
v_test_one_string (XEp_main, "count(//*/comment())", "2"); |
|---|
| 82 |
v_test_one_string (XEp_main, "sum(//@*)", "123"); |
|---|
| 83 |
v_test_one_string (XEp_main, "sum(//*/comment())", "378"); |
|---|
| 84 |
v_test_one_string (XEp_main, "true()", "true"); |
|---|
| 85 |
v_test_one_string (XEp_main, "not(false())", "true"); |
|---|
| 86 |
|
|---|
| 87 |
v_test_one_string (XEp_main, "count(//*[position()=2])", "2"); |
|---|
| 88 |
v_test_one_string (XEp_main, "name(/*/*/*[position()=2])", "c"); |
|---|
| 89 |
v_test_one_string (XEp_main, "name(/*/*/*[last()])", "d"); |
|---|
| 90 |
|
|---|
| 91 |
v_test_one_string (XEp_main, "count(//c/following::*)", "2"); |
|---|
| 92 |
v_test_one_string (XEp_main, "count(/a/b/b/following::*)", "3"); |
|---|
| 93 |
v_test_one_string (XEp_main, "count(//d/preceding::*)", "2"); |
|---|
| 94 |
v_test_one_string (XEp_main, "name(//attribute::*)", "val"); |
|---|
| 95 |
v_test_one_string (XEp_main, "count(//b/child::*)", "3"); |
|---|
| 96 |
v_test_one_string (XEp_main, "count(//x/ancestor-or-self::*)", "2"); |
|---|
| 97 |
v_test_one_string (XEp_main, "count(//b/descendant-or-self::*)", "4"); |
|---|
| 98 |
v_test_one_string (XEp_main, "count(//self::*)", "6"); |
|---|
| 99 |
v_test_one_string (XEp_main, "count(/a/descendant::*)", "5"); |
|---|
| 100 |
v_test_one_string (XEp_main, "count(/a/descendant::x)", "1"); |
|---|
| 101 |
v_test_one_string (XEp_main, "count(/a/descendant::b)", "2"); |
|---|
| 102 |
v_test_one_string (XEp_main, "count(/a/descendant::b[@val=123])", "1"); |
|---|
| 103 |
v_test_one_string (XEp_main, "count(//c/ancestor::a)", "1"); |
|---|
| 104 |
v_test_one_string (XEp_main, "name(//d/parent::*)", "b"); |
|---|
| 105 |
v_test_one_string (XEp_main, "count(//c/ancestor::*)", "2"); |
|---|
| 106 |
v_test_one_string (XEp_main, "name(/a/b/ancestor::*)", "a"); |
|---|
| 107 |
v_test_one_string (XEp_main, "name(/a/b/c/following-sibling::*)", "d"); |
|---|
| 108 |
v_test_one_string (XEp_main, "count(//b/following-sibling::*)", "3"); |
|---|
| 109 |
v_test_one_string (XEp_main, "count(//b|//a)", "3"); |
|---|
| 110 |
v_test_one_string (XEp_main, "count(//d/preceding-sibling::*)", "2"); |
|---|
| 111 |
|
|---|
| 112 |
v_test_one_string (XEp_main, "-3 * 4", "-12"); |
|---|
| 113 |
v_test_one_string (XEp_main, "-3.1 * 4", "-12.4"); |
|---|
| 114 |
v_test_one_string (XEp_main, "12 div 5", "2.4"); |
|---|
| 115 |
v_test_one_string (XEp_main, "3 * 7", "21"); |
|---|
| 116 |
|
|---|
| 117 |
v_test_one_string (XEp_main, "-5.5 >= -5.5", "true"); |
|---|
| 118 |
v_test_one_string (XEp_main, "-5.5 < 3", "true"); |
|---|
| 119 |
v_test_one_string (XEp_main, "-6.0 < -7", "false"); |
|---|
| 120 |
v_test_one_string (XEp_main, "12 < 14", "true"); |
|---|
| 121 |
v_test_one_string (XEp_main, "12 > 14", "false"); |
|---|
| 122 |
v_test_one_string (XEp_main, "14 <= 14", "true"); |
|---|
| 123 |
|
|---|
| 124 |
v_test_one_string (XEp_main, "/a or /b", "true"); |
|---|
| 125 |
v_test_one_string (XEp_main, "/c or /b", "false"); |
|---|
| 126 |
|
|---|
| 127 |
v_test_one_string (XEp_main, "/a and /b", "false"); |
|---|
| 128 |
v_test_one_string (XEp_main, "/a and /*/b", "true"); |
|---|
| 129 |
|
|---|
| 130 |
v_test_one_string (XEp_main, "18-12", "6"); |
|---|
| 131 |
v_test_one_string (XEp_main, "18+12", "30"); |
|---|
| 132 |
|
|---|
| 133 |
v_test_one_string (XEp_main, "count(//a|//b)", "3"); |
|---|
| 134 |
v_test_one_string (XEp_main, "count(//*[@val])", "1"); |
|---|
| 135 |
v_test_one_string (XEp_main, "name(//*[@val=123])", "b"); |
|---|
| 136 |
|
|---|
| 137 |
v_test_one_string (XEp_main, "3=4", "false"); |
|---|
| 138 |
v_test_one_string (XEp_main, "3!=4", "true"); |
|---|
| 139 |
v_test_one_string (XEp_main, "12=12", "true"); |
|---|
| 140 |
v_test_one_string (XEp_main, "'here is a string'='here is a string'", "true"); |
|---|
| 141 |
v_test_one_string (XEp_main, "'here is a string'!='here is a string'", "false"); |
|---|
| 142 |
|
|---|
| 143 |
v_test_one_string (XEp_main, "/a/b/@val", "123"); |
|---|
| 144 |
v_test_one_string (XEp_main, "count(//*/b)", "2"); |
|---|
| 145 |
v_test_one_string (XEp_main, "name(/*/*/*[2])", "c"); |
|---|
| 146 |
v_test_one_string (XEp_main, "name(/*)", "a"); |
|---|
| 147 |
v_test_one_string (XEp_main, "name(/a)", "a"); |
|---|
| 148 |
v_test_one_string (XEp_main, "name(/a/b)", "b"); |
|---|
| 149 |
v_test_one_string (XEp_main, "name(/*/*)", "b"); |
|---|
| 150 |
v_test_one_string (XEp_main, "name(/a/b/c)", "c"); |
|---|
| 151 |
v_test_one_string (XEp_main, "count(/a/b/*)", "3"); |
|---|
| 152 |
v_test_one_string (XEp_main, "ceiling(3.5)", "4"); |
|---|
| 153 |
v_test_one_string (XEp_main, "concat('first ','second',' third','')", "first second third"); |
|---|
| 154 |
v_test_one_string (XEp_main, "ceiling(5)", "5"); |
|---|
| 155 |
v_test_one_string (XEp_main, "floor(3.5)", "3"); |
|---|
| 156 |
v_test_one_string (XEp_main, "floor(5)", "5"); |
|---|
| 157 |
v_test_one_string (XEp_main, "string-length('try')", "3"); |
|---|
| 158 |
v_test_one_string (XEp_main, "concat(name(/a/b[1]/*[1]),' ',name(/a/b/*[2]))", "b c"); |
|---|
| 159 |
v_test_one_string (XEp_main, "count(/a/b/*)", "3"); |
|---|
| 160 |
v_test_one_string (XEp_main, "count(//*)", "6"); |
|---|
| 161 |
v_test_one_string (XEp_main, "count(//b)", "2"); |
|---|
| 162 |
v_test_one_string (XEp_main, "contains('base','as')", "true"); |
|---|
| 163 |
v_test_one_string (XEp_main, "contains('base','x')", "false"); |
|---|
| 164 |
v_test_one_string (XEp_main, "not(contains('base','as'))", "false"); |
|---|
| 165 |
v_test_one_string (XEp_main, "starts-with('blabla','bla')", "true"); |
|---|
| 166 |
v_test_one_string (XEp_main, "starts-with('blebla','bla')", "false"); |
|---|
| 167 |
v_test_one_string (XEp_main, "substring('12345',2,3)", "234"); |
|---|
| 168 |
v_test_one_string (XEp_main, "substring('12345',2)", "2345"); |
|---|
| 169 |
v_test_one_string (XEp_main, "substring('12345',2,6)", "2345"); |
|---|
| 170 |
v_test_one_string (XEp_main, "concat('[',normalize-space(' before and after '),']')", "[before and after]"); |
|---|
| 171 |
|
|---|
| 172 |
// test for u_compute_xpath_node_set |
|---|
| 173 |
|
|---|
| 174 |
uint u_nb; |
|---|
| 175 |
|
|---|
| 176 |
auto xpath_processor xp_proc = new xpath_processor(XEp_main, "//*"); // build the list of all element nodes in the tree |
|---|
| 177 |
u_nb = xp_proc . u_compute_xpath_node_set (); // retrieve number of nodes |
|---|
| 178 |
ca_res = .toString(u_nb); |
|---|
| 179 |
o_ok = (u_nb == 6); |
|---|
| 180 |
v_out_one_line ("//*", ca_res, "6", o_ok); |
|---|
| 181 |
|
|---|
| 182 |
// regression test for multiple additive expressions |
|---|
| 183 |
v_test_one_string (XEp_main, "2+3+4+5", "14"); |
|---|
| 184 |
v_test_one_string (XEp_main, "20-2-3+5", "20"); |
|---|
| 185 |
|
|---|
| 186 |
// regression test for predicate count bug |
|---|
| 187 |
v_test_one_string (XEp_main, "count(/a/x[1])", "1"); |
|---|
| 188 |
v_test_one_string (XEp_main, "name(/a/*[2])", "x"); |
|---|
| 189 |
v_test_one_string (XEp_main, "name(/a/*[1])", "b"); |
|---|
| 190 |
v_test_one_string (XEp_main, "name(/a/x[1])", "x"); |
|---|
| 191 |
|
|---|
| 192 |
// regression test for bug in position() |
|---|
| 193 |
v_test_one_string (XEp_main, "count(/a/b/c[1])", "1"); |
|---|
| 194 |
v_test_one_string (XEp_main, "count(/a/b/c[position()=1])", "1"); |
|---|
| 195 |
v_test_one_string (XEp_main, "count(/a/b/d[position()=3])", "0"); |
|---|
| 196 |
|
|---|
| 197 |
// regression test for bug in i_compute_xpath |
|---|
| 198 |
i_res = i_xpath_int (XEp_main, "//*[@val]/@val"); |
|---|
| 199 |
ca_res = .toString(i_res); |
|---|
| 200 |
v_out_one_line ("//*[@val]/@val", ca_res, "123", i_res == 123); |
|---|
| 201 |
|
|---|
| 202 |
// regression test for bug in text in expressions |
|---|
| 203 |
v_test_one_string (XEp_main, "//x[text()='sub text']/@target", "xyz"); |
|---|
| 204 |
|
|---|
| 205 |
// regression test for bug in o_xpath_double |
|---|
| 206 |
o_res = o_xpath_double (XEp_main, "substring('123.4',1)", d_out); |
|---|
| 207 |
if (o_res) |
|---|
| 208 |
{ |
|---|
| 209 |
ca_res = .toString(d_out); |
|---|
| 210 |
o_ok = (ca_res == "123.4"); |
|---|
| 211 |
} |
|---|
| 212 |
else |
|---|
| 213 |
o_ok = false; |
|---|
| 214 |
v_out_one_line ("substring('123.4',1)", ca_res, "123.4", o_ok); |
|---|
| 215 |
|
|---|
| 216 |
// testing for syntax error |
|---|
| 217 |
auto xpath_processor xp_proc_2 = new xpath_processor (XEp_main, "//**"); |
|---|
| 218 |
i_res = xp_proc_2 . i_compute_xpath (); |
|---|
| 219 |
if (xp_proc_2 . e_error == Errors.e_error_syntax) |
|---|
| 220 |
{ |
|---|
| 221 |
o_ok = true; |
|---|
| 222 |
ca_res = "syntax error"; |
|---|
| 223 |
} |
|---|
| 224 |
else |
|---|
| 225 |
{ |
|---|
| 226 |
o_ok = false; |
|---|
| 227 |
ca_res = "error " ~ .toString( cast(int)xp_proc_2 . e_error); |
|---|
| 228 |
} |
|---|
| 229 |
v_out_one_line ("//**", ca_res, "syntax error", o_ok); |
|---|
| 230 |
|
|---|
| 231 |
// regression test for bug in "text" being an element |
|---|
| 232 |
out_html.writeLine("</table>"); |
|---|
| 233 |
|
|---|
| 234 |
delete XDp_doc; |
|---|
| 235 |
XDp_doc = new TiXmlDocument (); |
|---|
| 236 |
XDp_doc . Parse ("<xml><text>within</text></xml>"); |
|---|
| 237 |
XEp_main = XDp_doc.RootElement (); |
|---|
| 238 |
out_html.writeLine("<h2>Input XML tree</h2>"); |
|---|
| 239 |
v_out_html (out_html, XDp_doc, 0); |
|---|
| 240 |
out_html.writeLine("<br /><br />"); |
|---|
| 241 |
out_html.writeLine("<table border='1'><tr><th>Expression</th><th>Result</th><th>Expected (compiled)</th></tr>\n"); |
|---|
| 242 |
|
|---|
| 243 |
v_test_one_string (XEp_main, "/xml/text/text()", "within" ); |
|---|
| 244 |
|
|---|
| 245 |
delete XDp_doc; |
|---|
| 246 |
out_html.writeLine("</table>"); |
|---|
| 247 |
out_html.writeLine("<h2>RSS feed examples</h2>"); |
|---|
| 248 |
out_html.writeLine("These examples show how to decode a typical XML file : the <a href='http://sourceforge.net/export/rss2_projnews.php?group_id=53396&rss_fulltext=1'>TinyXPath RSS feed</a><br /><br />"); |
|---|
| 249 |
|
|---|
| 250 |
XDp_doc = new TiXmlDocument; |
|---|
| 251 |
if (! XDp_doc . LoadFile ("rss2_projnews.xml")) |
|---|
| 252 |
{ |
|---|
| 253 |
printf ("Can't find rss2_projnews.xml !\n"); |
|---|
| 254 |
return 99; |
|---|
| 255 |
} |
|---|
| 256 |
|
|---|
| 257 |
static const char[] cp_rss_count = "count(/rss/channel/item)"; |
|---|
| 258 |
static const char[] cp_rss_ver = "/rss/@version"; |
|---|
| 259 |
|
|---|
| 260 |
int i_nb_news, i_news; |
|---|
| 261 |
char[] ca_expr; |
|---|
| 262 |
|
|---|
| 263 |
i_nb_news = i_xpath_int (XDp_doc . RootElement (), cp_rss_count); |
|---|
| 264 |
out_html.format("RSS version (XPath expr : <b>%s</b>) : %s<br />\n",cp_rss_ver, S_xpath_string (XDp_doc . RootElement (), cp_rss_ver)); |
|---|
| 265 |
out_html.format("Nb of news messages (XPath expr : <b>%s</b>) : %d<br />\n",cp_rss_count, i_nb_news); |
|---|
| 266 |
out_html.writeLine("<br /><table border='1'><tr><th>Xpath expr</th><th>value</th></tr>"); |
|---|
| 267 |
|
|---|
| 268 |
for (i_news = 0; i_news < i_nb_news; i_news++) |
|---|
| 269 |
{ |
|---|
| 270 |
ca_expr = format("concat(/rss/channel/item[%d]/pubDate/text(),' : ',/rss/channel/item[%d]/title/text())", i_news + 1, i_news + 1); |
|---|
| 271 |
out_html.format("<tr><td>%s</td><td>%s</td></tr>\n", ca_expr, S_xpath_string (XDp_doc . RootElement (), ca_expr)); |
|---|
| 272 |
} |
|---|
| 273 |
out_html.writeLine("</table>"); |
|---|
| 274 |
|
|---|
| 275 |
delete XDp_doc; |
|---|
| 276 |
out_html.writeLine("<br /> <br /></body></html>"); |
|---|
| 277 |
delete out_html; |
|---|
| 278 |
return 0; |
|---|
| 279 |
} |
|---|