tests/test.lua

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
 
5
if string.find(_VERSION, " 5.0") then
6
	lxp = assert(loadlib("./lxp.so", "luaopen_lxp"))()
7
else
8
	lxp = require"lxp"
9
	gcinfo = function () return collectgarbage"count" end
10
end
11
print (lxp._VERSION)
12
assert(lxp.new, "Cannot find function lxp.new ("..tostring(lxp.new)..")")
13
 
14
-- basic test with no preamble
15
local p = lxp.new{}
16
p:setencoding("ISO-8859-1")
17
assert(p:parse[[<tag cap="5">hi</tag>]])
18
p:close()
19
 
20
 
21
preamble = [[
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
 
44
X = {}
45
if 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
53
else
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]]()
80
end
81
 
82
 
83
 
84
-------------------------------
85
print("testing start/end tags")
86
callbacks = {
87
  StartElement = getargs,
88
  EndElement = getargs,
89
}
90
p = lxp.new(callbacks)
91
assert(p:getcallbacks() == callbacks)
92
assert(p:parse(preamble))
93
assert(p:parse([[
94
<to priority="10" xu = "hi">
95
]]))
96
assert(X.n == 3 and X[1] == p and X[2] == "to")
97
x = X[3]
98
assert(x.priority=="10" and x.xu=="hi" and x.method=="POST")
99
assert(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)))
100
assert(p:parse("</to>"))
101
assert(p:parse())
102
p:close()
103
 
104
 
105
-------------------------------
106
print("testing CharacterData/Cdata")
107
callbacks = {
108
  CharacterData = getargs,
109
}
110
p = lxp.new(callbacks)
111
assert(p:parse(preamble))
112
assert(p:parse"<to>a basic text&lt;<![CDATA[<<ha>>]]></to>")
113
assert(X[1] == p and X[2] == "a basic text<<<ha>>")
114
callbacks.chardata = error   -- no more calls to `chardata'
115
assert(p:parse(""))
116
assert(p:parse())
117
-- assert(p:parse())   -- no problem to finish twice. alas, it has problems
118
assert(p:getcallbacks() == callbacks)
119
p:close()
120
 
121
-------------------------------
122
callbacks = {
123
  CharacterData = xgetargs"c",
124
  StartCdataSection = xgetargs"s",
125
  EndCdataSection = xgetargs"e", 
126
}
127
X = {}
128
p = lxp.new(callbacks)
129
assert(p:parse(preamble))
130
assert(p:parse"<to>")
131
assert(p:parse"<![CDATA[hi]]>")
132
assert(table.getn(X) == 3)
133
assert(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))
134
assert(X[2][1] == "c" and X[2][2] == p and X[2][3] == "hi")
135
assert(X[3][1] == "e" and X[3][2] == p)
136
assert(p:parse"</to>")
137
p:close()
138
 
139
 
140
-------------------------------
141
print("testing ProcessingInstruction")
142
callbacks = {ProcessingInstruction = getargs}
143
p = lxp.new(callbacks)
144
assert(p:parse[[
145
<to>
146
  <?lua how is this passed to <here>? ?>
147
</to>
148
]])
149
assert(X[1] == p and X[2] == "lua" and
150
       X[3] == "how is this passed to <here>? ")
151
p:close()
152
 
153
 
154
------------------------------
155
print("testing Comment")
156
callbacks = {Comment = xgetargs"c"; CharacterData = xgetargs"t"}
157
X = {}
158
p = lxp.new(callbacks)
159
assert(p:parse[[
160
<to>some text
161
<!-- <a comment> with some & symbols -->
162
some more text</to>
163
 
164
]])
165
p:close()
166
 
167
assert(X[1][1] == "t" and X[2][1] == "c" and X[3][1] == "t")
168
assert(X[1][2] == X[2][2] and X[2][2] == X[3][2] and X[3][2] == p)
169
assert(X[1][3] == "some text\n")
170
assert(X[2][3] == " <a comment> with some & symbols ")
171
assert(X[3][3] == "\nsome more text")
172
 
173
 
174
----------------------------
175
print("testing ExternalEntity")
176
entities = {
177
["entity1.xml"] = "<hi/>"
178
}
179
 
180
callbacks = {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
 
186
X = {}
187
p = lxp.new(callbacks)
188
p:setbase("/base")
189
assert(p:parse(preamble))
190
assert(p:parse[[
191
<to> &test-entity;
192
</to>
193
]])
194
assert(p:getbase() == "/base")
195
p:close()
196
assert(X[1][1] == "s" and X[1][3] == "to")
197
assert(X[2][1] == "s" and X[2][3] == "hi")
198
assert(X[3][1] == "e" and X[3][3] == "hi")
199
assert(X[4][1] == "e" and X[4][3] == "to")
200
 
201
 
202
----------------------------
203
print("testing default handles")
204
text = [[<to> hi &xuxu; </to>]]
205
local t = ""
206
 
207
callbacks = { Default = function (p, s) t = t .. s end }
208
p = lxp.new(callbacks)
209
assert(p:parse(preamble))
210
assert(p:parse(text))
211
p:close()
212
assert(t == preamble..text)
213
 
214
t = ""
215
callbacks = { DefaultExpand = function (p, s) t = t .. s end }
216
p = lxp.new(callbacks)
217
assert(p:parse(preamble))
218
assert(p:parse(text))
219
p:close()
220
assert(t == preamble..string.gsub(text, "&xuxu;", "is this a xuxu?"))
221
 
222
 
223
----------------------------
224
print("testing notation declarations and unparsed entities")
225
 
226
callbacks = {
227
  UnparsedEntityDecl = getargs,
228
  NotationDecl = function (p, name, base, systemId, publicId)
229
    assert(name == "TXT" and systemId == "txt" and base == "/base")
230
  end,
231
 }
232
p = lxp.new(callbacks)
233
p:setbase("/base")
234
assert(p:parse(preamble))
235
assert(p:parse[[<hihi explanation="test-unparsed"/>]])
236
p:close()
237
assert(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
----------------------------
243
print("testing namespace declarations")
244
callbacks = { StartNamespaceDecl = xgetargs"sn",
245
              EndNamespaceDecl = xgetargs"en",
246
              StartElement = xgetargs"s",
247
              EndElement = xgetargs"e",
248
}
249
X = {}
250
p = lxp.new(callbacks, "?")
251
assert(p:parse[[
252
<x xmlns:space='a/namespace'>
253
  <space:a/>
254
</x>
255
]])
256
p:close()
257
x = X[1]
258
assert(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)))
259
x = X[3]
260
assert(x[1] == "s" and x[3] == "a/namespace?a")
261
x = X[4]
262
assert(x[1] == "e" and x[3] == "a/namespace?a")
263
x = X[6]
264
assert(x[1] == "en" and x[3] == "space" and table.getn(x) == 3)
265
 
266
----------------------------
267
print("testing doctype declarations")
268
 
269
callbacks = {
270
  StartDoctypeDecl = getargs
271
 }
272
p = lxp.new(callbacks)
273
assert(p:parse([[<!DOCTYPE root PUBLIC "foo" "hello-world">]]))
274
assert(p:parse[[<root/>]])
275
p:close()
276
assert(X[2] == "root" and X[3] == "hello-world" and X[4] == "foo" and
277
       X[5] == false)
278
 
279
-- Error reporting
280
p = lxp.new{}
281
data = [[
282
<tag>
283
  <other< </other>
284
</tag>
285
]]
286
local status, msg, line, col, byte = p:parse(data)
287
assert(status == nil and type(msg) == "string" and line == 2 and col == 9)
288
assert(string.sub(data, byte, byte) == "<")
289
 
290
p = lxp.new{}
291
p:parse("<to>")
292
local status, msg, line, col, byte = p:parse()
293
assert(status == nil and line == 1 and col == 5 and byte == 5)
294
 
295
 
296
-- position reporting
297
callbacks = { ProcessingInstruction = function (p)
298
  X = {p:pos()}
299
end
300
}
301
 
302
p = lxp.new(callbacks)
303
assert(p:parse[[
304
<to> <?test where is `pos'? ?>
305
</to>
306
]])
307
p:close()
308
assert(X[1] == 1  and X[2] == 6 and X[3] == 6)  -- line, column, abs. position
309
 
310
 
311
 
312
print("testing errors")
313
-- invalid keys
314
assert(not pcall(lxp.new, {StatCdata=print}))
315
assert(pcall(lxp.new, {StatCdata=print, _nonstrict = true}))
316
 
317
-- invalid sequences
318
p = lxp.new{}
319
assert(p:parse[[<to></to>]])
320
assert(p:parse())
321
assert(p:parse(" ") == nil)
322
 
323
-- closing unfinished document
324
p = lxp.new{}
325
assert(p:parse[[<to>]])
326
local status, err = pcall(p.close, p)
327
assert(not status and string.find(err, "error closing parser"))
328
 
329
-- closing unfinished document
330
print("testing parser:stop()");
331
local stopped;
332
p = 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
}
342
local ok, err = p:parse[[<root><parseme>Hello</parseme><stop>here</stop><notparsed/></root>]];
343
assert(not ok)
344
assert(err == "parsing aborted")
345
assert(stopped == true, "parser not stopped")
346
 
347
 
348
-- test for GC
349
print("\ntesting garbage collection")
350
collectgarbage(); collectgarbage()
351
local x = gcinfo()
352
for 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({})
356
end
357
collectgarbage(); collectgarbage()
358
assert(math.abs(gcinfo() - x) <= 2)
359
 
360
-- test for GC (circular references)
361
print("testing garbage collection (circular reference)")
362
collectgarbage(); collectgarbage()
363
local x = gcinfo()
364
for i=1,100000 do
365
  -- due to a small bug in Lua...
366
  if (math.mod or math.fmod)(i, 100) == 0 then collectgarbage() end
367
  local p;
368
  p = lxp.new({ StartElement = function () print(p) end });
369
end
370
collectgarbage(); collectgarbage()
371
assert(math.abs(gcinfo() - x) <= 2)
372
 
373
print("OK", _VERSION)