--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/scansion/stanzacmp.lua Sat Sep 05 23:24:15 2015 +0100 @@ -0,0 +1,105 @@ +-- This is my attempt at a utility library to compare two XMPP stanzas +-- It is not testing for exact equivalency, but through some vague rules that felt right. +-- For example, the second stanza passed to stanzas_match() is allowed to have unexpected +-- elements and attributes at the top level. Beyond this, they must match exactly, except +-- for whitespace differences only. +-- +-- There are probably bugs, and it can probably be smarter, but I don't want to spend too +-- much time on it right now. + +local function trim(s) + return (s:gsub("^%s+", ""):gsub("%s+$", "")); +end + +local function stanzas_strict_match(stanza1, stanza2) + if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then + return false; + end + + for k, v in pairs(stanza1.attr) do + if stanza2.attr[k] ~= v then + return false; + end + end + + for k, v in pairs(stanza2.attr) do + if stanza1.attr[k] ~= v then + return false; + end + end + + if #stanza1.tags ~= #stanza2.tags then + return false; + end + + local stanza2_pos = 1; + for _, child in ipairs(stanza1) do + if type(child) == "table" or child:match("%S") then + local match; + local child2 = stanza2[stanza2_pos]; + while child2 and not(type(child2) == "table" or child:match("%S")) do + stanza2_pos = stanza2_pos + 1; + child2 = stanza2[stanza2_pos]; + end + if type(child) ~= type(child2) then + return false; + end + if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then + -- Strict deep match + match = stanzas_strict_match(child, child2); + elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace + match = trim(child) == trim(child2); + end + if not match then + return false; + end + end + end + return true; +end + +-- Everything in stanza1 should be present in stanza2 + +local function stanzas_match(stanza1, stanza2) + if stanza1.name ~= stanza2.name or stanza1.attr.xmlns ~= stanza2.attr.xmlns then + return false; + end + + for k, v in pairs(stanza1.attr) do + if stanza2.attr[k] ~= v then + return false; + end + end + + local stanza2_pos = 1; + for _, child in ipairs(stanza1) do + if type(child) == "table" or child:match("%S") then + local match; + -- Iterate through remaining nodes in stanza2, looking for a match + while stanza2_pos <= #stanza2 do + local child2 = stanza2[stanza2_pos]; + stanza2_pos = stanza2_pos + 1; + + if type(child2) == type(child) then + if type(child) == "table" and child2.name == child.name and child2.attr.xmlns == child.attr.xmlns then + -- Strict deep match + match = stanzas_strict_match(child, child2); + elseif type(child) == "string" then -- Text nodes, must be equal, ignoring leading/trailing whitespace + match = trim(child) == trim(child2); + end + break; + end + end + if not match then + return false; + end + end + end + return true; +end + + +return { + stanzas_match = stanzas_match; + stanzas_strict_match = stanzas_strict_match; +};