diff options
Diffstat (limited to 'Swiften/Queries')
25 files changed, 878 insertions, 0 deletions
diff --git a/Swiften/Queries/DummyIQChannel.h b/Swiften/Queries/DummyIQChannel.h new file mode 100644 index 0000000..f72d7a5 --- /dev/null +++ b/Swiften/Queries/DummyIQChannel.h @@ -0,0 +1,25 @@ +#ifndef SWIFTEN_DummyIQChannel_H +#define SWIFTEN_DummyIQChannel_H + +#include <vector> + +#include "Swiften/Queries/IQChannel.h" + +namespace Swift { + class DummyIQChannel : public IQChannel { + public: + DummyIQChannel() {} + + virtual void sendIQ(boost::shared_ptr<IQ> iq) { + iqs_.push_back(iq); + } + + virtual String getNewIQID() { + return "test-id"; + } + + std::vector<boost::shared_ptr<IQ> > iqs_; + }; +} + +#endif diff --git a/Swiften/Queries/GenericRequest.h b/Swiften/Queries/GenericRequest.h new file mode 100644 index 0000000..c81760f --- /dev/null +++ b/Swiften/Queries/GenericRequest.h @@ -0,0 +1,30 @@ +#ifndef SWIFTEN_GenericRequest_H +#define SWIFTEN_GenericRequest_H + +#include <boost/signal.hpp> + +#include "Swiften/Queries/Request.h" + +namespace Swift { + template<typename PAYLOAD_TYPE> + class GenericRequest : public Request { + public: + GenericRequest( + IQ::Type type, + const JID& receiver, + boost::shared_ptr<Payload> payload, + IQRouter* router, + AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) : + Request(type, receiver, payload, router, autoDeleteBehavior) { + } + + virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<Error> error) { + onResponse(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(payload), error); + } + + public: + boost::signal<void (boost::shared_ptr<PAYLOAD_TYPE>, const boost::optional<Error>&)> onResponse; + }; +} + +#endif diff --git a/Swiften/Queries/IQChannel.cpp b/Swiften/Queries/IQChannel.cpp new file mode 100644 index 0000000..539dea0 --- /dev/null +++ b/Swiften/Queries/IQChannel.cpp @@ -0,0 +1,8 @@ +#include "Swiften/Queries/IQChannel.h" + +namespace Swift { + +IQChannel::~IQChannel() { +} + +} diff --git a/Swiften/Queries/IQChannel.h b/Swiften/Queries/IQChannel.h new file mode 100644 index 0000000..0dbb1be --- /dev/null +++ b/Swiften/Queries/IQChannel.h @@ -0,0 +1,22 @@ +#ifndef SWIFTEN_IQChannel_H +#define SWIFTEN_IQChannel_H + +#include <boost/signal.hpp> +#include <boost/shared_ptr.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/Elements/IQ.h" + +namespace Swift { + class IQChannel { + public: + virtual ~IQChannel(); + + virtual void sendIQ(boost::shared_ptr<IQ>) = 0; + virtual String getNewIQID() = 0; + + boost::signal<void (boost::shared_ptr<IQ>)> onIQReceived; + }; +} + +#endif diff --git a/Swiften/Queries/IQHandler.cpp b/Swiften/Queries/IQHandler.cpp new file mode 100644 index 0000000..b5e7e49 --- /dev/null +++ b/Swiften/Queries/IQHandler.cpp @@ -0,0 +1,14 @@ +#include "Swiften/Queries/IQHandler.h" +#include "Swiften/Queries/IQRouter.h" + +namespace Swift { + +IQHandler::IQHandler(IQRouter* router) : router_(router) { + router_->addHandler(this); +} + +IQHandler::~IQHandler() { + router_->removeHandler(this); +} + +} diff --git a/Swiften/Queries/IQHandler.h b/Swiften/Queries/IQHandler.h new file mode 100644 index 0000000..7a8d008 --- /dev/null +++ b/Swiften/Queries/IQHandler.h @@ -0,0 +1,28 @@ +#ifndef SWIFTEN_IQHandler_H +#define SWIFTEN_IQHandler_H + +#include <boost/shared_ptr.hpp> + +#include "Swiften/Elements/IQ.h" + +namespace Swift { + class IQRouter; + + class IQHandler { + public: + IQHandler(IQRouter* router); + virtual ~IQHandler(); + + virtual bool handleIQ(boost::shared_ptr<IQ>) = 0; + + protected: + IQRouter* getRouter() const { + return router_; + } + + private: + IQRouter* router_; + }; +} + +#endif diff --git a/Swiften/Queries/IQRouter.cpp b/Swiften/Queries/IQRouter.cpp new file mode 100644 index 0000000..b474640 --- /dev/null +++ b/Swiften/Queries/IQRouter.cpp @@ -0,0 +1,46 @@ +#include "Swiften/Queries/IQRouter.h" + +#include <algorithm> +#include <boost/bind.hpp> + +#include "Swiften/Base/foreach.h" +#include "Swiften/Queries/IQHandler.h" +#include "Swiften/Queries/IQChannel.h" +#include "Swiften/Elements/Error.h" + +namespace Swift { + +IQRouter::IQRouter(IQChannel* channel) : channel_(channel) { + channel->onIQReceived.connect(boost::bind(&IQRouter::handleIQ, this, _1)); +} + +void IQRouter::handleIQ(boost::shared_ptr<IQ> iq) { + bool handled = false; + foreach(IQHandler* handler, handlers_) { + handled |= handler->handleIQ(iq); + if (handled) { + break; + } + } + if (!handled && (iq->getType() == IQ::Get || iq->getType() == IQ::Set) ) { + channel_->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), Error::FeatureNotImplemented, Error::Cancel)); + } +} + +void IQRouter::addHandler(IQHandler* handler) { + handlers_.push_back(handler); +} + +void IQRouter::removeHandler(IQHandler* handler) { + handlers_.erase(std::remove(handlers_.begin(), handlers_.end(), handler), handlers_.end()); +} + +void IQRouter::sendIQ(boost::shared_ptr<IQ> iq) { + channel_->sendIQ(iq); +} + +String IQRouter::getNewIQID() { + return channel_->getNewIQID(); +} + +} diff --git a/Swiften/Queries/IQRouter.h b/Swiften/Queries/IQRouter.h new file mode 100644 index 0000000..2240dfb --- /dev/null +++ b/Swiften/Queries/IQRouter.h @@ -0,0 +1,33 @@ +#ifndef SWIFTEN_IQRouter_H +#define SWIFTEN_IQRouter_H + +#include <boost/shared_ptr.hpp> +#include <vector> + +#include "Swiften/Base/String.h" +#include "Swiften/Elements/IQ.h" + +namespace Swift { + class IQChannel; + class IQHandler; + + class IQRouter { + public: + IQRouter(IQChannel* channel); + + void addHandler(IQHandler* handler); + void removeHandler(IQHandler* handler); + + void sendIQ(boost::shared_ptr<IQ> iq); + String getNewIQID(); + + private: + void handleIQ(boost::shared_ptr<IQ> iq); + + private: + IQChannel* channel_; + std::vector<IQHandler*> handlers_; + }; +} + +#endif diff --git a/Swiften/Queries/Makefile.inc b/Swiften/Queries/Makefile.inc new file mode 100644 index 0000000..53a712d --- /dev/null +++ b/Swiften/Queries/Makefile.inc @@ -0,0 +1,8 @@ +SWIFTEN_SOURCES += \ + Swiften/Queries/IQRouter.cpp \ + Swiften/Queries/IQHandler.cpp \ + Swiften/Queries/IQChannel.cpp \ + Swiften/Queries/Request.cpp + +include Swiften/Queries/Responders/Makefile.inc +include Swiften/Queries/UnitTest/Makefile.inc diff --git a/Swiften/Queries/Request.cpp b/Swiften/Queries/Request.cpp new file mode 100644 index 0000000..ac361cc --- /dev/null +++ b/Swiften/Queries/Request.cpp @@ -0,0 +1,39 @@ +#include "Swiften/Queries/Request.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/EventLoop/MainEventLoop.h" + +namespace Swift { + +Request::Request(IQ::Type type, const JID& receiver, boost::shared_ptr<Payload> payload, IQRouter* router, AutoDeleteBehavior autoDeleteBehavior) : IQHandler(router), type_(type), receiver_(receiver), payload_(payload), autoDeleteBehavior_(autoDeleteBehavior) { + id_ = getRouter()->getNewIQID(); +} + +void Request::send() { + boost::shared_ptr<IQ> iq(new IQ(type_)); + iq->setTo(receiver_); + iq->addPayload(payload_); + iq->setID(id_); + getRouter()->sendIQ(iq); +} + +bool Request::handleIQ(boost::shared_ptr<IQ> iq) { + if (iq->getID() == id_) { + if (iq->getType() == IQ::Result) { + handleResponse(iq->getPayloadOfSameType(payload_), boost::optional<Error>()); + if (autoDeleteBehavior_ == AutoDeleteAfterResponse) { + MainEventLoop::deleteLater(this); + } + return true; + } + else { + // FIXME: Get proper error + handleResponse(boost::shared_ptr<Payload>(), boost::optional<Error>(Error::UndefinedCondition)); + return true; + } + } + else { + return false; + } +} + +} diff --git a/Swiften/Queries/Request.h b/Swiften/Queries/Request.h new file mode 100644 index 0000000..f084303 --- /dev/null +++ b/Swiften/Queries/Request.h @@ -0,0 +1,46 @@ +#ifndef SWIFTEN_Request_H +#define SWIFTEN_Request_H + +#include <boost/shared_ptr.hpp> +#include <boost/optional.hpp> + +#include "Swiften/Base/String.h" +#include "Swiften/Queries/IQHandler.h" +#include "Swiften/Elements/IQ.h" +#include "Swiften/Elements/Payload.h" +#include "Swiften/Elements/Error.h" +#include "Swiften/JID/JID.h" + +namespace Swift { + class Request : public IQHandler { + public: + enum AutoDeleteBehavior { + DoNotAutoDelete, + AutoDeleteAfterResponse + }; + + Request( + IQ::Type type, + const JID& receiver, + boost::shared_ptr<Payload> payload, + IQRouter* router, + AutoDeleteBehavior = DoNotAutoDelete); + + void send(); + + protected: + virtual void handleResponse(boost::shared_ptr<Payload>, boost::optional<Error>) = 0; + + private: + bool handleIQ(boost::shared_ptr<IQ>); + + private: + IQ::Type type_; + JID receiver_; + boost::shared_ptr<Payload> payload_; + AutoDeleteBehavior autoDeleteBehavior_; + String id_; + }; +} + +#endif diff --git a/Swiften/Queries/Requests/GetDiscoInfoRequest.h b/Swiften/Queries/Requests/GetDiscoInfoRequest.h new file mode 100644 index 0000000..0e8508e --- /dev/null +++ b/Swiften/Queries/Requests/GetDiscoInfoRequest.h @@ -0,0 +1,16 @@ +#ifndef SWIFTEN_GetDiscoInfoRequest_H +#define SWIFTEN_GetDiscoInfoRequest_H + +#include "Swiften/Queries/GenericRequest.h" +#include "Swiften/Elements/DiscoInfo.h" + +namespace Swift { + class GetDiscoInfoRequest : public GenericRequest<DiscoInfo> { + public: + GetDiscoInfoRequest(const JID& jid, IQRouter* router, AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) : + GenericRequest<DiscoInfo>(IQ::Get, jid, boost::shared_ptr<DiscoInfo>(new DiscoInfo()), router, autoDeleteBehavior) { + } + }; +} + +#endif diff --git a/Swiften/Queries/Requests/GetRosterRequest.h b/Swiften/Queries/Requests/GetRosterRequest.h new file mode 100644 index 0000000..2364d81 --- /dev/null +++ b/Swiften/Queries/Requests/GetRosterRequest.h @@ -0,0 +1,16 @@ +#ifndef SWIFTEN_GetRosterRequest_H +#define SWIFTEN_GetRosterRequest_H + +#include "Swiften/Queries/GenericRequest.h" +#include "Swiften/Elements/RosterPayload.h" + +namespace Swift { + class GetRosterRequest : public GenericRequest<RosterPayload> { + public: + GetRosterRequest(IQRouter* router, AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) : + GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router, autoDeleteBehavior) { + } + }; +} + +#endif diff --git a/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h new file mode 100644 index 0000000..53ca3eb --- /dev/null +++ b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h @@ -0,0 +1,20 @@ +#ifndef SWIFTEN_GetSecurityLabelsCatalogRequest_H +#define SWIFTEN_GetSecurityLabelsCatalogRequest_H + +#include "Swiften/Queries/GenericRequest.h" +#include "Swiften/Elements/SecurityLabelsCatalog.h" + +namespace Swift { + class GetSecurityLabelsCatalogRequest : public GenericRequest<SecurityLabelsCatalog> { + public: + GetSecurityLabelsCatalogRequest( + const JID& recipient, + IQRouter* router, + AutoDeleteBehavior autoDeleteBehavior = DoNotAutoDelete) : + GenericRequest<SecurityLabelsCatalog>( + IQ::Get, JID(), boost::shared_ptr<SecurityLabelsCatalog>(new SecurityLabelsCatalog(recipient)), router, autoDeleteBehavior) { + } + }; +} + +#endif diff --git a/Swiften/Queries/Responder.h b/Swiften/Queries/Responder.h new file mode 100644 index 0000000..b6029ae --- /dev/null +++ b/Swiften/Queries/Responder.h @@ -0,0 +1,49 @@ +#ifndef SWIFTEN_Responder_H +#define SWIFTEN_Responder_H + +#include "Swiften/Queries/IQHandler.h" +#include "Swiften/Elements/Error.h" + +namespace Swift { + template<typename PAYLOAD_TYPE> + class Responder : public IQHandler { + public: + Responder(IQRouter* router) : IQHandler(router) { + } + + protected: + virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<PAYLOAD_TYPE> payload) = 0; + virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<PAYLOAD_TYPE> payload) = 0; + + void sendResponse(const JID& to, const String& id, boost::shared_ptr<Payload> payload) { + getRouter()->sendIQ(IQ::createResult(to, id, payload)); + } + + void sendError(const JID& to, const String& id, Error::Condition condition, Error::Type type) { + getRouter()->sendIQ(IQ::createError(to, id, condition, type)); + } + + private: + virtual bool handleIQ(boost::shared_ptr<IQ> iq) { + if (iq->getType() == IQ::Set || iq->getType() == IQ::Get) { + boost::shared_ptr<PAYLOAD_TYPE> payload(iq->getPayload<PAYLOAD_TYPE>()); + if (payload) { + bool result; + if (iq->getType() == IQ::Set) { + result = handleSetRequest(iq->getFrom(), iq->getID(), payload); + } + else { + result = handleGetRequest(iq->getFrom(), iq->getID(), payload); + } + if (!result) { + getRouter()->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), Error::NotAllowed, Error::Cancel)); + } + return true; + } + } + return false; + } + }; +} + +#endif diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.cpp b/Swiften/Queries/Responders/DiscoInfoResponder.cpp new file mode 100644 index 0000000..e207133 --- /dev/null +++ b/Swiften/Queries/Responders/DiscoInfoResponder.cpp @@ -0,0 +1,40 @@ +#include "Swiften/Queries/Responders/DiscoInfoResponder.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Elements/DiscoInfo.h" + +namespace Swift { + +DiscoInfoResponder::DiscoInfoResponder(IQRouter* router) : Responder<DiscoInfo>(router) { +} + +void DiscoInfoResponder::setDiscoInfo(const DiscoInfo& info) { + info_ = info; +} + +void DiscoInfoResponder::setDiscoInfo(const String& node, const DiscoInfo& info) { + DiscoInfo newInfo(info); + newInfo.setNode(node); + nodeInfo_[node] = newInfo; +} + +bool DiscoInfoResponder::handleGetRequest(const JID& from, const String& id, boost::shared_ptr<DiscoInfo> info) { + if (info->getNode().isEmpty()) { + sendResponse(from, id, boost::shared_ptr<DiscoInfo>(new DiscoInfo(info_))); + } + else { + std::map<String,DiscoInfo>::const_iterator i = nodeInfo_.find(info->getNode()); + if (i != nodeInfo_.end()) { + sendResponse(from, id, boost::shared_ptr<DiscoInfo>(new DiscoInfo((*i).second))); + } + else { + sendError(from, id, Error::ItemNotFound, Error::Cancel); + } + } + return true; +} + +bool DiscoInfoResponder::handleSetRequest(const JID&, const String&, boost::shared_ptr<DiscoInfo>) { + return false; +} + +} diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.h b/Swiften/Queries/Responders/DiscoInfoResponder.h new file mode 100644 index 0000000..aa79163 --- /dev/null +++ b/Swiften/Queries/Responders/DiscoInfoResponder.h @@ -0,0 +1,29 @@ +#ifndef SWIFTEN_DiscoInfoResponder_H +#define SWIFTEN_DiscoInfoResponder_H + +#include <map> + +#include "Swiften/Queries/Responder.h" +#include "Swiften/Elements/DiscoInfo.h" + +namespace Swift { + class IQRouter; + + class DiscoInfoResponder : public Responder<DiscoInfo> { + public: + DiscoInfoResponder(IQRouter* router); + + void setDiscoInfo(const DiscoInfo& info); + void setDiscoInfo(const String& node, const DiscoInfo& info); + + private: + virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<DiscoInfo> payload); + virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<DiscoInfo> payload); + + private: + DiscoInfo info_; + std::map<String, DiscoInfo> nodeInfo_; + }; +} + +#endif diff --git a/Swiften/Queries/Responders/Makefile.inc b/Swiften/Queries/Responders/Makefile.inc new file mode 100644 index 0000000..5049440 --- /dev/null +++ b/Swiften/Queries/Responders/Makefile.inc @@ -0,0 +1,5 @@ +SWIFTEN_SOURCES += \ + Swiften/Queries/Responders/SoftwareVersionResponder.cpp \ + Swiften/Queries/Responders/DiscoInfoResponder.cpp + +include Swiften/Queries/Responders/UnitTest/Makefile.inc diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.cpp b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp new file mode 100644 index 0000000..dad2442 --- /dev/null +++ b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp @@ -0,0 +1,20 @@ +#include "Swiften/Queries/Responders/SoftwareVersionResponder.h" +#include "Swiften/Queries/IQRouter.h" + +namespace Swift { + +SoftwareVersionResponder::SoftwareVersionResponder( + const String& client, const String& version, IQRouter* router) : + Responder<SoftwareVersion>(router), client_(client), version_(version) { +} + +bool SoftwareVersionResponder::handleGetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion>) { + sendResponse(from, id, boost::shared_ptr<SoftwareVersion>(new SoftwareVersion(client_, version_))); + return true; +} + +bool SoftwareVersionResponder::handleSetRequest(const JID&, const String&, boost::shared_ptr<SoftwareVersion>) { + return false; +} + +} diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.h b/Swiften/Queries/Responders/SoftwareVersionResponder.h new file mode 100644 index 0000000..d66e168 --- /dev/null +++ b/Swiften/Queries/Responders/SoftwareVersionResponder.h @@ -0,0 +1,24 @@ +#ifndef SWIFTEN_SoftwareVersionResponder_H +#define SWIFTEN_SoftwareVersionResponder_H + +#include "Swiften/Queries/Responder.h" +#include "Swiften/Elements/SoftwareVersion.h" + +namespace Swift { + class IQRouter; + + class SoftwareVersionResponder : public Responder<SoftwareVersion> { + public: + SoftwareVersionResponder(const String& client, const String& version, IQRouter* router); + + private: + virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload); + virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload); + + private: + String client_; + String version_; + }; +} + +#endif diff --git a/Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp b/Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp new file mode 100644 index 0000000..44db138 --- /dev/null +++ b/Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp @@ -0,0 +1,84 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <typeinfo> + +#include "Swiften/Queries/Responders/DiscoInfoResponder.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Queries/DummyIQChannel.h" + +using namespace Swift; + +class DiscoInfoResponderTest : public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(DiscoInfoResponderTest); + CPPUNIT_TEST(testHandleRequest_GetToplevelInfo); + CPPUNIT_TEST(testHandleRequest_GetNodeInfo); + CPPUNIT_TEST(testHandleRequest_GetInvalidNodeInfo); + CPPUNIT_TEST_SUITE_END(); + + public: + DiscoInfoResponderTest() {} + + void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + } + + void tearDown() { + delete router_; + delete channel_; + } + + void testHandleRequest_GetToplevelInfo() { + DiscoInfoResponder testling(router_); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(discoInfo); + + boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(String(""), payload->getNode()); + CPPUNIT_ASSERT(payload->hasFeature("foo")); + } + + void testHandleRequest_GetNodeInfo() { + DiscoInfoResponder testling(router_); + DiscoInfo discoInfo; + discoInfo.addFeature("foo"); + testling.setDiscoInfo(discoInfo); + DiscoInfo discoInfoBar; + discoInfoBar.addFeature("bar"); + testling.setDiscoInfo("bar-node", discoInfoBar); + + boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); + query->setNode("bar-node"); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + boost::shared_ptr<DiscoInfo> payload(channel_->iqs_[0]->getPayload<DiscoInfo>()); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(String("bar-node"), payload->getNode()); + CPPUNIT_ASSERT(payload->hasFeature("bar")); + } + + void testHandleRequest_GetInvalidNodeInfo() { + DiscoInfoResponder testling(router_); + + boost::shared_ptr<DiscoInfo> query(new DiscoInfo()); + query->setNode("bar-node"); + channel_->onIQReceived(IQ::createRequest(IQ::Get, JID("foo@bar.com"), "id-1", query)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + boost::shared_ptr<Error> payload(channel_->iqs_[0]->getPayload<Error>()); + CPPUNIT_ASSERT(payload); + } + + private: + IQRouter* router_; + DummyIQChannel* channel_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoResponderTest); diff --git a/Swiften/Queries/Responders/UnitTest/Makefile.inc b/Swiften/Queries/Responders/UnitTest/Makefile.inc new file mode 100644 index 0000000..8f06682 --- /dev/null +++ b/Swiften/Queries/Responders/UnitTest/Makefile.inc @@ -0,0 +1,2 @@ +UNITTEST_SOURCES += \ + Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp diff --git a/Swiften/Queries/UnitTest/Makefile.inc b/Swiften/Queries/UnitTest/Makefile.inc new file mode 100644 index 0000000..265357f --- /dev/null +++ b/Swiften/Queries/UnitTest/Makefile.inc @@ -0,0 +1,3 @@ +UNITTEST_SOURCES += \ + Swiften/Queries/UnitTest/RequestTest.cpp \ + Swiften/Queries/UnitTest/ResponderTest.cpp diff --git a/Swiften/Queries/UnitTest/RequestTest.cpp b/Swiften/Queries/UnitTest/RequestTest.cpp new file mode 100644 index 0000000..31c0603 --- /dev/null +++ b/Swiften/Queries/UnitTest/RequestTest.cpp @@ -0,0 +1,139 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/shared_ptr.hpp> +#include <boost/bind.hpp> + +#include "Swiften/Queries/GenericRequest.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Queries/DummyIQChannel.h" +#include "Swiften/Elements/Payload.h" + +using namespace Swift; + +class RequestTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(RequestTest); + CPPUNIT_TEST(testSendGet); + CPPUNIT_TEST(testSendSet); + CPPUNIT_TEST(testHandleIQ); + CPPUNIT_TEST(testHandleIQ_InvalidID); + CPPUNIT_TEST(testHandleIQ_Error); + CPPUNIT_TEST_SUITE_END(); + + public: + class MyPayload : public Payload { + public: + MyPayload(const String& s = "") : text_(s) {} + String text_; + }; + + typedef GenericRequest<MyPayload> MyRequest; + + public: + RequestTest() {} + + void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + payload_ = boost::shared_ptr<Payload>(new MyPayload("foo")); + responsePayload_ = boost::shared_ptr<Payload>(new MyPayload("bar")); + responsesReceived_ = 0; + errorsReceived_ = 0; + } + + void tearDown() { + delete router_; + delete channel_; + } + + void testSendSet() { + MyRequest testling(IQ::Set, JID("foo@bar.com/baz"), payload_, router_); + testling.send(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), channel_->iqs_[0]->getTo()); + CPPUNIT_ASSERT_EQUAL(IQ::Set, channel_->iqs_[0]->getType()); + CPPUNIT_ASSERT_EQUAL(String("test-id"), channel_->iqs_[0]->getID()); + } + + void testSendGet() { + MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); + testling.send(); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + CPPUNIT_ASSERT_EQUAL(IQ::Get, channel_->iqs_[0]->getType()); + } + + void testHandleIQ() { + MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); + testling.send(); + + channel_->onIQReceived(createResponse("test-id")); + + CPPUNIT_ASSERT_EQUAL(1, responsesReceived_); + CPPUNIT_ASSERT_EQUAL(0, errorsReceived_); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + } + + // FIXME: Doesn't test that it didn't handle the payload + void testHandleIQ_InvalidID() { + MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); + testling.send(); + + channel_->onIQReceived(createResponse("different-id")); + + CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); + CPPUNIT_ASSERT_EQUAL(0, errorsReceived_); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + } + + void testHandleIQ_Error() { + MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_); + testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2)); + testling.send(); + + channel_->onIQReceived(createError("test-id")); + + CPPUNIT_ASSERT_EQUAL(0, responsesReceived_); + CPPUNIT_ASSERT_EQUAL(1, errorsReceived_); + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size())); + } + + private: + void handleResponse(boost::shared_ptr<Payload> p, const boost::optional<Error>& e) { + if (e) { + ++errorsReceived_; + } + else { + boost::shared_ptr<MyPayload> payload(boost::dynamic_pointer_cast<MyPayload>(p)); + CPPUNIT_ASSERT(payload); + CPPUNIT_ASSERT_EQUAL(String("bar"), payload->text_); + ++responsesReceived_; + } + } + + boost::shared_ptr<IQ> createResponse(const String& id) { + boost::shared_ptr<IQ> iq(new IQ(IQ::Result)); + iq->addPayload(responsePayload_); + iq->setID(id); + return iq; + } + + boost::shared_ptr<IQ> createError(const String& id) { + boost::shared_ptr<IQ> iq(new IQ(IQ::Error)); + iq->setID(id); + return iq; + } + + private: + IQRouter* router_; + DummyIQChannel* channel_; + boost::shared_ptr<Payload> payload_; + boost::shared_ptr<Payload> responsePayload_; + int responsesReceived_; + int errorsReceived_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(RequestTest); diff --git a/Swiften/Queries/UnitTest/ResponderTest.cpp b/Swiften/Queries/UnitTest/ResponderTest.cpp new file mode 100644 index 0000000..5c758e4 --- /dev/null +++ b/Swiften/Queries/UnitTest/ResponderTest.cpp @@ -0,0 +1,132 @@ +#include <cppunit/extensions/HelperMacros.h> +#include <cppunit/extensions/TestFactoryRegistry.h> +#include <boost/shared_ptr.hpp> +#include <boost/bind.hpp> + +#include "Swiften/Queries/Responder.h" +#include "Swiften/Queries/IQRouter.h" +#include "Swiften/Queries/DummyIQChannel.h" +#include "Swiften/Elements/SoftwareVersion.h" + +using namespace Swift; + +class ResponderTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE(ResponderTest); + CPPUNIT_TEST(testConstructor); + CPPUNIT_TEST(testHandleIQ_Set); + CPPUNIT_TEST(testHandleIQ_Get); + CPPUNIT_TEST(testHandleIQ_Error); + CPPUNIT_TEST(testHandleIQ_Result); + CPPUNIT_TEST(testHandleIQ_NoPayload); + CPPUNIT_TEST_SUITE_END(); + + public: + ResponderTest() {} + + void setUp() { + channel_ = new DummyIQChannel(); + router_ = new IQRouter(channel_); + payload_ = boost::shared_ptr<SoftwareVersion>(new SoftwareVersion("foo")); + } + + void tearDown() { + delete router_; + delete channel_; + } + + void testConstructor() { + MyResponder testling(router_); + + channel_->onIQReceived(createRequest(IQ::Set)); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(testling.setPayloads_.size())); + } + + void testHandleIQ_Set() { + MyResponder testling(router_); + + CPPUNIT_ASSERT(dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Set))); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(testling.setPayloads_.size())); + CPPUNIT_ASSERT(payload_ == testling.setPayloads_[0]); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size())); + } + + void testHandleIQ_Get() { + MyResponder testling(router_); + + CPPUNIT_ASSERT(dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Get))); + + CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(testling.getPayloads_.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size())); + CPPUNIT_ASSERT(payload_ == testling.getPayloads_[0]); + } + + void testHandleIQ_Error() { + MyResponder testling(router_); + + CPPUNIT_ASSERT(!dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Error))); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size())); + } + + void testHandleIQ_Result() { + MyResponder testling(router_); + + CPPUNIT_ASSERT(!dynamic_cast<IQHandler*>(&testling)->handleIQ(createRequest(IQ::Result))); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size())); + } + + void testHandleIQ_NoPayload() { + MyResponder testling(router_); + + CPPUNIT_ASSERT(!dynamic_cast<IQHandler*>(&testling)->handleIQ(boost::shared_ptr<IQ>(new IQ(IQ::Get)))); + + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.getPayloads_.size())); + CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(testling.setPayloads_.size())); + } + + private: + boost::shared_ptr<IQ> createRequest(IQ::Type type) { + boost::shared_ptr<IQ> iq(new IQ(type)); + iq->addPayload(payload_); + iq->setID("myid"); + iq->setFrom(JID("foo@bar.com/baz")); + return iq; + } + + private: + class MyResponder : public Responder<SoftwareVersion> { + public: + MyResponder(IQRouter* router) : Responder<SoftwareVersion>(router), getRequestResponse_(true), setRequestResponse_(true) {} + + virtual bool handleGetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload) { + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), from); + CPPUNIT_ASSERT_EQUAL(String("myid"), id); + getPayloads_.push_back(payload); + return getRequestResponse_; + } + virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<SoftwareVersion> payload) { + CPPUNIT_ASSERT_EQUAL(JID("foo@bar.com/baz"), from); + CPPUNIT_ASSERT_EQUAL(String("myid"), id); + setPayloads_.push_back(payload); + return setRequestResponse_; + } + + bool getRequestResponse_; + bool setRequestResponse_; + std::vector<boost::shared_ptr<SoftwareVersion> > getPayloads_; + std::vector<boost::shared_ptr<SoftwareVersion> > setPayloads_; + }; + + private: + IQRouter* router_; + DummyIQChannel* channel_; + boost::shared_ptr<SoftwareVersion> payload_; +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(ResponderTest); |