/* * Copyright (c) 2010-2016 Isode Limited. * All rights reserved. * See the COPYING file for more information. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(testProviderUpdateAfterGetDoesNotTriggerChange); CPPUNIT_TEST(testProviderUpdateBareJIDAfterGetFullJID); CPPUNIT_TEST(testRemoveProviderDisconnectsUpdates); CPPUNIT_TEST(testAddRemoveFallthrough); 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::shared_ptr testling(createProvider()); boost::optional hash = testling->getAvatarHash(user1); CPPUNIT_ASSERT(!hash); } void testGetAvatarWithSingleAvatarProvider() { std::shared_ptr testling(createProvider()); avatarProvider1->avatars[user1] = avatarHash1; testling->addProvider(avatarProvider1); boost::optional hash = testling->getAvatarHash(user1); CPPUNIT_ASSERT(hash); CPPUNIT_ASSERT_EQUAL(avatarHash1, *hash); } void testGetAvatarWithMultipleAvatarProviderReturnsFirstAvatar() { std::shared_ptr testling(createProvider()); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider2->avatars[user1] = avatarHash2; testling->addProvider(avatarProvider1); testling->addProvider(avatarProvider2); boost::optional hash = testling->getAvatarHash(user1); CPPUNIT_ASSERT(hash); CPPUNIT_ASSERT_EQUAL(avatarHash1, *hash); } void testGetAvatarWithMultipleAvatarProviderAndFailingFirstProviderReturnsSecondAvatar() { std::shared_ptr testling(createProvider()); avatarProvider2->avatars[user1] = avatarHash2; testling->addProvider(avatarProvider1); testling->addProvider(avatarProvider2); boost::optional hash = testling->getAvatarHash(user1); CPPUNIT_ASSERT(hash); CPPUNIT_ASSERT_EQUAL(avatarHash2, *hash); } void testProviderUpdateTriggersChange() { std::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(1, static_cast(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateWithoutChangeDoesNotTriggerChange() { std::shared_ptr 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(changes.size())); } void testProviderSecondUpdateTriggersChange() { std::shared_ptr 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(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateWithAvatarDisappearingTriggersChange() { std::shared_ptr 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(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateAfterAvatarDisappearedTriggersChange() { std::shared_ptr 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(changes.size())); CPPUNIT_ASSERT_EQUAL(user1, changes[0]); } void testProviderUpdateAfterGetDoesNotTriggerChange() { std::shared_ptr testling(createProvider()); testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1] = avatarHash1; testling->getAvatarHash(user1); avatarProvider1->onAvatarChanged(user1); CPPUNIT_ASSERT_EQUAL(0, static_cast(changes.size())); } void testRemoveProviderDisconnectsUpdates() { std::shared_ptr 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(changes.size())); } void testProviderUpdateBareJIDAfterGetFullJID() { std::shared_ptr testling(createProvider()); avatarProvider1->useBare = true; testling->addProvider(avatarProvider1); avatarProvider1->avatars[user1.toBare()] = avatarHash1; testling->getAvatarHash(user1); avatarProvider1->avatars[user1.toBare()] = avatarHash2; avatarProvider1->onAvatarChanged(user1.toBare()); boost::optional hash = testling->getAvatarHash(user1); CPPUNIT_ASSERT(hash); CPPUNIT_ASSERT_EQUAL(avatarHash2, *hash); } void testAddRemoveFallthrough() { JID ownJID = JID("user0@own.com/res"); JID user1 = JID("user1@bar.com/bla"); std::shared_ptr crypto = std::shared_ptr(PlatformCryptoProvider::create()); DummyStanzaChannel* stanzaChannel = new DummyStanzaChannel(); stanzaChannel->setAvailable(true); IQRouter* iqRouter = new IQRouter(stanzaChannel); DummyMUCRegistry* mucRegistry = new DummyMUCRegistry(); AvatarMemoryStorage* avatarStorage = new AvatarMemoryStorage(); VCardMemoryStorage* vcardStorage = new VCardMemoryStorage(crypto.get()); VCardManager* vcardManager = new VCardManager(ownJID, iqRouter, vcardStorage); std::shared_ptr updateManager(new VCardUpdateAvatarManager(vcardManager, stanzaChannel, avatarStorage, crypto.get(), mucRegistry)); updateManager->onAvatarChanged.connect(boost::bind(&CombinedAvatarProviderTest::handleAvatarChanged, this, _1)); std::shared_ptr manager(new VCardAvatarManager(vcardManager, avatarStorage, crypto.get(), mucRegistry)); manager->onAvatarChanged.connect(boost::bind(&CombinedAvatarProviderTest::handleAvatarChanged, this, _1)); std::shared_ptr offlineManager(new OfflineAvatarManager(avatarStorage)); offlineManager->onAvatarChanged.connect(boost::bind(&CombinedAvatarProviderTest::handleAvatarChanged, this, _1)); std::shared_ptr testling(createProvider()); avatarProvider1->useBare = true; testling->addProvider(updateManager.get()); testling->addProvider(manager.get()); testling->addProvider(offlineManager.get()); /* place an avatar in the cache, check that it reads back OK */ CPPUNIT_ASSERT_EQUAL(size_t(0), changes.size()); ByteArray avatar1 = createByteArray("abcdefg"); std::string avatar1Hash = Hexify::hexify(crypto->getSHA1Hash(avatar1)); VCard::ref vcard1(new VCard()); vcard1->setPhoto(avatar1); vcardStorage->setVCard(user1.toBare(), vcard1); boost::optional testHash = testling->getAvatarHash(user1.toBare()); CPPUNIT_ASSERT(testHash); CPPUNIT_ASSERT_EQUAL(avatar1Hash, *testHash); VCard::ref storedVCard = vcardStorage->getVCard(user1.toBare()); CPPUNIT_ASSERT(!!storedVCard); testHash = Hexify::hexify(crypto->getSHA1Hash(storedVCard->getPhoto())); CPPUNIT_ASSERT_EQUAL(avatar1Hash, *testHash); /* change the avatar by sending an VCard IQ */ vcardManager->requestVCard(user1.toBare()); CPPUNIT_ASSERT_EQUAL(size_t(1), stanzaChannel->sentStanzas.size()); IQ::ref request = std::dynamic_pointer_cast(stanzaChannel->sentStanzas.back()); VCard::ref payload = request->getPayload(); CPPUNIT_ASSERT(!!payload); stanzaChannel->sentStanzas.pop_back(); ByteArray avatar2 = createByteArray("1234567"); std::string avatar2Hash = Hexify::hexify(crypto->getSHA1Hash(avatar2)); VCard::ref vcard2(new VCard()); vcard2->setPhoto(avatar2); IQ::ref reply = std::make_shared(); reply->setTo(request->getFrom()); reply->setFrom(request->getTo()); reply->setID(request->getID()); reply->addPayload(vcard2); reply->setType(IQ::Result); stanzaChannel->onIQReceived(reply); /* check that we changed the avatar successfully and that we were notified about the changes */ testHash = testling->getAvatarHash(user1.toBare()); CPPUNIT_ASSERT(testHash); CPPUNIT_ASSERT_EQUAL(avatar2Hash, *testHash); CPPUNIT_ASSERT_EQUAL(size_t(3), changes.size()); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[0]); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[1]); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[2]); changes.clear(); storedVCard = vcardStorage->getVCard(user1.toBare()); CPPUNIT_ASSERT(!!storedVCard); testHash = Hexify::hexify(crypto->getSHA1Hash(storedVCard->getPhoto())); CPPUNIT_ASSERT_EQUAL(avatar2Hash, *testHash); /* change the avatar to the empty avatar */ vcardManager->requestVCard(user1.toBare()); CPPUNIT_ASSERT_EQUAL(size_t(1), stanzaChannel->sentStanzas.size()); request = std::dynamic_pointer_cast(stanzaChannel->sentStanzas.back()); payload = request->getPayload(); CPPUNIT_ASSERT(!!payload); stanzaChannel->sentStanzas.pop_back(); VCard::ref vcard3(new VCard()); reply = std::make_shared(); reply->setTo(request->getFrom()); reply->setFrom(request->getTo()); reply->setID(request->getID()); reply->addPayload(vcard3); reply->setType(IQ::Result); stanzaChannel->onIQReceived(reply); /* check that we changed the avatar successfully */ testHash = testling->getAvatarHash(user1.toBare()); CPPUNIT_ASSERT(testHash); CPPUNIT_ASSERT_EQUAL(std::string(""), *testHash); CPPUNIT_ASSERT_EQUAL(size_t(3), changes.size()); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[0]); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[1]); CPPUNIT_ASSERT_EQUAL(user1.toBare(), changes[2]); changes.clear(); storedVCard = vcardStorage->getVCard(user1.toBare()); CPPUNIT_ASSERT(!!storedVCard); CPPUNIT_ASSERT(!storedVCard->getPhoto().size()); delete vcardManager; delete vcardStorage; delete mucRegistry; delete iqRouter; delete stanzaChannel; } private: std::shared_ptr createProvider() { std::shared_ptr 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 { DummyAvatarProvider() : useBare(false) { } boost::optional getAvatarHash(const JID& jid) const { JID actualJID = useBare ? jid.toBare() : jid; std::map::const_iterator i = avatars.find(actualJID); if (i != avatars.end()) { return i->second; } else { return boost::optional(); } } bool useBare; std::map avatars; }; struct DummyMUCRegistry : public MUCRegistry { bool isMUC(const JID& jid) const { return std::find(mucs_.begin(), mucs_.end(), jid) != mucs_.end(); } std::vector mucs_; }; DummyAvatarProvider* avatarProvider1; DummyAvatarProvider* avatarProvider2; JID user1; JID user2; std::string avatarHash1; std::string avatarHash2; std::string avatarHash3; std::vector changes; }; CPPUNIT_TEST_SUITE_REGISTRATION(CombinedAvatarProviderTest);