diff options
-rw-r--r-- | Swift/Controllers/Chat/MUCController.cpp | 8 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 68 |
2 files changed, 75 insertions, 1 deletions
diff --git a/Swift/Controllers/Chat/MUCController.cpp b/Swift/Controllers/Chat/MUCController.cpp index 8fa98f6..545eab5 100644 --- a/Swift/Controllers/Chat/MUCController.cpp +++ b/Swift/Controllers/Chat/MUCController.cpp @@ -512,62 +512,68 @@ std::string MUCController::roleToFriendlyName(MUCOccupant::Role role) { } assert(false); return ""; } std::string MUCController::roleToSortName(MUCOccupant::Role role) { switch (role) { case MUCOccupant::Moderator: return "1"; case MUCOccupant::Participant: return "2"; case MUCOccupant::Visitor: return "3"; case MUCOccupant::NoRole: return "4"; } assert(false); return "5"; } JID MUCController::nickToJID(const std::string& nick) { return muc_->getJID().withResource(nick); } bool MUCController::messageTargetsMe(std::shared_ptr<Message> message) { std::string stringRegexp(".*\\b" + boost::to_lower_copy(nick_) + "\\b.*"); boost::regex myRegexp(stringRegexp); return boost::regex_match(boost::to_lower_copy(message->getBody().get_value_or("")), myRegexp); } void MUCController::preHandleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) { if (messageEvent->getStanza()->getType() == Message::Groupchat) { lastActivity_ = boost::posix_time::microsec_clock::universal_time(); } - clearPresenceQueue(); std::shared_ptr<Message> message = messageEvent->getStanza(); + + // This avoids clearing join/leave queue for chat state notification messages + // which are not readable (e.g. have no body content). + if (!(!messageEvent->isReadable() && message->getPayload<ChatState>())) { + clearPresenceQueue(); + } + if (joined_ && messageEvent->getStanza()->getFrom().getResource() != nick_ && messageTargetsMe(message) && !message->getPayload<Delay>() && messageEvent->isReadable()) { chatWindow_->flash(); } else { messageEvent->setTargetsMe(false); } if (messageEvent->isReadable() && isImpromptu_) { chatWindow_->flash(); /* behave like a regular char*/ } if (joined_) { std::string nick = message->getFrom().getResource(); if (nick != nick_ && currentOccupants_.find(nick) != currentOccupants_.end()) { completer_->addWord(nick); } } /*Buggy implementations never send the status code, so use an incoming message as a hint that joining's done (e.g. the old ejabberd on psi-im.org).*/ receivedActivity(); joined_ = true; if (message->hasSubject() && !message->getPayload<Body>() && !message->getPayload<Thread>()) { if (!isInitialJoin_) { displaySubjectIfChanged(message->getSubject()); } isInitialJoin_ = false; chatWindow_->setSubject(message->getSubject()); doneGettingHistory_ = true; subject_ = message->getSubject(); } if (!doneGettingHistory_ && !message->getPayload<Delay>()) { diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index 00df3da..1958408 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -89,60 +89,61 @@ class DummyNotifier : public Notifier { Type type; std::string subject; std::string description; boost::filesystem::path picture; boost::function<void()> callback; }; public: std::vector<Notification> notifications; }; 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_SUITE_END(); public: void setUp() { mocks_ = new MockRepository(); notifier_ = std::unique_ptr<DummyNotifier>(new DummyNotifier()); jid_ = JID("test@test.com/resource"); stanzaChannel_ = new DummyStanzaChannel(); iqRouter_ = new IQRouter(stanzaChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>(); joinMUCWindowFactory_ = mocks_->InterfaceMock<JoinMUCWindowFactory>(); @@ -1068,60 +1069,127 @@ public: MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); std::shared_ptr<Message> 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<Message>(); incomingMUCInvite->setFrom(messageJID); auto invitePayload = std::make_shared<MUCInvitationPayload>(); 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->lastReplacedMessage_)); 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<JoinMUCUIEvent>(mucJID, boost::optional<std::string>(), 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<MUCUserPayload>(); + 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<MUCUserPayload>(); + 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->lastReplacedMessage_)); + window->resetLastMessages(); + + { + auto presence = genRemoteMUCPresence(); + presence->setFrom(mucJID.withResource("Romeo")); + auto userPayload = std::make_shared<MUCUserPayload>(); + 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->lastReplacedMessage_)); + window->resetLastMessages(); + + { + auto message = std::make_shared<Message>(); + message->setFrom(mucJID.withResource("Romeo")); + message->setTo(mucJID); + message->setType(Message::Groupchat); + message->addPayload(std::make_shared<ChatState>(ChatState::Composing)); + manager_->handleIncomingMessage(message); + } + + { + auto presence = genRemoteMUCPresence(); + presence->setFrom(mucJID.withResource("Juliet")); + auto userPayload = std::make_shared<MUCUserPayload>(); + 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->lastReplacedMessage_)); + } + template <typename CarbonsType> Message::ref createCarbonsMessage(std::shared_ptr<CarbonsType> carbons, std::shared_ptr<Message> forwardedMessage) { auto messageWrapper = std::make_shared<Message>(); messageWrapper->setFrom(jid_.toBare()); messageWrapper->setTo(jid_); messageWrapper->setType(Message::Chat); messageWrapper->addPayload(carbons); auto forwarded = std::make_shared<Forwarded>(); 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> 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 { |