From 0091fcc571758791442f82ece2a72444b6fe79cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= Date: Sun, 29 Aug 2010 14:51:45 +0200 Subject: Added Stanza Ack Requester & Responder. 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 #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 { 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 { 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(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("")); 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 + +namespace Swift { + +static const unsigned int MAX_HANDLED_STANZA_COUNT = boost::numeric_cast((1ULL<<32) - 1); + +StanzaAckRequester::StanzaAckRequester() : lastHandledStanzasCount(0) { + +} + +void StanzaAckRequester::handleStanzaSent(boost::shared_ptr 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 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 +#include + +#include "Swiften/Elements/Stanza.h" +#include "Swiften/Base/boost_bsignals.h" + +namespace Swift { + class StanzaAckRequester { + public: + StanzaAckRequester(); + + void handleStanzaSent(boost::shared_ptr stanza); + void handleAckReceived(unsigned int handledStanzasCount); + + public: + boost::signal onRequestAck; + boost::signal)> onStanzaAcked; + + private: + friend class StanzaAckRequesterTest; + unsigned int lastHandledStanzasCount; + std::deque > 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 + +namespace Swift { + +static const unsigned int MAX_HANDLED_STANZA_COUNT = boost::numeric_cast((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 + +#include "Swiften/Elements/Stanza.h" +#include "Swiften/Base/boost_bsignals.h" + +namespace Swift { + class StanzaAckResponder { + public: + StanzaAckResponder(); + + void handleStanzaReceived(); + void handleAckRequestReceived(); + + public: + boost::signal 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 +#include +#include +#include + +#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 testling(createRequester()); + testling->handleStanzaSent(createMessage("m1")); + + CPPUNIT_ASSERT_EQUAL(1, acksRequested); + } + + void testHandleAckReceived_AcksStanza() { + std::auto_ptr testling(createRequester()); + testling->handleStanzaSent(createMessage("m1")); + + testling->handleAckReceived(1); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(ackedStanzas.size())); + CPPUNIT_ASSERT_EQUAL(String("m1"), ackedStanzas[0]->getID()); + } + + void testHandleAckReceived_AcksMultipleStanzas() { + std::auto_ptr testling(createRequester()); + testling->handleStanzaSent(createMessage("m1")); + testling->handleStanzaSent(createMessage("m2")); + + testling->handleAckReceived(2); + + CPPUNIT_ASSERT_EQUAL(2, static_cast(ackedStanzas.size())); + CPPUNIT_ASSERT_EQUAL(String("m1"), ackedStanzas[0]->getID()); + CPPUNIT_ASSERT_EQUAL(String("m2"), ackedStanzas[1]->getID()); + } + + void testHandleAckReceived_MultipleAcks() { + std::auto_ptr 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(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 testling(createRequester()); + testling->lastHandledStanzasCount = boost::numeric_cast((1ULL<<32) - 1); + testling->handleStanzaSent(createMessage("m1")); + testling->handleStanzaSent(createMessage("m2")); + + testling->handleAckReceived(1); + + CPPUNIT_ASSERT_EQUAL(2, static_cast(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) { + ackedStanzas.push_back(stanza); + } + + private: + int acksRequested; + std::vector< boost::shared_ptr > 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 +#include +#include +#include + +#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 testling(createResponder()); + testling->handleStanzaReceived(); + + testling->handleAckRequestReceived(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(acks.size())); + CPPUNIT_ASSERT_EQUAL(1U, acks[0]); + } + + void testHandleAckRequestReceived_AcksMultipleStanzas() { + std::auto_ptr testling(createResponder()); + testling->handleStanzaReceived(); + testling->handleStanzaReceived(); + + testling->handleAckRequestReceived(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(acks.size())); + CPPUNIT_ASSERT_EQUAL(2U, acks[0]); + } + + void testHandleAckRequestReceived_MultipleAcks() { + std::auto_ptr testling(createResponder()); + testling->handleStanzaReceived(); + testling->handleAckRequestReceived(); + + testling->handleStanzaReceived(); + testling->handleAckRequestReceived(); + + CPPUNIT_ASSERT_EQUAL(2, static_cast(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 testling(createResponder()); + testling->handledStanzasCount = boost::numeric_cast((1ULL<<32) - 1); + testling->handleStanzaReceived(); + testling->handleStanzaReceived(); + + testling->handleAckRequestReceived(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast(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 acks; +}; + +} + +CPPUNIT_TEST_SUITE_REGISTRATION(Swift::StanzaAckResponderTest); -- cgit v0.10.2-6-g49f6