/* * Copyright (c) 2010-2018 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Swift; class DummyNotifier : public Notifier { public: virtual void showMessage( Type type, const std::string& subject, const std::string& description, const boost::filesystem::path& picture, boost::function callback) { notifications.push_back({type, subject, description, picture, callback}); } /** Remove any pending callbacks. */ virtual void purgeCallbacks() { } public: struct Notification { Type type; std::string subject; std::string description; boost::filesystem::path picture; boost::function callback; }; public: std::vector notifications; }; class ExtendedChatsManager : public ChatsManager { public: ExtendedChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRouter, EventController* eventController, ChatWindowFactory* chatWindowFactory, JoinMUCWindowFactory* joinMUCWindowFactory, NickResolver* nickResolver, PresenceOracle* presenceOracle, PresenceSender* presenceSender, UIEventStream* uiEventStream, ChatListWindowFactory* chatListWindowFactory, bool useDelayForLatency, TimerFactory* timerFactory, MUCRegistry* mucRegistry, EntityCapsProvider* entityCapsProvider, MUCManager* mucManager, MUCSearchWindowFactory* mucSearchWindowFactory, ProfileSettingsProvider* profileSettings, FileTransferOverview* ftOverview, XMPPRoster* roster, bool eagleMode, SettingsProvider* settings, HistoryController* historyController_, WhiteboardManager* whiteboardManager, HighlightManager* highlightManager, ClientBlockListManager* clientBlockListManager, const std::map& emoticons, VCardManager* vcardManager, Chattables& chattables) : ChatsManager(jid, stanzaChannel, iqRouter, eventController, chatWindowFactory, joinMUCWindowFactory, nickResolver, presenceOracle, presenceSender, uiEventStream, chatListWindowFactory, useDelayForLatency, timerFactory, mucRegistry, entityCapsProvider, mucManager, mucSearchWindowFactory, profileSettings, ftOverview, roster, eagleMode, settings, historyController_, whiteboardManager, highlightManager, clientBlockListManager, emoticons, vcardManager, chattables) { } MUCBookmarkManager* getBookmarkManager() { return mucBookmarkManager_; } }; class ChatsManagerTest : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(ChatsManagerTest); CPPUNIT_TEST(testFirstOpenWindowIncoming); CPPUNIT_TEST(testSecondOpenWindowIncoming); CPPUNIT_TEST(testFirstOpenWindowOutgoing); CPPUNIT_TEST(testFirstOpenWindowBareToFull); CPPUNIT_TEST(testSecondWindow); CPPUNIT_TEST(testUnbindRebind); CPPUNIT_TEST(testNoDuplicateUnbind); CPPUNIT_TEST(testThreeMUCWindows); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnRemoveFromRoster); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnAddToRoster); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth); CPPUNIT_TEST(testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom); CPPUNIT_TEST(testChatControllerFullJIDBindingOnMessageAndNotReceipt); CPPUNIT_TEST(testChatControllerFullJIDBindingOnTypingAndNotActive); CPPUNIT_TEST(testLocalMUCServiceDiscoveryResetOnDisconnect); CPPUNIT_TEST(testPresenceChangeDoesNotReplaceMUCInvite); CPPUNIT_TEST(testNotSplittingMUCPresenceJoinLeaveLinesOnChatStateNotifications); // MUC PM Tests CPPUNIT_TEST(testChatControllerPMPresenceHandling); CPPUNIT_TEST(testChatControllerMucPmUnavailableErrorHandling); // Highlighting tests CPPUNIT_TEST(testChatControllerHighlightingNotificationTesting); CPPUNIT_TEST(testChatControllerHighlightingNotificationDeduplicateSounds); CPPUNIT_TEST(testChatControllerHighlightingNotificationKeyword); CPPUNIT_TEST(testChatControllerMeMessageHandling); CPPUNIT_TEST(testRestartingMUCComponentCrash); CPPUNIT_TEST(testChatControllerMeMessageHandlingInMUC); // Carbons tests CPPUNIT_TEST(testCarbonsForwardedIncomingMessageToSecondResource); CPPUNIT_TEST(testCarbonsForwardedOutgoingMessageFromSecondResource); CPPUNIT_TEST(testCarbonsForwardedIncomingDuplicates); // Message correction tests CPPUNIT_TEST(testChatControllerMessageCorrectionCorrectReplaceID); CPPUNIT_TEST(testChatControllerMessageCorrectionIncorrectReplaceID); CPPUNIT_TEST(testChatControllerMessageCorrectionReplaceBySameResource); CPPUNIT_TEST(testChatControllerMessageCorrectionReplaceByOtherResource); CPPUNIT_TEST(testMUCControllerMessageCorrectionNoIDMatchRequired); // Chat window title tests CPPUNIT_TEST(testImpromptuChatTitle); CPPUNIT_TEST(testImpromptuChatWindowTitle); CPPUNIT_TEST(testStandardMUCChatWindowTitle); // Bookmark tests CPPUNIT_TEST(testReceivingBookmarksWithDomainJID); CPPUNIT_TEST(testReceivingBookmarksWithBareJID); CPPUNIT_TEST(testReceivingBookmarksWithFullJID); CPPUNIT_TEST(testAutoJoinBookmarksAndChattables); CPPUNIT_TEST(testJoinNoAutojoinBookmark); CPPUNIT_TEST(testJoinAndBookmarkMUC); CPPUNIT_TEST(testReceivingNoBookmarks); CPPUNIT_TEST(testReceivingNullBookmarks); CPPUNIT_TEST(testReceivingBookmarksError); CPPUNIT_TEST_SUITE_END(); public: void setUp() { mocks_ = new MockRepository(); notifier_ = std::make_unique(); jid_ = JID("test@test.com/resource"); stanzaChannel_ = new DummyStanzaChannel(); iqRouter_ = new IQRouter(stanzaChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock(); joinMUCWindowFactory_ = mocks_->InterfaceMock(); xmppRoster_ = new XMPPRosterImpl(); mucRegistry_ = new MUCRegistry(); nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, nullptr, mucRegistry_); presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_); serverDiscoInfo_ = std::make_shared(); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); mucManager_ = new MUCManager(stanzaChannel_, iqRouter_, directedPresenceSender_, mucRegistry_); uiEventStream_ = new UIEventStream(); entityCapsProvider_ = new DummyEntityCapsProvider(); chatListWindowFactory_ = mocks_->InterfaceMock(); mucSearchWindowFactory_ = mocks_->InterfaceMock(); settings_ = new DummySettingsProvider(); profileSettings_ = new ProfileSettingsProvider("a", settings_); chatListWindow_ = new MockChatListWindow(); ftManager_ = new DummyFileTransferManager(); ftOverview_ = new FileTransferOverview(ftManager_); avatarManager_ = new NullAvatarManager(); eventNotifier_ = new EventNotifier(eventController_, notifier_.get(), avatarManager_, nickResolver_); wbSessionManager_ = new WhiteboardSessionManager(iqRouter_, stanzaChannel_, presenceOracle_, entityCapsProvider_); wbManager_ = new WhiteboardManager(whiteboardWindowFactory_, uiEventStream_, nickResolver_, wbSessionManager_); highlightManager_ = new HighlightManager(settings_); highlightManager_->resetToDefaultConfiguration(); handledHighlightActions_ = 0; soundsPlayed_.clear(); highlightManager_->onHighlight.connect(boost::bind(&ChatsManagerTest::handleHighlightAction, this, _1)); crypto_ = PlatformCryptoProvider::create(); vcardStorage_ = new VCardMemoryStorage(crypto_); vcardManager_ = new VCardManager(jid_, iqRouter_, vcardStorage_); mocks_->ExpectCall(chatListWindowFactory_, ChatListWindowFactory::createChatListWindow).With(uiEventStream_).Return(chatListWindow_); clientBlockListManager_ = new ClientBlockListManager(iqRouter_); timerFactory_ = new DummyTimerFactory(); chattables_ = std::make_unique(); manager_ = new ExtendedChatsManager(jid_, stanzaChannel_, iqRouter_, eventController_, chatWindowFactory_, joinMUCWindowFactory_, nickResolver_, presenceOracle_, directedPresenceSender_, uiEventStream_, chatListWindowFactory_, true, timerFactory_, mucRegistry_, entityCapsProvider_, mucManager_, mucSearchWindowFactory_, profileSettings_, ftOverview_, xmppRoster_, false, settings_, nullptr, wbManager_, highlightManager_, clientBlockListManager_, emoticons_, vcardManager_, *chattables_); manager_->setAvatarManager(avatarManager_); } void tearDown() { delete highlightManager_; delete profileSettings_; delete eventNotifier_; delete avatarManager_; delete manager_; delete timerFactory_; delete clientBlockListManager_; delete vcardManager_; delete vcardStorage_; delete crypto_; delete ftOverview_; delete ftManager_; delete wbSessionManager_; delete wbManager_; delete directedPresenceSender_; delete presenceSender_; delete presenceOracle_; delete nickResolver_; delete mucRegistry_; delete iqRouter_; delete stanzaChannel_; delete eventController_; delete uiEventStream_; delete mucManager_; delete xmppRoster_; delete entityCapsProvider_; delete chatListWindow_; delete mocks_; delete settings_; } void testFirstOpenWindowIncoming() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This is a legible message. >HEH@)oeueu"); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); } void testSecondOpenWindowIncoming() { JID messageJID1("testling@test.com/resource1"); MockChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1, uiEventStream_).Return(window1); std::shared_ptr message1(new Message()); message1->setFrom(messageJID1); std::string body1("This is a legible message. >HEH@)oeueu"); message1->setBody(body1); manager_->handleIncomingMessage(message1); CPPUNIT_ASSERT_EQUAL(body1, MockChatWindow::bodyFromMessage(window1->lastAddedMessage_)); JID messageJID2("testling@test.com/resource2"); std::shared_ptr message2(new Message()); message2->setFrom(messageJID2); std::string body2("This is a legible message. .cmaulm.chul"); message2->setBody(body2); manager_->handleIncomingMessage(message2); CPPUNIT_ASSERT_EQUAL(body2, MockChatWindow::bodyFromMessage(window1->lastAddedMessage_)); } void testFirstOpenWindowOutgoing() { std::string messageJIDString("testling@test.com"); ChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString), uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(JID(messageJIDString))); } void testFirstOpenWindowBareToFull() { std::string bareJIDString("testling@test.com"); std::string fullJIDString("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString), uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(JID(bareJIDString))); std::shared_ptr message(new Message()); message->setFrom(JID(fullJIDString)); std::string body("This is a legible message. mjuga3089gm8G(*>M)@*("); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); } void testSecondWindow() { std::string messageJIDString1("testling1@test.com"); ChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1), uiEventStream_).Return(window1); uiEventStream_->send(std::make_shared(JID(messageJIDString1))); std::string messageJIDString2("testling2@test.com"); ChatWindow* window2 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2), uiEventStream_).Return(window2); uiEventStream_->send(std::make_shared(JID(messageJIDString2))); } /** Complete cycle. Create unbound window. Bind it. Unbind it. Rebind it. */ void testUnbindRebind() { std::string bareJIDString("testling@test.com"); std::string fullJIDString1("testling@test.com/resource1"); std::string fullJIDString2("testling@test.com/resource2"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString), uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(JID(bareJIDString))); std::shared_ptr message1(new Message()); message1->setFrom(JID(fullJIDString1)); std::string messageBody1("This is a legible message."); message1->setBody(messageBody1); manager_->handleIncomingMessage(message1); CPPUNIT_ASSERT_EQUAL(messageBody1, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); std::shared_ptr jid1Online(new Presence()); jid1Online->setFrom(JID(fullJIDString1)); std::shared_ptr jid1Offline(new Presence()); jid1Offline->setFrom(JID(fullJIDString1)); jid1Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid1Offline); std::shared_ptr message2(new Message()); message2->setFrom(JID(fullJIDString2)); std::string messageBody2("This is another legible message."); message2->setBody(messageBody2); manager_->handleIncomingMessage(message2); CPPUNIT_ASSERT_EQUAL(messageBody2, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); } /** * Test that MUC PMs get opened in the right windows */ void testThreeMUCWindows() { JID muc("testling@test.com"); ChatWindow* mucWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(muc, uiEventStream_).Return(mucWindow); uiEventStream_->send(std::make_shared(muc, std::string("nick"))); std::string messageJIDString1("testling@test.com/1"); ChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1), uiEventStream_).Return(window1); uiEventStream_->send(std::make_shared(JID(messageJIDString1))); std::string messageJIDString2("testling@test.com/2"); ChatWindow* window2 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2), uiEventStream_).Return(window2); uiEventStream_->send(std::make_shared(JID(messageJIDString2))); std::string messageJIDString3("testling@test.com/3"); ChatWindow* window3 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString3), uiEventStream_).Return(window3); uiEventStream_->send(std::make_shared(JID(messageJIDString3))); /* Refetch an earlier window */ /* We do not expect a new window to be created */ uiEventStream_->send(std::make_shared(JID(messageJIDString1))); } /** Test that a second window isn't unbound where there's already an unbound one. Bind 1 Bind 2 Unbind 1 Unbind 2 (but it doesn't) Sent to bound 2 Rebind 1 */ void testNoDuplicateUnbind() { JID messageJID1("testling@test.com/resource1"); MockChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1, uiEventStream_).Return(window1); std::shared_ptr message1(new Message()); message1->setFrom(messageJID1); message1->setBody("This is a legible message1."); manager_->handleIncomingMessage(message1); JID messageJID2("testling@test.com/resource2"); std::shared_ptr message2(new Message()); message2->setFrom(messageJID2); message2->setBody("This is a legible message2."); manager_->handleIncomingMessage(message2); std::shared_ptr jid1Online(new Presence()); jid1Online->setFrom(JID(messageJID1)); std::shared_ptr jid1Offline(new Presence()); jid1Offline->setFrom(JID(messageJID1)); jid1Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid1Offline); std::shared_ptr jid2Online(new Presence()); jid2Online->setFrom(JID(messageJID2)); std::shared_ptr jid2Offline(new Presence()); jid2Offline->setFrom(JID(messageJID2)); jid2Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid2Offline); JID messageJID3("testling@test.com/resource3"); std::shared_ptr message3(new Message()); message3->setFrom(messageJID3); std::string body3("This is a legible message3."); message3->setBody(body3); manager_->handleIncomingMessage(message3); CPPUNIT_ASSERT_EQUAL(body3, MockChatWindow::bodyFromMessage(window1->lastAddedMessage_)); std::shared_ptr message2b(new Message()); message2b->setFrom(messageJID2); std::string body2b("This is a legible message2b."); message2b->setBody(body2b); manager_->handleIncomingMessage(message2b); CPPUNIT_ASSERT_EQUAL(body2b, MockChatWindow::bodyFromMessage(window1->lastAddedMessage_)); } /** * Test that ChatController doesn't send receipts anymore after removal of the contact from the roster. */ void testChatControllerPresenceAccessUpdatedOnRemoveFromRoster() { JID messageJID("testling@test.com/resource1"); xmppRoster_->addContact(messageJID, "foo", std::vector(), RosterItemPayload::Both); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); std::shared_ptr message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex(1); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType()); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload() != nullptr); xmppRoster_->removeContact(messageJID); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType()); } /** * Test that ChatController sends receipts after the contact has been added to the roster. */ void testChatControllerPresenceAccessUpdatedOnAddToRoster() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); std::shared_ptr message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(0), stanzaChannel_->countSentStanzaOfType()); xmppRoster_->addContact(messageJID, "foo", std::vector(), RosterItemPayload::Both); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType()); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex(1); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload() != nullptr); } /** * Test that ChatController sends receipts if requested after change from subscription state To to subscription state Both. */ void testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToBoth() { testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::To, RosterItemPayload::Both); } /** * Test that ChatController sends receipts if requested after change from subscription state To to subscription state From. */ void testChatControllerPresenceAccessUpdatedOnSubscriptionChangeToFrom() { testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::To, RosterItemPayload::From); } void testChatControllerFullJIDBindingOnMessageAndNotReceipt() { JID ownJID("test@test.com/resource"); JID sender("foo@test.com"); std::vector senderResource; senderResource.push_back(sender.withResource("resourceA")); senderResource.push_back(sender.withResource("resourceB")); // We support delivery receipts. settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); // Open chat window to a sender. MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(sender, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(sender)); for (const auto& senderJID : senderResource) { // The sender supports delivery receipts. DiscoInfo::ref disco = std::make_shared(); disco->addFeature(DiscoInfo::MessageDeliveryReceiptsFeature); entityCapsProvider_->caps[senderJID] = disco; // The sender is online. Presence::ref senderPresence = std::make_shared(); senderPresence->setFrom(senderJID); senderPresence->setTo(ownJID); stanzaChannel_->onPresenceReceived(senderPresence); entityCapsProvider_->onCapsChanged(senderJID); } // Send first message. window->onSendMessageRequest("hello there", false); // A bare message is send because no resources is bound. CPPUNIT_ASSERT_EQUAL(sender, stanzaChannel_->getStanzaAtIndex(1)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(1)->getPayload()); // Two resources respond with message receipts. for (const auto& senderJID : senderResource) { Message::ref receiptReply = std::make_shared(); receiptReply->setFrom(senderJID); receiptReply->setTo(ownJID); std::shared_ptr receipt = std::make_shared(); receipt->setReceivedID(stanzaChannel_->getStanzaAtIndex(1)->getID()); receiptReply->addPayload(receipt); manager_->handleIncomingMessage(receiptReply); } // Send second message. window->onSendMessageRequest("how are you?", false); // A bare message is send because no resources is bound. CPPUNIT_ASSERT_EQUAL(sender, stanzaChannel_->getStanzaAtIndex(1)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(1)->getPayload()); // Two resources respond with message receipts. for (const auto& senderJID : senderResource) { Message::ref receiptReply = std::make_shared(); receiptReply->setFrom(senderJID); receiptReply->setTo(ownJID); std::shared_ptr receipt = std::make_shared(); receipt->setReceivedID(stanzaChannel_->getStanzaAtIndex(1)->getID()); receiptReply->addPayload(receipt); manager_->handleIncomingMessage(receiptReply); } // Reply with a message including a body text. Message::ref reply = std::make_shared(); reply->setFrom(senderResource[0]); reply->setTo(ownJID); reply->setBody("fine."); manager_->handleIncomingMessage(reply); // Send third message. window->onSendMessageRequest("great to hear.", false); // The chat session is bound to the full JID of the first resource. CPPUNIT_ASSERT_EQUAL(senderResource[0], stanzaChannel_->getStanzaAtIndex(3)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(2)->getPayload()); // Receive random receipt from second sender resource. reply = std::make_shared(); reply->setFrom(senderResource[1]); reply->setTo(ownJID); std::shared_ptr receipt = std::make_shared(); receipt->setReceivedID(stanzaChannel_->getStanzaAtIndex(2)->getID()); reply->addPayload(receipt); manager_->handleIncomingMessage(reply); // Send forth message. window->onSendMessageRequest("what else is new?", false); // The chat session is bound to the full JID of the first resource. CPPUNIT_ASSERT_EQUAL(senderResource[0], stanzaChannel_->getStanzaAtIndex(3)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(3)->getPayload()); // Reply with a message including a body text from second resource. reply = std::make_shared(); reply->setFrom(senderResource[1]); reply->setTo(ownJID); reply->setBody("nothing."); manager_->handleIncomingMessage(reply); // Send fifth message. window->onSendMessageRequest("okay", false); // The chat session is now bound to the full JID of the second resource. CPPUNIT_ASSERT_EQUAL(senderResource[1], stanzaChannel_->getStanzaAtIndex(5)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(5)->getPayload()); } void testChatControllerFullJIDBindingOnTypingAndNotActive() { JID ownJID("test@test.com/resource"); JID sender("foo@test.com"); std::vector senderResource; senderResource.push_back(sender.withResource("resourceA")); senderResource.push_back(sender.withResource("resourceB")); // We support delivery receipts. settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); // Open chat window to a sender. MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(sender, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(sender)); for (const auto& senderJID : senderResource) { // The sender supports delivery receipts. DiscoInfo::ref disco = std::make_shared(); disco->addFeature(DiscoInfo::MessageDeliveryReceiptsFeature); entityCapsProvider_->caps[senderJID] = disco; // The sender is online. Presence::ref senderPresence = std::make_shared(); senderPresence->setFrom(senderJID); senderPresence->setTo(ownJID); stanzaChannel_->onPresenceReceived(senderPresence); entityCapsProvider_->onCapsChanged(senderJID); } // Send first message. window->onSendMessageRequest("hello there", false); // A bare message is send because no resources is bound. CPPUNIT_ASSERT_EQUAL(sender, stanzaChannel_->getStanzaAtIndex(1)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(1)->getPayload()); // Two resources respond with message receipts. for (const auto& senderJID : senderResource) { Message::ref reply = std::make_shared(); reply->setFrom(senderJID); reply->setTo(ownJID); std::shared_ptr csn = std::make_shared(); csn->setChatState(ChatState::Active); reply->addPayload(csn); manager_->handleIncomingMessage(reply); } // Send second message. window->onSendMessageRequest("how are you?", false); // A bare message is send because no resources is bound. CPPUNIT_ASSERT_EQUAL(sender, stanzaChannel_->getStanzaAtIndex(1)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(1)->getPayload()); // Two resources respond with message receipts. for (const auto& senderJID : senderResource) { Message::ref receiptReply = std::make_shared(); receiptReply->setFrom(senderJID); receiptReply->setTo(ownJID); std::shared_ptr receipt = std::make_shared(); receipt->setReceivedID(stanzaChannel_->getStanzaAtIndex(1)->getID()); receiptReply->addPayload(receipt); manager_->handleIncomingMessage(receiptReply); } // Reply with a message including a CSN. Message::ref reply = std::make_shared(); reply->setFrom(senderResource[0]); reply->setTo(ownJID); std::shared_ptr csn = std::make_shared(); csn->setChatState(ChatState::Composing); reply->addPayload(csn); manager_->handleIncomingMessage(reply); // Send third message. window->onSendMessageRequest("great to hear.", false); // The chat session is now bound to the full JID of the first resource due to its recent composing message. CPPUNIT_ASSERT_EQUAL(senderResource[0], stanzaChannel_->getStanzaAtIndex(3)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(3)->getPayload()); // Reply with a message including a CSN from the other resource. reply = std::make_shared(); reply->setFrom(senderResource[1]); reply->setTo(ownJID); csn = std::make_shared(); csn->setChatState(ChatState::Composing); reply->addPayload(csn); manager_->handleIncomingMessage(reply); // Send third message. window->onSendMessageRequest("ping.", false); // The chat session is now bound to the full JID of the second resource due to its recent composing message. CPPUNIT_ASSERT_EQUAL(senderResource[1], stanzaChannel_->getStanzaAtIndex(4)->getTo()); CPPUNIT_ASSERT(stanzaChannel_->getStanzaAtIndex(4)->getPayload()); } void testChatControllerPMPresenceHandling() { JID participantA = JID("test@rooms.test.com/participantA"); JID participantB = JID("test@rooms.test.com/participantB"); mucRegistry_->addMUC("test@rooms.test.com"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(participantA, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(JID(participantA))); Presence::ref presence = Presence::create(); presence->setFrom(participantA); presence->setShow(StatusShow::Online); stanzaChannel_->onPresenceReceived(presence); CPPUNIT_ASSERT_EQUAL(std::string("participantA has become available."), MockChatWindow::bodyFromMessage(window->lastAddedPresence_)); presence = Presence::create(); presence->setFrom(participantB); presence->setShow(StatusShow::Away); stanzaChannel_->onPresenceReceived(presence); presence = Presence::create(); presence->setFrom(participantA); presence->setShow(StatusShow::None); presence->setType(Presence::Unavailable); stanzaChannel_->onPresenceReceived(presence); CPPUNIT_ASSERT_EQUAL(std::string("participantA has gone offline."), MockChatWindow::bodyFromMessage(window->lastReplacedLastMessage_)); } void testChatControllerMucPmUnavailableErrorHandling() { auto mucJID = JID("test@rooms.test.com"); auto participantA = mucJID.withResource("participantA"); auto participantB = mucJID.withResource("participantB"); auto mucWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(mucWindow); uiEventStream_->send(std::make_shared(mucJID, participantB.getResource())); CPPUNIT_ASSERT_EQUAL(true, mucWindow->mucType_.is_initialized()); auto window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(participantA, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(participantA)); CPPUNIT_ASSERT_EQUAL(false, window->mucType_.is_initialized()); Presence::ref presence = Presence::create(); presence->setFrom(participantA); presence->setShow(StatusShow::Online); stanzaChannel_->onPresenceReceived(presence); CPPUNIT_ASSERT_EQUAL(std::string("participantA has become available."), MockChatWindow::bodyFromMessage(window->lastAddedPresence_)); // send message to participantA auto messageBody = std::string("message body to send"); window->onSendMessageRequest(messageBody, false); auto sendMessageStanza = stanzaChannel_->getStanzaAtIndex(3); CPPUNIT_ASSERT_EQUAL(messageBody, *sendMessageStanza->getBody()); // receive reply with error auto messageErrorReply = std::make_shared(); messageErrorReply->setID(stanzaChannel_->getNewIQID()); messageErrorReply->setType(Message::Error); messageErrorReply->setFrom(participantA); messageErrorReply->setTo(jid_); messageErrorReply->addPayload(std::make_shared(ErrorPayload::ItemNotFound, ErrorPayload::Cancel, "Recipient not in room")); auto lastMUCWindowErrorMessageBeforeError = MockChatWindow::bodyFromMessage(mucWindow->lastAddedErrorMessage_); manager_->handleIncomingMessage(messageErrorReply); // assert that error is not routed to MUC window CPPUNIT_ASSERT_EQUAL(lastMUCWindowErrorMessageBeforeError, MockChatWindow::bodyFromMessage(mucWindow->lastAddedErrorMessage_)); // assert that error is routed to PM CPPUNIT_ASSERT_EQUAL(std::string("This user could not be found in the room."), MockChatWindow::bodyFromMessage(window->lastAddedErrorMessage_)); } void testLocalMUCServiceDiscoveryResetOnDisconnect() { JID ownJID("test@test.com/resource"); JID sender("foo@test.com"); manager_->setOnline(true); // Open chat window to a sender. MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(sender, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(sender)); CPPUNIT_ASSERT_EQUAL(false, window->impromptuMUCSupported_); std::shared_ptr infoRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[1]); std::shared_ptr infoResponse = IQ::createResult(infoRequest->getFrom(), infoRequest->getTo(), infoRequest->getID()); DiscoInfo info; info.addIdentity(DiscoInfo::Identity("Shakespearean Chat Service", "conference", "text")); info.addFeature("http://jabber.org/protocol/muc"); infoResponse->addPayload(std::make_shared(info)); stanzaChannel_->onIQReceived(infoResponse); CPPUNIT_ASSERT_EQUAL(true, window->impromptuMUCSupported_); manager_->setOnline(false); CPPUNIT_ASSERT_EQUAL(false, window->impromptuMUCSupported_); } void testhelperChatControllerPresenceAccessUpdatedOnSubscriptionChangeReceiptsAllowed(RosterItemPayload::Subscription from, RosterItemPayload::Subscription to) { JID messageJID("testling@test.com/resource1"); xmppRoster_->addContact(messageJID, "foo", std::vector(), from); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); std::shared_ptr message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(0), stanzaChannel_->countSentStanzaOfType()); xmppRoster_->addContact(messageJID, "foo", std::vector(), to); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType()); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex(1); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload() != nullptr); } void testChatControllerHighlightingNotificationTesting() { HighlightConfiguration::KeywordHightlight keywordRuleA; keywordRuleA.keyword = "Romeo"; keywordRuleA.action.setFrontColor(boost::optional("yellow")); keywordRuleA.action.setSoundFilePath(boost::optional("")); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleA); HighlightConfiguration::KeywordHightlight keywordRuleB; keywordRuleB.keyword = "Juliet"; keywordRuleB.action.setFrontColor(boost::optional("green")); keywordRuleB.action.setSoundFilePath(boost::optional("/tmp/someotherfile.wav")); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleB); JID messageJID = JID("testling@test.com"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This message should cause two sounds: Juliet and Romeo."); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(2, handledHighlightActions_); CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleA.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end()); CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end()); CPPUNIT_ASSERT_EQUAL(size_t(1), notifier_->notifications.size()); } void testChatControllerHighlightingNotificationDeduplicateSounds() { auto keywordRuleA = HighlightConfiguration::KeywordHightlight(); keywordRuleA.keyword = "Romeo"; keywordRuleA.action.setFrontColor(boost::optional("yellow")); keywordRuleA.action.setSoundFilePath(boost::optional("")); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleA); auto keywordRuleB = HighlightConfiguration::KeywordHightlight(); keywordRuleB.keyword = "Juliet"; keywordRuleB.action.setFrontColor(boost::optional("green")); keywordRuleB.action.setSoundFilePath(boost::optional("")); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleB); JID messageJID = JID("testling@test.com"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This message should cause one sound, because both actions have the same sound: Juliet and Romeo."); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(1, handledHighlightActions_); CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleA.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end()); CPPUNIT_ASSERT(soundsPlayed_.find(keywordRuleB.action.getSoundFilePath().get_value_or("")) != soundsPlayed_.end()); CPPUNIT_ASSERT_EQUAL(size_t(1), notifier_->notifications.size()); } void testChatControllerHighlightingNotificationKeyword() { auto keywordRuleA = HighlightConfiguration::KeywordHightlight(); keywordRuleA.keyword = "Swift"; keywordRuleA.action.setFrontColor(boost::optional("yellow")); keywordRuleA.action.setSoundFilePath(boost::optional("")); keywordRuleA.action.setSystemNotificationEnabled(true); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordRuleA); JID messageJID = JID("testling@test.com"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("Let's see if the Swift highlight kicks off a system notification."); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(size_t(2), notifier_->notifications.size()); } void testChatControllerMeMessageHandling() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("/me is feeling delighted."); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("is feeling delighted."), window->bodyFromMessage(window->lastAddedAction_)); } void testRestartingMUCComponentCrash() { JID mucJID = JID("teaparty@rooms.wonderland.lit"); JID self = JID("girl@wonderland.lit/rabbithole"); std::string nick = "aLiCe"; MockChatWindow* window; auto genRemoteMUCPresence = [=]() { auto presence = Presence::create(); presence->setFrom(mucJID.withResource(nick)); presence->setTo(self); return presence; }; // User rejoins. window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); // Join room { auto joinRoomEvent = std::make_shared(mucJID, boost::optional(), nick); uiEventStream_->send(joinRoomEvent); } { auto firstPresence = genRemoteMUCPresence(); firstPresence->setType(Presence::Unavailable); auto userPayload = std::make_shared(); userPayload->addItem(MUCItem(MUCOccupant::Owner, MUCOccupant::NoRole)); firstPresence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(firstPresence); } CPPUNIT_ASSERT_EQUAL(std::string("Couldn't enter room: Unable to enter this room."), MockChatWindow::bodyFromMessage(window->lastAddedErrorMessage_)); { auto presence = genRemoteMUCPresence(); presence->setType(Presence::Unavailable); auto userPayload = std::make_shared(); userPayload->addStatusCode(303); auto item = MUCItem(MUCOccupant::Owner, self, MUCOccupant::Moderator); item.nick = nick; userPayload->addItem(item); userPayload->addStatusCode(110); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } } void testChatControllerMeMessageHandlingInMUC() { JID mucJID("mucroom@rooms.test.com"); std::string nickname = "toodles"; //highlightManager_->resetToDefaultConfiguration(); // add highlight rule for 'foo' HighlightConfiguration::KeywordHightlight keywordHighlight; keywordHighlight.keyword = "foo"; keywordHighlight.action.setBackColor(boost::optional("green")); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordHighlight); HighlightConfiguration::KeywordHightlight keywordHighlightNotification; keywordHighlightNotification.keyword = "Swift"; keywordHighlightNotification.action.setBackColor(boost::optional("green")); keywordHighlightNotification.action.setSystemNotificationEnabled(true); highlightManager_->getConfiguration()->keywordHighlights.push_back(keywordHighlightNotification); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(mucJID, boost::optional(), nickname)); auto genRemoteMUCPresence = [=]() { auto presence = Presence::create(); presence->setFrom(mucJID.withResource(nickname)); presence->setTo(jid_); return presence; }; { auto presence = genRemoteMUCPresence(); auto userPayload = std::make_shared(); userPayload->addStatusCode(110); userPayload->addItem(MUCItem(MUCOccupant::Owner, jid_, MUCOccupant::Moderator)); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } { auto presence = genRemoteMUCPresence(); presence->setFrom(mucJID.withResource("someDifferentNickname")); auto userPayload = std::make_shared(); userPayload->addItem(MUCItem(MUCOccupant::Member, JID("foo@bar.com"), MUCOccupant::Moderator)); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } window->onSendMessageRequest("/me sends a test message with foo", false); window->resetLastMessages(); { Message::ref mucMirrored = std::make_shared(); mucMirrored->setFrom(mucJID.withResource(nickname)); mucMirrored->setTo(jid_); mucMirrored->setType(Message::Groupchat); mucMirrored->setBody("/me sends a test message with foo"); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("sends a test message with foo"), window->bodyFromMessage(window->lastAddedAction_)); window->resetLastMessages(); { Message::ref mucMirrored = std::make_shared(); mucMirrored->setFrom(mucJID.withResource("someDifferentNickname")); mucMirrored->setTo(jid_); mucMirrored->setType(Message::Groupchat); mucMirrored->setBody("/me says hello with a test message with foo and foo"); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("says hello with a test message with foo and foo"), window->bodyFromMessage(window->lastAddedAction_)); window->resetLastMessages(); { Message::ref keywordMessage = std::make_shared(); keywordMessage->setFrom(mucJID.withResource("someOtherDifferentNickname")); keywordMessage->setTo(jid_); keywordMessage->setType(Message::Groupchat); keywordMessage->setBody("Let's mention Swift and see what happens."); manager_->handleIncomingMessage(keywordMessage); } CPPUNIT_ASSERT_EQUAL(std::string("Let's mention Swift and see what happens."), window->bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(size_t(1), notifier_->notifications.size()); } void testPresenceChangeDoesNotReplaceMUCInvite() { JID messageJID("testling@test.com/resource1"); auto generateIncomingPresence = [=](Presence::Type type) { auto presence = std::make_shared(); presence->setType(type); presence->setFrom(messageJID); presence->setTo(jid_); return presence; }; stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Available)); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This is a legible message. >HEH@)oeueu"); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); auto incomingMUCInvite = std::make_shared(); incomingMUCInvite->setFrom(messageJID); auto invitePayload = std::make_shared(); invitePayload->setJID("room@muc.service.com"); incomingMUCInvite->addPayload(invitePayload); stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Unavailable)); stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Available)); window->resetLastMessages(); manager_->handleIncomingMessage(incomingMUCInvite); CPPUNIT_ASSERT_EQUAL(JID("room@muc.service.com"), window->lastMUCInvitationJID_); stanzaChannel_->onPresenceReceived(generateIncomingPresence(Presence::Unavailable)); CPPUNIT_ASSERT_EQUAL(std::string(""), MockChatWindow::bodyFromMessage(window->lastReplacedLastMessage_)); CPPUNIT_ASSERT_EQUAL(std::string("testling@test.com has gone offline."), MockChatWindow::bodyFromMessage(window->lastAddedPresence_)); } void testNotSplittingMUCPresenceJoinLeaveLinesOnChatStateNotifications() { JID mucJID("mucroom@rooms.test.com"); std::string nickname = "toodles"; MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(mucJID, boost::optional(), nickname)); auto genRemoteMUCPresence = [=]() { auto presence = Presence::create(); presence->setFrom(mucJID.withResource(nickname)); presence->setTo(jid_); return presence; }; { auto presence = genRemoteMUCPresence(); auto userPayload = std::make_shared(); userPayload->addStatusCode(110); userPayload->addItem(MUCItem(MUCOccupant::Owner, jid_, MUCOccupant::Moderator)); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } { auto presence = genRemoteMUCPresence(); presence->setFrom(mucJID.withResource("someDifferentNickname")); auto userPayload = std::make_shared(); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } CPPUNIT_ASSERT_EQUAL(std::string("someDifferentNickname has entered the room."), window->bodyFromMessage(window->lastAddedPresence_)); CPPUNIT_ASSERT_EQUAL(std::string(), window->bodyFromMessage(window->lastReplacedLastMessage_)); window->resetLastMessages(); { auto presence = genRemoteMUCPresence(); presence->setFrom(mucJID.withResource("Romeo")); auto userPayload = std::make_shared(); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } CPPUNIT_ASSERT_EQUAL(std::string(), window->bodyFromMessage(window->lastAddedPresence_)); CPPUNIT_ASSERT_EQUAL(std::string("someDifferentNickname and Romeo have entered the room"), window->bodyFromMessage(window->lastReplacedLastMessage_)); window->resetLastMessages(); { auto message = std::make_shared(); message->setFrom(mucJID.withResource("Romeo")); message->setTo(mucJID); message->setType(Message::Groupchat); message->addPayload(std::make_shared(ChatState::Composing)); manager_->handleIncomingMessage(message); } { auto presence = genRemoteMUCPresence(); presence->setFrom(mucJID.withResource("Juliet")); auto userPayload = std::make_shared(); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } CPPUNIT_ASSERT_EQUAL(std::string(), window->bodyFromMessage(window->lastAddedPresence_)); CPPUNIT_ASSERT_EQUAL(std::string("someDifferentNickname, Romeo and Juliet have entered the room"), window->bodyFromMessage(window->lastReplacedLastMessage_)); } template Message::ref createCarbonsMessage(std::shared_ptr carbons, std::shared_ptr forwardedMessage) { auto messageWrapper = std::make_shared(); messageWrapper->setFrom(jid_.toBare()); messageWrapper->setTo(jid_); messageWrapper->setType(Message::Chat); messageWrapper->addPayload(carbons); auto forwarded = std::make_shared(); carbons->setForwarded(forwarded); forwarded->setStanza(forwardedMessage); return messageWrapper; } void testCarbonsForwardedIncomingMessageToSecondResource() { JID messageJID("testling@test.com/resource1"); JID jid2 = jid_.toBare().withResource("someOtherResource"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This is a legible message. >HEH@)oeueu"); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); // incoming carbons message from another resource { auto originalMessage = std::make_shared(); originalMessage->setFrom(messageJID); originalMessage->setTo(jid2); originalMessage->setType(Message::Chat); std::string forwardedBody = "Some further text."; originalMessage->setBody(forwardedBody); auto messageWrapper = createCarbonsMessage(std::make_shared(), originalMessage); manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); auto recentChats = manager_->getRecentChats(); CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getFrom().toBare()); CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some further text.")); } } void testCarbonsForwardedOutgoingMessageFromSecondResource() { JID messageJID("testling@test.com/resource1"); JID jid2 = jid_.toBare().withResource("someOtherResource"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This is a legible message. >HEH@)oeueu"); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); // incoming carbons message from another resource { auto originalMessage = std::make_shared(); originalMessage->setFrom(jid2); originalMessage->setTo(messageJID); originalMessage->setType(Message::Chat); originalMessage->setID("abcdefg123456"); std::string forwardedBody = "Some text my other resource sent."; originalMessage->setBody(forwardedBody); originalMessage->addPayload(std::make_shared()); auto messageWrapper = createCarbonsMessage(std::make_shared(), originalMessage); manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(true, window->lastAddedMessageSenderIsSelf_); CPPUNIT_ASSERT_EQUAL(size_t(1), window->receiptChanges_.size()); CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptRequested, window->receiptChanges_[0].second); auto recentChats = manager_->getRecentChats(); CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getTo().toBare()); CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some text my other resource sent.")); } // incoming carbons message for the received delivery receipt to the other resource { auto originalMessage = std::make_shared(); originalMessage->setFrom(messageJID); originalMessage->setTo(jid2); originalMessage->setType(Message::Chat); originalMessage->addPayload(std::make_shared("abcdefg123456")); auto messageWrapper = createCarbonsMessage(std::make_shared(), originalMessage); manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(size_t(2), window->receiptChanges_.size()); CPPUNIT_ASSERT_EQUAL(ChatWindow::ReceiptReceived, window->receiptChanges_[1].second); //Delivery receipt should not change the latest recent entry. Checking for the original message. auto recentChats = manager_->getRecentChats(); CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, messageJID.toBare()); CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some text my other resource sent.")); } } void testCarbonsForwardedIncomingDuplicates() { JID messageJID("testling@test.com/resource1"); JID jid2 = jid_.toBare().withResource("someOtherResource"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr message(new Message()); message->setFrom(messageJID); std::string body("This is a legible message. >HEH@)oeueu"); message->setBody(body); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(body, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); // incoming carbons message from another resource and duplicate of it { auto originalMessage = std::make_shared(); originalMessage->setFrom(messageJID); originalMessage->setTo(jid2); originalMessage->setID("BDD82F0B-2523-48BF-B8CA-17B23A314BC2"); originalMessage->setType(Message::Chat); std::string forwardedBody = "Some further text."; originalMessage->setBody(forwardedBody); auto messageWrapper = createCarbonsMessage(std::make_shared(), originalMessage); manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(forwardedBody, MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); window->resetLastMessages(); messageWrapper = createCarbonsMessage(std::make_shared(), originalMessage); manager_->handleIncomingMessage(messageWrapper); CPPUNIT_ASSERT_EQUAL(std::string(), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(false, window->lastAddedMessageSenderIsSelf_); auto recentChats = manager_->getRecentChats(); CPPUNIT_ASSERT_EQUAL(recentChats.size(), size_t(1)); CPPUNIT_ASSERT_EQUAL(recentChats[0].jid, originalMessage->getFrom().toBare()); CPPUNIT_ASSERT_EQUAL(recentChats[0].activity, std::string("Some further text.")); } } /** * This test case ensures correct handling of the ideal case where the replace * message refers to a message with a known ID. This results in the last * message being replaced. */ void testChatControllerMessageCorrectionCorrectReplaceID() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); auto message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text before edit"); message->setID("someID"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text before edit"), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text after edit"); message->addPayload(std::make_shared("someID")); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text before edit"), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(std::string("text after edit"), MockChatWindow::bodyFromMessage(window->lastReplacedMessage_)); } /** * This test case ensures correct handling of the case where the replace * message refers to a message with a unknown ID. The replace message should * be treated like a non-repalce message in this case, with no replacement * occuring. */ void testChatControllerMessageCorrectionIncorrectReplaceID() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); auto message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text before edit"); message->setID("someID"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text before edit"), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text after failed edit"); message->addPayload(std::make_shared("wrongID")); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text after failed edit"), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); CPPUNIT_ASSERT_EQUAL(std::string(""), MockChatWindow::bodyFromMessage(window->lastReplacedMessage_)); } void testChatControllerMessageCorrectionReplaceBySameResource() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); auto message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text before edit"); message->setID("someID"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text before edit"), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text after edit"); message->addPayload(std::make_shared("someID")); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text after edit"), MockChatWindow::bodyFromMessage(window->lastReplacedMessage_)); } void testChatControllerMessageCorrectionReplaceByOtherResource() { JID messageJID("testling@test.com/resource1"); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); auto message = std::make_shared(); message->setFrom(messageJID); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text before edit"); message->setID("someID"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text before edit"), MockChatWindow::bodyFromMessage(window->lastAddedMessage_)); message = std::make_shared(); message->setFrom(messageJID.toBare().withResource("resource2")); message->setTo(jid_); message->setType(Message::Chat); message->setBody("text after edit"); message->addPayload(std::make_shared("someID")); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(std::string("text after edit"), MockChatWindow::bodyFromMessage(window->lastReplacedMessage_)); } void testMUCControllerMessageCorrectionNoIDMatchRequired() { JID mucJID("SomeMUCRoom@test.com"); manager_->setOnline(true); // Open chat window to a sender. MockChatWindow* window = new MockChatWindow(); std::vector jids; jids.emplace_back("foo@test.com"); jids.emplace_back("bar@test.com"); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); auto nickname = std::string("SomeNickName"); // Join room { auto joinRoomEvent = std::make_shared(mucJID, boost::optional(), nickname); uiEventStream_->send(joinRoomEvent); } auto genRemoteMUCPresence = [=]() { auto presence = Presence::create(); presence->setFrom(mucJID.withResource(nickname)); presence->setTo(jid_); return presence; }; { auto presence = genRemoteMUCPresence(); auto userPayload = std::make_shared(); userPayload->addStatusCode(110); userPayload->addItem(MUCItem(MUCOccupant::Owner, jid_, MUCOccupant::Moderator)); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } { auto presence = genRemoteMUCPresence(); presence->setFrom(mucJID.withResource("someDifferentNickname")); auto userPayload = std::make_shared(); userPayload->addItem(MUCItem(MUCOccupant::Member, JID("foo@bar.com"), MUCOccupant::Moderator)); presence->addPayload(userPayload); stanzaChannel_->onPresenceReceived(presence); } { Message::ref mucMirrored = std::make_shared(); mucMirrored->setFrom(mucJID.withResource(nickname)); mucMirrored->setTo(jid_); mucMirrored->setType(Message::Groupchat); mucMirrored->setID("fooBlaID_1"); mucMirrored->setBody("Some misssssspelled message."); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("Some misssssspelled message."), window->bodyFromMessage(window->lastAddedMessage_)); // Replace message with non-matching ID { Message::ref mucMirrored = std::make_shared(); mucMirrored->setFrom(mucJID.withResource(nickname)); mucMirrored->setTo(jid_); mucMirrored->setType(Message::Groupchat); mucMirrored->setID("fooBlaID_3"); mucMirrored->setBody("Some correctly spelled message."); mucMirrored->addPayload(std::make_shared("fooBlaID_2")); manager_->handleIncomingMessage(mucMirrored); } CPPUNIT_ASSERT_EQUAL(std::string("Some correctly spelled message."), window->bodyFromMessage(window->lastReplacedMessage_)); } void impromptuChatSetup(MockChatWindow* window) { stanzaChannel_->uniqueIDs_ = true; JID mucJID("795B7BBE-9099-4A0D-81BA-C816F78E275C@test.com"); manager_->setOnline(true); std::shared_ptr infoRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[1]); CPPUNIT_ASSERT(infoRequest); std::shared_ptr infoResponse = IQ::createResult(infoRequest->getFrom(), infoRequest->getTo(), infoRequest->getID()); DiscoInfo info; info.addIdentity(DiscoInfo::Identity("Shakespearean Chat Service", "conference", "text")); info.addFeature("http://jabber.org/protocol/muc"); infoResponse->addPayload(std::make_shared(info)); stanzaChannel_->onIQReceived(infoResponse); std::vector jids; jids.emplace_back("foo@test.com"); jids.emplace_back("bar@test.com"); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(jids, mucJID, "")); CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); // Check the MUC security marking request auto mucInfoRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[2]); CPPUNIT_ASSERT(mucInfoRequest); auto mucJoinPresence = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[3]); CPPUNIT_ASSERT(mucJoinPresence); // MUC presence reply auto mucResponse = Presence::create(); mucResponse->setTo(jid_); mucResponse->setFrom(mucJoinPresence->getTo()); mucResponse->addPayload([]() { auto mucUser = std::make_shared(); mucUser->addItem(MUCItem(MUCOccupant::Member, MUCOccupant::Participant)); mucUser->addStatusCode(MUCUserPayload::StatusCode(110)); mucUser->addStatusCode(MUCUserPayload::StatusCode(210)); return mucUser; }()); stanzaChannel_->onPresenceReceived(mucResponse); // Before people join the impromptu room, the title is based on names coming from Roster CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); auto mucParticipantJoined = [&](const JID& jid) { auto participantJoinedPresence = Presence::create(); participantJoinedPresence->setTo(jid_); participantJoinedPresence->setFrom(mucJID.withResource(jid.toString())); auto mucUser = std::make_shared(); mucUser->addItem(MUCItem(MUCOccupant::Member, MUCOccupant::Participant)); participantJoinedPresence->addPayload(mucUser); return participantJoinedPresence; }; for (const auto& participantJID : jids) { stanzaChannel_->onPresenceReceived(mucParticipantJoined(participantJID)); } } void testImpromptuChatTitle() { // Open a new chat window to a sender. MockChatWindow* window = new MockChatWindow(); impromptuChatSetup(window); // After people joined, the title is the list of participant nicknames or names coming from Roster (if nicknames are unavailable) CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), manager_->getRecentChats()[0].getTitle()); } void testImpromptuChatWindowTitle() { // Open a new chat window to a sender. MockChatWindow* window = new MockChatWindow(); impromptuChatSetup(window); // After people joined, the title of chat window is combined of participant nicknames or names coming from Roster (if nicknames are unavailable) CPPUNIT_ASSERT_EQUAL(std::string("bar@test.com, foo@test.com"), window->name_); } void testStandardMUCChatWindowTitle() { JID mucJID("mucroom@rooms.test.com"); std::string nickname = "toodles"; MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(mucJID, uiEventStream_).Return(window); uiEventStream_->send(std::make_shared(mucJID, boost::optional(), nickname)); CPPUNIT_ASSERT_EQUAL(std::string("mucroom"), window->name_); } std::shared_ptr createBookmarkStorageWithJID(std::shared_ptr bookmarkRequest, const JID& jid, const bool autojoin) { CPPUNIT_ASSERT(bookmarkRequest); CPPUNIT_ASSERT_EQUAL(IQ::Get, bookmarkRequest->getType()); auto privateStorage = bookmarkRequest->getPayload(); CPPUNIT_ASSERT(privateStorage); auto storage = std::dynamic_pointer_cast(privateStorage->getPayload()); CPPUNIT_ASSERT(storage); auto roomsStorage = std::make_shared(); if (jid.isValid()) { auto room = Storage::Room(); room.jid = jid; room.autoJoin = autojoin; roomsStorage->addRoom(room); } return roomsStorage; } void testReceivingBookmarksWithDomainJID() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared(createBookmarkStorageWithJID(bookmarkRequest, "montague.lit", true)) ); stanzaChannel_->onIQReceived(response); } void testReceivingBookmarksWithBareJID() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(window); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared(createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit", true)) ); stanzaChannel_->onIQReceived(response); } void testReceivingNoBookmarks() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared() ); stanzaChannel_->onIQReceived(response); } void testReceivingNullBookmarks() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), nullptr ); stanzaChannel_->onIQReceived(response); } void testReceivingBookmarksError() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto response = IQ::createError( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), ErrorPayload::Condition::ServiceUnavailable, ErrorPayload::Type::Cancel, nullptr ); stanzaChannel_->onIQReceived(response); } void testReceivingBookmarksWithFullJID() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared(createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit/someresource", true)) ); stanzaChannel_->onIQReceived(response); } void testAutoJoinBookmarksAndChattables() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "autojoin@bookmark.lit", true); auto room = Storage::Room(); room.jid = "noAutojoin@bookmark.lit"; roomsStorage->addRoom(room); //Only autojoin@bookmark.lit window should open. MockChatWindow* autojoinBookmarkWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("autojoin@bookmark.lit"), uiEventStream_).Return(autojoinBookmarkWindow); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared(roomsStorage) ); stanzaChannel_->onIQReceived(response); //Both bookmarks should be added to the chattables. CPPUNIT_ASSERT_EQUAL(size_t(2), chattables_->get().size()); auto autoJoinState = chattables_->getState("autojoin@bookmark.lit"); CPPUNIT_ASSERT(autoJoinState.type == Chattables::State::Type::Room); CPPUNIT_ASSERT_EQUAL(autoJoinState.status, StatusShow::Online); auto noAutoJoinState = chattables_->getState("noAutojoin@bookmark.lit"); CPPUNIT_ASSERT(noAutoJoinState.type == Chattables::State::Type::Room); CPPUNIT_ASSERT_EQUAL(noAutoJoinState.status, StatusShow::None); } void testJoinNoAutojoinBookmark() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "example@montague.lit", false); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared(roomsStorage) ); stanzaChannel_->onIQReceived(response); //Join previous bookmarked room, expecting no increase in chattables and change of autojoin in bookmark to true MockChatWindow* newExampleChatWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID("example@montague.lit"), uiEventStream_).Return(newExampleChatWindow); uiEventStream_->send(std::make_shared("example@montague.lit", boost::optional(), boost::optional())); CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size()); auto state = chattables_->getState("example@montague.lit"); CPPUNIT_ASSERT(state.type == Chattables::State::Type::Room); CPPUNIT_ASSERT_EQUAL(state.status, StatusShow::Online); auto bookmarks = manager_->getBookmarkManager()->getBookmarks(); CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1)); CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit")); CPPUNIT_ASSERT(bookmarks[0].getAutojoin()); } void testJoinAndBookmarkMUC() { auto bookmarkRequest = std::dynamic_pointer_cast(stanzaChannel_->sentStanzas[0]); auto roomsStorage = createBookmarkStorageWithJID(bookmarkRequest, "", true); auto response = IQ::createResult( bookmarkRequest->getFrom(), bookmarkRequest->getTo(), bookmarkRequest->getID(), std::make_shared(roomsStorage) ); stanzaChannel_->onIQReceived(response); //Join non-bookmarked room expecting for the room to get bookmarked with autojoin to true MockChatWindow* exampleChatWindow = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With("example@montague.lit", uiEventStream_).Return(exampleChatWindow); uiEventStream_->send(std::make_shared("example@montague.lit", boost::optional(), boost::optional())); { CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size()); auto state = chattables_->getState("example@montague.lit"); CPPUNIT_ASSERT(state.type == Chattables::State::Type::Room); CPPUNIT_ASSERT_EQUAL(state.status, StatusShow::Online); auto bookmarks = manager_->getBookmarkManager()->getBookmarks(); CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1)); CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit")); CPPUNIT_ASSERT(bookmarks[0].getAutojoin()); } //Exiting room that is bookmarked, expecting chattable to stay but bookmark autojoin change to false. exampleChatWindow->onClosed(); { CPPUNIT_ASSERT_EQUAL(size_t(1), chattables_->get().size()); auto bookmarks = manager_->getBookmarkManager()->getBookmarks(); CPPUNIT_ASSERT_EQUAL(bookmarks.size(), size_t(1)); CPPUNIT_ASSERT(bookmarks[0].getRoom() == JID("example@montague.lit")); CPPUNIT_ASSERT(!bookmarks[0].getAutojoin()); } } private: std::shared_ptr makeDeliveryReceiptTestMessage(const JID& from, const std::string& id) { std::shared_ptr message = std::make_shared(); message->setFrom(from); message->setID(id); message->setBody("This will cause the window to open"); message->addPayload(std::make_shared()); return message; } size_t st(int i) { return static_cast(i); } void handleHighlightAction(const HighlightAction& action) { handledHighlightActions_++; if (action.getSoundFilePath()) { soundsPlayed_.insert(action.getSoundFilePath().get_value_or("")); } } private: JID jid_; std::unique_ptr notifier_; ExtendedChatsManager* manager_; DummyStanzaChannel* stanzaChannel_; IQRouter* iqRouter_; EventController* eventController_; ChatWindowFactory* chatWindowFactory_; JoinMUCWindowFactory* joinMUCWindowFactory_; NickResolver* nickResolver_; PresenceOracle* presenceOracle_; AvatarManager* avatarManager_; EventNotifier* eventNotifier_; std::shared_ptr serverDiscoInfo_; XMPPRosterImpl* xmppRoster_; PresenceSender* presenceSender_; MockRepository* mocks_; UIEventStream* uiEventStream_; ChatListWindowFactory* chatListWindowFactory_; WhiteboardWindowFactory* whiteboardWindowFactory_; MUCSearchWindowFactory* mucSearchWindowFactory_; MUCRegistry* mucRegistry_; DirectedPresenceSender* directedPresenceSender_; DummyEntityCapsProvider* entityCapsProvider_; MUCManager* mucManager_; DummySettingsProvider* settings_; ProfileSettingsProvider* profileSettings_; ChatListWindow* chatListWindow_; FileTransferOverview* ftOverview_; FileTransferManager* ftManager_; WhiteboardSessionManager* wbSessionManager_; WhiteboardManager* wbManager_; HighlightManager* highlightManager_; ClientBlockListManager* clientBlockListManager_; VCardManager* vcardManager_; CryptoProvider* crypto_; VCardStorage* vcardStorage_; std::map emoticons_; int handledHighlightActions_; std::set soundsPlayed_; DummyTimerFactory* timerFactory_; std::unique_ptr chattables_; }; CPPUNIT_TEST_SUITE_REGISTRATION(ChatsManagerTest);