diff --git a/slaxdom.lua b/slaxdom.lua index da30122..56b31a9 100644 --- a/slaxdom.lua +++ b/slaxdom.lua @@ -1,130 +1,130 @@ --- Optional parser that creates a flat DOM from parsing -local SLAXML = require 'slaxml' -function SLAXML:dom(xml,opts) - if not opts then opts={} end - local rich = not opts.simple - local push, pop = table.insert, table.remove - local doc = {type="document", name="#doc", kids={}} - local current,stack = doc, {doc} - local builder = SLAXML:parser{ - startElement = function(name,nsURI,nsPrefix) - local el = { type="element", name=name, kids={}, el=rich and {} or nil, attr={}, nsURI=nsURI, nsPrefix=nsPrefix, parent=rich and current or nil } - if current==doc then - if doc.root then error(("Encountered element '%s' when the document already has a root '%s' element"):format(name,doc.root.name)) end - doc.root = rich and el or nil - end - push(current.kids,el) - if current.el then push(current.el,el) end - current = el - push(stack,el) - end, - attribute = function(name,value,nsURI,nsPrefix) - if not current or current.type~="element" then error(("Encountered an attribute %s=%s but I wasn't inside an element"):format(name,value)) end - local attr = {type='attribute',name=name,nsURI=nsURI,nsPrefix=nsPrefix,value=value,parent=rich and current or nil} - if rich then current.attr[name] = value end - push(current.attr,attr) - end, - closeElement = function(name) - if current.name~=name or current.type~="element" then error(("Received a close element notification for '%s' but was inside a '%s' %s"):format(name,current.name,current.type)) end - pop(stack) - current = stack[#stack] - end, - text = function(value,cdata) - -- documents may only have text node children that are whitespace: https://www.w3.org/TR/xml/#NT-Misc - if current.type=='document' and not value:find('^%s+$') then error(("Document has non-whitespace text at root: '%s'"):format(value:gsub('[\r\n\t]',{['\r']='\\r', ['\n']='\\n', ['\t']='\\t'}))) end - push(current.kids,{type='text',name='#text',cdata=cdata and true or nil,value=value,parent=rich and current or nil}) - end, - comment = function(value) - push(current.kids,{type='comment',name='#comment',value=value,parent=rich and current or nil}) - end, - pi = function(name,value) - push(current.kids,{type='pi',name=name,value=value,parent=rich and current or nil}) - end - } - builder:parse(xml,opts) - return doc -end - -local escmap = {["<"]="<", [">"]=">", ["&"]="&", ['"']=""", ["'"]="'"} -local function esc(s) return s:gsub('[<>&"]', escmap) end - --- opts.indent: number of spaces, or string -function SLAXML:xml(n,opts) - opts = opts or {} - local out = {} - local tab = opts.indent and (type(opts.indent)=="number" and string.rep(" ",opts.indent) or opts.indent) or "" - local ser = {} - local omit = {} - if opts.omit then for _,s in ipairs(opts.omit) do omit[s]=true end end - - function ser.document(n) - for _,kid in ipairs(n.kids) do - if ser[kid.type] then ser[kid.type](kid,0) end - end - end - - function ser.pi(n,depth) - depth = depth or 0 - table.insert(out, tab:rep(depth)..'') - end - - function ser.element(n,depth) - if n.nsURI and omit[n.nsURI] then return end - depth = depth or 0 - local indent = tab:rep(depth) - local name = n.nsPrefix and n.nsPrefix..':'..n.name or n.name - local result = indent..'<'..name - if n.attr and n.attr[1] then - local sorted = n.attr - if opts.sort then - sorted = {} - for i,a in ipairs(n.attr) do sorted[i]=a end - table.sort(sorted,function(a,b) - if a.nsPrefix and b.nsPrefix then - return a.nsPrefix==b.nsPrefix and a.name' or '/>') - table.insert(out, result) - if n.kids and n.kids[1] then - for _,kid in ipairs(n.kids) do - if ser[kid.type] then ser[kid.type](kid,depth+1) end - end - table.insert(out, indent..'') - end - end - - function ser.text(n,depth) - if n.cdata then - table.insert(out, tab:rep(depth)..'') - else - table.insert(out, tab:rep(depth)..esc(n.value)) - end - end - - function ser.comment(n,depth) - table.insert(out, tab:rep(depth)..'') - end - - ser[n.type](n,0) - - return table.concat(out, opts.indent and '\n' or '') -end - -return SLAXML +-- Optional parser that creates a flat DOM from parsing +local SLAXML = require 'slaxml' +function SLAXML:dom(xml,opts) + if not opts then opts={} end + local rich = not opts.simple + local push, pop = table.insert, table.remove + local doc = {type="document", name="#doc", kids={}} + local current,stack = doc, {doc} + local builder = SLAXML:parser{ + startElement = function(name,nsURI,nsPrefix) + local el = { type="element", name=name, kids={}, el=rich and {} or nil, attr={}, nsURI=nsURI, nsPrefix=nsPrefix, parent=rich and current or nil } + if current==doc then + if doc.root then error(("Encountered element '%s' when the document already has a root '%s' element"):format(name,doc.root.name)) end + doc.root = rich and el or nil + end + push(current.kids,el) + if current.el then push(current.el,el) end + current = el + push(stack,el) + end, + attribute = function(name,value,nsURI,nsPrefix) + if not current or current.type~="element" then error(("Encountered an attribute %s=%s but I wasn't inside an element"):format(name,value)) end + local attr = {type='attribute',name=name,nsURI=nsURI,nsPrefix=nsPrefix,value=value,parent=rich and current or nil} + if rich then current.attr[name] = value end + push(current.attr,attr) + end, + closeElement = function(name) + if current.name~=name or current.type~="element" then error(("Received a close element notification for '%s' but was inside a '%s' %s"):format(name,current.name,current.type)) end + pop(stack) + current = stack[#stack] + end, + text = function(value,cdata) + -- documents may only have text node children that are whitespace: https://www.w3.org/TR/xml/#NT-Misc + if current.type=='document' and not value:find('^%s+$') then error(("Document has non-whitespace text at root: '%s'"):format(value:gsub('[\r\n\t]',{['\r']='\\r', ['\n']='\\n', ['\t']='\\t'}))) end + push(current.kids,{type='text',name='#text',cdata=cdata and true or nil,value=value,parent=rich and current or nil}) + end, + comment = function(value) + push(current.kids,{type='comment',name='#comment',value=value,parent=rich and current or nil}) + end, + pi = function(name,value) + push(current.kids,{type='pi',name=name,value=value,parent=rich and current or nil}) + end + } + builder:parse(xml,opts) + return doc +end + +local escmap = {["<"]="<", [">"]=">", ["&"]="&", ['"']=""", ["'"]="'"} +local function esc(s) return s:gsub('[<>&"]', escmap) end + +-- opts.indent: number of spaces, or string +function SLAXML:xml(n,opts) + opts = opts or {} + local out = {} + local tab = opts.indent and (type(opts.indent)=="number" and string.rep(" ",opts.indent) or opts.indent) or "" + local ser = {} + local omit = {} + if opts.omit then for _,s in ipairs(opts.omit) do omit[s]=true end end + + function ser.document(n) + for _,kid in ipairs(n.kids) do + if ser[kid.type] then ser[kid.type](kid,0) end + end + end + + function ser.pi(n,depth) + depth = depth or 0 + table.insert(out, tab:rep(depth)..'') + end + + function ser.element(n,depth) + if n.nsURI and omit[n.nsURI] then return end + depth = depth or 0 + local indent = tab:rep(depth) + local name = n.nsPrefix and n.nsPrefix..':'..n.name or n.name + local result = indent..'<'..name + if n.attr and n.attr[1] then + local sorted = n.attr + if opts.sort then + sorted = {} + for i,a in ipairs(n.attr) do sorted[i]=a end + table.sort(sorted,function(a,b) + if a.nsPrefix and b.nsPrefix then + return a.nsPrefix==b.nsPrefix and a.name' or '/>') + table.insert(out, result) + if n.kids and n.kids[1] then + for _,kid in ipairs(n.kids) do + if ser[kid.type] then ser[kid.type](kid,depth+1) end + end + table.insert(out, indent..'') + end + end + + function ser.text(n,depth) + if n.cdata then + table.insert(out, tab:rep(depth)..'') + else + table.insert(out, tab:rep(depth)..esc(n.value)) + end + end + + function ser.comment(n,depth) + table.insert(out, tab:rep(depth)..'') + end + + ser[n.type](n,0) + + return table.concat(out, opts.indent and '\n' or '') +end + +return SLAXML diff --git a/slaxml.lua b/slaxml.lua index 98e9819..5a52794 100644 --- a/slaxml.lua +++ b/slaxml.lua @@ -74,7 +74,13 @@ function SLAXML:parse(xml,options) end local entityMap = { ["lt"]="<", ["gt"]=">", ["amp"]="&", ["quot"]='"', ["apos"]="'" } local entitySwap = function(orig,n,s) return entityMap[s] or n=="#" and utf8(tonumber('0'..s)) or orig end - local function unescape(str) return gsub( str, '(&(#?)([%d%a]+);)', entitySwap ) end + local function unescape(str) + local s = gsub(str, '(&(#?)([%d%a]+);)', "") -- remove all valid entities + if find(s, '&') then + error("Invalid ampersand in string: "..str) + end + return gsub( str, '(&(#?)([%d%a]+);)', entitySwap ) + end local function finishText() if first>textStart and self._call.text then @@ -135,7 +141,7 @@ function SLAXML:parse(xml,options) for i=#nsStack,1,-1 do if nsStack[i]['!'] then currentElement[2] = nsStack[i]['!']; break end end end currentAttributeCt = 0 - push(nsStack,{}) + push(nsStack,{['__name__']=currentElement[1]}) return true end end @@ -153,6 +159,8 @@ function SLAXML:parse(xml,options) if first then pos = last+1 match2 = unescape(match2) + else + error("Attribute value must be quoted for attribute: "..match1) end end end @@ -225,6 +233,7 @@ function SLAXML:parse(xml,options) end if first then finishText() + if nsStack[#nsStack]['__name__'] ~= match1 then error("Mismatched closing tag: expected "..nsStack[#nsStack]['__name__']..", got "..match1) end if self._call.closeElement then self._call.closeElement(match1,nsURI) end pos = last+1 textStart = pos diff --git a/test/lunity.lua b/test/lunity.lua index 62b3445..9f78646 100644 --- a/test/lunity.lua +++ b/test/lunity.lua @@ -1,383 +1,383 @@ ---[=========================================================================[ - Lunity v0.12 by Gavin Kistner - See http://github.com/Phrogz/Lunity for usage documentation. - Licensed under Creative Commons Attribution 3.0 United States License. - See http://creativecommons.org/licenses/by/3.0/us/ for details. ---]=========================================================================] - --- Cache these so we can silence the real ones during a run -local print,write = print,io.write - --- FIXME: this will fail if two test suites are running interleaved -local assertsPassed, assertsAttempted -local function assertionSucceeded() - assertsPassed = assertsPassed + 1 - write('.') - return true -end - --- This is the table that will be used as the environment for the tests, --- making assertions available within the file. -local lunity = setmetatable({}, {__index=_G}) - -function lunity.fail(msg) - assertsAttempted = assertsAttempted + 1 - if not msg then msg = "(test failure)" end - error(msg, 2) -end - -function lunity.assert(testCondition, msg) - assertsAttempted = assertsAttempted + 1 - if not testCondition then - if not msg then msg = "assert() failed: value was "..tostring(testCondition) end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertEqual(actual, expected, msg) - assertsAttempted = assertsAttempted + 1 - if actual~=expected then - if not msg then - msg = string.format("assertEqual() failed: expected %s, was %s", - tostring(expected), - tostring(actual) - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertType(actual, expectedType, msg) - assertsAttempted = assertsAttempted + 1 - if type(actual) ~= expectedType then - if not msg then - msg = string.format("assertType() failed: value %s is a %s, expected to be a %s", - tostring(actual), - type(actual), - expectedType - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertTableEquals(actual, expected, msg, keyPath) - assertsAttempted = assertsAttempted + 1 - -- Easy out - if actual == expected then - if not keyPath then - return assertionSucceeded() - else - return true - end - end - - if not keyPath then keyPath = {} end - - if type(actual) ~= 'table' then - if not msg then - msg = "Value passed to assertTableEquals() was not a table." - end - error(msg, 2 + #keyPath) - end - - -- Ensure all keys in t1 match in t2 - for key,expectedValue in pairs(expected) do - keyPath[#keyPath+1] = tostring(key) - local actualValue = actual[key] - if type(expectedValue)=='table' then - if type(actualValue)~='table' then - if not msg then - msg = "Tables not equal; expected "..table.concat(keyPath,'.').." to be a table, but was a "..type(actualValue) - end - error(msg, 1 + #keyPath) - elseif expectedValue ~= actualValue then - lunity.assertTableEquals(actualValue, expectedValue, msg, keyPath) - end - else - if actualValue ~= expectedValue then - if not msg then - if actualValue == nil then - msg = "Tables not equal; missing key '"..table.concat(keyPath,'.').."'." - else - msg = "Tables not equal; expected '"..table.concat(keyPath,'.').."' to be "..tostring(expectedValue)..", but was "..tostring(actualValue) - end - end - error(msg, 1 + #keyPath) - end - end - keyPath[#keyPath] = nil - end - - -- Ensure actual doesn't have keys that aren't expected - for k,_ in pairs(actual) do - if expected[k] == nil then - if not msg then - msg = "Tables not equal; found unexpected key '"..table.concat(keyPath,'.').."."..tostring(k).."'" - end - error(msg, 2 + #keyPath) - end - end - - return assertionSucceeded() -end - -function lunity.assertNotEqual(actual, expected, msg) - assertsAttempted = assertsAttempted + 1 - if actual==expected then - if not msg then - msg = string.format("assertNotEqual() failed: value not allowed to be %s", - tostring(actual) - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertTrue(actual, msg) - assertsAttempted = assertsAttempted + 1 - if actual ~= true then - if not msg then - msg = string.format("assertTrue() failed: value was %s, expected true", - tostring(actual) - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertFalse(actual, msg) - assertsAttempted = assertsAttempted + 1 - if actual ~= false then - if not msg then - msg = string.format("assertFalse() failed: value was %s, expected false", - tostring(actual) - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertNil(actual, msg) - assertsAttempted = assertsAttempted + 1 - if actual ~= nil then - if not msg then - msg = string.format("assertNil() failed: value was %s, expected nil", - tostring(actual) - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertNotNil(actual, msg) - assertsAttempted = assertsAttempted + 1 - if actual == nil then - if not msg then msg = "assertNotNil() failed: value was nil" end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertTableEmpty(actual, msg) - assertsAttempted = assertsAttempted + 1 - if type(actual) ~= "table" then - msg = string.format("assertTableEmpty() failed: expected a table, but got a %s", - type(table) - ) - error(msg, 2) - else - local key, value = next(actual) - if key ~= nil then - if not msg then - msg = string.format("assertTableEmpty() failed: table has non-nil key %s=%s", - tostring(key), - tostring(value) - ) - end - error(msg, 2) - end - return assertionSucceeded() - end -end - -function lunity.assertTableNotEmpty(actual, msg) - assertsAttempted = assertsAttempted + 1 - if type(actual) ~= "table" then - msg = string.format("assertTableNotEmpty() failed: expected a table, but got a %s", - type(actual) - ) - error(msg, 2) - else - if next(actual) == nil then - if not msg then - msg = "assertTableNotEmpty() failed: table has no keys" - end - error(msg, 2) - end - return assertionSucceeded() - end -end - -function lunity.assertSameKeys(t1, t2, msg) - assertsAttempted = assertsAttempted + 1 - local function bail(k,x,y) - if not msg then msg = string.format("Table #%d has key '%s' not present in table #%d",x,tostring(k),y) end - error(msg, 3) - end - for k,_ in pairs(t1) do if t2[k]==nil then bail(k,1,2) end end - for k,_ in pairs(t2) do if t1[k]==nil then bail(k,2,1) end end - return assertionSucceeded() -end - --- Ensures that the value is a function OR may be called as one -function lunity.assertInvokable(value, msg) - assertsAttempted = assertsAttempted + 1 - local meta = getmetatable(value) - if (type(value) ~= 'function') and not (meta and meta.__call and (type(meta.__call)=='function')) then - if not msg then - msg = string.format("assertInvokable() failed: '%s' can not be called as a function", - tostring(value) - ) - end - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertErrors(invokable, ...) - lunity.assertInvokable(invokable) - if pcall(invokable,...) then - local msg = string.format("assertErrors() failed: %s did not raise an error", - tostring(invokable) - ) - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.assertDoesNotError(invokable, ...) - lunity.assertInvokable(invokable) - if not pcall(invokable,...) then - local msg = string.format("assertDoesNotError() failed: %s raised an error", - tostring(invokable) - ) - error(msg, 2) - end - return assertionSucceeded() -end - -function lunity.is_nil(value) return type(value)=='nil' end -function lunity.is_boolean(value) return type(value)=='boolean' end -function lunity.is_number(value) return type(value)=='number' end -function lunity.is_string(value) return type(value)=='string' end -function lunity.is_table(value) return type(value)=='table' end -function lunity.is_function(value) return type(value)=='function' end -function lunity.is_thread(value) return type(value)=='thread' end -function lunity.is_userdata(value) return type(value)=='userdata' end - -local function run(self, opts) - if not opts then opts = {} end - if opts.quiet then - _G.print = function() end - io.write = function() end - end - - assertsPassed = 0 - assertsAttempted = 0 - - local useANSI,useHTML = true, false - if opts.useHTML ~= nil then useHTML=opts.useHTML end - if not useHTML and opts.useANSI ~= nil then useANSI=opts.useANSI end - - local suiteName = getmetatable(self).name - - if useHTML then - print("

"..suiteName.."

")
-	else
-		print(string.rep('=',78))
-		print(suiteName)
-		print(string.rep('=',78))
-	end
-	io.stdout:flush()
-
-
-	local testnames = {}
-	for name, test in pairs(self) do
-		if type(test)=='function' and name~='before' and name~='after' then
-			testnames[#testnames+1]=name
-		end
-	end
-	table.sort(testnames)
-
-
-	local startTime = os.clock()
-	local passed = 0
-	for _,name in ipairs(testnames) do
-		local scratchpad = {}
-		write(name..": ")
-		if self.before then self.before(scratchpad) end
-		local successFlag, errorMessage = pcall(self[name], scratchpad)
-		if successFlag then
-			print("pass")
-			passed = passed + 1
-		else
-			if useANSI then
-				print("\27[31m\27[1mFAIL!\27[0m")
-				print("\27[31m"..errorMessage.."\27[0m")
-			elseif useHTML then
-				print("FAIL!")
-				print(""..errorMessage.."")
-			else
-				print("FAIL!")
-				print(errorMessage)
-			end
-		end
-		io.stdout:flush()
-		if self.after then self.after(scratchpad) end
-	end
-	local elapsed = os.clock() - startTime
-
-	if useHTML then
-		print("
") - else - print(string.rep('-', 78)) - end - - print(string.format("%d/%d tests passed (%0.1f%%)", - passed, - #testnames, - 100 * passed / #testnames - )) - - if useHTML then print("
") end - - print(string.format("%d total successful assertion%s in ~%.0fms (%.0f assertions/second)", - assertsPassed, - assertsPassed == 1 and "" or "s", - elapsed*1000, - assertsAttempted / elapsed - )) - - if not useHTML then print("") end - io.stdout:flush() - - if opts.quiet then - _G.print = print - io.write = write - end -end - -return function(name) - return setmetatable( - {test=setmetatable({}, {__call=run, name=name or '(test suite)'})}, - {__index=lunity} - ) +--[=========================================================================[ + Lunity v0.12 by Gavin Kistner + See http://github.com/Phrogz/Lunity for usage documentation. + Licensed under Creative Commons Attribution 3.0 United States License. + See http://creativecommons.org/licenses/by/3.0/us/ for details. +--]=========================================================================] + +-- Cache these so we can silence the real ones during a run +local print,write = print,io.write + +-- FIXME: this will fail if two test suites are running interleaved +local assertsPassed, assertsAttempted +local function assertionSucceeded() + assertsPassed = assertsPassed + 1 + write('.') + return true +end + +-- This is the table that will be used as the environment for the tests, +-- making assertions available within the file. +local lunity = setmetatable({}, {__index=_G}) + +function lunity.fail(msg) + assertsAttempted = assertsAttempted + 1 + if not msg then msg = "(test failure)" end + error(msg, 2) +end + +function lunity.assert(testCondition, msg) + assertsAttempted = assertsAttempted + 1 + if not testCondition then + if not msg then msg = "assert() failed: value was "..tostring(testCondition) end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertEqual(actual, expected, msg) + assertsAttempted = assertsAttempted + 1 + if actual~=expected then + if not msg then + msg = string.format("assertEqual() failed: expected %s, was %s", + tostring(expected), + tostring(actual) + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertType(actual, expectedType, msg) + assertsAttempted = assertsAttempted + 1 + if type(actual) ~= expectedType then + if not msg then + msg = string.format("assertType() failed: value %s is a %s, expected to be a %s", + tostring(actual), + type(actual), + expectedType + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertTableEquals(actual, expected, msg, keyPath) + assertsAttempted = assertsAttempted + 1 + -- Easy out + if actual == expected then + if not keyPath then + return assertionSucceeded() + else + return true + end + end + + if not keyPath then keyPath = {} end + + if type(actual) ~= 'table' then + if not msg then + msg = "Value passed to assertTableEquals() was not a table." + end + error(msg, 2 + #keyPath) + end + + -- Ensure all keys in t1 match in t2 + for key,expectedValue in pairs(expected) do + keyPath[#keyPath+1] = tostring(key) + local actualValue = actual[key] + if type(expectedValue)=='table' then + if type(actualValue)~='table' then + if not msg then + msg = "Tables not equal; expected "..table.concat(keyPath,'.').." to be a table, but was a "..type(actualValue) + end + error(msg, 1 + #keyPath) + elseif expectedValue ~= actualValue then + lunity.assertTableEquals(actualValue, expectedValue, msg, keyPath) + end + else + if actualValue ~= expectedValue then + if not msg then + if actualValue == nil then + msg = "Tables not equal; missing key '"..table.concat(keyPath,'.').."'." + else + msg = "Tables not equal; expected '"..table.concat(keyPath,'.').."' to be "..tostring(expectedValue)..", but was "..tostring(actualValue) + end + end + error(msg, 1 + #keyPath) + end + end + keyPath[#keyPath] = nil + end + + -- Ensure actual doesn't have keys that aren't expected + for k,_ in pairs(actual) do + if expected[k] == nil then + if not msg then + msg = "Tables not equal; found unexpected key '"..table.concat(keyPath,'.').."."..tostring(k).."'" + end + error(msg, 2 + #keyPath) + end + end + + return assertionSucceeded() +end + +function lunity.assertNotEqual(actual, expected, msg) + assertsAttempted = assertsAttempted + 1 + if actual==expected then + if not msg then + msg = string.format("assertNotEqual() failed: value not allowed to be %s", + tostring(actual) + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertTrue(actual, msg) + assertsAttempted = assertsAttempted + 1 + if actual ~= true then + if not msg then + msg = string.format("assertTrue() failed: value was %s, expected true", + tostring(actual) + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertFalse(actual, msg) + assertsAttempted = assertsAttempted + 1 + if actual ~= false then + if not msg then + msg = string.format("assertFalse() failed: value was %s, expected false", + tostring(actual) + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertNil(actual, msg) + assertsAttempted = assertsAttempted + 1 + if actual ~= nil then + if not msg then + msg = string.format("assertNil() failed: value was %s, expected nil", + tostring(actual) + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertNotNil(actual, msg) + assertsAttempted = assertsAttempted + 1 + if actual == nil then + if not msg then msg = "assertNotNil() failed: value was nil" end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertTableEmpty(actual, msg) + assertsAttempted = assertsAttempted + 1 + if type(actual) ~= "table" then + msg = string.format("assertTableEmpty() failed: expected a table, but got a %s", + type(table) + ) + error(msg, 2) + else + local key, value = next(actual) + if key ~= nil then + if not msg then + msg = string.format("assertTableEmpty() failed: table has non-nil key %s=%s", + tostring(key), + tostring(value) + ) + end + error(msg, 2) + end + return assertionSucceeded() + end +end + +function lunity.assertTableNotEmpty(actual, msg) + assertsAttempted = assertsAttempted + 1 + if type(actual) ~= "table" then + msg = string.format("assertTableNotEmpty() failed: expected a table, but got a %s", + type(actual) + ) + error(msg, 2) + else + if next(actual) == nil then + if not msg then + msg = "assertTableNotEmpty() failed: table has no keys" + end + error(msg, 2) + end + return assertionSucceeded() + end +end + +function lunity.assertSameKeys(t1, t2, msg) + assertsAttempted = assertsAttempted + 1 + local function bail(k,x,y) + if not msg then msg = string.format("Table #%d has key '%s' not present in table #%d",x,tostring(k),y) end + error(msg, 3) + end + for k,_ in pairs(t1) do if t2[k]==nil then bail(k,1,2) end end + for k,_ in pairs(t2) do if t1[k]==nil then bail(k,2,1) end end + return assertionSucceeded() +end + +-- Ensures that the value is a function OR may be called as one +function lunity.assertInvokable(value, msg) + assertsAttempted = assertsAttempted + 1 + local meta = getmetatable(value) + if (type(value) ~= 'function') and not (meta and meta.__call and (type(meta.__call)=='function')) then + if not msg then + msg = string.format("assertInvokable() failed: '%s' can not be called as a function", + tostring(value) + ) + end + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertErrors(invokable, ...) + lunity.assertInvokable(invokable) + if pcall(invokable,...) then + local msg = string.format("assertErrors() failed: %s did not raise an error", + tostring(invokable) + ) + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.assertDoesNotError(invokable, ...) + lunity.assertInvokable(invokable) + if not pcall(invokable,...) then + local msg = string.format("assertDoesNotError() failed: %s raised an error", + tostring(invokable) + ) + error(msg, 2) + end + return assertionSucceeded() +end + +function lunity.is_nil(value) return type(value)=='nil' end +function lunity.is_boolean(value) return type(value)=='boolean' end +function lunity.is_number(value) return type(value)=='number' end +function lunity.is_string(value) return type(value)=='string' end +function lunity.is_table(value) return type(value)=='table' end +function lunity.is_function(value) return type(value)=='function' end +function lunity.is_thread(value) return type(value)=='thread' end +function lunity.is_userdata(value) return type(value)=='userdata' end + +local function run(self, opts) + if not opts then opts = {} end + if opts.quiet then + _G.print = function() end + io.write = function() end + end + + assertsPassed = 0 + assertsAttempted = 0 + + local useANSI,useHTML = true, false + if opts.useHTML ~= nil then useHTML=opts.useHTML end + if not useHTML and opts.useANSI ~= nil then useANSI=opts.useANSI end + + local suiteName = getmetatable(self).name + + if useHTML then + print("

"..suiteName.."

")
+	else
+		print(string.rep('=',78))
+		print(suiteName)
+		print(string.rep('=',78))
+	end
+	io.stdout:flush()
+
+
+	local testnames = {}
+	for name, test in pairs(self) do
+		if type(test)=='function' and name~='before' and name~='after' then
+			testnames[#testnames+1]=name
+		end
+	end
+	table.sort(testnames)
+
+
+	local startTime = os.clock()
+	local passed = 0
+	for _,name in ipairs(testnames) do
+		local scratchpad = {}
+		write(name..": ")
+		if self.before then self.before(scratchpad) end
+		local successFlag, errorMessage = pcall(self[name], scratchpad)
+		if successFlag then
+			print("pass")
+			passed = passed + 1
+		else
+			if useANSI then
+				print("\27[31m\27[1mFAIL!\27[0m")
+				print("\27[31m"..errorMessage.."\27[0m")
+			elseif useHTML then
+				print("FAIL!")
+				print(""..errorMessage.."")
+			else
+				print("FAIL!")
+				print(errorMessage)
+			end
+		end
+		io.stdout:flush()
+		if self.after then self.after(scratchpad) end
+	end
+	local elapsed = os.clock() - startTime
+
+	if useHTML then
+		print("
") + else + print(string.rep('-', 78)) + end + + print(string.format("%d/%d tests passed (%0.1f%%)", + passed, + #testnames, + 100 * passed / #testnames + )) + + if useHTML then print("
") end + + print(string.format("%d total successful assertion%s in ~%.0fms (%.0f assertions/second)", + assertsPassed, + assertsPassed == 1 and "" or "s", + elapsed*1000, + assertsAttempted / elapsed + )) + + if not useHTML then print("") end + io.stdout:flush() + + if opts.quiet then + _G.print = print + io.write = write + end +end + +return function(name) + return setmetatable( + {test=setmetatable({}, {__call=run, name=name or '(test suite)'})}, + {__index=lunity} + ) end \ No newline at end of file diff --git a/test/test.lua b/test/test.lua index dbf5c34..0b6ea83 100644 --- a/test/test.lua +++ b/test/test.lua @@ -1,366 +1,371 @@ -package.path = '../?.lua;' .. package.path -_ENV = require('lunity')() - -local SLAXML = require 'slaxdom' - -local XML = {} -for filename in io.popen('ls files'):lines() do - XML[filename:match('^(.-)%.[^.]+$')] = io.open("files/"..filename):read('*all') -end - -local function countParsings(xmlName,options,expected) - local counts,counters = {},{} - expected.closeElement = expected.startElement - for name,_ in pairs(expected) do - counts[name] = 0 - counters[name] = function() counts[name]=counts[name]+1 end - end - SLAXML:parser(counters):parse(XML[xmlName],options) - for name,ct in pairs(expected) do - assertEqual(counts[name],ct,"There should have been be exactly "..ct.." "..name.."() callback(s) in "..xmlName..", not "..counts[name]) - end -end - -function test:namespace() - local elementStack = {} - SLAXML:parser{ - startElement = function(name,nsURI) - table.insert(elementStack,{name=name,nsURI=nsURI}) - end, - closeElement = function(name,nsURI) - local pop = table.remove(elementStack) - assertEqual(name,pop.name,"Got close "..name.." to close "..pop.name) - assertEqual(nsURI,pop.nsURI,"Got close namespace "..(nsURI or "nil").." to close namespace "..(pop.nsURI or "nil")) - end, - }:parse(XML['namespace_prefix']) -end - -function test:dom() - local function checkParentage(el) - for _,child in ipairs(el.kids) do - assertEqual(child.parent,el,("'%s' children should have a .parent pointing to their parent '%s'"):format(child.type,el.type)) - if child.kids then checkParentage(child) end - end - end - - local doc = SLAXML:dom(XML['entities_and_namespaces']) - assertEqual(doc.type,'document') - assertEqual(doc.kids[1].type,'pi') - assertEqual(#doc.kids,3) - assertEqual(doc.kids[3],doc.root) - assertEqual(#doc.root.kids,7) - assertEqual(#doc.root.el,3) - assertEqual(doc.root.attr.version,"1.0") - assertEqual(doc.root.attr.xmlns,"http://www.w3.org/2005/07/scxml") - assertEqual(doc.root.attr['xmlns:p'],"http://phrogz.net/") - - checkParentage(doc) - - local s = doc.root.el[1] - assertEqual(s.name,'script') - assertEqual(s.type,'element') - assertEqual(#s.kids,2) - assertEqual(#s.el,0) - assertEqual(s.kids[1].type,'text') - assertEqual(s.kids[2].type,'text') - - local t = doc.root.el[2].el[1] - assertEqual(t.name,'transition') - assertEqual(t.kids[6].type,'comment') - - for _,attr in ipairs(doc.root.attr) do - assertEqual(attr.parent,doc.root,"Attributes should reference their parent element") - assertEqual(attr.type,"attribute") - assertNil(attr.nsURI,"No attribute on the root of this document has a namespace") - end -end - -function test:slim_and_trim_dom() - local function checkParentage(el) - for _,child in ipairs(el.kids) do - assertNil(child.parent,'"slim" dom children should not have a parent') - if child.kids then checkParentage(child) end - end - end - - local doc = SLAXML:dom(XML['entities_and_namespaces'],{simple=true,stripWhitespace=true}) - assertEqual(doc.type,'document') - assertEqual(doc.kids[1].type,'pi') - assertEqual(#doc.kids,2) - local root = doc.kids[2] - assertEqual(#root.kids,3) - assertNil(root.el) - assertNil(root.attr.version) - assertNil(root.attr.xmlns) - assertNil(root.attr['xmlns:p']) - assertEqual(#root.attr,3) - - checkParentage(doc) - - local s = root.kids[1] - assertEqual(s.name,'script') - assertEqual(s.type,'element') - assertEqual(#s.kids,2) - assertEqual(s.kids[1].type,'text') - assertEqual(s.kids[2].type,'text') - - local t = root.kids[2].kids[1] - assertEqual(t.name,'transition') - assertEqual(#t.kids,5) - assertEqual(t.kids[3].type,'comment') -end - -function test:dom_entities() - local doc = SLAXML:dom(XML['entities_and_namespaces']) - local s = doc.root.el[1] - assertEqual(s.kids[1].value,' ampersand = "&"; ') - assertEqual(s.kids[2].value,"quote = '\"'; apos = \"'\"") - - local t = doc.root.el[2].el[1] - assertEqual(t.attr.cond,[[ampersand=='&' and quote=='"' and apos=="'"]]) - - assertEqual(t.kids[6].value,' your code > all ') -end - -function test:xml_namespace() - local doc = SLAXML:dom(XML['xml_namespace']) - for i,attr in ipairs(doc.root.attr) do - if attr.name=='space' then - assertEqual(attr.nsURI,[[http://www.w3.org/XML/1998/namespace]]) - break - end - end -end - -function test:xml_namespace_immediate_use() - local doc = SLAXML:dom(XML['namespace_declare_and_use']) - local cat1 = doc.root.el[1] - assertEqual(cat1.name,'cat') - assertEqual(cat1.nsURI,'cat') - local cat2 = cat1.el[1] - assertEqual(cat2.name, 'cat') - assertEqual(cat2.nsURI,'cat') - local dog1 = cat1.el[2] - assertEqual(dog1.name, 'dog') - assertEqual(dog1.nsURI,'dog') - local cat3 = dog1.el[1] - assertEqual(cat3.name, 'cat') - assertEqual(cat3.nsURI,'cat') - local hog1 = dog1.el[2] - assertEqual(hog1.name, 'hog') - assertEqual(hog1.nsURI,'hog') - for _,attr in ipairs(hog1.attr) do - if attr.value=='yes' then - assertEqual(attr.nsURI,attr.name) - end - end - local hog2 = hog1.el[1] - assertEqual(hog2.name, 'hog') - assertEqual(hog2.nsURI,'hog') - local bog1 = hog1.el[2] - assertEqual(bog1.name, 'bog') - assertEqual(bog1.nsURI,'bog') - local dog2 = dog1.el[3] - assertEqual(dog2.name, 'dog') - assertEqual(dog2.nsURI,'dog') - local cog2 = doc.root.el[2] - assertEqual(cog2.name, 'cog') - assertEqual(cog2.nsURI,'cog') -end - -function test:dom_serializer() - -end - -function test:dom_namespaces() - local scxmlNS = "http://www.w3.org/2005/07/scxml" - local phrogzNS = "http://phrogz.net/" - local barNS = "bar" - local xNS,yNS = "xNS", "yNS" - - local doc = SLAXML:dom(XML['entities_and_namespaces']) - local s = doc.root.el[1] - local p = doc.root.el[2].el[1].el[2] - local t = doc.root.el[2].el[1] - local foo = t.el[3] - local bar1 = foo.el[1] - local bar2 = t.el[4] - local wrap = doc.root.el[3] - local e = wrap.el[1] - - assertEqual(doc.root.nsURI,scxmlNS) - assertEqual(s.nsURI,scxmlNS) - assertEqual(p.name,'goToSlide') - assertEqual(p.nsURI,phrogzNS) - - assertEqual(foo.name,'foo') - assertEqual(foo.nsURI,barNS) - assertEqual(bar1.nsURI,barNS) - assertEqual(bar2.nsURI,scxmlNS) - - assertEqual(wrap.nsURI,scxmlNS) - assertEqual(wrap.attr['xmlns:x'],xNS) - assertEqual(wrap.attr['xmlns:y'],yNS) - assertEqual(e.name,'e') - assertEqual(e.nsURI,scxmlNS) - assertEqual(#e.attr,6) - assertEqual(e.attr.a1,"a1") - assert(e.attr.a2=="a2" or e.attr.a2=="a2-x") - - local nsByValue = {} - for _,attr in ipairs(e.attr) do nsByValue[attr.value] = attr.nsURI end - assertNil(nsByValue['a1']) - assertNil(nsByValue['a2']) - assertNil(nsByValue['a3']) - assertEqual(nsByValue['a2-x'],xNS) - assertEqual(nsByValue['a3-x'],xNS) - assertEqual(nsByValue['a3-y'],yNS) -end - -function test:invalid_documents() - local silentParser = SLAXML:parser{} - assertErrors(silentParser.parse, silentParser, XML['invalid_unquoted'] ) - assertErrors(silentParser.parse, silentParser, XML['invalid_pi_only'] ) - assertErrors(silentParser.parse, silentParser, XML['invalid_unclosed_tags'] ) - assertErrors(silentParser.parse, silentParser, XML['invalid_literal_gtamplt'] ) -end - -function test:comments() - countParsings('commentwrapper',{},{ - pi = 0, - comment = 2, - startElement = 1, - attribute = 0, - text = 2, - namespace = 0, - }) -end - -function test:serializer() - local doc = SLAXML:dom(XML['commentwrapper'],{}) - local xml = SLAXML:xml(doc) - assertEqual(xml, XML['commentwrapper']) - - local doc = SLAXML:dom(XML['commentwrapper'],{stripWhitespace=true}) - local xml = SLAXML:xml(doc,{indent='\t'}) - assertEqual(xml, '\n\n') - - local doc = SLAXML:dom(XML['cdata']) - local xml = SLAXML:xml(doc) - assertEqual(xml, XML['cdata']) - - local doc = SLAXML:dom(XML['utf8'],{stripWhitespace=true}) - - local xml = SLAXML:xml(doc) - assertEqual(xml, [[crêpes: €3crêpes: €3crêpes: €3]]) - - local xml = SLAXML:xml(doc,{indent='\t'}) - assertEqual(xml, [[ - - - crêpes: €3 - - - crêpes: €3 - - - crêpes: €3 - -]]) - - local xml = SLAXML:xml(doc,{indent=3}) - assertEqual(xml, [[ - - - crêpes: €3 - - - crêpes: €3 - - - crêpes: €3 - -]]) -end - -function test:serialize_sorting() - local doc = SLAXML:dom(XML['state'],{stripWhitespace=true}) - - local xml = SLAXML:xml(doc,{omit={'nope', 'http://nvidia.com/drive/ar/scxml', 'http://www.w3.org/2005/07/scxml'}}) - assertEqual(xml, '') - - local xml = SLAXML:xml(doc,{sort=true, omit={'nope', 'http://nvidia.com/drive/ar/scxml'}}) - assertEqual(xml, '') - - local xml = SLAXML:xml(doc,{indent='\t', sort=true, omit={'nope'}}) - assertEqual(xml, [====[ - - - - - - -]====]) - - local xml = SLAXML:xml(doc,{indent='\t', sort=true}) - assertEqual(xml, [====[ - - - - - - - - - -]====]) - - - -end - -function test:simplest() - countParsings('root_only',{},{ - pi = 0, - comment = 0, - startElement = 1, - attribute = 0, - text = 0, - namespace = 0, - }) -end - -function test:whitespace() - countParsings('lotsaspace',{},{ - pi = 0, - comment = 0, - startElement = 3, - attribute = 2, - text = 5, - namespace = 0, - }) - - countParsings('lotsaspace',{stripWhitespace=true},{ - pi = 0, - comment = 0, - startElement = 3, - attribute = 2, - text = 2, - namespace = 0, - }) - - local simple = SLAXML:dom(XML['lotsaspace'],{stripWhitespace=true}).root - local a = simple.el[1] - assertEqual(a.kids[1].value,"It's the end of the world\n as we know it, and I feel\n fine.") - assertEqual(a.kids[2].value,"\nIt's a [raw][[raw]] >\nstring that not care\n about honey badgers.\n\n ") -end - -function test:utf8() - local root = SLAXML:dom(XML['utf8'],{stripWhitespace=true}).root - for _,s in ipairs(root.kids) do - assertEqual(s.attr.a,"crêpes: €3") - assertEqual(s.kids[1].value,"crêpes: €3") - end -end - +package.path = '../?.lua;' .. package.path +local env = require('lunity')() +if _VERSION:find("5.1") then + setfenv(1, env) +else + _ENV = env +end + +local SLAXML = require 'slaxdom' + +local XML = {} +for filename in io.popen('ls files'):lines() do + XML[filename:match('^(.-)%.[^.]+$')] = io.open("files/"..filename):read('*all') +end + +local function countParsings(xmlName,options,expected) + local counts,counters = {},{} + expected.closeElement = expected.startElement + for name,_ in pairs(expected) do + counts[name] = 0 + counters[name] = function() counts[name]=counts[name]+1 end + end + SLAXML:parser(counters):parse(XML[xmlName],options) + for name,ct in pairs(expected) do + assertEqual(counts[name],ct,"There should have been be exactly "..ct.." "..name.."() callback(s) in "..xmlName..", not "..counts[name]) + end +end + +function test:namespace() + local elementStack = {} + SLAXML:parser{ + startElement = function(name,nsURI) + table.insert(elementStack,{name=name,nsURI=nsURI}) + end, + closeElement = function(name,nsURI) + local pop = table.remove(elementStack) + assertEqual(name,pop.name,"Got close "..name.." to close "..pop.name) + assertEqual(nsURI,pop.nsURI,"Got close namespace "..(nsURI or "nil").." to close namespace "..(pop.nsURI or "nil")) + end, + }:parse(XML['namespace_prefix']) +end + +function test:dom() + local function checkParentage(el) + for _,child in ipairs(el.kids) do + assertEqual(child.parent,el,("'%s' children should have a .parent pointing to their parent '%s'"):format(child.type,el.type)) + if child.kids then checkParentage(child) end + end + end + + local doc = SLAXML:dom(XML['entities_and_namespaces']) + assertEqual(doc.type,'document') + assertEqual(doc.kids[1].type,'pi') + assertEqual(#doc.kids,3) + assertEqual(doc.kids[3],doc.root) + assertEqual(#doc.root.kids,7) + assertEqual(#doc.root.el,3) + assertEqual(doc.root.attr.version,"1.0") + assertEqual(doc.root.attr.xmlns,"http://www.w3.org/2005/07/scxml") + assertEqual(doc.root.attr['xmlns:p'],"http://phrogz.net/") + + checkParentage(doc) + + local s = doc.root.el[1] + assertEqual(s.name,'script') + assertEqual(s.type,'element') + assertEqual(#s.kids,2) + assertEqual(#s.el,0) + assertEqual(s.kids[1].type,'text') + assertEqual(s.kids[2].type,'text') + + local t = doc.root.el[2].el[1] + assertEqual(t.name,'transition') + assertEqual(t.kids[6].type,'comment') + + for _,attr in ipairs(doc.root.attr) do + assertEqual(attr.parent,doc.root,"Attributes should reference their parent element") + assertEqual(attr.type,"attribute") + assertNil(attr.nsURI,"No attribute on the root of this document has a namespace") + end +end + +function test:slim_and_trim_dom() + local function checkParentage(el) + for _,child in ipairs(el.kids) do + assertNil(child.parent,'"slim" dom children should not have a parent') + if child.kids then checkParentage(child) end + end + end + + local doc = SLAXML:dom(XML['entities_and_namespaces'],{simple=true,stripWhitespace=true}) + assertEqual(doc.type,'document') + assertEqual(doc.kids[1].type,'pi') + assertEqual(#doc.kids,2) + local root = doc.kids[2] + assertEqual(#root.kids,3) + assertNil(root.el) + assertNil(root.attr.version) + assertNil(root.attr.xmlns) + assertNil(root.attr['xmlns:p']) + assertEqual(#root.attr,3) + + checkParentage(doc) + + local s = root.kids[1] + assertEqual(s.name,'script') + assertEqual(s.type,'element') + assertEqual(#s.kids,2) + assertEqual(s.kids[1].type,'text') + assertEqual(s.kids[2].type,'text') + + local t = root.kids[2].kids[1] + assertEqual(t.name,'transition') + assertEqual(#t.kids,5) + assertEqual(t.kids[3].type,'comment') +end + +function test:dom_entities() + local doc = SLAXML:dom(XML['entities_and_namespaces']) + local s = doc.root.el[1] + assertEqual(s.kids[1].value,' ampersand = "&"; ') + assertEqual(s.kids[2].value,"quote = '\"'; apos = \"'\"") + + local t = doc.root.el[2].el[1] + assertEqual(t.attr.cond,[[ampersand=='&' and quote=='"' and apos=="'"]]) + + assertEqual(t.kids[6].value,' your code > all ') +end + +function test:xml_namespace() + local doc = SLAXML:dom(XML['xml_namespace']) + for i,attr in ipairs(doc.root.attr) do + if attr.name=='space' then + assertEqual(attr.nsURI,[[http://www.w3.org/XML/1998/namespace]]) + break + end + end +end + +function test:xml_namespace_immediate_use() + local doc = SLAXML:dom(XML['namespace_declare_and_use']) + local cat1 = doc.root.el[1] + assertEqual(cat1.name,'cat') + assertEqual(cat1.nsURI,'cat') + local cat2 = cat1.el[1] + assertEqual(cat2.name, 'cat') + assertEqual(cat2.nsURI,'cat') + local dog1 = cat1.el[2] + assertEqual(dog1.name, 'dog') + assertEqual(dog1.nsURI,'dog') + local cat3 = dog1.el[1] + assertEqual(cat3.name, 'cat') + assertEqual(cat3.nsURI,'cat') + local hog1 = dog1.el[2] + assertEqual(hog1.name, 'hog') + assertEqual(hog1.nsURI,'hog') + for _,attr in ipairs(hog1.attr) do + if attr.value=='yes' then + assertEqual(attr.nsURI,attr.name) + end + end + local hog2 = hog1.el[1] + assertEqual(hog2.name, 'hog') + assertEqual(hog2.nsURI,'hog') + local bog1 = hog1.el[2] + assertEqual(bog1.name, 'bog') + assertEqual(bog1.nsURI,'bog') + local dog2 = dog1.el[3] + assertEqual(dog2.name, 'dog') + assertEqual(dog2.nsURI,'dog') + local cog2 = doc.root.el[2] + assertEqual(cog2.name, 'cog') + assertEqual(cog2.nsURI,'cog') +end + +function test:dom_serializer() + +end + +function test:dom_namespaces() + local scxmlNS = "http://www.w3.org/2005/07/scxml" + local phrogzNS = "http://phrogz.net/" + local barNS = "bar" + local xNS,yNS = "xNS", "yNS" + + local doc = SLAXML:dom(XML['entities_and_namespaces']) + local s = doc.root.el[1] + local p = doc.root.el[2].el[1].el[2] + local t = doc.root.el[2].el[1] + local foo = t.el[3] + local bar1 = foo.el[1] + local bar2 = t.el[4] + local wrap = doc.root.el[3] + local e = wrap.el[1] + + assertEqual(doc.root.nsURI,scxmlNS) + assertEqual(s.nsURI,scxmlNS) + assertEqual(p.name,'goToSlide') + assertEqual(p.nsURI,phrogzNS) + + assertEqual(foo.name,'foo') + assertEqual(foo.nsURI,barNS) + assertEqual(bar1.nsURI,barNS) + assertEqual(bar2.nsURI,scxmlNS) + + assertEqual(wrap.nsURI,scxmlNS) + assertEqual(wrap.attr['xmlns:x'],xNS) + assertEqual(wrap.attr['xmlns:y'],yNS) + assertEqual(e.name,'e') + assertEqual(e.nsURI,scxmlNS) + assertEqual(#e.attr,6) + assertEqual(e.attr.a1,"a1") + assert(e.attr.a2=="a2" or e.attr.a2=="a2-x") + + local nsByValue = {} + for _,attr in ipairs(e.attr) do nsByValue[attr.value] = attr.nsURI end + assertNil(nsByValue['a1']) + assertNil(nsByValue['a2']) + assertNil(nsByValue['a3']) + assertEqual(nsByValue['a2-x'],xNS) + assertEqual(nsByValue['a3-x'],xNS) + assertEqual(nsByValue['a3-y'],yNS) +end + +function test:invalid_documents() + local silentParser = SLAXML:parser{ text = function() end } + assertErrors(silentParser.parse, silentParser, XML['invalid_unquoted'] ) + assertErrors(silentParser.parse, silentParser, XML['invalid_pi_only'] ) + assertErrors(silentParser.parse, silentParser, XML['invalid_unclosed_tags'] ) + assertErrors(silentParser.parse, silentParser, XML['invalid_literal_gtamplt'] ) +end + +function test:comments() + countParsings('commentwrapper',{},{ + pi = 0, + comment = 2, + startElement = 1, + attribute = 0, + text = 2, + namespace = 0, + }) +end + +function test:serializer() + local doc = SLAXML:dom(XML['commentwrapper'],{}) + local xml = SLAXML:xml(doc) + assertEqual(xml, XML['commentwrapper']) + + local doc = SLAXML:dom(XML['commentwrapper'],{stripWhitespace=true}) + local xml = SLAXML:xml(doc,{indent='\t'}) + assertEqual(xml, '\n\n') + + local doc = SLAXML:dom(XML['cdata']) + local xml = SLAXML:xml(doc) + assertEqual(xml, XML['cdata']) + + local doc = SLAXML:dom(XML['utf8'],{stripWhitespace=true}) + + local xml = SLAXML:xml(doc) + assertEqual(xml, [[crêpes: €3crêpes: €3crêpes: €3]]) + + local xml = SLAXML:xml(doc,{indent='\t'}) + assertEqual(xml, [[ + + + crêpes: €3 + + + crêpes: €3 + + + crêpes: €3 + +]]) + + local xml = SLAXML:xml(doc,{indent=3}) + assertEqual(xml, [[ + + + crêpes: €3 + + + crêpes: €3 + + + crêpes: €3 + +]]) +end + +function test:serialize_sorting() + local doc = SLAXML:dom(XML['state'],{stripWhitespace=true}) + + local xml = SLAXML:xml(doc,{omit={'nope', 'http://nvidia.com/drive/ar/scxml', 'http://www.w3.org/2005/07/scxml'}}) + assertEqual(xml, '') + + local xml = SLAXML:xml(doc,{sort=true, omit={'nope', 'http://nvidia.com/drive/ar/scxml'}}) + assertEqual(xml, '') + + local xml = SLAXML:xml(doc,{indent='\t', sort=true, omit={'nope'}}) + assertEqual(xml, [====[ + + + + + + +]====]) + + local xml = SLAXML:xml(doc,{indent='\t', sort=true}) + assertEqual(xml, [====[ + + + + + + + + + +]====]) + + + +end + +function test:simplest() + countParsings('root_only',{},{ + pi = 0, + comment = 0, + startElement = 1, + attribute = 0, + text = 0, + namespace = 0, + }) +end + +function test:whitespace() + countParsings('lotsaspace',{},{ + pi = 0, + comment = 0, + startElement = 3, + attribute = 2, + text = 5, + namespace = 0, + }) + + countParsings('lotsaspace',{stripWhitespace=true},{ + pi = 0, + comment = 0, + startElement = 3, + attribute = 2, + text = 2, + namespace = 0, + }) + + local simple = SLAXML:dom(XML['lotsaspace'],{stripWhitespace=true}).root + local a = simple.el[1] + assertEqual(a.kids[1].value,"It's the end of the world\n as we know it, and I feel\n fine.") + assertEqual(a.kids[2].value,"\nIt's a [raw][[raw]] >\nstring that not care\n about honey badgers.\n\n ") +end + +function test:utf8() + local root = SLAXML:dom(XML['utf8'],{stripWhitespace=true}).root + for _,s in ipairs(root.kids) do + assertEqual(s.attr.a,"crêpes: €3") + assertEqual(s.kids[1].value,"crêpes: €3") + end +end + test{useANSI=false} \ No newline at end of file