summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'Swiften/Queries')
-rw-r--r--Swiften/Queries/DummyIQChannel.h29
-rw-r--r--Swiften/Queries/GenericRequest.h26
-rw-r--r--Swiften/Queries/GetResponder.h14
-rw-r--r--Swiften/Queries/IQChannel.cpp8
-rw-r--r--Swiften/Queries/IQChannel.h24
-rw-r--r--Swiften/Queries/IQHandler.cpp9
-rw-r--r--Swiften/Queries/IQHandler.h19
-rw-r--r--Swiften/Queries/IQRouter.cpp81
-rw-r--r--Swiften/Queries/IQRouter.h41
-rw-r--r--Swiften/Queries/Request.cpp55
-rw-r--r--Swiften/Queries/Request.h50
-rw-r--r--Swiften/Queries/Requests/GetDiscoInfoRequest.h13
-rw-r--r--Swiften/Queries/Requests/GetPrivateStorageRequest.h30
-rw-r--r--Swiften/Queries/Requests/GetRosterRequest.h13
-rw-r--r--Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h16
-rw-r--r--Swiften/Queries/Requests/GetVCardRequest.h12
-rw-r--r--Swiften/Queries/Requests/SetPrivateStorageRequest.h24
-rw-r--r--Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp106
-rw-r--r--Swiften/Queries/Responder.h58
-rw-r--r--Swiften/Queries/Responders/DiscoInfoResponder.cpp36
-rw-r--r--Swiften/Queries/Responders/DiscoInfoResponder.h28
-rw-r--r--Swiften/Queries/Responders/RosterPushResponder.h23
-rw-r--r--Swiften/Queries/Responders/SoftwareVersionResponder.cpp16
-rw-r--r--Swiften/Queries/Responders/SoftwareVersionResponder.h23
-rw-r--r--Swiften/Queries/Responders/UnitTest/DiscoInfoResponderTest.cpp84
-rw-r--r--Swiften/Queries/SetResponder.h14
-rw-r--r--Swiften/Queries/UnitTest/IQRouterTest.cpp143
-rw-r--r--Swiften/Queries/UnitTest/RequestTest.cpp167
-rw-r--r--Swiften/Queries/UnitTest/ResponderTest.cpp132
29 files changed, 1294 insertions, 0 deletions
diff --git a/Swiften/Queries/DummyIQChannel.h b/Swiften/Queries/DummyIQChannel.h
new file mode 100644
index 0000000..c0a4a25
--- /dev/null
+++ b/Swiften/Queries/DummyIQChannel.h
@@ -0,0 +1,29 @@
+#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";
+ }
+
+ virtual bool isAvailable() {
+ return true;
+ }
+
+ 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..77dae52
--- /dev/null
+++ b/Swiften/Queries/GenericRequest.h
@@ -0,0 +1,26 @@
+#pragma once
+
+#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) :
+ Request(type, receiver, payload, router) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<ErrorPayload> error) {
+ onResponse(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(payload), error);
+ }
+
+ public:
+ boost::signal<void (boost::shared_ptr<PAYLOAD_TYPE>, const boost::optional<ErrorPayload>&)> onResponse;
+ };
+}
diff --git a/Swiften/Queries/GetResponder.h b/Swiften/Queries/GetResponder.h
new file mode 100644
index 0000000..2bd2fe2
--- /dev/null
+++ b/Swiften/Queries/GetResponder.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Swiften/Queries/Responder.h"
+
+namespace Swift {
+ template<typename T>
+ class GetResponder : public Responder<T> {
+ public:
+ GetResponder(IQRouter* router) : Responder<T>(router) {}
+
+ private:
+ virtual bool handleSetRequest(const JID&, const String&, boost::shared_ptr<T>) { return false; }
+ };
+}
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..1692afe
--- /dev/null
+++ b/Swiften/Queries/IQChannel.h
@@ -0,0 +1,24 @@
+#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;
+
+ virtual bool isAvailable() = 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..81bd773
--- /dev/null
+++ b/Swiften/Queries/IQHandler.cpp
@@ -0,0 +1,9 @@
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+IQHandler::~IQHandler() {
+}
+
+}
diff --git a/Swiften/Queries/IQHandler.h b/Swiften/Queries/IQHandler.h
new file mode 100644
index 0000000..7389b3a
--- /dev/null
+++ b/Swiften/Queries/IQHandler.h
@@ -0,0 +1,19 @@
+#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:
+ virtual ~IQHandler();
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) = 0;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/IQRouter.cpp b/Swiften/Queries/IQRouter.cpp
new file mode 100644
index 0000000..fdfa00b
--- /dev/null
+++ b/Swiften/Queries/IQRouter.cpp
@@ -0,0 +1,81 @@
+#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/ErrorPayload.h"
+
+namespace Swift {
+
+static void noop(IQHandler*) {}
+
+IQRouter::IQRouter(IQChannel* channel) : channel_(channel), queueRemoves_(false) {
+ channel->onIQReceived.connect(boost::bind(&IQRouter::handleIQ, this, _1));
+}
+
+IQRouter::~IQRouter() {
+}
+
+bool IQRouter::isAvailable() {
+ return channel_->isAvailable();
+}
+
+void IQRouter::handleIQ(boost::shared_ptr<IQ> iq) {
+ queueRemoves_ = true;
+
+ bool handled = false;
+ foreach(boost::shared_ptr<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(), ErrorPayload::FeatureNotImplemented, ErrorPayload::Cancel));
+ }
+
+ processPendingRemoves();
+
+ queueRemoves_ = false;
+}
+
+void IQRouter::processPendingRemoves() {
+ foreach(boost::shared_ptr<IQHandler> handler, queuedRemoves_) {
+ handlers_.erase(std::remove(handlers_.begin(), handlers_.end(), handler), handlers_.end());
+ }
+ queuedRemoves_.clear();
+}
+
+void IQRouter::addHandler(IQHandler* handler) {
+ addHandler(boost::shared_ptr<IQHandler>(handler, noop));
+}
+
+void IQRouter::removeHandler(IQHandler* handler) {
+ removeHandler(boost::shared_ptr<IQHandler>(handler, noop));
+}
+
+void IQRouter::addHandler(boost::shared_ptr<IQHandler> handler) {
+ handlers_.push_back(handler);
+}
+
+void IQRouter::removeHandler(boost::shared_ptr<IQHandler> handler) {
+ if (queueRemoves_) {
+ queuedRemoves_.push_back(handler);
+ }
+ else {
+ 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..717a845
--- /dev/null
+++ b/Swiften/Queries/IQRouter.h
@@ -0,0 +1,41 @@
+#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);
+ ~IQRouter();
+
+ void addHandler(IQHandler* handler);
+ void removeHandler(IQHandler* handler);
+ void addHandler(boost::shared_ptr<IQHandler> handler);
+ void removeHandler(boost::shared_ptr<IQHandler> handler);
+
+ void sendIQ(boost::shared_ptr<IQ> iq);
+ String getNewIQID();
+
+ bool isAvailable();
+
+ private:
+ void handleIQ(boost::shared_ptr<IQ> iq);
+ void processPendingRemoves();
+
+ private:
+ IQChannel* channel_;
+ std::vector< boost::shared_ptr<IQHandler> > handlers_;
+ std::vector< boost::shared_ptr<IQHandler> > queuedRemoves_;
+ bool queueRemoves_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Request.cpp b/Swiften/Queries/Request.cpp
new file mode 100644
index 0000000..ce9f763
--- /dev/null
+++ b/Swiften/Queries/Request.cpp
@@ -0,0 +1,55 @@
+#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) : router_(router), type_(type), receiver_(receiver), payload_(payload), sent_(false) {
+}
+
+Request::Request(IQ::Type type, const JID& receiver, IQRouter* router) : router_(router), type_(type), receiver_(receiver), sent_(false) {
+}
+
+void Request::send() {
+ assert(payload_);
+ assert(!sent_);
+ sent_ = true;
+
+ boost::shared_ptr<IQ> iq(new IQ(type_));
+ iq->setTo(receiver_);
+ iq->addPayload(payload_);
+ id_ = router_->getNewIQID();
+ iq->setID(id_);
+
+ try {
+ router_->addHandler(shared_from_this());
+ }
+ catch (const std::exception&) {
+ router_->addHandler(this);
+ }
+
+ router_->sendIQ(iq);
+}
+
+bool Request::handleIQ(boost::shared_ptr<IQ> iq) {
+ bool handled = false;
+ if (sent_ && iq->getID() == id_) {
+ if (iq->getType() == IQ::Result) {
+ handleResponse(iq->getPayloadOfSameType(payload_), boost::optional<ErrorPayload>());
+ }
+ else {
+ boost::shared_ptr<ErrorPayload> errorPayload = iq->getPayload<ErrorPayload>();
+ if (errorPayload) {
+ handleResponse(boost::shared_ptr<Payload>(), boost::optional<ErrorPayload>(*errorPayload));
+ }
+ else {
+ handleResponse(boost::shared_ptr<Payload>(), boost::optional<ErrorPayload>(ErrorPayload::UndefinedCondition));
+ }
+ }
+ router_->removeHandler(this);
+ handled = true;
+ }
+ return handled;
+}
+
+}
diff --git a/Swiften/Queries/Request.h b/Swiften/Queries/Request.h
new file mode 100644
index 0000000..cc4a58e
--- /dev/null
+++ b/Swiften/Queries/Request.h
@@ -0,0 +1,50 @@
+#ifndef SWIFTEN_Request_H
+#define SWIFTEN_Request_H
+
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <boost/enable_shared_from_this.hpp>
+
+#include "Swiften/Base/String.h"
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Elements/IQ.h"
+#include "Swiften/Elements/Payload.h"
+#include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class Request : public IQHandler, public boost::enable_shared_from_this<Request> {
+ public:
+ Request(
+ IQ::Type type,
+ const JID& receiver,
+ boost::shared_ptr<Payload> payload,
+ IQRouter* router);
+ Request(
+ IQ::Type type,
+ const JID& receiver,
+ IQRouter* router);
+
+ void send();
+
+ protected:
+ virtual void setPayload(boost::shared_ptr<Payload> p) {
+ payload_ = p;
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload>, boost::optional<ErrorPayload>) = 0;
+
+ private:
+ bool handleIQ(boost::shared_ptr<IQ>);
+
+ private:
+ IQRouter* router_;
+ IQ::Type type_;
+ JID receiver_;
+ boost::shared_ptr<Payload> payload_;
+ String id_;
+ bool sent_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Requests/GetDiscoInfoRequest.h b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
new file mode 100644
index 0000000..70f09ca
--- /dev/null
+++ b/Swiften/Queries/Requests/GetDiscoInfoRequest.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class GetDiscoInfoRequest : public GenericRequest<DiscoInfo> {
+ public:
+ GetDiscoInfoRequest(const JID& jid, IQRouter* router) :
+ GenericRequest<DiscoInfo>(IQ::Get, jid, boost::shared_ptr<DiscoInfo>(new DiscoInfo()), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/GetPrivateStorageRequest.h b/Swiften/Queries/Requests/GetPrivateStorageRequest.h
new file mode 100644
index 0000000..5d6440e
--- /dev/null
+++ b/Swiften/Queries/Requests/GetPrivateStorageRequest.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Queries/Request.h"
+#include "Swiften/Elements/PrivateStorage.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class GetPrivateStorageRequest : public Request {
+ public:
+ GetPrivateStorageRequest(IQRouter* router) : Request(IQ::Get, JID(), boost::shared_ptr<PrivateStorage>(new PrivateStorage(boost::shared_ptr<Payload>(new PAYLOAD_TYPE()))), router) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<ErrorPayload> error) {
+ boost::shared_ptr<PrivateStorage> storage = boost::dynamic_pointer_cast<PrivateStorage>(payload);
+ if (storage) {
+ onResponse(boost::dynamic_pointer_cast<PAYLOAD_TYPE>(storage->getPayload()), error);
+ }
+ else {
+ onResponse(boost::shared_ptr<PAYLOAD_TYPE>(), error);
+ }
+ }
+
+ public:
+ boost::signal<void (boost::shared_ptr<PAYLOAD_TYPE>, const boost::optional<ErrorPayload>&)> onResponse;
+ };
+}
diff --git a/Swiften/Queries/Requests/GetRosterRequest.h b/Swiften/Queries/Requests/GetRosterRequest.h
new file mode 100644
index 0000000..cac1c12
--- /dev/null
+++ b/Swiften/Queries/Requests/GetRosterRequest.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/RosterPayload.h"
+
+namespace Swift {
+ class GetRosterRequest : public GenericRequest<RosterPayload> {
+ public:
+ GetRosterRequest(IQRouter* router) :
+ GenericRequest<RosterPayload>(IQ::Get, JID(), boost::shared_ptr<Payload>(new RosterPayload()), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
new file mode 100644
index 0000000..87b51ce
--- /dev/null
+++ b/Swiften/Queries/Requests/GetSecurityLabelsCatalogRequest.h
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/SecurityLabelsCatalog.h"
+
+namespace Swift {
+ class GetSecurityLabelsCatalogRequest : public GenericRequest<SecurityLabelsCatalog> {
+ public:
+ GetSecurityLabelsCatalogRequest(
+ const JID& recipient,
+ IQRouter* router) :
+ GenericRequest<SecurityLabelsCatalog>(
+ IQ::Get, JID(), boost::shared_ptr<SecurityLabelsCatalog>(new SecurityLabelsCatalog(recipient)), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/GetVCardRequest.h b/Swiften/Queries/Requests/GetVCardRequest.h
new file mode 100644
index 0000000..8fc6e17
--- /dev/null
+++ b/Swiften/Queries/Requests/GetVCardRequest.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "Swiften/Queries/GenericRequest.h"
+#include "Swiften/Elements/VCard.h"
+
+namespace Swift {
+ class GetVCardRequest : public GenericRequest<VCard> {
+ public:
+ GetVCardRequest(const JID& jid, IQRouter* router) : GenericRequest<VCard>(IQ::Get, jid, boost::shared_ptr<Payload>(new VCard()), router) {
+ }
+ };
+}
diff --git a/Swiften/Queries/Requests/SetPrivateStorageRequest.h b/Swiften/Queries/Requests/SetPrivateStorageRequest.h
new file mode 100644
index 0000000..834ddd8
--- /dev/null
+++ b/Swiften/Queries/Requests/SetPrivateStorageRequest.h
@@ -0,0 +1,24 @@
+#pragma once
+
+#include <boost/signals.hpp>
+#include <boost/shared_ptr.hpp>
+
+#include "Swiften/Queries/Request.h"
+#include "Swiften/Elements/PrivateStorage.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class SetPrivateStorageRequest : public Request {
+ public:
+ SetPrivateStorageRequest(boost::shared_ptr<PAYLOAD_TYPE> payload, IQRouter* router) : Request(IQ::Set, JID(), boost::shared_ptr<PrivateStorage>(new PrivateStorage(payload)), router) {
+ }
+
+ virtual void handleResponse(boost::shared_ptr<Payload> payload, boost::optional<ErrorPayload> error) {
+ onResponse(error);
+ }
+
+ public:
+ boost::signal<void (const boost::optional<ErrorPayload>&)> onResponse;
+ };
+}
diff --git a/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp b/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
new file mode 100644
index 0000000..a86a111
--- /dev/null
+++ b/Swiften/Queries/Requests/UnitTest/GetPrivateStorageRequestTest.cpp
@@ -0,0 +1,106 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/Requests/GetPrivateStorageRequest.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+#include "Swiften/Elements/Payload.h"
+
+using namespace Swift;
+
+class GetPrivateStorageRequestTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(GetPrivateStorageRequestTest);
+ CPPUNIT_TEST(testSend);
+ CPPUNIT_TEST(testHandleResponse);
+ CPPUNIT_TEST(testHandleResponse_Error);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ class MyPayload : public Payload {
+ public:
+ MyPayload(const String& text = "") : text(text) {}
+ String text;
+ };
+
+ public:
+ GetPrivateStorageRequestTest() {}
+
+ void setUp() {
+ channel = new DummyIQChannel();
+ router = new IQRouter(channel);
+ }
+
+ void tearDown() {
+ delete router;
+ delete channel;
+ }
+
+ void testSend() {
+ GetPrivateStorageRequest<MyPayload> request(router);
+ request.send();
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(JID(), channel->iqs_[0]->getTo());
+ CPPUNIT_ASSERT_EQUAL(IQ::Get, channel->iqs_[0]->getType());
+ boost::shared_ptr<PrivateStorage> storage = channel->iqs_[0]->getPayload<PrivateStorage>();
+ CPPUNIT_ASSERT(storage);
+ boost::shared_ptr<MyPayload> payload = boost::dynamic_pointer_cast<MyPayload>(storage->getPayload());
+ CPPUNIT_ASSERT(payload);
+ }
+
+ void testHandleResponse() {
+ GetPrivateStorageRequest<MyPayload> testling(router);
+ testling.onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
+ testling.send();
+ channel->onIQReceived(createResponse("test-id", "foo"));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(responses.size()));
+ CPPUNIT_ASSERT_EQUAL(String("foo"), boost::dynamic_pointer_cast<MyPayload>(responses[0])->text);
+ }
+
+ void testHandleResponse_Error() {
+ GetPrivateStorageRequest<MyPayload> testling(router);
+ testling.onResponse.connect(boost::bind(&GetPrivateStorageRequestTest::handleResponse, this, _1, _2));
+ testling.send();
+ channel->onIQReceived(createError("test-id"));
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(responses.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(errors.size()));
+ }
+
+ private:
+ void handleResponse(boost::shared_ptr<Payload> p, const boost::optional<ErrorPayload>& e) {
+ if (e) {
+ errors.push_back(*e);
+ }
+ else {
+ responses.push_back(p);
+ }
+ }
+
+ boost::shared_ptr<IQ> createResponse(const String& id, const String& text) {
+ boost::shared_ptr<IQ> iq(new IQ(IQ::Result));
+ boost::shared_ptr<PrivateStorage> storage(new PrivateStorage());
+ storage->setPayload(boost::shared_ptr<Payload>(new MyPayload(text)));
+ iq->addPayload(storage);
+ 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;
+ std::vector< ErrorPayload > errors;
+ std::vector< boost::shared_ptr<Payload> > responses;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(GetPrivateStorageRequestTest);
diff --git a/Swiften/Queries/Responder.h b/Swiften/Queries/Responder.h
new file mode 100644
index 0000000..9c025eb
--- /dev/null
+++ b/Swiften/Queries/Responder.h
@@ -0,0 +1,58 @@
+#ifndef SWIFTEN_Responder_H
+#define SWIFTEN_Responder_H
+
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Elements/ErrorPayload.h"
+
+namespace Swift {
+ template<typename PAYLOAD_TYPE>
+ class Responder : public IQHandler {
+ public:
+ Responder(IQRouter* router) : router_(router) {
+ router_->addHandler(this);
+ }
+
+ ~Responder() {
+ router_->removeHandler(this);
+ }
+
+ 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) {
+ router_->sendIQ(IQ::createResult(to, id, payload));
+ }
+
+ void sendError(const JID& to, const String& id, ErrorPayload::Condition condition, ErrorPayload::Type type) {
+ router_->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) {
+ router_->sendIQ(IQ::createError(iq->getFrom(), iq->getID(), ErrorPayload::NotAllowed, ErrorPayload::Cancel));
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private:
+ IQRouter* router_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.cpp b/Swiften/Queries/Responders/DiscoInfoResponder.cpp
new file mode 100644
index 0000000..572f83f
--- /dev/null
+++ b/Swiften/Queries/Responders/DiscoInfoResponder.cpp
@@ -0,0 +1,36 @@
+#include "Swiften/Queries/Responders/DiscoInfoResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+
+DiscoInfoResponder::DiscoInfoResponder(IQRouter* router) : GetResponder<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, ErrorPayload::ItemNotFound, ErrorPayload::Cancel);
+ }
+ }
+ return true;
+}
+
+}
diff --git a/Swiften/Queries/Responders/DiscoInfoResponder.h b/Swiften/Queries/Responders/DiscoInfoResponder.h
new file mode 100644
index 0000000..3270d5d
--- /dev/null
+++ b/Swiften/Queries/Responders/DiscoInfoResponder.h
@@ -0,0 +1,28 @@
+#ifndef SWIFTEN_DiscoInfoResponder_H
+#define SWIFTEN_DiscoInfoResponder_H
+
+#include <map>
+
+#include "Swiften/Queries/GetResponder.h"
+#include "Swiften/Elements/DiscoInfo.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class DiscoInfoResponder : public GetResponder<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);
+
+ private:
+ DiscoInfo info_;
+ std::map<String, DiscoInfo> nodeInfo_;
+ };
+}
+
+#endif
diff --git a/Swiften/Queries/Responders/RosterPushResponder.h b/Swiften/Queries/Responders/RosterPushResponder.h
new file mode 100644
index 0000000..69185c8
--- /dev/null
+++ b/Swiften/Queries/Responders/RosterPushResponder.h
@@ -0,0 +1,23 @@
+#pragma once
+
+#include <boost/signal.hpp>
+
+#include "Swiften/Queries/SetResponder.h"
+#include "Swiften/Elements/RosterPayload.h"
+
+namespace Swift {
+ class RosterPushResponder : public SetResponder<RosterPayload> {
+ public:
+ RosterPushResponder(IQRouter* router) : SetResponder<RosterPayload>(router) {}
+
+ public:
+ boost::signal<void (boost::shared_ptr<RosterPayload>)> onRosterReceived;
+
+ private:
+ virtual bool handleSetRequest(const JID& from, const String& id, boost::shared_ptr<RosterPayload> payload) {
+ onRosterReceived(payload);
+ sendResponse(from, id, boost::shared_ptr<Payload>());
+ return true;
+ }
+ };
+}
diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.cpp b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp
new file mode 100644
index 0000000..e608f24
--- /dev/null
+++ b/Swiften/Queries/Responders/SoftwareVersionResponder.cpp
@@ -0,0 +1,16 @@
+#include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
+#include "Swiften/Queries/IQRouter.h"
+
+namespace Swift {
+
+SoftwareVersionResponder::SoftwareVersionResponder(
+ const String& client, const String& version, IQRouter* router) :
+ GetResponder<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;
+}
+
+}
diff --git a/Swiften/Queries/Responders/SoftwareVersionResponder.h b/Swiften/Queries/Responders/SoftwareVersionResponder.h
new file mode 100644
index 0000000..85d1089
--- /dev/null
+++ b/Swiften/Queries/Responders/SoftwareVersionResponder.h
@@ -0,0 +1,23 @@
+#ifndef SWIFTEN_SoftwareVersionResponder_H
+#define SWIFTEN_SoftwareVersionResponder_H
+
+#include "Swiften/Queries/GetResponder.h"
+#include "Swiften/Elements/SoftwareVersion.h"
+
+namespace Swift {
+ class IQRouter;
+
+ class SoftwareVersionResponder : public GetResponder<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);
+
+ 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..5993d0c
--- /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<ErrorPayload> payload(channel_->iqs_[0]->getPayload<ErrorPayload>());
+ CPPUNIT_ASSERT(payload);
+ }
+
+ private:
+ IQRouter* router_;
+ DummyIQChannel* channel_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(DiscoInfoResponderTest);
diff --git a/Swiften/Queries/SetResponder.h b/Swiften/Queries/SetResponder.h
new file mode 100644
index 0000000..51fe39a
--- /dev/null
+++ b/Swiften/Queries/SetResponder.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#include "Swiften/Queries/Responder.h"
+
+namespace Swift {
+ template<typename T>
+ class SetResponder : public Responder<T> {
+ public:
+ SetResponder(IQRouter* router) : Responder<T>(router) {}
+
+ private:
+ virtual bool handleGetRequest(const JID&, const String&, boost::shared_ptr<T>) { return false; }
+ };
+}
diff --git a/Swiften/Queries/UnitTest/IQRouterTest.cpp b/Swiften/Queries/UnitTest/IQRouterTest.cpp
new file mode 100644
index 0000000..5760b09
--- /dev/null
+++ b/Swiften/Queries/UnitTest/IQRouterTest.cpp
@@ -0,0 +1,143 @@
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include "Swiften/Queries/IQHandler.h"
+#include "Swiften/Queries/IQRouter.h"
+#include "Swiften/Queries/DummyIQChannel.h"
+
+using namespace Swift;
+
+class IQRouterTest : public CppUnit::TestFixture
+{
+ CPPUNIT_TEST_SUITE(IQRouterTest);
+ CPPUNIT_TEST(testRemoveHandler);
+ CPPUNIT_TEST(testRemoveHandler_AfterHandleIQ);
+ CPPUNIT_TEST(testHandleIQ_SuccesfulHandlerFirst);
+ CPPUNIT_TEST(testHandleIQ_SuccesfulHandlerLast);
+ CPPUNIT_TEST(testHandleIQ_NoSuccesfulHandler);
+ CPPUNIT_TEST(testHandleIQ_HandlerRemovedDuringHandle);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ IQRouterTest() {}
+
+ void setUp() {
+ channel_ = new DummyIQChannel();
+ }
+
+ void tearDown() {
+ delete channel_;
+ }
+
+ void testRemoveHandler() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(true, &testling);
+ DummyIQHandler handler2(true, &testling);
+ testling.removeHandler(&handler1);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(0, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(1, handler2.called);
+ }
+
+ void testRemoveHandler_AfterHandleIQ() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(true, &testling);
+ DummyIQHandler handler2(true, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+ testling.removeHandler(&handler1);
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(1, handler2.called);
+ }
+
+ void testHandleIQ_SuccesfulHandlerFirst() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(true, &testling);
+ DummyIQHandler handler2(false, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(0, handler2.called);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ void testHandleIQ_SuccesfulHandlerLast() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler1(false, &testling);
+ DummyIQHandler handler2(true, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(1, handler2.called);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ void testHandleIQ_NoSuccesfulHandler() {
+ IQRouter testling(channel_);
+ DummyIQHandler handler(false, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT(channel_->iqs_[0]->getPayload<ErrorPayload>());
+ }
+
+
+ void testHandleIQ_HandlerRemovedDuringHandle() {
+ IQRouter testling(channel_);
+ RemovingIQHandler handler1(&testling);
+ DummyIQHandler handler2(true, &testling);
+
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+ channel_->onIQReceived(boost::shared_ptr<IQ>(new IQ()));
+
+ CPPUNIT_ASSERT_EQUAL(1, handler1.called);
+ CPPUNIT_ASSERT_EQUAL(2, handler2.called);
+ }
+
+ private:
+ struct DummyIQHandler : public IQHandler {
+ DummyIQHandler(bool handle, IQRouter* router) : handle(handle), router(router), called(0) {
+ router->addHandler(this);
+ }
+
+ ~DummyIQHandler() {
+ router->removeHandler(this);
+ }
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) {
+ called++;
+ return handle;
+ }
+ bool handle;
+ IQRouter* router;
+ int called;
+ };
+
+ struct RemovingIQHandler : public IQHandler {
+ RemovingIQHandler(IQRouter* router) : router(router), called(0) {
+ router->addHandler(this);
+ }
+
+ virtual bool handleIQ(boost::shared_ptr<IQ>) {
+ called++;
+ router->removeHandler(this);
+ return false;
+ }
+ IQRouter* router;
+ int called;
+ };
+
+
+ DummyIQChannel* channel_;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(IQRouterTest);
diff --git a/Swiften/Queries/UnitTest/RequestTest.cpp b/Swiften/Queries/UnitTest/RequestTest.cpp
new file mode 100644
index 0000000..c569bb5
--- /dev/null
+++ b/Swiften/Queries/UnitTest/RequestTest.cpp
@@ -0,0 +1,167 @@
+#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(testHandleIQ_ErrorWithoutPayload);
+ CPPUNIT_TEST(testHandleIQ_BeforeSend);
+ 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;
+ }
+
+ 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, static_cast<int>(receivedErrors.size()));
+ 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, static_cast<int>(receivedErrors.size()));
+ 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();
+
+ boost::shared_ptr<IQ> error = createError("test-id");
+ boost::shared_ptr<Payload> errorPayload = boost::shared_ptr<ErrorPayload>(new ErrorPayload(ErrorPayload::FeatureNotImplemented));
+ error->addPayload(errorPayload);
+ channel_->onIQReceived(error);
+
+ CPPUNIT_ASSERT_EQUAL(0, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(receivedErrors.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::FeatureNotImplemented, receivedErrors[0].getCondition());
+ }
+
+ void testHandleIQ_ErrorWithoutPayload() {
+ 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, static_cast<int>(receivedErrors.size()));
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(channel_->iqs_.size()));
+ CPPUNIT_ASSERT_EQUAL(ErrorPayload::UndefinedCondition, receivedErrors[0].getCondition());
+ }
+
+ void testHandleIQ_BeforeSend() {
+ MyRequest testling(IQ::Get, JID("foo@bar.com/baz"), payload_, router_);
+ testling.onResponse.connect(boost::bind(&RequestTest::handleResponse, this, _1, _2));
+ channel_->onIQReceived(createResponse("test-id"));
+
+ CPPUNIT_ASSERT_EQUAL(0, responsesReceived_);
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(receivedErrors.size()));
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(channel_->iqs_.size()));
+ }
+
+ private:
+ void handleResponse(boost::shared_ptr<Payload> p, const boost::optional<ErrorPayload>& e) {
+ if (e) {
+ receivedErrors.push_back(*e);
+ }
+ 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_;
+ std::vector<ErrorPayload> receivedErrors;
+};
+
+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);