1#!/usr/local/bin/lua5.1 2-- See Copyright Notice in license.html 3-- $Id: test.lua,v 1.6 2006/06/08 20:34:52 tomas Exp $ 4 5if string.find(_VERSION, " 5.0") then 6 lxp = assert(loadlib("./lxp.so", "luaopen_lxp"))() 7else 8 lxp = require"lxp" 9 gcinfo = function () return collectgarbage"count" end 10end 11print (lxp._VERSION) 12assert(lxp.new, "Cannot find function lxp.new ("..tostring(lxp.new)..")") 13 14-- basic test with no preamble 15local p = lxp.new{} 16p:setencoding("ISO-8859-1") 17assert(p:parse[[<tag cap="5">hi</tag>]]) 18p:close() 19 20 21preamble = [[ 22<?xml version="1.0" encoding="ISO-8859-1"?> 23 24<!DOCTYPE greeting [ 25 <!ENTITY xuxu "is this a xuxu?"> 26 27 <!ATTLIST to 28 method CDATA #FIXED "POST" 29 > 30 31 <!ENTITY test-entity 32 SYSTEM "entity1.xml"> 33 34 <!NOTATION TXT SYSTEM "txt"> 35 36 <!ENTITY test-unparsed SYSTEM "unparsed.txt" NDATA txt> 37 38 <!ATTLIST hihi 39 explanation ENTITY #REQUIRED> 40 41]> 42]] 43 44X = {} 45if string.find(_VERSION, " 5.0") then 46 function getargs (...) X = arg end 47 function xgetargs (c) 48 return function (...) 49 table.insert(arg, 1, c) 50 table.insert(X, arg) 51 end 52 end 53else 54 (loadstring or load)[[ 55 function getargs (...) 56 X = { ... } 57 X.n = select('#', ...) 58 end 59 function xgetargs (c) 60 return function (...) 61 local arg = { ... } 62 arg.n = select('#', ...) + 1 63 table.insert(arg, 1, c) 64 table.insert(X, arg) 65 end 66 end 67 table.getn = function (t) 68 if t.n then 69 return t.n 70 else 71 local n = 0 72 for i in pairs(t) do 73 if type(i) == "number" then 74 n = math.max(n, i) 75 end 76 end 77 return n 78 end 79 end]]() 80end 81 82 83 84------------------------------- 85print("testing start/end tags") 86callbacks = { 87 StartElement = getargs, 88 EndElement = getargs, 89} 90p = lxp.new(callbacks) 91assert(p:getcallbacks() == callbacks) 92assert(p:parse(preamble)) 93assert(p:parse([[ 94<to priority="10" xu = "hi"> 95]])) 96assert(X.n == 3 and X[1] == p and X[2] == "to") 97x = X[3] 98assert(x.priority=="10" and x.xu=="hi" and x.method=="POST") 99assert(x[1] == "priority" and x[2] == "xu" and table.getn(x) == 2, "x[1] == "..tostring(x[1])..", x[2] == "..tostring(x[2])..", #x == "..tostring(table.getn(x))) 100assert(p:parse("</to>")) 101assert(p:parse()) 102p:close() 103 104 105------------------------------- 106print("testing CharacterData/Cdata") 107callbacks = { 108 CharacterData = getargs, 109} 110p = lxp.new(callbacks) 111assert(p:parse(preamble)) 112assert(p:parse"<to>a basic text<<![CDATA[<<ha>>]]></to>") 113assert(X[1] == p and X[2] == "a basic text<<<ha>>") 114callbacks.chardata = error -- no more calls to `chardata' 115assert(p:parse("")) 116assert(p:parse()) 117-- assert(p:parse()) -- no problem to finish twice. alas, it has problems 118assert(p:getcallbacks() == callbacks) 119p:close() 120 121------------------------------- 122callbacks = { 123 CharacterData = xgetargs"c", 124 StartCdataSection = xgetargs"s", 125 EndCdataSection = xgetargs"e", 126} 127X = {} 128p = lxp.new(callbacks) 129assert(p:parse(preamble)) 130assert(p:parse"<to>") 131assert(p:parse"<![CDATA[hi]]>") 132assert(table.getn(X) == 3) 133assert(X[1][1] == "s" and X[1][2] == p, "X[1][1] == "..tostring(X[1][1])..", X[1][2] == "..tostring(X[1][2])..", p == "..tostring(p)) 134assert(X[2][1] == "c" and X[2][2] == p and X[2][3] == "hi") 135assert(X[3][1] == "e" and X[3][2] == p) 136assert(p:parse"</to>") 137p:close() 138 139 140------------------------------- 141print("testing ProcessingInstruction") 142callbacks = {ProcessingInstruction = getargs} 143p = lxp.new(callbacks) 144assert(p:parse[[ 145<to> 146 <?lua how is this passed to <here>? ?> 147</to> 148]]) 149assert(X[1] == p and X[2] == "lua" and 150 X[3] == "how is this passed to <here>? ") 151p:close() 152 153 154------------------------------ 155print("testing Comment") 156callbacks = {Comment = xgetargs"c"; CharacterData = xgetargs"t"} 157X = {} 158p = lxp.new(callbacks) 159assert(p:parse[[ 160<to>some text 161<!-- <a comment> with some & symbols --> 162some more text</to> 163 164]]) 165p:close() 166 167assert(X[1][1] == "t" and X[2][1] == "c" and X[3][1] == "t") 168assert(X[1][2] == X[2][2] and X[2][2] == X[3][2] and X[3][2] == p) 169assert(X[1][3] == "some text\n") 170assert(X[2][3] == " <a comment> with some & symbols ") 171assert(X[3][3] == "\nsome more text") 172 173 174---------------------------- 175print("testing ExternalEntity") 176entities = { 177["entity1.xml"] = "<hi/>" 178} 179 180callbacks = {StartElement = xgetargs"s", EndElement = xgetargs"e", 181 ExternalEntityRef = function (p, context, base, systemID, publicId) 182 assert(base == "/base") 183 return context:parse(entities[systemID]) 184 end} 185 186X = {} 187p = lxp.new(callbacks) 188p:setbase("/base") 189assert(p:parse(preamble)) 190assert(p:parse[[ 191<to> &test-entity; 192</to> 193]]) 194assert(p:getbase() == "/base") 195p:close() 196assert(X[1][1] == "s" and X[1][3] == "to") 197assert(X[2][1] == "s" and X[2][3] == "hi") 198assert(X[3][1] == "e" and X[3][3] == "hi") 199assert(X[4][1] == "e" and X[4][3] == "to") 200 201 202---------------------------- 203print("testing default handles") 204text = [[<to> hi &xuxu; </to>]] 205local t = "" 206 207callbacks = { Default = function (p, s) t = t .. s end } 208p = lxp.new(callbacks) 209assert(p:parse(preamble)) 210assert(p:parse(text)) 211p:close() 212assert(t == preamble..text) 213 214t = "" 215callbacks = { DefaultExpand = function (p, s) t = t .. s end } 216p = lxp.new(callbacks) 217assert(p:parse(preamble)) 218assert(p:parse(text)) 219p:close() 220assert(t == preamble..string.gsub(text, "&xuxu;", "is this a xuxu?")) 221 222 223---------------------------- 224print("testing notation declarations and unparsed entities") 225 226callbacks = { 227 UnparsedEntityDecl = getargs, 228 NotationDecl = function (p, name, base, systemId, publicId) 229 assert(name == "TXT" and systemId == "txt" and base == "/base") 230 end, 231 } 232p = lxp.new(callbacks) 233p:setbase("/base") 234assert(p:parse(preamble)) 235assert(p:parse[[<hihi explanation="test-unparsed"/>]]) 236p:close() 237assert(X[2] == "test-unparsed" and X[3] == "/base" and 238 X[4] == "unparsed.txt" and X[6] == "txt" and X.n == 6) 239 240 241 242---------------------------- 243print("testing namespace declarations") 244callbacks = { StartNamespaceDecl = xgetargs"sn", 245 EndNamespaceDecl = xgetargs"en", 246 StartElement = xgetargs"s", 247 EndElement = xgetargs"e", 248} 249X = {} 250p = lxp.new(callbacks, "?") 251assert(p:parse[[ 252<x xmlns:space='a/namespace'> 253 <space:a/> 254</x> 255]]) 256p:close() 257x = X[1] 258assert(x[1] == "sn" and x[3] == "space" and x[4] == "a/namespace" and table.getn(x) == 4, "x[1] == "..tostring(x[1])..", x[3] == "..tostring(x[3])..", x[4] == "..tostring(x[4])..", #x == "..tostring(table.getn(x))) 259x = X[3] 260assert(x[1] == "s" and x[3] == "a/namespace?a") 261x = X[4] 262assert(x[1] == "e" and x[3] == "a/namespace?a") 263x = X[6] 264assert(x[1] == "en" and x[3] == "space" and table.getn(x) == 3) 265 266---------------------------- 267print("testing doctype declarations") 268 269callbacks = { 270 StartDoctypeDecl = getargs 271 } 272p = lxp.new(callbacks) 273assert(p:parse([[<!DOCTYPE root PUBLIC "foo" "hello-world">]])) 274assert(p:parse[[<root/>]]) 275p:close() 276assert(X[2] == "root" and X[3] == "hello-world" and X[4] == "foo" and 277 X[5] == false) 278 279-- Error reporting 280p = lxp.new{} 281data = [[ 282<tag> 283 <other< </other> 284</tag> 285]] 286local status, msg, line, col, byte = p:parse(data) 287assert(status == nil and type(msg) == "string" and line == 2 and col == 9) 288assert(string.sub(data, byte, byte) == "<") 289 290p = lxp.new{} 291p:parse("<to>") 292local status, msg, line, col, byte = p:parse() 293assert(status == nil and line == 1 and col == 5 and byte == 5) 294 295 296-- position reporting 297callbacks = { ProcessingInstruction = function (p) 298 X = {p:pos()} 299end 300} 301 302p = lxp.new(callbacks) 303assert(p:parse[[ 304<to> <?test where is `pos'? ?> 305</to> 306]]) 307p:close() 308assert(X[1] == 1 and X[2] == 6 and X[3] == 6) -- line, column, abs. position 309 310 311 312print("testing errors") 313-- invalid keys 314assert(not pcall(lxp.new, {StatCdata=print})) 315assert(pcall(lxp.new, {StatCdata=print, _nonstrict = true})) 316 317-- invalid sequences 318p = lxp.new{} 319assert(p:parse[[<to></to>]]) 320assert(p:parse()) 321assert(p:parse(" ") == nil) 322 323-- closing unfinished document 324p = lxp.new{} 325assert(p:parse[[<to>]]) 326local status, err = pcall(p.close, p) 327assert(not status and string.find(err, "error closing parser")) 328 329-- closing unfinished document 330print("testing parser:stop()"); 331local stopped; 332p = lxp.new{ 333 StartElement = function (parser, name, attr) 334 if name == "stop" then 335 parser:stop() 336 stopped = true 337 else 338 stopped = false 339 end 340 end 341} 342local ok, err = p:parse[[<root><parseme>Hello</parseme><stop>here</stop><notparsed/></root>]]; 343assert(not ok) 344assert(err == "parsing aborted") 345assert(stopped == true, "parser not stopped") 346 347 348-- test for GC 349print("\ntesting garbage collection") 350collectgarbage(); collectgarbage() 351local x = gcinfo() 352for i=1,100000 do 353 -- due to a small bug in Lua... 354 if (math.mod or math.fmod)(i, 100) == 0 then collectgarbage() end 355 lxp.new({}) 356end 357collectgarbage(); collectgarbage() 358assert(math.abs(gcinfo() - x) <= 2) 359 360 361print"OK" 362 363