From b107bdd08df9ae3a978ad8f966a26eb8d551bd13 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be>
Date: Sun, 24 Oct 2010 14:10:44 +0200
Subject: Creating abstract XMPPRoster base class.


diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
index 46b5580..9f38bb8 100644
--- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
+++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp
@@ -25,7 +25,7 @@
 #include "Swiften/VCards/VCardManager.h"
 #include "Swiften/VCards/VCardMemoryStorage.h"
 #include "Swift/Controllers/NickResolver.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swift/Controllers/UnitTest/MockChatWindow.h"
 #include "Swiften/Client/DummyStanzaChannel.h"
 #include "Swiften/Queries/DummyIQChannel.h"
@@ -66,7 +66,7 @@ public:
 		capsProvider_ = new DummyCapsProvider();
 		eventController_ = new EventController();
 		chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>();
-		xmppRoster_ = new XMPPRoster();
+		xmppRoster_ = new XMPPRosterImpl();
 		mucRegistry_ = new MUCRegistry();
 		nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, NULL, mucRegistry_);
 		presenceOracle_ = new PresenceOracle(stanzaChannel_);
@@ -323,7 +323,7 @@ private:
 	PresenceOracle* presenceOracle_;
 	AvatarManager* avatarManager_;
 	boost::shared_ptr<DiscoInfo> serverDiscoInfo_;
-	XMPPRoster* xmppRoster_;
+	XMPPRosterImpl* xmppRoster_;
 	PresenceSender* presenceSender_;
 	MockRepository* mocks_;
 	UIEventStream* uiEventStream_;
diff --git a/Swift/Controllers/UnitTest/NickResolverTest.cpp b/Swift/Controllers/UnitTest/NickResolverTest.cpp
index 1608731..2b1e49c 100644
--- a/Swift/Controllers/UnitTest/NickResolverTest.cpp
+++ b/Swift/Controllers/UnitTest/NickResolverTest.cpp
@@ -9,7 +9,7 @@
 
 #include "Swift/Controllers/NickResolver.h"
 #include "Swiften/MUC/MUCRegistry.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swiften/VCards/VCardManager.h"
 #include "Swiften/VCards/VCardMemoryStorage.h"
 #include "Swiften/Queries/IQRouter.h"
@@ -35,7 +35,7 @@ class NickResolverTest : public CppUnit::TestFixture {
 	public:
 		void setUp() {
 			ownJID_ = JID("kev@wonderland.lit");
-			xmppRoster_ = new XMPPRoster();
+			xmppRoster_ = new XMPPRosterImpl();
 			stanzaChannel_ = new DummyStanzaChannel();
 		  iqRouter_ = new IQRouter(stanzaChannel_);
 			vCardStorage_ = new VCardMemoryStorage();
@@ -136,7 +136,7 @@ class NickResolverTest : public CppUnit::TestFixture {
 	
 	private:
 		std::vector<String> groups_;
-		XMPPRoster* xmppRoster_;
+		XMPPRosterImpl* xmppRoster_;
 		VCardStorage* vCardStorage_;
 		IQRouter* iqRouter_;
 		DummyStanzaChannel* stanzaChannel_;
diff --git a/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp b/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp
index 9addab2..5ebd5df 100644
--- a/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp
+++ b/Swift/Controllers/UnitTest/PresenceNotifierTest.cpp
@@ -14,7 +14,7 @@
 #include "SwifTools/Notifier/LoggingNotifier.h"
 #include "Swiften/Client/DummyStanzaChannel.h"
 #include "Swiften/MUC/MUCRegistry.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swiften/Presence/PresenceOracle.h"
 #include "Swiften/Avatars/DummyAvatarManager.h"
 #include "Swiften/Network/DummyTimerFactory.h"
@@ -52,7 +52,7 @@ class PresenceNotifierTest : public CppUnit::TestFixture {
 			user1 = JID("user1@bar.com/bla");
 			user2 = JID("user2@foo.com/baz");
 			avatarManager = new DummyAvatarManager();
-			roster = new XMPPRoster();
+			roster = new XMPPRosterImpl();
 			nickResolver = new NickResolver(JID("foo@bar.com"), roster, NULL, mucRegistry);
 			presenceOracle = new PresenceOracle(stanzaChannel);
 			timerFactory = new DummyTimerFactory();
@@ -303,7 +303,7 @@ class PresenceNotifierTest : public CppUnit::TestFixture {
 		LoggingNotifier* notifier;
 		MUCRegistry* mucRegistry;
 		DummyAvatarManager* avatarManager;
-		XMPPRoster* roster;
+		XMPPRosterImpl* roster;
 		NickResolver* nickResolver;
 		PresenceOracle* presenceOracle;
 		DummyTimerFactory* timerFactory;
diff --git a/Swift/Controllers/UnitTest/RosterControllerTest.cpp b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
index 82611f7..92336f6 100644
--- a/Swift/Controllers/UnitTest/RosterControllerTest.cpp
+++ b/Swift/Controllers/UnitTest/RosterControllerTest.cpp
@@ -16,7 +16,7 @@
 #include "Swiften/Queries/DummyIQChannel.h"
 #include "Swiften/Client/DummyStanzaChannel.h"
 #include "Swiften/Queries/IQRouter.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swiften/Roster/Roster.h"
 #include "Swiften/Roster/GroupRosterItem.h"
 #include "Swiften/Roster/ContactRosterItem.h"
@@ -50,7 +50,7 @@ class RosterControllerTest : public CppUnit::TestFixture
 
 		void setUp() {
 			jid_ = JID("testjid@swift.im/swift");
-			xmppRoster_ = new XMPPRoster();
+			xmppRoster_ = new XMPPRosterImpl();
 			avatarManager_ = new NullAvatarManager();
 			mainWindowFactory_ = new MockMainWindowFactory();
 			mucRegistry_ = new MUCRegistry();
@@ -196,7 +196,7 @@ class RosterControllerTest : public CppUnit::TestFixture
 
 	private:
 		JID jid_;
-		XMPPRoster* xmppRoster_;
+		XMPPRosterImpl* xmppRoster_;
 		MUCRegistry* mucRegistry_;
 		AvatarManager* avatarManager_;
 		MockMainWindowFactory* mainWindowFactory_;
diff --git a/Swiften/Client/Client.cpp b/Swiften/Client/Client.cpp
index 20a0c47..f1ca207 100644
--- a/Swiften/Client/Client.cpp
+++ b/Swiften/Client/Client.cpp
@@ -7,7 +7,7 @@
 #include "Swiften/Client/Client.h"
 
 #include "Swiften/Queries/Responders/SoftwareVersionResponder.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 #include "Swiften/Roster/XMPPRosterController.h"
 #include "Swiften/Presence/PresenceOracle.h"
 
@@ -17,7 +17,7 @@ Client::Client(const JID& jid, const String& password) : CoreClient(jid, passwor
 	softwareVersionResponder = new SoftwareVersionResponder(getIQRouter());
 	softwareVersionResponder->start();
 
-	roster = new XMPPRoster();
+	roster = new XMPPRosterImpl();
 	rosterController = new XMPPRosterController(getIQRouter(), roster);
 
 	presenceOracle = new PresenceOracle(getStanzaChannel());
@@ -35,6 +35,10 @@ Client::~Client() {
 	delete softwareVersionResponder;
 }
 
+XMPPRoster* Client::getRoster() const {
+	return roster;
+}
+
 void Client::setSoftwareVersion(const String& name, const String& version) {
 	softwareVersionResponder->setVersion(name, version);
 }
diff --git a/Swiften/Client/Client.h b/Swiften/Client/Client.h
index bf19f2f..4de630c 100644
--- a/Swiften/Client/Client.h
+++ b/Swiften/Client/Client.h
@@ -11,6 +11,7 @@
 namespace Swift {
 	class SoftwareVersionResponder;
 	class XMPPRoster;
+	class XMPPRosterImpl;
 	class XMPPRosterController;
 	class PresenceOracle;
 
@@ -46,9 +47,7 @@ namespace Swift {
 			 *
 			 * \see requestRoster()
 			 */
-			XMPPRoster* getRoster() const {
-				return roster;
-			}
+			XMPPRoster* getRoster() const;
 
 			/**
 			 * Requests the roster from the server.
@@ -84,7 +83,7 @@ namespace Swift {
 
 		private:
 			SoftwareVersionResponder* softwareVersionResponder;
-			XMPPRoster* roster;
+			XMPPRosterImpl* roster;
 			XMPPRosterController* rosterController;
 			PresenceOracle* presenceOracle;
 	};
diff --git a/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp
index 33fc04a..17c5527 100644
--- a/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp
+++ b/Swiften/Roster/UnitTest/XMPPRosterControllerTest.cpp
@@ -13,7 +13,7 @@
 #include "Swiften/Elements/RosterPayload.h"
 #include "Swiften/Queries/DummyIQChannel.h"
 #include "Swiften/Queries/IQRouter.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 
 using namespace Swift;
 
@@ -31,7 +31,7 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture
 		void setUp() {
 			channel_ = new DummyIQChannel();
 			router_ = new IQRouter(channel_);
-			xmppRoster_ = new XMPPRoster();
+			xmppRoster_ = new XMPPRosterImpl();
 		}
 
 		void tearDown() {
@@ -79,7 +79,7 @@ class XMPPRosterControllerTest : public CppUnit::TestFixture
 	private:
 		DummyIQChannel* channel_;
 		IQRouter* router_;
-		XMPPRoster* xmppRoster_;
+		XMPPRosterImpl* xmppRoster_;
 };
 
 CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterControllerTest);
diff --git a/Swiften/Roster/UnitTest/XMPPRosterImplTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterImplTest.cpp
new file mode 100644
index 0000000..fbc3430
--- /dev/null
+++ b/Swiften/Roster/UnitTest/XMPPRosterImplTest.cpp
@@ -0,0 +1,170 @@
+/*
+ * 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/shared_ptr.hpp>
+#include <boost/bind.hpp>
+
+#include <vector>
+
+#include "Swiften/Roster/XMPPRosterImpl.h"
+
+
+using namespace Swift;
+
+enum XMPPRosterEvents {None, Add, Remove, Update};
+
+class XMPPRosterSignalHandler {
+public:
+	XMPPRosterSignalHandler(XMPPRoster* roster) {
+		lastEvent_ = None;
+		roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1));
+		roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1));
+		roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3));
+	}
+
+	XMPPRosterEvents getLastEvent() {
+		return lastEvent_;
+	}
+
+	JID getLastJID() {
+		return lastJID_;
+	}
+
+	String getLastOldName() {
+		return lastOldName_;
+	}
+
+	std::vector<String> getLastOldGroups() {
+		return lastOldGroups_;
+	}
+
+	void reset() {
+		lastEvent_ = None;
+	}
+
+private:
+	void handleJIDAdded(const JID& jid) {
+		lastJID_ = jid;
+		lastEvent_ = Add;
+	}
+
+	void handleJIDRemoved(const JID& jid) {
+		lastJID_ = jid;
+		lastEvent_ = Remove;
+	}
+
+	void handleJIDUpdated(const JID& jid, const String& oldName, const std::vector<String>& oldGroups) {
+		CPPUNIT_ASSERT_EQUAL(None, lastEvent_);
+		lastJID_ = jid;
+		lastOldName_ = oldName;
+		lastOldGroups_ = oldGroups;
+		lastEvent_ = Update;
+	}
+
+	XMPPRosterEvents lastEvent_;
+	JID lastJID_;
+	String lastOldName_;
+	std::vector<String> lastOldGroups_;
+
+};
+
+class XMPPRosterImplTest : public CppUnit::TestFixture
+{
+		CPPUNIT_TEST_SUITE(XMPPRosterImplTest);
+		CPPUNIT_TEST(testJIDAdded);
+		CPPUNIT_TEST(testJIDRemoved);
+		CPPUNIT_TEST(testJIDUpdated);
+		CPPUNIT_TEST_SUITE_END();
+
+	private:
+		XMPPRosterImpl* roster_;
+		XMPPRosterSignalHandler* handler_;
+		JID jid1_;
+		JID jid2_; 
+		JID jid3_;
+		std::vector<String> groups1_;
+		std::vector<String> groups2_;
+
+
+	public:
+		XMPPRosterImplTest() : jid1_(JID("a@b.c")), jid2_(JID("b@c.d")), jid3_(JID("c@d.e")) {}
+
+		void setUp() {
+			roster_ = new XMPPRosterImpl();
+			handler_ = new XMPPRosterSignalHandler(roster_);
+			groups1_.push_back("bobs");
+			groups1_.push_back("berts");
+			groups2_.push_back("ernies");
+		}
+
+		void tearDown() {
+			delete handler_;
+			delete roster_;
+		}
+
+		void testJIDAdded() {
+			roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+			handler_->reset();
+			roster_->addContact(jid2_, "NameTwo", groups1_, RosterItemPayload::Both);
+			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+			CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid2_));
+			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid2_));
+			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+			handler_->reset();
+			roster_->addContact(jid3_, "NewName", groups2_, RosterItemPayload::Both);
+			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID());
+			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid3_));
+			CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid3_));
+		}
+
+		void testJIDRemoved() {
+			roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+			handler_->reset();
+			roster_->removeContact(jid1_);
+			CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+			handler_->reset();
+			roster_->addContact(jid1_, "NewName2", groups1_, RosterItemPayload::Both);
+			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+			CPPUNIT_ASSERT_EQUAL(String("NewName2"), roster_->getNameForJID(jid1_));
+			roster_->addContact(jid2_, "NewName3", groups1_, RosterItemPayload::Both);
+			handler_->reset();
+			roster_->removeContact(jid2_);
+			CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
+			handler_->reset();
+			roster_->removeContact(jid1_);
+			CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+		}
+
+		void testJIDUpdated() {
+			roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
+			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
+			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
+			handler_->reset();
+			roster_->addContact(jid1_, "NameTwo", groups2_, RosterItemPayload::Both);
+			CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent());
+			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
+			CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid1_));
+			CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid1_));
+		}
+
+};
+CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterImplTest);
+
diff --git a/Swiften/Roster/UnitTest/XMPPRosterTest.cpp b/Swiften/Roster/UnitTest/XMPPRosterTest.cpp
deleted file mode 100644
index 7e64d8c..0000000
--- a/Swiften/Roster/UnitTest/XMPPRosterTest.cpp
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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/shared_ptr.hpp>
-#include <boost/bind.hpp>
-
-#include <vector>
-
-#include "Swiften/Roster/XMPPRoster.h"
-
-
-using namespace Swift;
-
-enum XMPPRosterEvents {None, Add, Remove, Update};
-
-class XMPPRosterSignalHandler {
-public:
-	XMPPRosterSignalHandler(XMPPRoster* roster) {
-		lastEvent_ = None;
-		roster->onJIDAdded.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDAdded, this, _1));
-		roster->onJIDRemoved.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDRemoved, this, _1));
-		roster->onJIDUpdated.connect(boost::bind(&XMPPRosterSignalHandler::handleJIDUpdated, this, _1, _2, _3));
-	}
-
-	XMPPRosterEvents getLastEvent() {
-		return lastEvent_;
-	}
-
-	JID getLastJID() {
-		return lastJID_;
-	}
-
-	String getLastOldName() {
-		return lastOldName_;
-	}
-
-	std::vector<String> getLastOldGroups() {
-		return lastOldGroups_;
-	}
-
-	void reset() {
-		lastEvent_ = None;
-	}
-
-private:
-	void handleJIDAdded(const JID& jid) {
-		lastJID_ = jid;
-		lastEvent_ = Add;
-	}
-
-	void handleJIDRemoved(const JID& jid) {
-		lastJID_ = jid;
-		lastEvent_ = Remove;
-	}
-
-	void handleJIDUpdated(const JID& jid, const String& oldName, const std::vector<String>& oldGroups) {
-		CPPUNIT_ASSERT_EQUAL(None, lastEvent_);
-		lastJID_ = jid;
-		lastOldName_ = oldName;
-		lastOldGroups_ = oldGroups;
-		lastEvent_ = Update;
-	}
-
-	XMPPRosterEvents lastEvent_;
-	JID lastJID_;
-	String lastOldName_;
-	std::vector<String> lastOldGroups_;
-
-};
-
-class XMPPRosterTest : public CppUnit::TestFixture
-{
-		CPPUNIT_TEST_SUITE(XMPPRosterTest);
-		CPPUNIT_TEST(testJIDAdded);
-		CPPUNIT_TEST(testJIDRemoved);
-		CPPUNIT_TEST(testJIDUpdated);
-		CPPUNIT_TEST_SUITE_END();
-
-	private:
-		XMPPRoster* roster_;
-		XMPPRosterSignalHandler* handler_;
-		JID jid1_;
-		JID jid2_; 
-		JID jid3_;
-		std::vector<String> groups1_;
-		std::vector<String> groups2_;
-
-
-	public:
-
-		XMPPRosterTest() : jid1_(JID("a@b.c")), jid2_(JID("b@c.d")), jid3_(JID("c@d.e")) {}
-
-		void setUp() {
-			roster_ = new XMPPRoster();
-			handler_ = new XMPPRosterSignalHandler(roster_);
-			groups1_.push_back("bobs");
-			groups1_.push_back("berts");
-			groups2_.push_back("ernies");
-		}
-
-		void tearDown() {
-			delete handler_;
-			delete roster_;
-		}
-
-		void testJIDAdded() {
-			roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
-			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
-			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
-			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
-			handler_->reset();
-			roster_->addContact(jid2_, "NameTwo", groups1_, RosterItemPayload::Both);
-			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
-			CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid2_));
-			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
-			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid2_));
-			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
-			handler_->reset();
-			roster_->addContact(jid3_, "NewName", groups2_, RosterItemPayload::Both);
-			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid3_, handler_->getLastJID());
-			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid3_));
-			CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid3_));
-		}
-
-		void testJIDRemoved() {
-			roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
-			handler_->reset();
-			roster_->removeContact(jid1_);
-			CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
-			handler_->reset();
-			roster_->addContact(jid1_, "NewName2", groups1_, RosterItemPayload::Both);
-			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
-			CPPUNIT_ASSERT_EQUAL(String("NewName2"), roster_->getNameForJID(jid1_));
-			roster_->addContact(jid2_, "NewName3", groups1_, RosterItemPayload::Both);
-			handler_->reset();
-			roster_->removeContact(jid2_);
-			CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid2_, handler_->getLastJID());
-			handler_->reset();
-			roster_->removeContact(jid1_);
-			CPPUNIT_ASSERT_EQUAL(Remove, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
-		}
-
-		void testJIDUpdated() {
-			roster_->addContact(jid1_, "NewName", groups1_, RosterItemPayload::Both);
-			CPPUNIT_ASSERT_EQUAL(Add, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
-			CPPUNIT_ASSERT_EQUAL(String("NewName"), roster_->getNameForJID(jid1_));
-			CPPUNIT_ASSERT(groups1_ == roster_->getGroupsForJID(jid1_));
-			handler_->reset();
-			roster_->addContact(jid1_, "NameTwo", groups2_, RosterItemPayload::Both);
-			CPPUNIT_ASSERT_EQUAL(Update, handler_->getLastEvent());
-			CPPUNIT_ASSERT_EQUAL(jid1_, handler_->getLastJID());
-			CPPUNIT_ASSERT_EQUAL(String("NameTwo"), roster_->getNameForJID(jid1_));
-			CPPUNIT_ASSERT(groups2_ == roster_->getGroupsForJID(jid1_));
-		}
-
-};
-CPPUNIT_TEST_SUITE_REGISTRATION(XMPPRosterTest);
-
diff --git a/Swiften/Roster/XMPPRoster.cpp b/Swiften/Roster/XMPPRoster.cpp
index 56616c2..61630b4 100644
--- a/Swiften/Roster/XMPPRoster.cpp
+++ b/Swiften/Roster/XMPPRoster.cpp
@@ -11,57 +11,8 @@ namespace Swift {
 XMPPRoster::XMPPRoster() {
 }
 
-void XMPPRoster::addContact(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) {
-	JID bareJID(jid.toBare());
-	bool exists = containsJID(bareJID);
-	String oldName = getNameForJID(bareJID);
-	std::vector<String> oldGroups = entries_[bareJID].groups;
-	if (exists) {
-		entries_.erase(bareJID);
-	}
-	XMPPRosterItem item;
-	item.groups = groups;
-	item.name = name;
-	item.jid = jid;
-	item.subscription = subscription;
-	entries_[bareJID] = item;
-	if (exists) {
-		onJIDUpdated(bareJID, oldName, oldGroups);
-	} else {
-		onJIDAdded(bareJID);
-	}
-}
-
-void XMPPRoster::removeContact(const JID& jid) {
-	entries_.erase(JID(jid.toBare()));
-	onJIDRemoved(jid);
-}
-
-void XMPPRoster::clear() {
-	entries_.clear();
-	onRosterCleared();
-}
-
-bool XMPPRoster::containsJID(const JID& jid) {
-	return entries_.find(JID(jid.toBare())) != entries_.end();
-}
-
-String XMPPRoster::getNameForJID(const JID& jid) const {
-	std::map<JID, XMPPRosterItem>::const_iterator i = entries_.find(jid.toBare());
-	if (i != entries_.end()) {
-		return i->second.name;
-	}
-	else {
-		return "";
-	}
-}
-
-const std::vector<String>& XMPPRoster::getGroupsForJID(const JID& jid) {
-	return entries_[JID(jid.toBare())].groups;
-}
 
-RosterItemPayload::Subscription XMPPRoster::getSubscriptionStateForJID(const JID& jid) {
-	return entries_[JID(jid.toBare())].subscription;
+XMPPRoster::~XMPPRoster() {
 }
 
 }
diff --git a/Swiften/Roster/XMPPRoster.h b/Swiften/Roster/XMPPRoster.h
index abafdfe..b88148d 100644
--- a/Swiften/Roster/XMPPRoster.h
+++ b/Swiften/Roster/XMPPRoster.h
@@ -10,36 +10,63 @@
 #include "Swiften/JID/JID.h"
 #include "Swiften/Elements/RosterItemPayload.h"
 
-#include <map>
 #include <vector>
 #include "Swiften/Base/boost_bsignals.h"
 
 namespace Swift {
+	/**
+	 * This class represents the roster of an account, as stored on the XMPP server.
+	 *
+	 * Changes to the roster (either due to subscription requests or by going online/offline) are
+	 * emitted through signals.
+	 */
 	class XMPPRoster {
 		public:
 			XMPPRoster();
+			virtual ~XMPPRoster();
 
-			void addContact(const JID& jid, const String& name, const std::vector<String>& groups, const RosterItemPayload::Subscription subscription);
-			void removeContact(const JID& jid);
-			void clear();
+			/**
+			 * Checks whether the bare jid of the given jid is in the roster.
+			 */
+			virtual bool containsJID(const JID& jid) = 0;
 
-			bool containsJID(const JID& jid);
-			RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid);
-			String getNameForJID(const JID& jid) const;
-			const std::vector<String>& getGroupsForJID(const JID& jid);
+			/**
+			 * Retrieves the subscription state for the given jid.
+			 */
+			virtual RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid) = 0;
 
+			/**
+			 * Retrieves the stored roster name for the given jid.
+			 */
+			virtual String getNameForJID(const JID& jid) const = 0;
+
+			/**
+			 * Returns the list of groups for the given JID.
+			 */
+			virtual const std::vector<String>& getGroupsForJID(const JID& jid) = 0;
+
+		public:
+			/**
+			 * Emitted when the given JID is added to the roster.
+			 */
 			boost::signal<void (const JID&)> onJIDAdded;
+
+			/**
+			 * Emitted when the given JID is removed from the roster.
+			 */
 			boost::signal<void (const JID&)> onJIDRemoved;
+
+			/**
+			 * Emitted when the name or the groups of the roster item with the
+			 * given JID changes.
+			 */
 			boost::signal<void (const JID&, const String&, const std::vector<String>&)> onJIDUpdated;
-			boost::signal<void ()> onRosterCleared;
 
-		private:
-			struct XMPPRosterItem {
-				JID jid;
-				String name;
-				std::vector<String> groups;
-				RosterItemPayload::Subscription subscription;
-			};
-			std::map<JID, XMPPRosterItem> entries_;
+			/**
+			 * Emitted when the roster is reset (e.g. due to logging in/logging out).
+			 * After this signal is emitted, the roster is empty. It will be repopulated through
+			 * onJIDAdded and onJIDRemoved events.
+			 */
+			boost::signal<void ()> onRosterCleared;
 	};
 }
diff --git a/Swiften/Roster/XMPPRosterController.cpp b/Swiften/Roster/XMPPRosterController.cpp
index 024cb1d..75ab494 100644
--- a/Swiften/Roster/XMPPRosterController.cpp
+++ b/Swiften/Roster/XMPPRosterController.cpp
@@ -16,14 +16,14 @@
 #include "Swiften/Roster/Roster.h"
 #include "Swiften/Roster/SetPresence.h"
 #include "Swiften/Roster/OfflineRosterFilter.h"
-#include "Swiften/Roster/XMPPRoster.h"
+#include "Swiften/Roster/XMPPRosterImpl.h"
 
 namespace Swift {
 	
 /**
  * The controller does not gain ownership of these parameters.
  */
-XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRoster* xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) {
+XMPPRosterController::XMPPRosterController(IQRouter* iqRouter, XMPPRosterImpl* xmppRoster) : iqRouter_(iqRouter), rosterPushResponder_(iqRouter), xmppRoster_(xmppRoster) {
 	rosterPushResponder_.onRosterReceived.connect(boost::bind(&XMPPRosterController::handleRosterReceived, this, _1));
 	rosterPushResponder_.start();
 }
diff --git a/Swiften/Roster/XMPPRosterController.h b/Swiften/Roster/XMPPRosterController.h
index d846b86..dedf090 100644
--- a/Swiften/Roster/XMPPRosterController.h
+++ b/Swiften/Roster/XMPPRosterController.h
@@ -17,11 +17,11 @@
 
 namespace Swift {
 	class IQRouter;
-	class XMPPRoster;
+	class XMPPRosterImpl;
 
 	class XMPPRosterController {
 		public:
-			XMPPRosterController(IQRouter *iqRouter, XMPPRoster* xmppRoster);
+			XMPPRosterController(IQRouter *iqRouter, XMPPRosterImpl* xmppRoster);
 			~XMPPRosterController();
 
 			void requestRoster();
@@ -31,7 +31,7 @@ namespace Swift {
 		private:
 			IQRouter* iqRouter_;
 			RosterPushResponder rosterPushResponder_;
-			XMPPRoster* xmppRoster_;
+			XMPPRosterImpl* xmppRoster_;
 	};
 }
 
diff --git a/Swiften/Roster/XMPPRosterImpl.cpp b/Swiften/Roster/XMPPRosterImpl.cpp
new file mode 100644
index 0000000..762ae29
--- /dev/null
+++ b/Swiften/Roster/XMPPRosterImpl.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Roster/XMPPRosterImpl.h"
+
+namespace Swift {
+
+XMPPRosterImpl::XMPPRosterImpl() {
+}
+
+void XMPPRosterImpl::addContact(const JID& jid, const String& name, const std::vector<String>& groups, RosterItemPayload::Subscription subscription) {
+	JID bareJID(jid.toBare());
+	bool exists = containsJID(bareJID);
+	String oldName = getNameForJID(bareJID);
+	std::vector<String> oldGroups = entries_[bareJID].groups;
+	if (exists) {
+		entries_.erase(bareJID);
+	}
+	XMPPRosterItem item;
+	item.groups = groups;
+	item.name = name;
+	item.jid = jid;
+	item.subscription = subscription;
+	entries_[bareJID] = item;
+	if (exists) {
+		onJIDUpdated(bareJID, oldName, oldGroups);
+	} else {
+		onJIDAdded(bareJID);
+	}
+}
+
+void XMPPRosterImpl::removeContact(const JID& jid) {
+	entries_.erase(JID(jid.toBare()));
+	onJIDRemoved(jid);
+}
+
+void XMPPRosterImpl::clear() {
+	entries_.clear();
+	onRosterCleared();
+}
+
+bool XMPPRosterImpl::containsJID(const JID& jid) {
+	return entries_.find(JID(jid.toBare())) != entries_.end();
+}
+
+String XMPPRosterImpl::getNameForJID(const JID& jid) const {
+	std::map<JID, XMPPRosterItem>::const_iterator i = entries_.find(jid.toBare());
+	if (i != entries_.end()) {
+		return i->second.name;
+	}
+	else {
+		return "";
+	}
+}
+
+const std::vector<String>& XMPPRosterImpl::getGroupsForJID(const JID& jid) {
+	return entries_[JID(jid.toBare())].groups;
+}
+
+RosterItemPayload::Subscription XMPPRosterImpl::getSubscriptionStateForJID(const JID& jid) {
+	return entries_[JID(jid.toBare())].subscription;
+}
+
+}
+
diff --git a/Swiften/Roster/XMPPRosterImpl.h b/Swiften/Roster/XMPPRosterImpl.h
new file mode 100644
index 0000000..c2d2458
--- /dev/null
+++ b/Swiften/Roster/XMPPRosterImpl.h
@@ -0,0 +1,36 @@
+/*
+ * 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 <map>
+
+#include "Swiften/Roster/XMPPRoster.h"
+
+namespace Swift {
+	class XMPPRosterImpl : public XMPPRoster {
+		public:
+			XMPPRosterImpl();
+
+			void addContact(const JID& jid, const String& name, const std::vector<String>& groups, const RosterItemPayload::Subscription subscription);
+			void removeContact(const JID& jid);
+			void clear();
+
+			bool containsJID(const JID& jid);
+			RosterItemPayload::Subscription getSubscriptionStateForJID(const JID& jid);
+			String getNameForJID(const JID& jid) const;
+			const std::vector<String>& getGroupsForJID(const JID& jid);
+
+		private:
+			struct XMPPRosterItem {
+				JID jid;
+				String name;
+				std::vector<String> groups;
+				RosterItemPayload::Subscription subscription;
+			};
+			std::map<JID, XMPPRosterItem> entries_;
+	};
+}
diff --git a/Swiften/SConscript b/Swiften/SConscript
index e96a868..12703fc 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -60,6 +60,7 @@ if env["SCONS_STAGE"] == "build" :
 			"Roster/RosterItem.cpp",
 			"Roster/Roster.cpp",
 			"Roster/XMPPRoster.cpp",
+			"Roster/XMPPRosterImpl.cpp",
 			"Roster/XMPPRosterController.cpp",
 			"Serializer/AuthRequestSerializer.cpp",
 			"Serializer/AuthSuccessSerializer.cpp",
@@ -222,7 +223,7 @@ if env["SCONS_STAGE"] == "build" :
 			File("Queries/UnitTest/ResponderTest.cpp"),
 			File("Roster/UnitTest/OfflineRosterFilterTest.cpp"),
 			File("Roster/UnitTest/RosterTest.cpp"),
-			File("Roster/UnitTest/XMPPRosterTest.cpp"),
+			File("Roster/UnitTest/XMPPRosterImplTest.cpp"),
 			File("Roster/UnitTest/XMPPRosterControllerTest.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/PayloadsSerializer.cpp"),
 			File("Serializer/PayloadSerializers/UnitTest/CapsInfoSerializerTest.cpp"),
-- 
cgit v0.10.2-6-g49f6