From 904db4e398210192093b688ebf1ad66fb017b6d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Remko=20Tron=C3=A7on?= <git@el-tramo.be> Date: Thu, 9 Sep 2010 15:20:18 +0200 Subject: Added CombinedAvatarProvider 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"), -- cgit v0.10.2-6-g49f6