diff options
Diffstat (limited to 'Swiften/StreamManagement')
-rw-r--r-- | Swiften/StreamManagement/SConscript | 8 | ||||
-rw-r--r-- | Swiften/StreamManagement/StanzaAckRequester.cpp | 39 | ||||
-rw-r--r-- | Swiften/StreamManagement/StanzaAckRequester.h | 33 | ||||
-rw-r--r-- | Swiften/StreamManagement/StanzaAckResponder.cpp | 24 | ||||
-rw-r--r-- | Swiften/StreamManagement/StanzaAckResponder.h | 30 | ||||
-rw-r--r-- | Swiften/StreamManagement/UnitTest/StanzaAckRequesterTest.cpp | 120 | ||||
-rw-r--r-- | Swiften/StreamManagement/UnitTest/StanzaAckResponderTest.cpp | 98 |
7 files changed, 352 insertions, 0 deletions
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); |