client: Implement send/receive, including new stanzacmp library

Sat, 05 Sep 2015 23:24:15 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Sat, 05 Sep 2015 23:24:15 +0100
changeset 6
0c94ea0cabec
parent 5
6a95814d66ce
child 7
ecac723bb6e1

client: Implement send/receive, including new stanzacmp library

scansion/objects/client.lua file | annotate | diff | comparison | revisions
scansion/stanzacmp.lua file | annotate | diff | comparison | revisions
--- a/scansion/objects/client.lua	Sat Sep 05 23:20:17 2015 +0100
+++ b/scansion/objects/client.lua	Sat Sep 05 23:24:15 2015 +0100
@@ -3,6 +3,11 @@
 verse.set_log_handler(verse._default_log_handler, { "debug", "info", "warn", "error" });
 
 local parse_xml = require "scansion.xml".parse;
+
+local stanza_timeout = 5;
+
+local stanzacmp = require "scansion.stanzacmp";
+
 return {
 	_validate = function (client)
 		assert(client.jid, "No JID specified");
@@ -14,12 +19,25 @@
 	end;
 
 	sends = function (client, data)
-		print(("%q"):format(table.concat(data)))
-		print(parse_xml(table.concat(data)));
-		print(stanza)
+		local stanza =  parse_xml(table.concat(data));
+		client.stream:send(stanza);
 	end;
 
 	receives = function (client, data)
+		local expected_stanza = parse_xml(table.concat(data));
+		local function stanza_handler(received_stanza)
+			if not stanzacmp.stanzas_match(expected_stanza, received_stanza) then
+				print("NOT IT!")
+				verse.quit();
+			else
+				print("YES!")
+			end
+			expected_stanza = nil;
+		end
+		client.stream:hook("stanza", stanza_handler, 100);
+		verse.add_task(stanza_timeout, function ()
+			if not expected_stanza then return; end
+		end);
 	end;
 
 	disconnects = function (client)
--- /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