summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRemko Tronçon <git@el-tramo.be>2010-09-09 13:20:18 (GMT)
committerRemko Tronçon <git@el-tramo.be>2010-09-11 21:53:44 (GMT)
commit904db4e398210192093b688ebf1ad66fb017b6d2 (patch)
tree083f01449f64dc27aff63e2be0aa00a73cbd511b /Swiften
parent10d8ba50a9e09517cfe4a6d4c3d51a768e989125 (diff)
downloadswift-904db4e398210192093b688ebf1ad66fb017b6d2.zip
swift-904db4e398210192093b688ebf1ad66fb017b6d2.tar.bz2
Added CombinedAvatarProvider
Diffstat (limited to 'Swiften')
-rw-r--r--Swiften/Avatars/AvatarManager.cpp6
-rw-r--r--Swiften/Avatars/AvatarManager.h2
-rw-r--r--Swiften/Avatars/CombinedAvatarProvider.cpp57
-rw-r--r--Swiften/Avatars/CombinedAvatarProvider.h30
-rw-r--r--Swiften/Avatars/SConscript1
-rw-r--r--Swiften/Avatars/UnitTest/CombinedAvatarProviderTest.cpp194
-rw-r--r--Swiften/Avatars/VCardUpdateAvatarManager.h2
-rw-r--r--Swiften/SConscript1
8 files changed, 290 insertions, 3 deletions
diff --git a/Swiften/Avatars/AvatarManager.cpp b/Swiften/Avatars/AvatarManager.cpp
index 9c3255d..6811a8e 100644
--- a/Swiften/Avatars/AvatarManager.cpp
+++ b/Swiften/Avatars/AvatarManager.cpp
@@ -15,15 +15,17 @@ namespace Swift {
AvatarManager::AvatarManager(VCardManager* vcardManager, StanzaChannel* stanzaChannel, AvatarStorage* avatarStorage, MUCRegistry* mucRegistry) : avatarStorage(avatarStorage) {
vcardUpdateAvatarManager = new VCardUpdateAvatarManager(vcardManager, stanzaChannel, avatarStorage, mucRegistry);
- vcardUpdateAvatarManager->onAvatarChanged.connect(boost::ref(onAvatarChanged));
+ combinedAvatarProvider.addProvider(vcardUpdateAvatarManager);
+ combinedAvatarProvider.onAvatarChanged.connect(boost::ref(onAvatarChanged));
}
AvatarManager::~AvatarManager() {
+ combinedAvatarProvider.removeProvider(vcardUpdateAvatarManager);
delete vcardUpdateAvatarManager;
}
boost::filesystem::path AvatarManager::getAvatarPath(const JID& jid) const {
- String hash = vcardUpdateAvatarManager->getAvatarHash(jid);
+ String hash = combinedAvatarProvider.getAvatarHash(jid);
if (!hash.isEmpty()) {
return avatarStorage->getAvatarPath(hash);
}
diff --git a/Swiften/Avatars/AvatarManager.h b/Swiften/Avatars/AvatarManager.h
index d3cdbae..04ce496 100644
--- a/Swiften/Avatars/AvatarManager.h
+++ b/Swiften/Avatars/AvatarManager.h
@@ -16,6 +16,7 @@
#include "Swiften/Elements/Presence.h"
#include "Swiften/Elements/VCard.h"
#include "Swiften/Elements/ErrorPayload.h"
+#include "Swiften/Avatars/CombinedAvatarProvider.h"
namespace Swift {
class MUCRegistry;
@@ -35,6 +36,7 @@ namespace Swift {
boost::signal<void (const JID&)> onAvatarChanged;
private:
+ CombinedAvatarProvider combinedAvatarProvider;
VCardUpdateAvatarManager* vcardUpdateAvatarManager;
AvatarStorage* avatarStorage;
};
diff --git a/Swiften/Avatars/CombinedAvatarProvider.cpp b/Swiften/Avatars/CombinedAvatarProvider.cpp
new file mode 100644
index 0000000..0e4a704
--- /dev/null
+++ b/Swiften/Avatars/CombinedAvatarProvider.cpp
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include "Swiften/Avatars/CombinedAvatarProvider.h"
+
+#include <algorithm>
+#include <boost/bind.hpp>
+
+namespace Swift {
+
+String CombinedAvatarProvider::getAvatarHash(const JID& jid) const {
+ for (size_t i = 0; i < providers.size(); ++i) {
+ String hash = providers[i]->getAvatarHash(jid);
+ if (!hash.isEmpty()) {
+ return hash;
+ }
+ }
+ return String();
+}
+
+void CombinedAvatarProvider::addProvider(AvatarProvider* provider) {
+ provider->onAvatarChanged.connect(reinterpret_cast<int>(this), boost::bind(&CombinedAvatarProvider::handleAvatarChanged, this, _1));
+ providers.push_back(provider);
+}
+
+void CombinedAvatarProvider::removeProvider(AvatarProvider* provider) {
+ std::vector<AvatarProvider*>::iterator i = std::remove(providers.begin(), providers.end(), provider);
+ for(std::vector<AvatarProvider*>::iterator j = i; j < providers.end(); ++j) {
+ provider->onAvatarChanged.disconnect(reinterpret_cast<int>(this));
+ }
+ providers.erase(i, providers.end());
+}
+
+void CombinedAvatarProvider::handleAvatarChanged(const JID& jid) {
+ String hash = getAvatarHash(jid);
+ std::map<JID, String>::iterator i = avatars.find(jid);
+ if (i != avatars.end()) {
+ if (i->second != hash) {
+ if (hash.isEmpty()) {
+ avatars.erase(i);
+ }
+ else {
+ avatars.insert(std::make_pair(jid, hash));
+ }
+ onAvatarChanged(jid);
+ }
+ }
+ else if (!hash.isEmpty()) {
+ avatars.insert(std::make_pair(jid, hash));
+ onAvatarChanged(jid);
+ }
+}
+
+}
diff --git a/Swiften/Avatars/CombinedAvatarProvider.h b/Swiften/Avatars/CombinedAvatarProvider.h
new file mode 100644
index 0000000..fbd6ce7
--- /dev/null
+++ b/Swiften/Avatars/CombinedAvatarProvider.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#pragma once
+
+#include <vector>
+#include <map>
+
+#include "Swiften/Avatars/AvatarProvider.h"
+#include "Swiften/JID/JID.h"
+
+namespace Swift {
+ class CombinedAvatarProvider : public AvatarProvider {
+ public:
+ virtual String getAvatarHash(const JID&) const;
+
+ void addProvider(AvatarProvider*);
+ void removeProvider(AvatarProvider*);
+
+ private:
+ void handleAvatarChanged(const JID&);
+
+ private:
+ std::vector<AvatarProvider*> providers;
+ std::map<JID, String> avatars;
+ };
+}
diff --git a/Swiften/Avatars/SConscript b/Swiften/Avatars/SConscript
index f9ce320..c5181e8 100644
--- a/Swiften/Avatars/SConscript
+++ b/Swiften/Avatars/SConscript
@@ -6,5 +6,6 @@ objects = swiften_env.StaticObject([
"AvatarManager.cpp",
"AvatarStorage.cpp",
"AvatarProvider.cpp",
+ "CombinedAvatarProvider.cpp",
])
swiften_env.Append(SWIFTEN_OBJECTS = [objects])
diff --git a/Swiften/Avatars/UnitTest/CombinedAvatarProviderTest.cpp b/Swiften/Avatars/UnitTest/CombinedAvatarProviderTest.cpp
new file mode 100644
index 0000000..ad3a0f1
--- /dev/null
+++ b/Swiften/Avatars/UnitTest/CombinedAvatarProviderTest.cpp
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2010 Remko Tronçon
+ * Licensed under the GNU General Public License v3.
+ * See Documentation/Licenses/GPLv3.txt for more information.
+ */
+
+#include <cppunit/extensions/HelperMacros.h>
+#include <cppunit/extensions/TestFactoryRegistry.h>
+#include <boost/bind.hpp>
+
+#include "Swiften/JID/JID.h"
+#include "Swiften/Base/String.h"
+#include "Swiften/Avatars/CombinedAvatarProvider.h"
+
+using namespace Swift;
+
+class CombinedAvatarProviderTest : public CppUnit::TestFixture {
+ CPPUNIT_TEST_SUITE(CombinedAvatarProviderTest);
+ CPPUNIT_TEST(testGetAvatarWithNoAvatarProviderReturnsEmpty);
+ CPPUNIT_TEST(testGetAvatarWithSingleAvatarProvider);
+ CPPUNIT_TEST(testGetAvatarWithMultipleAvatarProviderReturnsFirstAvatar);
+ CPPUNIT_TEST(testGetAvatarWithMultipleAvatarProviderAndFailingFirstProviderReturnsSecondAvatar);
+ CPPUNIT_TEST(testProviderUpdateTriggersChange);
+ CPPUNIT_TEST(testProviderUpdateWithoutChangeDoesNotTriggerChange);
+ CPPUNIT_TEST(testProviderSecondUpdateTriggersChange);
+ CPPUNIT_TEST(testProviderUpdateWithAvatarDisappearingTriggersChange);
+ CPPUNIT_TEST(testProviderUpdateAfterAvatarDisappearedTriggersChange);
+ CPPUNIT_TEST(testRemoveProviderDisconnectsUpdates);
+ CPPUNIT_TEST_SUITE_END();
+
+ public:
+ void setUp() {
+ avatarProvider1 = new DummyAvatarProvider();
+ avatarProvider2 = new DummyAvatarProvider();
+ user1 = JID("user1@bar.com/bla");
+ user2 = JID("user2@foo.com/baz");
+ avatarHash1 = "ABCDEFG";
+ avatarHash2 = "XYZU";
+ avatarHash3 = "IDGH";
+ }
+
+ void tearDown() {
+ delete avatarProvider1;
+ delete avatarProvider2;
+ }
+
+ void testGetAvatarWithNoAvatarProviderReturnsEmpty() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+
+ CPPUNIT_ASSERT(testling->getAvatarHash(user1).isEmpty());
+ }
+
+ void testGetAvatarWithSingleAvatarProvider() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ avatarProvider1->avatars[user1] = avatarHash1;
+ testling->addProvider(avatarProvider1);
+
+ CPPUNIT_ASSERT_EQUAL(avatarHash1, testling->getAvatarHash(user1));
+ }
+
+ void testGetAvatarWithMultipleAvatarProviderReturnsFirstAvatar() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider2->avatars[user1] = avatarHash2;
+ testling->addProvider(avatarProvider1);
+ testling->addProvider(avatarProvider2);
+
+ CPPUNIT_ASSERT_EQUAL(avatarHash1, testling->getAvatarHash(user1));
+ }
+
+ void testGetAvatarWithMultipleAvatarProviderAndFailingFirstProviderReturnsSecondAvatar() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ avatarProvider2->avatars[user1] = avatarHash2;
+ testling->addProvider(avatarProvider1);
+ testling->addProvider(avatarProvider2);
+
+ CPPUNIT_ASSERT_EQUAL(avatarHash2, testling->getAvatarHash(user1));
+ }
+
+ void testProviderUpdateTriggersChange() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ testling->addProvider(avatarProvider1);
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider1->onAvatarChanged(user1);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size()));
+ CPPUNIT_ASSERT_EQUAL(user1, changes[0]);
+ }
+
+ void testProviderUpdateWithoutChangeDoesNotTriggerChange() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ testling->addProvider(avatarProvider1);
+ testling->addProvider(avatarProvider2);
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider1->onAvatarChanged(user1);
+ changes.clear();
+
+ avatarProvider2->avatars[user1] = avatarHash2;
+ avatarProvider2->onAvatarChanged(user1);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size()));
+ }
+
+ void testProviderSecondUpdateTriggersChange() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ testling->addProvider(avatarProvider1);
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider1->onAvatarChanged(user1);
+ changes.clear();
+ avatarProvider1->avatars[user1] = avatarHash2;
+ avatarProvider1->onAvatarChanged(user1);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size()));
+ CPPUNIT_ASSERT_EQUAL(user1, changes[0]);
+ }
+
+
+ void testProviderUpdateWithAvatarDisappearingTriggersChange() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ testling->addProvider(avatarProvider1);
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider1->onAvatarChanged(user1);
+ changes.clear();
+ avatarProvider1->avatars.clear();
+ avatarProvider1->onAvatarChanged(user1);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size()));
+ CPPUNIT_ASSERT_EQUAL(user1, changes[0]);
+ }
+
+ void testProviderUpdateAfterAvatarDisappearedTriggersChange() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ testling->addProvider(avatarProvider1);
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider1->onAvatarChanged(user1);
+ avatarProvider1->avatars.clear();
+ avatarProvider1->onAvatarChanged(user1);
+ changes.clear();
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider1->onAvatarChanged(user1);
+
+ CPPUNIT_ASSERT_EQUAL(1, static_cast<int>(changes.size()));
+ CPPUNIT_ASSERT_EQUAL(user1, changes[0]);
+ }
+
+ void testRemoveProviderDisconnectsUpdates() {
+ std::auto_ptr<CombinedAvatarProvider> testling(createProvider());
+ testling->addProvider(avatarProvider1);
+ testling->addProvider(avatarProvider2);
+ testling->removeProvider(avatarProvider1);
+ avatarProvider1->avatars[user1] = avatarHash1;
+ avatarProvider2->avatars[user1] = avatarHash2;
+ avatarProvider1->onAvatarChanged(user1);
+
+ CPPUNIT_ASSERT_EQUAL(0, static_cast<int>(changes.size()));
+ }
+
+ private:
+ std::auto_ptr<CombinedAvatarProvider> createProvider() {
+ std::auto_ptr<CombinedAvatarProvider> result(new CombinedAvatarProvider());
+ result->onAvatarChanged.connect(boost::bind(&CombinedAvatarProviderTest::handleAvatarChanged, this, _1));
+ return result;
+ }
+
+ void handleAvatarChanged(const JID& jid) {
+ changes.push_back(jid);
+ }
+
+ private:
+ struct DummyAvatarProvider : public AvatarProvider {
+ String getAvatarHash(const JID& jid) const {
+ std::map<JID, String>::const_iterator i = avatars.find(jid);
+ if (i != avatars.end()) {
+ return i->second;
+ }
+ else {
+ return String();
+ }
+ }
+
+ std::map<JID, String> avatars;
+ };
+
+ DummyAvatarProvider* avatarProvider1;
+ DummyAvatarProvider* avatarProvider2;
+ JID user1;
+ JID user2;
+ String avatarHash1;
+ String avatarHash2;
+ String avatarHash3;
+ std::vector<JID> changes;
+};
+
+CPPUNIT_TEST_SUITE_REGISTRATION(CombinedAvatarProviderTest);
diff --git a/Swiften/Avatars/VCardUpdateAvatarManager.h b/Swiften/Avatars/VCardUpdateAvatarManager.h
index 2220906..8e11223 100644
--- a/Swiften/Avatars/VCardUpdateAvatarManager.h
+++ b/Swiften/Avatars/VCardUpdateAvatarManager.h
@@ -21,7 +21,7 @@ namespace Swift {
class StanzaChannel;
class VCardManager;
- class VCardUpdateAvatarManager : public AvatarProvider {
+ class VCardUpdateAvatarManager : public AvatarProvider, public boost::bsignals::trackable {
public:
VCardUpdateAvatarManager(VCardManager*, StanzaChannel*, AvatarStorage*, MUCRegistry* = NULL);
virtual ~VCardUpdateAvatarManager();
diff --git a/Swiften/SConscript b/Swiften/SConscript
index f19698e..588bd47 100644
--- a/Swiften/SConscript
+++ b/Swiften/SConscript
@@ -141,6 +141,7 @@ if env["SCONS_STAGE"] == "build" :
env.Append(UNITTEST_SOURCES = [
File("Application/UnitTest/ApplicationPathProviderTest.cpp"),
File("Avatars/UnitTest/VCardUpdateAvatarManagerTest.cpp"),
+ File("Avatars/UnitTest/CombinedAvatarProviderTest.cpp"),
File("Base/UnitTest/IDGeneratorTest.cpp"),
File("Base/UnitTest/StringTest.cpp"),
File("Base/UnitTest/ByteArrayTest.cpp"),