diff options
Diffstat (limited to 'Swift/Controllers')
-rw-r--r-- | Swift/Controllers/Chat/ChatControllerBase.cpp | 8 | ||||
-rw-r--r-- | Swift/Controllers/Chat/ChatsManager.cpp | 14 | ||||
-rw-r--r-- | Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp | 71 | ||||
-rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 7 |
4 files changed, 84 insertions, 16 deletions
diff --git a/Swift/Controllers/Chat/ChatControllerBase.cpp b/Swift/Controllers/Chat/ChatControllerBase.cpp index b339bd0..da9064e 100644 --- a/Swift/Controllers/Chat/ChatControllerBase.cpp +++ b/Swift/Controllers/Chat/ChatControllerBase.cpp @@ -243,61 +243,67 @@ std::string ChatControllerBase::addMessage(const ChatWindow::ChatMessage& chatMe } void ChatControllerBase::replaceMessage(const ChatWindow::ChatMessage& chatMessage, const std::string& id, const boost::posix_time::ptime& time) { if (chatMessage.isMeCommand()) { chatWindow_->replaceWithAction(chatMessage, id, time); } else { chatWindow_->replaceMessage(chatMessage, id, time); } } bool ChatControllerBase::isFromContact(const JID& from) { return from.toBare() == toJID_.toBare(); } void ChatControllerBase::handleIncomingMessage(std::shared_ptr<MessageEvent> messageEvent) { preHandleIncomingMessage(messageEvent); if (messageEvent->isReadable() && !messageEvent->getConcluded()) { unreadMessages_.push_back(messageEvent); if (messageEvent->targetsMe()) { targetedUnreadMessages_.push_back(messageEvent); } } std::shared_ptr<Message> message = messageEvent->getStanza(); ChatWindow::ChatMessage chatMessage; boost::optional<std::string> optionalBody = message->getBody(); std::string body = optionalBody.get_value_or(""); if (message->isError()) { if (!message->getTo().getResource().empty()) { - std::string errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>())); + std::string errorMessage; + if (message->getPayload<Swift::ErrorPayload>()->getCondition() == ErrorPayload::ItemNotFound) { + errorMessage = QT_TRANSLATE_NOOP("", "This user could not be found in the room."); + } + else { + errorMessage = str(format(QT_TRANSLATE_NOOP("", "Couldn't send message: %1%")) % getErrorMessage(message->getPayload<ErrorPayload>())); + } chatWindow_->addErrorMessage(chatMessageParser_->parseMessageBody(errorMessage)); } } else if (messageEvent->getStanza()->getPayload<MUCInvitationPayload>()) { handleMUCInvitation(messageEvent->getStanza()); return; } else if (messageEvent->getStanza()->getPayload<MUCUserPayload>() && messageEvent->getStanza()->getPayload<MUCUserPayload>()->getInvite()) { handleMediatedMUCInvitation(messageEvent->getStanza()); return; } else { if (!messageEvent->isReadable()) { return; } showChatWindow(); JID from = message->getFrom(); std::vector<std::shared_ptr<Delay> > delayPayloads = message->getPayloads<Delay>(); for (size_t i = 0; useDelayForLatency_ && i < delayPayloads.size(); i++) { if (!delayPayloads[i]->getFrom()) { continue; } boost::posix_time::ptime now = boost::posix_time::microsec_clock::universal_time(); std::ostringstream s; s << "The following message took " << (now - delayPayloads[i]->getStamp()).total_milliseconds() / 1000.0 << " seconds to be delivered from " << delayPayloads[i]->getFrom()->toString() << "."; chatWindow_->addSystemMessage(chatMessageParser_->parseMessageBody(std::string(s.str())), ChatWindow::DefaultDirection); } std::shared_ptr<SecurityLabel> label = message->getPayload<SecurityLabel>(); // Determine the timestamp diff --git a/Swift/Controllers/Chat/ChatsManager.cpp b/Swift/Controllers/Chat/ChatsManager.cpp index b9e2cf4..4f95d71 100644 --- a/Swift/Controllers/Chat/ChatsManager.cpp +++ b/Swift/Controllers/Chat/ChatsManager.cpp @@ -910,62 +910,72 @@ void ChatsManager::handleIncomingMessage(std::shared_ptr<Message> incomingMessag (forwardedMessage = std::dynamic_pointer_cast<Message>(forwarded->getStanza()))) { message = forwardedMessage; } else if ((carbonsSent = incomingMessage->getPayload<CarbonsSent>()) && (forwarded = carbonsSent->getForwarded()) && (forwardedMessage = std::dynamic_pointer_cast<Message>(forwarded->getStanza()))) { JID toJID = forwardedMessage->getTo(); ChatController* controller = getChatControllerOrCreate(toJID); if (controller) { controller->handleIncomingOwnMessage(forwardedMessage); } else { SWIFT_LOG(error) << "Carbons message ignored." << std::endl; } return; } } JID fromJID = message->getFrom(); std::shared_ptr<MessageEvent> event(new MessageEvent(message)); bool isInvite = !!message->getPayload<MUCInvitationPayload>(); bool isMediatedInvite = (message->getPayload<MUCUserPayload>() && message->getPayload<MUCUserPayload>()->getInvite()); if (isMediatedInvite) { fromJID = (*message->getPayload<MUCUserPayload>()->getInvite()).from; } if (!event->isReadable() && !message->getPayload<ChatState>() && !message->getPayload<DeliveryReceipt>() && !message->getPayload<DeliveryReceiptRequest>() && !isInvite && !isMediatedInvite && !message->hasSubject()) { return; } - // Try to deliver it to a MUC - if (message->getType() == Message::Groupchat || message->getType() == Message::Error /*|| (isInvite && message->getType() == Message::Normal)*/) { + // Try to deliver MUC errors to a MUC PM window if a suitable window is open. + if (message->getType() == Message::Error) { + auto controller = getChatControllerIfExists(fromJID, messageCausesSessionBinding(message)); + if (controller) { + controller->handleIncomingMessage(event); + return; + } + } + + // Try to deliver it to a MUC. + if (message->getType() == Message::Groupchat || message->getType() == Message::Error) { + // Try to deliver it to a MUC room. std::map<JID, MUCController*>::iterator i = mucControllers_.find(fromJID.toBare()); if (i != mucControllers_.end()) { i->second->handleIncomingMessage(event); return; } else if (message->getType() == Message::Groupchat) { //FIXME: Error handling - groupchat messages from an unknown muc. return; } } // check for impromptu invite to potentially auto-accept MUCInvitationPayload::ref invite = message->getPayload<MUCInvitationPayload>(); if (invite && autoAcceptMUCInviteDecider_->isAutoAcceptedInvite(message->getFrom(), invite)) { if (invite->getIsContinuation()) { // check for existing chat controller for the from JID ChatController* controller = getChatControllerIfExists(fromJID); if (controller) { ChatWindow* window = controller->detachChatWindow(); chatControllers_.erase(fromJID); delete controller; handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true, window); return; } } else { handleJoinMUCRequest(invite->getJID(), boost::optional<std::string>(), boost::optional<std::string>(), false, false, true); return; } } diff --git a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp index a5e68cf..cff54f8 100644 --- a/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/Chat/UnitTest/ChatsManagerTest.cpp @@ -54,64 +54,67 @@ #include <Swift/Controllers/UIEvents/RequestChatUIEvent.h> #include <Swift/Controllers/UIEvents/UIEventStream.h> #include <Swift/Controllers/UIInterfaces/ChatListWindowFactory.h> #include <Swift/Controllers/UIInterfaces/ChatWindow.h> #include <Swift/Controllers/UIInterfaces/ChatWindowFactory.h> #include <Swift/Controllers/UIInterfaces/JoinMUCWindowFactory.h> #include <Swift/Controllers/UIInterfaces/MUCSearchWindowFactory.h> #include <Swift/Controllers/UIInterfaces/WhiteboardWindowFactory.h> #include <Swift/Controllers/UnitTest/MockChatWindow.h> #include <Swift/Controllers/WhiteboardManager.h> #include <Swift/Controllers/XMPPEvents/EventController.h> using namespace Swift; 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(testChatControllerPMPresenceHandling); CPPUNIT_TEST(testLocalMUCServiceDiscoveryResetOnDisconnect); CPPUNIT_TEST(testPresenceChangeDoesNotReplaceMUCInvite); + // MUC PM Tests + CPPUNIT_TEST(testChatControllerPMPresenceHandling); + CPPUNIT_TEST(testChatControllerMucPmUnavailableErrorHandling); + // Highlighting tests CPPUNIT_TEST(testChatControllerHighlightingNotificationTesting); CPPUNIT_TEST(testChatControllerHighlightingNotificationDeduplicateSounds); 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(); jid_ = JID("test@test.com/resource"); stanzaChannel_ = new DummyStanzaChannel(); iqRouter_ = new IQRouter(stanzaChannel_); eventController_ = new EventController(); chatWindowFactory_ = mocks_->InterfaceMock<ChatWindowFactory>(); joinMUCWindowFactory_ = mocks_->InterfaceMock<JoinMUCWindowFactory>(); xmppRoster_ = new XMPPRosterImpl(); mucRegistry_ = new MUCRegistry(); nickResolver_ = new NickResolver(jid_.toBare(), xmppRoster_, nullptr, mucRegistry_); presenceOracle_ = new PresenceOracle(stanzaChannel_, xmppRoster_); serverDiscoInfo_ = std::make_shared<DiscoInfo>(); presenceSender_ = new StanzaChannelPresenceSender(stanzaChannel_); directedPresenceSender_ = new DirectedPresenceSender(presenceSender_); mucManager_ = new MUCManager(stanzaChannel_, iqRouter_, directedPresenceSender_, mucRegistry_); @@ -163,202 +166,202 @@ public: 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> 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_->InterfaceMock<ChatWindow>(); + MockChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1, uiEventStream_).Return(window1); std::shared_ptr<Message> 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<Message> 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_->InterfaceMock<ChatWindow>(); + ChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString), uiEventStream_).Return(window); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(messageJIDString))); } void testFirstOpenWindowBareToFull() { std::string bareJIDString("testling@test.com"); std::string fullJIDString("testling@test.com/resource1"); - MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString), uiEventStream_).Return(window); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(bareJIDString))); std::shared_ptr<Message> 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_->InterfaceMock<ChatWindow>(); + ChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1), uiEventStream_).Return(window1); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(messageJIDString1))); std::string messageJIDString2("testling2@test.com"); - ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + ChatWindow* window2 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2), uiEventStream_).Return(window2); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(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_->InterfaceMock<ChatWindow>(); + MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString), uiEventStream_).Return(window); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(bareJIDString))); std::shared_ptr<Message> 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<Presence> jid1Online(new Presence()); jid1Online->setFrom(JID(fullJIDString1)); std::shared_ptr<Presence> jid1Offline(new Presence()); jid1Offline->setFrom(JID(fullJIDString1)); jid1Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid1Offline); std::shared_ptr<Message> 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<JoinMUCUIEvent>(muc, std::string("nick"))); std::string messageJIDString1("testling@test.com/1"); - ChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + ChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1), uiEventStream_).Return(window1); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(messageJIDString1))); std::string messageJIDString2("testling@test.com/2"); - ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + ChatWindow* window2 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2), uiEventStream_).Return(window2); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(messageJIDString2))); std::string messageJIDString3("testling@test.com/3"); - ChatWindow* window3 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + ChatWindow* window3 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString3), uiEventStream_).Return(window3); uiEventStream_->send(std::make_shared<RequestChatUIEvent>(JID(messageJIDString3))); /* Refetch an earlier window */ /* We do not expect a new window to be created */ uiEventStream_->send(std::make_shared<RequestChatUIEvent>(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_->InterfaceMock<ChatWindow>(); + MockChatWindow* window1 = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1, uiEventStream_).Return(window1); std::shared_ptr<Message> 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<Message> message2(new Message()); message2->setFrom(messageJID2); message2->setBody("This is a legible message2."); manager_->handleIncomingMessage(message2); std::shared_ptr<Presence> jid1Online(new Presence()); jid1Online->setFrom(JID(messageJID1)); std::shared_ptr<Presence> jid1Offline(new Presence()); jid1Offline->setFrom(JID(messageJID1)); jid1Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid1Offline); std::shared_ptr<Presence> jid2Online(new Presence()); jid2Online->setFrom(JID(messageJID2)); std::shared_ptr<Presence> jid2Offline(new Presence()); jid2Offline->setFrom(JID(messageJID2)); jid2Offline->setType(Presence::Unavailable); presenceOracle_->onPresenceChange(jid2Offline); JID messageJID3("testling@test.com/resource3"); @@ -380,61 +383,61 @@ public: /** * 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<std::string>(), RosterItemPayload::Both); MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); std::shared_ptr<Message> message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex<Stanza>(1); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType<Message>()); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != nullptr); xmppRoster_->removeContact(messageJID); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType<Message>()); } /** * 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_->InterfaceMock<ChatWindow>(); + MockChatWindow* window = new MockChatWindow(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID, uiEventStream_).Return(window); settings_->storeSetting(SettingConstants::REQUEST_DELIVERYRECEIPTS, true); std::shared_ptr<Message> message = makeDeliveryReceiptTestMessage(messageJID, "1"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(0), stanzaChannel_->countSentStanzaOfType<Message>()); xmppRoster_->addContact(messageJID, "foo", std::vector<std::string>(), RosterItemPayload::Both); message->setID("2"); manager_->handleIncomingMessage(message); CPPUNIT_ASSERT_EQUAL(st(1), stanzaChannel_->countSentStanzaOfType<Message>()); Stanza::ref stanzaContactOnRoster = stanzaChannel_->getStanzaAtIndex<Stanza>(1); CPPUNIT_ASSERT(stanzaContactOnRoster->getPayload<DeliveryReceipt>() != 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); } @@ -661,60 +664,104 @@ public: 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<RequestChatUIEvent>(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->lastReplacedMessage_)); } + 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<JoinMUCUIEvent>(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<RequestChatUIEvent>(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<Message>(2); + CPPUNIT_ASSERT_EQUAL(messageBody, *sendMessageStanza->getBody()); + + // receive reply with error + auto messageErrorReply = std::make_shared<Message>(); + messageErrorReply->setID(stanzaChannel_->getNewIQID()); + messageErrorReply->setType(Message::Error); + messageErrorReply->setFrom(participantA); + messageErrorReply->setTo(jid_); + messageErrorReply->addPayload(std::make_shared<ErrorPayload>(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<RequestChatUIEvent>(sender)); CPPUNIT_ASSERT_EQUAL(false, window->impromptuMUCSupported_); std::shared_ptr<IQ> infoRequest = std::dynamic_pointer_cast<IQ>(stanzaChannel_->sentStanzas[1]); std::shared_ptr<IQ> 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<DiscoInfo>(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"); diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 9b943f0..76e5194 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -42,61 +42,65 @@ namespace Swift { virtual void addErrorMessage(const ChatMessage& message) { lastAddedErrorMessage_ = message; } virtual void replaceMessage(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/) {} virtual void replaceWithAction(const ChatMessage& /*message*/, const std::string& /*id*/, const boost::posix_time::ptime& /*time*/) {} virtual void replaceLastMessage(const ChatMessage& message, const TimestampBehaviour /*timestampBehaviour*/) { lastReplacedMessage_ = message; } virtual void replaceSystemMessage(const ChatMessage& message, const std::string& /*id*/, const TimestampBehaviour /*timestampBehaviour*/) { lastReplacedSystemMessage_ = message; } // File transfer related stuff virtual std::string addFileTransfer(const std::string& /*senderName*/, bool /*senderIsSelf*/,const std::string& /*filename*/, const boost::uintmax_t /*sizeInBytes*/, const std::string& /*description*/) { return nullptr; } virtual void setFileTransferProgress(std::string /*id*/, const int /*alreadyTransferedBytes*/) { } virtual void setFileTransferStatus(std::string /*id*/, const FileTransferState /*state*/, const std::string& /*msg*/) { } virtual void setMessageReceiptState(const std::string & id, ReceiptState state) { receiptChanges_.emplace_back(id, state); } virtual void setContactChatState(ChatState::ChatStateType /*state*/) {} virtual void setName(const std::string& name) {name_ = name;} virtual void show() {} virtual bool isVisible() const { return true; } virtual void activate() {} virtual void setAvailableSecurityLabels(const std::vector<SecurityLabelsCatalog::Item>& labels) {labels_ = labels;} virtual void setSecurityLabelsEnabled(bool enabled) {labelsEnabled_ = enabled;} virtual void setUnreadMessageCount(int /*count*/) {} - virtual void convertToMUC(MUCType /*mucType*/) {} + + virtual void convertToMUC(MUCType mucType) { + mucType_ = mucType; + } + virtual void setSecurityLabelsError() {} virtual SecurityLabelsCatalog::Item getSelectedSecurityLabel() {return label_;} virtual void setOnline(bool /*online*/) {} virtual void setRosterModel(Roster* roster) { roster_ = roster; } Roster* getRosterModel() { return roster_; } virtual void setTabComplete(TabComplete*) {} void setAckState(const std::string& /*id*/, AckState /*state*/) {} virtual void flash() {} virtual AlertID addAlert(const std::string& /*alertText*/) { return 0; } virtual void removeAlert(const AlertID /*id*/) {} virtual void setCorrectionEnabled(Tristate /*enabled*/) {} virtual void setFileTransferEnabled(Tristate /*enabled*/) {} void setAvailableOccupantActions(const std::vector<OccupantAction>&/* actions*/) {} void setSubject(const std::string& /*subject*/) {} virtual void showRoomConfigurationForm(Form::ref) {} virtual void addMUCInvitation(const std::string& /*senderName*/, const JID& jid, const std::string& /*reason*/, const std::string& /*password*/, bool = true, bool = false, bool = false) { lastMUCInvitationJID_ = jid; } virtual std::string addWhiteboardRequest(bool) {return "";} virtual void setWhiteboardSessionStatus(std::string, const ChatWindow::WhiteboardSessionState){} virtual void setAffiliations(MUCOccupant::Affiliation, const std::vector<JID>&) {} virtual void setAvailableRoomActions(const std::vector<RoomAction> &) {} virtual void setBlockingState(BlockingState) {} virtual void setCanInitiateImpromptuChats(bool supportsImpromptu) { impromptuMUCSupported_ = supportsImpromptu; } @@ -117,33 +121,34 @@ namespace Swift { } } return body; } void resetLastMessages() { lastAddedMessage_ = lastAddedAction_ = lastAddedPresence_ = lastReplacedMessage_ = lastAddedSystemMessage_ = lastReplacedSystemMessage_ = ChatMessage(); lastAddedMessageSenderName_ = lastAddedActionSenderName_ = ""; lastAddedMessageSenderIsSelf_ = lastAddedActionSenderIsSelf_ = false; } std::string name_; ChatMessage lastAddedMessage_; std::string lastAddedMessageSenderName_; bool lastAddedMessageSenderIsSelf_ = false; ChatMessage lastAddedAction_; std::string lastAddedActionSenderName_; bool lastAddedActionSenderIsSelf_ = false; ChatMessage lastAddedPresence_; ChatMessage lastReplacedMessage_; ChatMessage lastAddedSystemMessage_; ChatMessage lastReplacedSystemMessage_; ChatMessage lastAddedErrorMessage_; JID lastMUCInvitationJID_; std::vector<SecurityLabelsCatalog::Item> labels_; bool labelsEnabled_ = false; bool impromptuMUCSupported_ = false; SecurityLabelsCatalog::Item label_; Roster* roster_ = nullptr; std::vector<std::pair<std::string, ReceiptState>> receiptChanges_; + boost::optional<MUCType> mucType_; }; } |