diff options
-rw-r--r-- | Swift/Controllers/ChatsManager.cpp | 38 | ||||
-rw-r--r-- | Swift/Controllers/ChatsManager.h | 4 | ||||
-rw-r--r-- | Swift/Controllers/UnitTest/ChatsManagerTest.cpp | 141 | ||||
-rw-r--r-- | Swift/Controllers/UnitTest/MockChatWindow.h | 3 |
4 files changed, 166 insertions, 20 deletions
diff --git a/Swift/Controllers/ChatsManager.cpp b/Swift/Controllers/ChatsManager.cpp index ef187f4..fe8efb3 100644 --- a/Swift/Controllers/ChatsManager.cpp +++ b/Swift/Controllers/ChatsManager.cpp @@ -1,5 +1,7 @@ #include "Swift/Controllers/ChatsManager.h" +#include <boost/bind.hpp> + #include "Swiften/Client/Client.h" #include "Swift/Controllers/ChatController.h" @@ -23,6 +25,7 @@ ChatsManager::ChatsManager(JID jid, StanzaChannel* stanzaChannel, IQRouter* iqRo avatarManager_ = NULL; serverDiscoInfo_ = serverDiscoInfo; presenceSender_ = presenceSender; + presenceOracle_->onPresenceChange.connect(boost::bind(&ChatsManager::handlePresenceChange, this, _1, _2)); } ChatsManager::~ChatsManager() { @@ -35,6 +38,20 @@ ChatsManager::~ChatsManager() { } +/** + * If a resource goes offline, release bound chatdialog to that resource. + */ +void ChatsManager::handlePresenceChange(boost::shared_ptr<Presence> /*oldPresence*/, boost::shared_ptr<Presence> newPresence) { + if (newPresence->getType() != Presence::Unavailable) return; + JID fullJID(newPresence->getFrom()); + std::map<JID, ChatController*>::iterator it = chatControllers_.find(fullJID); + if (it == chatControllers_.end()) return; + JID bareJID(fullJID.toBare()); + //It doesn't make sense to have two unbound dialogs. + if (chatControllers_.find(bareJID) != chatControllers_.end()) return; + rebindControllerJID(fullJID, bareJID); +} + void ChatsManager::setAvatarManager(AvatarManager* avatarManager) { avatarManager_ = avatarManager; } @@ -83,19 +100,20 @@ void ChatsManager::handleChatRequest(const String &contact) { } ChatController* ChatsManager::getChatController(const JID &contact) { - JID lookupContact(contact); - if (chatControllers_.find(lookupContact) == chatControllers_.end()) { - lookupContact = JID(contact.toBare()); - } - if (chatControllers_.find(lookupContact) == chatControllers_.end()) { - chatControllers_[contact] = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_); - chatControllers_[contact]->setAvailableServerFeatures(serverDiscoInfo_); - lookupContact = contact; + if (chatControllers_.find(contact) == chatControllers_.end()) { + //Need to look for an unboud window to bind first + JID bare(contact.toBare()); + if (chatControllers_.find(bare) != chatControllers_.end()) { + rebindControllerJID(bare, contact); + } else { + chatControllers_[contact] = new ChatController(jid_, stanzaChannel_, iqRouter_, chatWindowFactory_, contact, nickResolver_, presenceOracle_, avatarManager_); + chatControllers_[contact]->setAvailableServerFeatures(serverDiscoInfo_); + } } - return chatControllers_[lookupContact]; + return chatControllers_[contact]; } -void ChatsManager::handleChatControllerJIDChanged(const JID& from, const JID& to) { +void ChatsManager::rebindControllerJID(const JID& from, const JID& to) { chatControllers_[to] = chatControllers_[from]; chatControllers_.erase(from); } diff --git a/Swift/Controllers/ChatsManager.h b/Swift/Controllers/ChatsManager.h index e897e59..260bd1d 100644 --- a/Swift/Controllers/ChatsManager.h +++ b/Swift/Controllers/ChatsManager.h @@ -7,6 +7,7 @@ #include "Swiften/Base/String.h" #include "Swiften/Elements/DiscoInfo.h" #include "Swiften/Elements/Message.h" +#include "Swiften/Elements/Presence.h" #include "Swiften/JID/JID.h" #include "Swiften/MUC/MUCRegistry.h" @@ -33,7 +34,8 @@ namespace Swift { void handleChatRequest(const String& contact); void handleJoinMUCRequest(const JID& muc, const String& nick); private: - void handleChatControllerJIDChanged(const JID& from, const JID& to); + void rebindControllerJID(const JID& from, const JID& to); + void handlePresenceChange(boost::shared_ptr<Presence> oldPresence, boost::shared_ptr<Presence> newPresence); ChatController* getChatController(const JID &contact); virtual bool isMUC(const JID& muc) const; diff --git a/Swift/Controllers/UnitTest/ChatsManagerTest.cpp b/Swift/Controllers/UnitTest/ChatsManagerTest.cpp index 071cd5b..9df244f 100644 --- a/Swift/Controllers/UnitTest/ChatsManagerTest.cpp +++ b/Swift/Controllers/UnitTest/ChatsManagerTest.cpp @@ -27,9 +27,12 @@ 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_SUITE_END(); public: @@ -70,13 +73,41 @@ public: void testFirstOpenWindowIncoming() { JID messageJID("testling@test.com/resource1"); - ChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID).Return(window); boost::shared_ptr<Message> message(new Message()); message->setFrom(messageJID); - message->setBody("This is a legible message."); + String body("This is a legible message. >HEH@)oeueu"); + message->setBody(body); manager_->handleIncomingMessage(message); + CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); + } + + void testSecondOpenWindowIncoming() { + JID messageJID1("testling@test.com/resource1"); + + MockChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1).Return(window1); + + boost::shared_ptr<Message> message1(new Message()); + message1->setFrom(messageJID1); + String body1("This is a legible message. >HEH@)oeueu"); + message1->setBody(body1); + manager_->handleIncomingMessage(message1); + CPPUNIT_ASSERT_EQUAL(body1, window1->lastMessageBody_); + + JID messageJID2("testling@test.com/resource2"); + + MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2).Return(window2); + + boost::shared_ptr<Message> message2(new Message()); + message2->setFrom(messageJID2); + String body2("This is a legible message. .cmaulm.chul"); + message2->setBody(body2); + manager_->handleIncomingMessage(message2); + CPPUNIT_ASSERT_EQUAL(body2, window2->lastMessageBody_); } void testFirstOpenWindowOutgoing() { @@ -99,28 +130,122 @@ public: boost::shared_ptr<Message> message(new Message()); message->setFrom(JID(fullJIDString)); - message->setBody("This is a legible message."); + String body("This is a legible message. mjuga3089gm8G(*>M)@*("); + message->setBody(body); manager_->handleIncomingMessage(message); - /*FIXME: check the message got through. For now, checking that there isn't a new window created is useful enough.*/ - //CPPUNIT_ASSERT_EQUAL(fullJIDString, window->name_); + CPPUNIT_ASSERT_EQUAL(body, window->lastMessageBody_); } void testSecondWindow() { String messageJIDString1("testling1@test.com"); - ChatWindow* window1 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString1)).Return(window1); - manager_->handleChatRequest(messageJIDString1); String messageJIDString2("testling2@test.com"); - ChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(messageJIDString2)).Return(window2); manager_->handleChatRequest(messageJIDString2); } + /** Complete cycle. + Create unbound window. + Bind it. + Unbind it. + Rebind it. + */ + void testUnbindRebind() { + String bareJIDString("testling@test.com"); + String fullJIDString1("testling@test.com/resource1"); + String fullJIDString2("testling@test.com/resource2"); + + MockChatWindow* window = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(JID(bareJIDString)).Return(window); + manager_->handleChatRequest(bareJIDString); + + boost::shared_ptr<Message> message1(new Message()); + message1->setFrom(JID(fullJIDString1)); + String messageBody1("This is a legible message."); + message1->setBody(messageBody1); + manager_->handleIncomingMessage(message1); + CPPUNIT_ASSERT_EQUAL(messageBody1, window->lastMessageBody_); + + boost::shared_ptr<Presence> jid1Online(new Presence()); + jid1Online->setFrom(JID(fullJIDString1)); + boost::shared_ptr<Presence> jid1Offline(new Presence()); + jid1Offline->setFrom(JID(fullJIDString1)); + jid1Offline->setType(Presence::Unavailable); + presenceOracle_->onPresenceChange(jid1Online, jid1Offline); + + boost::shared_ptr<Message> message2(new Message()); + message2->setFrom(JID(fullJIDString2)); + String messageBody2("This is another legible message."); + message2->setBody(messageBody2); + manager_->handleIncomingMessage(message2); + CPPUNIT_ASSERT_EQUAL(messageBody2, window->lastMessageBody_); + } + + /** + 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>(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID1).Return(window1); + + boost::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"); + + MockChatWindow* window2 = new MockChatWindow();//mocks_->InterfaceMock<ChatWindow>(); + mocks_->ExpectCall(chatWindowFactory_, ChatWindowFactory::createChatWindow).With(messageJID2).Return(window2); + + boost::shared_ptr<Message> message2(new Message()); + message2->setFrom(messageJID2); + message2->setBody("This is a legible message2."); + manager_->handleIncomingMessage(message2); + + boost::shared_ptr<Presence> jid1Online(new Presence()); + jid1Online->setFrom(JID(messageJID1)); + boost::shared_ptr<Presence> jid1Offline(new Presence()); + jid1Offline->setFrom(JID(messageJID1)); + jid1Offline->setType(Presence::Unavailable); + presenceOracle_->onPresenceChange(jid1Online, jid1Offline); + + boost::shared_ptr<Presence> jid2Online(new Presence()); + jid2Online->setFrom(JID(messageJID2)); + boost::shared_ptr<Presence> jid2Offline(new Presence()); + jid2Offline->setFrom(JID(messageJID2)); + jid2Offline->setType(Presence::Unavailable); + presenceOracle_->onPresenceChange(jid2Online, jid2Offline); + + JID messageJID3("testling@test.com/resource3"); + + boost::shared_ptr<Message> message3(new Message()); + message3->setFrom(messageJID3); + String body3("This is a legible message3."); + message3->setBody(body3); + manager_->handleIncomingMessage(message3); + CPPUNIT_ASSERT_EQUAL(body3, window1->lastMessageBody_); + + boost::shared_ptr<Message> message2b(new Message()); + message2b->setFrom(messageJID2); + String body2b("This is a legible message2b."); + message2b->setBody(body2b); + manager_->handleIncomingMessage(message2b); + CPPUNIT_ASSERT_EQUAL(body2b, window2->lastMessageBody_); + } private: JID jid_; diff --git a/Swift/Controllers/UnitTest/MockChatWindow.h b/Swift/Controllers/UnitTest/MockChatWindow.h index 015bb9b..2625553 100644 --- a/Swift/Controllers/UnitTest/MockChatWindow.h +++ b/Swift/Controllers/UnitTest/MockChatWindow.h @@ -8,7 +8,7 @@ namespace Swift { MockChatWindow() {}; virtual ~MockChatWindow(); - virtual void addMessage(const String& /*message*/, const String& /*senderName*/, bool /*senderIsSelf*/, const boost::optional<SecurityLabel>& /*label*/, const String& /*avatarPath*/) {}; + virtual void addMessage(const String& message, const String& /*senderName*/, bool /*senderIsSelf*/, const boost::optional<SecurityLabel>& /*label*/, const String& /*avatarPath*/) {lastMessageBody_ = message;}; virtual void addSystemMessage(const String& /*message*/) {}; virtual void addErrorMessage(const String& /*message*/) {}; @@ -29,6 +29,7 @@ namespace Swift { boost::signal<void (const String&)> onSendMessageRequest; String name_; + String lastMessageBody_; std::vector<SecurityLabel> labels_; bool labelsEnabled_; }; |