scansion/stanzacmp.lua

changeset 6
0c94ea0cabec
child 32
0eabed6c91e7
--- /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;
+};

mercurial