summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-08-29 12:51:45 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-08-29 13:30:18 (GMT)
commit0091fcc571758791442f82ece2a72444b6fe79cf (patch)
tree807f20a4047604a87f56d247d8ad7180489fe82b
parent901f620b6ccabde19ef460368028fa0464aa56c5 (diff)
downloadswift-contrib-0091fcc571758791442f82ece2a72444b6fe79cf.zip
swift-contrib-0091fcc571758791442f82ece2a72444b6fe79cf.tar.bz2
Added Stanza Ack Requester & Responder.
-rw-r--r--QA/UnitTest/SConscript3
-rw-r--r--Swiften/Elements/Message.h4
-rw-r--r--Swiften/Elements/Stanza.h1
-rw-r--r--Swiften/Elements/StanzaAck.h10
-rw-r--r--Swiften/Parser/StanzaAckParser.cpp1
-rw-r--r--Swiften/Parser/UnitTest/StanzaAckParserTest.cpp2
-rw-r--r--Swiften/SConscript4
-rw-r--r--Swiften/StreamManagement/SConscript8
-rw-r--r--Swiften/StreamManagement/StanzaAckRequester.cpp39
-rw-r--r--Swiften/StreamManagement/StanzaAckRequester.h33
-rw-r--r--Swiften/StreamManagement/StanzaAckResponder.cpp24
-rw-r--r--Swiften/StreamManagement/StanzaAckResponder.h30
-rw-r--r--Swiften/StreamManagement/UnitTest/StanzaAckRequesterTest.cpp120
-rw-r--r--Swiften/StreamManagement/UnitTest/StanzaAckResponderTest.cpp98
14 files changed, 367 insertions, 10 deletions
diff --git a/QA/UnitTest/SConscript b/QA/UnitTest/SConscript
index cd9cdc8..d47474d 100644
--- a/QA/UnitTest/SConscript
+++ b/QA/UnitTest/SConscript
@@ -5,6 +5,7 @@ Import("env")
if env["TEST"] :
if env["SCONS_STAGE"] == "flags" :
env["UNITTEST_SOURCES"] = []
+ env["UNITTEST_OBJECTS"] = []
if env["SCONS_STAGE"] == "test" :
myenv = env.Clone()
myenv.MergeFlags(env.get("CHECKER_FLAGS",""))
@@ -23,7 +24,7 @@ if env["TEST"] :
myenv.Append(CPPDEFINES = ["HAVE_LIBXML"])
if env.get("HAVE_EXPAT") :
myenv.Append(CPPDEFINES = ["HAVE_EXPAT"])
- checker = myenv.Program("checker", env["UNITTEST_SOURCES"])
+ checker = myenv.Program("checker", env["UNITTEST_SOURCES"] + env["UNITTEST_OBJECTS"])
for i in ["HOME", "USERPROFILE", "APPDATA"]:
if os.environ.get(i, "") :
myenv["ENV"][i] = os.environ[i]
diff --git a/Swiften/Elements/Message.h b/Swiften/Elements/Message.h
index f42aec6..e5e00ab 100644
--- a/Swiften/Elements/Message.h
+++ b/Swiften/Elements/Message.h
@@ -9,6 +9,7 @@
#include <boost/optional.hpp>
#include "Swiften/Base/String.h"
+#include "Swiften/Base/Shared.h"
#include "Swiften/Elements/Body.h"
#include "Swiften/Elements/Subject.h"
#include "Swiften/Elements/ErrorPayload.h"
@@ -16,8 +17,7 @@
namespace Swift
{
- class Message : public Stanza
- {
+ class Message : public Stanza, public Shared<Message> {
public:
enum Type { Normal, Chat, Error, Groupchat, Headline };
diff --git a/Swiften/Elements/Stanza.h b/Swiften/Elements/Stanza.h
index 20fb557..5d8fd6c 100644
--- a/Swiften/Elements/Stanza.h
+++ b/Swiften/Elements/Stanza.h
@@ -14,6 +14,7 @@
#include "Swiften/Elements/Element.h"
#include "Swiften/Elements/Payload.h"
#include "Swiften/Base/String.h"
+#include "Swiften/Base/Shared.h"
#include "Swiften/Base/foreach.h"
#include "Swiften/JID/JID.h"
diff --git a/Swiften/Elements/StanzaAck.h b/Swiften/Elements/StanzaAck.h
index a1a39f8..eaf4e26 100644
--- a/Swiften/Elements/StanzaAck.h
+++ b/Swiften/Elements/StanzaAck.h
@@ -12,21 +12,23 @@
namespace Swift {
class StanzaAck : public Element, public Shared<StanzaAck> {
public:
- StanzaAck() : handledStanzasCount(-1) {}
+ StanzaAck() : valid(false), handledStanzasCount(0) {}
- int getHandledStanzasCount() const {
+ unsigned int getHandledStanzasCount() const {
return handledStanzasCount;
}
void setHandledStanzasCount(int i) {
handledStanzasCount = i;
+ valid = true;
}
bool isValid() const {
- return handledStanzasCount != -1;
+ return valid;
}
private:
- int handledStanzasCount;
+ bool valid;
+ unsigned int handledStanzasCount;
};
}
diff --git a/Swiften/Parser/StanzaAckParser.cpp b/Swiften/Parser/StanzaAckParser.cpp
index 62e912b..d85eb9b 100644
--- a/Swiften/Parser/StanzaAckParser.cpp
+++ b/Swiften/Parser/StanzaAckParser.cpp
@@ -20,7 +20,6 @@ void StanzaAckParser::handleStartElement(const String&, const String&, const Att
getElementGeneric()->setHandledStanzasCount(boost::lexical_cast<int>(handledStanzasString.getUTF8String()));
}
catch (const boost::bad_lexical_cast &) {
- getElementGeneric()->setHandledStanzasCount(-1);
}
}
++depth;
diff --git a/Swiften/Parser/UnitTest/StanzaAckParserTest.cpp b/Swiften/Parser/UnitTest/StanzaAckParserTest.cpp
index 39e67a7..b90af6e 100644
--- a/Swiften/Parser/UnitTest/StanzaAckParserTest.cpp
+++ b/Swiften/Parser/UnitTest/StanzaAckParserTest.cpp
@@ -28,7 +28,7 @@ class StanzaAckParserTest : public CppUnit::TestFixture {
CPPUNIT_ASSERT(parser.parse("<a h=\"12\" xmlns=\"urn:xmpp:sm:2\"/>"));
CPPUNIT_ASSERT(testling.getElementGeneric()->isValid());
- CPPUNIT_ASSERT_EQUAL(12, testling.getElementGeneric()->getHandledStanzasCount());
+ CPPUNIT_ASSERT_EQUAL(12U, testling.getElementGeneric()->getHandledStanzasCount());
}
void testParse_Invalid() {
diff --git a/Swiften/SConscript b/Swiften/SConscript
index 3e5c35b..a23efa0 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -129,6 +129,7 @@ if env["SCONS_STAGE"] == "build" :
"History",
"StreamStack",
"LinkLocal",
+ "StreamManagement",
])
SConscript(test_only = True, dirs = [
"QA",
@@ -225,7 +226,8 @@ if env["SCONS_STAGE"] == "build" :
File("Serializer/UnitTest/AuthRequestSerializerTest.cpp"),
File("Serializer/UnitTest/AuthResponseSerializerTest.cpp"),
File("Serializer/XML/UnitTest/XMLElementTest.cpp"),
- File("Server/UnitTest/ServerStanzaRouterTest.cpp"),
+ File("StreamManagement/UnitTest/StanzaAckRequesterTest.cpp"),
+ File("StreamManagement/UnitTest/StanzaAckResponderTest.cpp"),
File("StreamStack/UnitTest/StreamStackTest.cpp"),
File("StreamStack/UnitTest/XMPPLayerTest.cpp"),
File("StringCodecs/UnitTest/Base64Test.cpp"),
diff --git a/Swiften/StreamManagement/SConscript b/Swiften/StreamManagement/SConscript
new file mode 100644
index 0000000..d3fab8e
--- /dev/null
+++ b/Swiften/StreamManagement/SConscript
@@ -0,0 +1,8 @@
+Import("swiften_env")
+
+sources = [
+ "StanzaAckRequester.cpp",
+ "StanzaAckResponder.cpp",
+ ]
+
+swiften_env.Append(SWIFTEN_OBJECTS = swiften_env.StaticObject(sources)) \ No newline at end of file
diff --git a/Swiften/StreamManagement/StanzaAckRequester.cpp b/Swiften/StreamManagement/StanzaAckRequester.cpp
new file mode 100644
index 0000000..b007675
--- /dev/null
+++ b/Swiften/StreamManagement/StanzaAckRequester.cpp
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/StreamManagement/StanzaAckRequester.h"
+
+#include <boost/numeric/conversion/cast.hpp>
+
+namespace Swift {
+
+static const unsigned int MAX_HANDLED_STANZA_COUNT = boost::numeric_cast<unsigned int>((1ULL<<32) - 1);
+
+StanzaAckRequester::StanzaAckRequester() : lastHandledStanzasCount(0) {
+
+}
+
+void StanzaAckRequester::handleStanzaSent(boost::shared_ptr<Stanza> stanza) {
+ unackedStanzas.push_back(stanza);
+ onRequestAck();
+}
+
+void StanzaAckRequester::handleAckReceived(unsigned int handledStanzasCount) {
+ unsigned int i = lastHandledStanzasCount;
+ while (i != handledStanzasCount) {
+ if (unackedStanzas.size() == 0) {
+ std::cerr << "Warning: Server acked more stanzas than we sent" << std::endl;
+ break;
+ }
+ boost::shared_ptr<Stanza> ackedStanza = unackedStanzas.front();
+ unackedStanzas.pop_front();
+ onStanzaAcked(ackedStanza);
+ i = (i == MAX_HANDLED_STANZA_COUNT ? 0 : i + 1);
+ }
+ lastHandledStanzasCount = handledStanzasCount;
+}
+
+}
diff --git a/Swiften/StreamManagement/StanzaAckRequester.h b/Swiften/StreamManagement/StanzaAckRequester.h
new file mode 100644
index 0000000..89f068e
--- /dev/null
+++ b/Swiften/StreamManagement/StanzaAckRequester.h
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+#include <deque>
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Base/boost_bsignals.h"
+
+namespace Swift {
+ class StanzaAckRequester {
+ public:
+ StanzaAckRequester();
+
+ void handleStanzaSent(boost::shared_ptr<Stanza> stanza);
+ void handleAckReceived(unsigned int handledStanzasCount);
+
+ public:
+ boost::signal<void ()> onRequestAck;
+ boost::signal<void (boost::shared_ptr<Stanza>)> onStanzaAcked;
+
+ private:
+ friend class StanzaAckRequesterTest;
+ unsigned int lastHandledStanzasCount;
+ std::deque<boost::shared_ptr<Stanza> > unackedStanzas;
+ };
+
+}
diff --git a/Swiften/StreamManagement/StanzaAckResponder.cpp b/Swiften/StreamManagement/StanzaAckResponder.cpp
new file mode 100644
index 0000000..05ab5c4
--- /dev/null
+++ b/Swiften/StreamManagement/StanzaAckResponder.cpp
@@ -0,0 +1,24 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/StreamManagement/StanzaAckResponder.h"
+
+#include <boost/numeric/conversion/cast.hpp>
+
+namespace Swift {
+
+static const unsigned int MAX_HANDLED_STANZA_COUNT = boost::numeric_cast<unsigned int>((1ULL<<32) - 1);
+
+StanzaAckResponder::StanzaAckResponder() : handledStanzasCount(0) {
+}
+
+void StanzaAckResponder::handleStanzaReceived() {
+ handledStanzasCount = (handledStanzasCount == MAX_HANDLED_STANZA_COUNT ? 0 : handledStanzasCount + 1);
+}
+
+void StanzaAckResponder::handleAckRequestReceived() {
+ onAck(handledStanzasCount);}
+}
diff --git a/Swiften/StreamManagement/StanzaAckResponder.h b/Swiften/StreamManagement/StanzaAckResponder.h
new file mode 100644
index 0000000..bc83aa1
--- /dev/null
+++ b/Swiften/StreamManagement/StanzaAckResponder.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Elements/Stanza.h"
+#include "Swiften/Base/boost_bsignals.h"
+
+namespace Swift {
+ class StanzaAckResponder {
+ public:
+ StanzaAckResponder();
+
+ void handleStanzaReceived();
+ void handleAckRequestReceived();
+
+ public:
+ boost::signal<void (unsigned int /* handledStanzaCount */)> onAck;
+
+ private:
+ friend class StanzaAckResponderTest;
+ unsigned int handledStanzasCount;
+ };
+
+}
diff --git a/Swiften/StreamManagement/UnitTest/StanzaAckRequesterTest.cpp b/Swiften/StreamManagement/UnitTest/StanzaAckRequesterTest.cpp
new file mode 100644
index 0000000..70fe6eb
--- /dev/null
+++ b/Swiften/StreamManagement/UnitTest/StanzaAckRequesterTest.cpp
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include "Swiften/StreamManagement/StanzaAckRequester.h"
+#include "Swiften/Elements/Message.h"
+
+using namespace Swift;
+
+namespace Swift {
+
+class StanzaAckRequesterTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(StanzaAckRequesterTest);
+ CPPUNIT_TEST(testHandleStanzaSent_RequestsAck);
+ CPPUNIT_TEST(testHandleAckReceived_AcksStanza);
+ CPPUNIT_TEST(testHandleAckReceived_AcksMultipleStanzas);
+ CPPUNIT_TEST(testHandleAckReceived_MultipleAcks);
+ CPPUNIT_TEST(testHandleAckReceived_WrapAround);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ acksRequested = 0;
+ }
+
+ void testHandleStanzaSent_RequestsAck() {
+ std::auto_ptr<StanzaAckRequester> testling(createRequester());
+ testling->handleStanzaSent(createMessage("m1"));
+
+ CPPUNIT_ASSERT_EQUAL(1, acksRequested);
+ }
+
+ void testHandleAckReceived_AcksStanza() {
+ std::auto_ptr<StanzaAckRequester> testling(createRequester());
+ testling->handleStanzaSent(createMessage("m1"));
+
+ testling->handleAckReceived(1);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(ackedStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(String("m1"), ackedStanzas[0]->getID());
+ }
+
+ void testHandleAckReceived_AcksMultipleStanzas() {
+ std::auto_ptr<StanzaAckRequester> testling(createRequester());
+ testling->handleStanzaSent(createMessage("m1"));
+ testling->handleStanzaSent(createMessage("m2"));
+
+ testling->handleAckReceived(2);
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(ackedStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(String("m1"), ackedStanzas[0]->getID());
+ CPPUNIT_ASSERT_EQUAL(String("m2"), ackedStanzas[1]->getID());
+ }
+
+ void testHandleAckReceived_MultipleAcks() {
+ std::auto_ptr<StanzaAckRequester> testling(createRequester());
+ testling->handleStanzaSent(createMessage("m1"));
+ testling->handleAckReceived(1);
+
+ testling->handleStanzaSent(createMessage("m2"));
+ testling->handleStanzaSent(createMessage("m3"));
+ testling->handleAckReceived(3);
+
+ CPPUNIT_ASSERT_EQUAL(3, static_cast<int>(ackedStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(String("m1"), ackedStanzas[0]->getID());
+ CPPUNIT_ASSERT_EQUAL(String("m2"), ackedStanzas[1]->getID());
+ CPPUNIT_ASSERT_EQUAL(String("m3"), ackedStanzas[2]->getID());
+ }
+
+ // Handle stanza ack count wrapping, as per the XEP
+ void testHandleAckReceived_WrapAround() {
+ std::auto_ptr<StanzaAckRequester> testling(createRequester());
+ testling->lastHandledStanzasCount = boost::numeric_cast<unsigned int>((1ULL<<32) - 1);
+ testling->handleStanzaSent(createMessage("m1"));
+ testling->handleStanzaSent(createMessage("m2"));
+
+ testling->handleAckReceived(1);
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(ackedStanzas.size()));
+ CPPUNIT_ASSERT_EQUAL(String("m1"), ackedStanzas[0]->getID());
+ CPPUNIT_ASSERT_EQUAL(String("m2"), ackedStanzas[1]->getID());
+ }
+
+ private:
+ Message::ref createMessage(const String& id) {
+ Message::ref result(new Message());
+ result->setID(id);
+ return result;
+ }
+
+ StanzaAckRequester* createRequester() {
+ StanzaAckRequester* requester = new StanzaAckRequester();
+ requester->onRequestAck.connect(boost::bind(&StanzaAckRequesterTest::handleRequestAck, this));
+ requester->onStanzaAcked.connect(boost::bind(&StanzaAckRequesterTest::handleStanzaAcked, this, _1));
+ return requester;
+ }
+
+ void handleRequestAck() {
+ acksRequested++;
+ }
+
+ void handleStanzaAcked(boost::shared_ptr<Stanza> stanza) {
+ ackedStanzas.push_back(stanza);
+ }
+
+ private:
+ int acksRequested;
+ std::vector< boost::shared_ptr<Stanza> > ackedStanzas;
+};
+
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Swift::StanzaAckRequesterTest);
diff --git a/Swiften/StreamManagement/UnitTest/StanzaAckResponderTest.cpp b/Swiften/StreamManagement/UnitTest/StanzaAckResponderTest.cpp
new file mode 100644
index 0000000..fa2b782
--- /dev/null
+++ b/Swiften/StreamManagement/UnitTest/StanzaAckResponderTest.cpp
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+#include <boost/numeric/conversion/cast.hpp>
+
+#include "Swiften/StreamManagement/StanzaAckResponder.h"
+#include "Swiften/Elements/Message.h"
+
+using namespace Swift;
+
+namespace Swift {
+
+class StanzaAckResponderTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(StanzaAckResponderTest);
+ CPPUNIT_TEST(testHandleAckRequestReceived_AcksStanza);
+ CPPUNIT_TEST(testHandleAckRequestReceived_AcksMultipleStanzas);
+ CPPUNIT_TEST(testHandleAckRequestReceived_MultipleAcks);
+ CPPUNIT_TEST(testHandleAckRequestReceived_WrapAround);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void testHandleAckRequestReceived_AcksStanza() {
+ std::auto_ptr<StanzaAckResponder> testling(createResponder());
+ testling->handleStanzaReceived();
+
+ testling->handleAckRequestReceived();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(acks.size()));
+ CPPUNIT_ASSERT_EQUAL(1U, acks[0]);
+ }
+
+ void testHandleAckRequestReceived_AcksMultipleStanzas() {
+ std::auto_ptr<StanzaAckResponder> testling(createResponder());
+ testling->handleStanzaReceived();
+ testling->handleStanzaReceived();
+
+ testling->handleAckRequestReceived();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(acks.size()));
+ CPPUNIT_ASSERT_EQUAL(2U, acks[0]);
+ }
+
+ void testHandleAckRequestReceived_MultipleAcks() {
+ std::auto_ptr<StanzaAckResponder> testling(createResponder());
+ testling->handleStanzaReceived();
+ testling->handleAckRequestReceived();
+
+ testling->handleStanzaReceived();
+ testling->handleAckRequestReceived();
+
+ CPPUNIT_ASSERT_EQUAL(2, static_cast<int>(acks.size()));
+ CPPUNIT_ASSERT_EQUAL(1U, acks[0]);
+ CPPUNIT_ASSERT_EQUAL(2U, acks[1]);
+ }
+
+ // Handle stanza ack count wrapping, as per the XEP
+ void testHandleAckRequestReceived_WrapAround() {
+ std::auto_ptr<StanzaAckResponder> testling(createResponder());
+ testling->handledStanzasCount = boost::numeric_cast<unsigned int>((1ULL<<32) - 1);
+ testling->handleStanzaReceived();
+ testling->handleStanzaReceived();
+
+ testling->handleAckRequestReceived();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(acks.size()));
+ CPPUNIT_ASSERT_EQUAL(1U, acks[0]);
+ }
+
+ private:
+ Message::ref createMessage(const String& id) {
+ Message::ref result(new Message());
+ result->setID(id);
+ return result;
+ }
+
+ StanzaAckResponder* createResponder() {
+ StanzaAckResponder* responder = new StanzaAckResponder();
+ responder->onAck.connect(boost::bind(&StanzaAckResponderTest::handleAck, this, _1));
+ return responder;
+ }
+
+ void handleAck(unsigned int h) {
+ acks.push_back(h);
+ }
+
+ private:
+ std::vector<unsigned int> acks;
+};
+
+}
+
+CPPUNIT_TEST_SUITE_REGISTRATION(Swift::StanzaAckResponderTest);